Chúng ta vẫn thường dùng annotation @Controller để phát triển các ứng dụng Spring MVC nhưng kể từ version 4.x, Spring giới thiệu một annotation khác tên là @RestController. Vậy mục đích của annotation @RestController là gì? Sự khác nhau giữa annotation @Controller và annotation @RestController như thế nào? Trong bài viết này, chúng ta hãy cùng tìm hiểu nhé các bạn!
Đầu tiên, mình sẽ tạo mới một Maven project để làm ví dụ:
Bạn nào chưa biết cách tạo thì làm theo hướng dẫn của bài viết này nhé các bạn!
Các dependencies mặc định mình đã thay đổi version như sau:
1 2 3 4 5 6 |
<properties> <java-version>1.8</java-version> <org.springframework-version>5.0.0.RELEASE</org.springframework-version> <org.aspectj-version>1.8.11</org.aspectj-version> <org.slf4j-version>1.7.25</org.slf4j-version> </properties> |
Để chạy project này, mình sẽ sử dụng Maven Jetty Pluggin:
1 2 3 4 5 |
<plugin> <groupId>org.eclipse.jetty</groupId> <artifactId>jetty-maven-plugin</artifactId> <version>9.4.7.v20170914</version> </plugin> |
OK, bắt đầu thôi các bạn!
Như các bạn đã biết, trong Spring MVC, Controller là class sẽ chịu trách nhiệm prepare Model và chọn View để hiển thị data trong Model lên View. Trong ví dụ mình vừa tạo, chúng ta có class HomeController là một Controller class.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 |
package com.huongdanjava.springmvc; import java.text.DateFormat; import java.util.Date; import java.util.Locale; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; /** * Handles requests for the application home page. */ @Controller public class HomeController { private static final Logger logger = LoggerFactory.getLogger(HomeController.class); /** * Simply selects the home view to render by returning its name. */ @RequestMapping(value = "/", method = RequestMethod.GET) public String home(Locale locale, Model model) { logger.info("Welcome home! The client locale is {}.", locale); Date date = new Date(); DateFormat dateFormat = DateFormat.getDateTimeInstance(DateFormat.LONG, DateFormat.LONG, locale); String formattedDate = dateFormat.format(date); model.addAttribute("serverTime", formattedDate ); return "home"; } } |
Nó thêm data “serverTime” vào Model và chọn View là tập tin home.jsp nằm trong thư mục /src/main/webapp/WEB-INF/views để render giao diện.
Kết quả sẽ như sau:
Bên cạnh nhiệm vụ prepare Model và chọn View này, Controller class còn có thể ghi dữ liệu trực tiếp vào luồng response mà không cần chọn view gì hết. Để làm được điều này, trong phương thức handle request, chúng ta phải thêm một annotation nữa là @ResponseBody.
Ví dụ như giờ mình thêm annotation @ResponseBody cho phương thức home() trong class HomeController như sau:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
/** * Simply selects the home view to render by returning its name. */ @RequestMapping(value = "/", method = RequestMethod.GET) @ResponseBody public String home(Locale locale, Model model) { logger.info("Welcome home! The client locale is {}.", locale); Date date = new Date(); DateFormat dateFormat = DateFormat.getDateTimeInstance(DateFormat.LONG, DateFormat.LONG, locale); String formattedDate = dateFormat.format(date); model.addAttribute("serverTime", formattedDate ); return "home"; } |
thì khi chạy các bạn sẽ không còn thấy kết quả như trước nữa. Thay vì nội dung của tập tin home.jsp với Model data, kết quả lúc này chỉ vỏn vẹn là “home” như phần return trong phương thức home().
Đây là những gì mà chúng ta thường làm để phát triển các ứng dụng RESTful Web Service phải không các bạn? Chúng ta chỉ return data ở các định dạng như JSON, XML,… ngoài ra thì không cần View gì cả.
Để thuận tiện cho việc phát triển các ứng dụng RESTful Web Service, Spring đã giới thiệu thêm annotation @RestController từ version 4.x. Với annotation @RestController thay thế cho annotation @Controller, chúng ta không cần phải khai báo annotation @ResponseBody nữa.
Bây giờ nếu mình thay thế annotation @Controller bằng annotation @RestController và remove annotation @ResponseBody trong phương thức home() đi:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 |
package com.huongdanjava.springmvc; import java.text.DateFormat; import java.util.Date; import java.util.Locale; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.ui.Model; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.RestController; /** * Handles requests for the application home page. */ @RestController public class HomeController { private static final Logger logger = LoggerFactory.getLogger(HomeController.class); /** * Simply selects the home view to render by returning its name. */ @RequestMapping(value = "/", method = RequestMethod.GET) public String home(Locale locale, Model model) { logger.info("Welcome home! The client locale is {}.", locale); Date date = new Date(); DateFormat dateFormat = DateFormat.getDateTimeInstance(DateFormat.LONG, DateFormat.LONG, locale); String formattedDate = dateFormat.format(date); model.addAttribute("serverTime", formattedDate ); return "home"; } } |
thì kết quả vẫn như vậy.
Nếu các bạn xem code của annotation @RestController, các bạn sẽ thấy rõ sự combine này.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 |
/* * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.springframework.web.bind.annotation; import java.lang.annotation.Documented; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; import org.springframework.stereotype.Controller; /** * A convenience annotation that is itself annotated with * {@link Controller @Controller} and {@link ResponseBody @ResponseBody}. * <p> * Types that carry this annotation are treated as controllers where * {@link RequestMapping @RequestMapping} methods assume * {@link ResponseBody @ResponseBody} semantics by default. * * <p><b>NOTE:</b> {@code @RestController} is processed if an appropriate * {@code HandlerMapping}-{@code HandlerAdapter} pair is configured such as the * {@code RequestMappingHandlerMapping}-{@code RequestMappingHandlerAdapter} * pair which are the default in the MVC Java config and the MVC namespace. * * @author Rossen Stoyanchev * @author Sam Brannen * @since 4.0 */ @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented @Controller @ResponseBody public @interface RestController { /** * The value may indicate a suggestion for a logical component name, * to be turned into a Spring bean in case of an autodetected component. * @return the suggested component name, if any * @since 4.0.1 */ String value() default ""; } |
Thái Thiên
Bài viêt rất hay và dễ hiểu. Thanks bạn
Hoàng
Anh có thể làm 1 bài hướng dẫn về restful webservice kết hợp giữa Spring boot và Angular hay react lại với nhau được ko ạ?
Khanh Nguyen
OK bạn, để có thời gian mình viết nhé!
X981
Như vậy @RestController là phục vụ cho restful webservice đúng không bạn.
Khanh Nguyen
Đúng rồi đó bạn!