|
|
51CTO旗下网站
|
|
移动端

吐血整理!14个编写Spring MVC控制器的实用小技巧

本文介绍了编写Spring MVC框架的控制器(controller)的基础技巧和最佳操作。在Spring MVC框架中,编写控制器类通常是为了处理用户提出的请求。

作者:读芯术来源:读芯术|2019-11-17 22:58

本文介绍了编写Spring MVC框架的控制器(controller)的基础技巧和最佳操作。在Spring MVC框架中,编写控制器类通常是为了处理用户提出的请求。

编写完成后,控制器会调用一个业务类来处理业务相关任务,进而重定向客户到逻辑视图名。Springdispatcher servlet会对逻辑视图名进行解析,并渲染结果或输出。这就是一个典型的“请求—响应”的完整流程。

1. 使用@controllerstereotype

创建一个能够处理单个或多个请求的控制器类,最简单的方法就是使用@controllerstereotype注解一个类,如:

  1. import org.springframework.stereotype.Controller; 
  2. import org.springframework.web.bind.annotation.RequestMapping; 
  3. @Controller 
  4. publicclassHomeController { 
  5.     @RequestMapping("/") 
  6.     publicString visitHome() { 
  7.         // do something before returning view name 
  8.         return"home"; 
  9.     } 

如上所示,visitHome()方法通过重定向跳转到视图名home来处理应用程序内容路径(/)收到的请求。 注意:只有在Spring配置文件中启用了注解驱动,才能使用@controllerstereotype。

  1. <annotation-driven/> 

启用注解驱动后,Spring的容器(container)会自动扫描如下包中的类:

  1. <context:component-scanbase-packagecontext:component-scanbase-package="net.codejava.spring"/> 

带有@controller注解的类会被标记成控制器。由于其简单方便,且不再需要对配置文件中的控制器声明beans,这一方法非常实用。 注意:使用@controller注解可以创建一个多动作控制器类,可同时处理多个不同的请求。如:

  1. @Controller 
  2. publicclassMultiActionController { 
  3.     @RequestMapping("/listUsers") 
  4.     public ModelAndView listUsers() { 
  5.     } 
  6.     @RequestMapping("/saveUser") 
  7.     public ModelAndView saveUser(User user) { 
  8.     } 
  9.     @RequestMapping("/deleteUser") 
  10.     public ModelAndView deleteUser(User user) { 
  11.     } 

如上所示,有三个处理器(handler)在分别处理三个请求,/listUsers,/saveUser,和/deleteUser。

2. 实现控制器接口

在Spring MVC中创建控制器还可以用另一个经典的方法,即对一个类实现Controller接口。如:

  1. import javax.servlet.http.HttpServletRequest; 
  2. import javax.servlet.http.HttpServletResponse; 
  3. import org.springframework.web.servlet.ModelAndView; 
  4. import org.springframework.web.servlet.mvc.Controller; 
  5. publicclassMainControllerimplements Controller { 
  6.     @Override 
  7.     public ModelAndView handleRequest(HttpServletRequest request, 
  8.             HttpServletResponse response) throws Exception { 
  9.         System.out.println("Welcome main"); 
  10.         returnnew ModelAndView("main"); 
  11.     } 

实现类必须重写handleRequest()方法(当收到相匹配的请求时,Spring dispatcher servlet会调用handleRequest)。由该控制器处理的请求URL模式在Spring的内容配置文件中的定义如下:

  1. <beannamebeanname="/main"class="net.codejava.spring.MainController"/> 

这一方法的缺点在于其控制类无法同时处理多个请求URL。

3. 继承AbstractController类

如果想要轻松控制受支持的HTTP方法、会话和内容缓存,让控制类继承AbstractController类是理想的方法。如:

  1. import javax.servlet.http.HttpServletRequest; 
  2. import javax.servlet.http.HttpServletResponse; 
  3. import org.springframework.web.servlet.ModelAndView; 
  4. import org.springframework.web.servlet.mvc.AbstractController; 
  5. publicclassBigControllerextends AbstractController { 
  6.     @Override 
  7.     protected ModelAndView handleRequestInternal(HttpServletRequest request, 
  8.             HttpServletResponse response) throws Exception { 
  9.         System.out.println("You're big!"); 
  10.         returnnew ModelAndView("big"); 
  11.     } 

上例创建了一个配置了受支持的方法、会话和缓存的单动作控制器,能够在控制器的bean声明中被指明。如:

  1. <beannamebeanname="/big"class="net.codejava.spring.BigController"> 
  2.     <propertynamepropertyname="supportedMethods"value="POST"/> 
  3. </bean> 

这一配置表明该控制器handler方法仅支持POST方法。了解更多配置(如会话、缓存),参见AbstractController。 SpringMVC还提供了多个支持特定目的的控制器类,包括:

  • AbstractUrlViewController
  • MultiActionController
  • ParameterizableViewController
  • ServletForwardingController
  • ServletWrappingController
  • UrlFilenameViewController

4. 为处理器指定URL映射

这是编写控制器类必不可少的一步,旨在处理一个及以上特定请求。Spring MVC提供了@RequestMapping注解,用于指定URL映射。如:

  1. @RequestMapping("/login") 

这一步映射了URL模式/login,并用注解或注解类对其进行了处理。@RequestMapping注解用于类上时,类变成了单动作控制器。如:

  1. import org.springframework.stereotype.Controller; 
  2. import org.springframework.web.bind.annotation.RequestMapping; 
  3. import org.springframework.web.bind.annotation.RequestMethod; 
  4. @Controller 
  5. @RequestMapping("/hello") 
  6. publicclassSingleActionController { 
  7.     @RequestMapping(method = RequestMethod.GET) 
  8.     publicString sayHello() { 
  9.         return"hello"; 
  10.     } 

@RequestMapping注解用于方法上时,则可生成多动作控制器。如:

  1. import org.springframework.stereotype.Controller; 
  2. import org.springframework.web.bind.annotation.RequestMapping; 
  3. @Controller 
  4. publicclassUserController { 
  5.     @RequestMapping("/listUsers") 
  6.     publicString listUsers() { 
  7.         return"ListUsers"; 
  8.     } 
  9.     @RequestMapping("/saveUser") 
  10.     publicString saveUser() { 
  11.         return"EditUser"; 
  12.     } 
  13.     @RequestMapping("/deleteUser") 
  14.     publicString deleteUser() { 
  15.         return"DeleteUser"; 
  16.     } 

@RequestMapping注解也可用于指定多个URL模式,并用单一方法对其进行处理。如:

  1. @RequestMapping({"/hello", "/hi", "/greetings"}) 

此外,该注解还有其他的属性,在一些情况下能发挥作用,如下一小节将讲到的method属性。

5. 为处理器方法指定HTTP请求方法

使用@RequestMapping注解的method属性,可以指定处理器方法支持的HTTP方法(包括GET、POST、PUT等)。如:

  1. import org.springframework.stereotype.Controller; 
  2. import org.springframework.web.bind.annotation.RequestMapping 
  3. import org.springframework.web.bind.annotation.RequestMethod; 
  4. @Controller 
  5. publicclassLoginController { 
  6.     @RequestMapping(value = "/login"method = RequestMethod.GET) 
  7.     publicString viewLogin() { 
  8.         return"LoginForm"; 
  9.     } 
  10.     @RequestMapping(value = "/login"method = RequestMethod.POST) 
  11.     publicString doLogin() { 
  12.         return"Home"; 
  13.     } 

如上所示,对于同一个URL模式/login,该控制器有两个处理方法。第一个方法用于GET方法,第二个则用于POST方法。 了解更多@RequestMapping注解相关知识,参见@RequestMapping注解。

6. 将请求参数映射至处理器方法

SpringMVC的特征之一,就是可以使用@RequestParam注解将请求参数作为处理器方法的常规参数取回。这是一个将控制器从ServletAPI的HttpServletRequest接口中解耦出来的好方法。 如:

  1. @RequestMapping(value = "/login"method = RequestMethod.POSTpublic String doLogin(@RequestParamString username @RequestParamString password) {} 

Spring将方法参数用户名及密码和命名相同的HTTP请求参数绑定到一起。这也就意味着可用如下方式调用一个URL(以GET请求方法为例):

http://localhost:8080/spring/login?username=scott&password=tiger 类型转换也自动完成了。如果对一个integer类型的参数声明如下:

  1. @RequestParamint securityNumber 

则Spring会在处理方法中自动将请求参数的值(String类型)转换为指定类型(integer)。 为防止参数名与变量名不同,可将参数实名指定如下:

  1. @RequestParam("SSN") int securityNumber 

@RequestParam注解还有另外两个属性,可在一些情况下发挥作用。其中一个属性是required,可指定一个参数是强制参数还是可选参数。如:

  1. @RequestParam(required = false) String country 

这就意味着参数country是可选的,在请求中可略去。当请求中没有参数country时,则变量country为空值。 另一个属性是defaultValue,可在请求参数为空时充当回退值(fallbackvalue)。如:

  1. @RequestParam(defaultValue = "18") int age 

当方法参数类型为Map

  1. doLogin(@RequestParam Map<String, String> params) 

则映射参数包含所有键值对形式的请求参数。了解更多@RequestParam注解相关知识,参见@RequestParam注解。

7. 返回模型和视图

处理器方法在处理完业务逻辑后,会返回一个视图,该视图随后由Springdispatcher servlet进行解析。Spring支持handler方法返回String对象或ModelAndView对象。如下所示,handler方法返回了一个String对象,并表示了视图名LoginForm:

  1. @RequestMapping(value = "/login"method = RequestMethod.GET) 
  2. public String viewLogin() { 
  3.     return"LoginForm"; 

这是返回视图名最简单的方法。但是如果想要发送其他数据到视图,则必须返回ModelAndView对象。如:

  1. @RequestMapping("/listUsers") 
  2. public ModelAndView listUsers() { 
  3.     List<User> listUser = new ArrayList<>(); 
  4.     // get user list from DAO... 
  5.     ModelAndView modelView = new ModelAndView("UserList"); 
  6.     modelView.addObject("listUser", listUser); 
  7.     return modelView; 

如上所示,该处理器方法返回了一个ModelAndView对象,该对象视图名为UserList,并有一个可用在视图中的User对象集。 Spring是一个非常灵活的框架,支持将ModelAndView对象声明为处理器方法的参数,而无需再重新创建一个。因此,上例可以重写为:

  1. @RequestMapping("/listUsers") 
  2. public ModelAndView listUsers(ModelAndView modelView) { 
  3.     List<User> listUser = new ArrayList<>(); 
  4.     // get user list from DAO... 
  5.     modelView.setViewName("UserList"); 
  6.     modelView.addObject("listUser", listUser); 
  7.     return modelView; 

了解更多ModelAndView类相关知识,参见ModelAndView类。

8. 将对象放入模型

在MVC架构的应用程序中,控制器将数据输入到模型中,该模型则被用在视图中。从上一节中的举例中可以看到,ModelAndView类的addObject()用于将对象以名值对的形式放入模型中:

  1. modelView.addObject("listUser", listUser); 
  2. modelView.addObject("siteName", newString("CodeJava.net")); 
  3. modelView.addObject("users", 1200000); 

Spring同样支持声明处理器方法中的Map类型参数。Spring使用这一映射存储将放入模型的对象。如:

  1. @RequestMapping(method = RequestMethod.GET) 
  2. publicStringviewStats(Map<String, Object> model) { 
  3.     model.put("siteName", "CodeJava.net"); 
  4.     model.put("pageviews", 320000); 
  5.     return"Stats"; 

这一方法比使用ModelAndView对象更加简单。Spring支持用户灵活选择Map对象和ModelAndView对象。

9. 处理器方法中的重定向

当条件允许时,只需在URL前加上redirect:/就可将用户重定向跳转到另一个URL。如:

  1. // check login status.... 
  2. if (!isLogin) { 
  3.     returnnew ModelAndView("redirect:/login"); 
  4. // return a list of Users 

在上述代码中,没有登陆的用户将会跳转到/loginURL。

10. 处理表单提交和表单验证

Spring中的@ModelAttribute注解支持将表单字段绑定到表单返回对象,BingingRequest接口则支持验证表单字段。这使得处理表单提交变得非常简单。一个处理和验证表单数据的典型处理器方法的代码如下所示:

  1. @Controller 
  2. publicclassRegistrationController { 
  3.     @RequestMapping(value = "/doRegister"method = RequestMethod.POST) 
  4.     publicString doRegister( 
  5.         @ModelAttribute("userForm") User user, BindingResult bindingResult) { 
  6.         if (bindingResult.hasErrors()) { 
  7.             // form validation error 
  8.         } else { 
  9.             // form input is OK 
  10.         } 
  11.         // process registration... 
  12.         return"Success"; 
  13.     } 

了解更多@ModelAttribute注解和BindingResult接口相关知识,参见Spring官方文档:

  • Using @ModelAttribute on a method argument
  • Using @ModelAttribute on a method
  • Interface BindingResult

11. 处理文件上传

Spring支持自动将上传数据绑定到CommonsMultiparFile数组对象,这使得在处理器方法中处理文件上传变得非常简单。Spring使用Apache CommonsFileUpload作为深层多部分解析器(underlyingmultipart resolver)。 简单上传用户文件的代码如下所示:

  1. @RequestMapping(value = "/uploadFiles"method = RequestMethod.POST) 
  2. publicStringhandleFileUpload( 
  3.         @RequestParam CommonsMultipartFile[] fileUpload) throws Exception { 
  4.     for (CommonsMultipartFile aFile : fileUpload){ 
  5.         // stores the uploaded file 
  6.         aFile.transferTo(new File(aFile.getOriginalFilename())); 
  7.     } 
  8.     return"Success"; 

了解Spring MVC处理文件上传的完整方法,参见Spring MVC 文件上传教程。

12. 在处理器中自动注入业务类

为了让控制器将业务逻辑处理委托到相关业务类,可以使用@Autowired注解,让Spring自动将业务类的实际实现注入到控制器中。如:

  1. @Controller 
  2. publicclassUserController { 
  3.     @Autowired 
  4.     private UserDAO userDAO; 
  5.     publicString listUser() { 
  6.         // handler method to list all users 
  7.         userDAO.list(); 
  8.     } 
  9.     publicString saveUser(User user) { 
  10.         // handler method to save/update a user 
  11.         userDAO.save(user); 
  12.     } 
  13.     publicString deleteUser(User user) { 
  14.         // handler method to delete a user 
  15.         userDAO.delete(user); 
  16.     } 
  17.     publicString getUser(int userId) { 
  18.         // handler method to get a user 
  19.         userDAO.get(userId); 
  20.     } 

本例中所有与用户管理相关的业务逻辑都由UserDAO接口的实现提供。如:

  1. interfaceUserDAO { 
  2.     List<User> list(); 
  3.     void save(User user); 
  4.     void checkLogin(User user); 

如上所示,使用@Autowired注解使处理器方法可以将任务委托到业务类:

  1. List<User> listUser = userDAO.list(); 

了解更多@Autowired注解相关知识,参见Annotation TypeAutowired。

13. 获取HttpServletRequest和HttpServletResponse

有些情况要求在处理器方法中直接获取HttpServletRequest或HttpServletResponse对象。在Spring灵活的框架中,仅需给处理器方法加上一个相关参数就可以完成此任务。如:

  1. @RequestMapping("/download") 
  2. publicStringdoDownloadFile( 
  3.         HttpServletRequest request, HttpServletResponse response) { 
  4.     // access the request 
  5.     // access the response 
  6.     return"DownloadPage"; 

Spring支持检测并自动将HttpServletRequest和HttpServletResponse对象注入到方法中。这样一来,就可以直接获取请求和响应,如获取InputStream、OutputStream或返回特定的HTTP代码。

14. 遵守单一职责原则

在Spring MVC中设计和编写控制器时,应遵循以下两个非常实用的操作:

  • 不要用控制器类来执行业务逻辑,应该用控制器类将业务处理委托到相关的业务类。这可以保证控制器专注于其指定职责,即控制应用程序的工作流。如:
    1. @Controller 
    2. publicclassUserController { 
    3.     @Autowired 
    4.     private UserDAO userDAO; 
    5.     publicString listUser() { 
    6.         // handler method to list all users 
    7.         userDAO.list(); 
    8.     } 
    9.     publicString saveUser(User user) { 
    10.         // handler method to save/update a user 
    11.         userDAO.save(user); 
    12.     } 
    13.     publicString deleteUser(User user) { 
    14.         // handler method to delete a user 
    15.         userDAO.delete(user); 
    16.     } 
    17.     publicString getUser(int userId) { 
    18.         // handler method to get a user 
    19.         userDAO.get(userId); 
    20.     } 
  • 给每个业务领域创建一个独立的控制器。如,用UserController控制用户管理的工作流,用OrderController控制订单处理的工作流,等等:
    1. @Controller 
    2. publicclassUserController { 
    3. @Controller 
    4. publicclassProductController { 
    5. @Controller 
    6. publicclassOrderController { 
    7. @Controller 
    8. publicclassPaymentController { 

以上就是本文全部内容,希望这14个小技巧可以帮助读者准确且高效地编写Spring MVC中的控制器类代码。

【编辑推荐】

  1. 开发人员花在实际编写代码上的时间有多少?
  2. 可以编写代码的代码:代码生成的利与弊
  3. 6个前端挑战性代码,你会编写吗?
【责任编辑:赵宁宁 TEL:(010)68476606】

点赞 0
分享:
大家都在看
猜你喜欢

订阅专栏+更多

骨干网与数据中心建设案例

骨干网与数据中心建设案例

高级网工必会
共20章 | 捷哥CCIE

384人订阅学习

中间件安全防护攻略

中间件安全防护攻略

4类安全防护
共4章 | hack_man

137人订阅学习

CentOS 8 全新学习术

CentOS 8 全新学习术

CentOS 8 正式发布
共16章 | UbuntuServer

286人订阅学习

视频课程+更多

强哥带你精通zabbix监控

强哥带你精通zabbix监控

讲师:周玉强20386人学习过

OCP培训 Oracle 12c/18c/19c OCP认证实战培训视频【会员2折秒杀】

OCP培训 Oracle 12c/18c/19c OCP认证实战培训

讲师:风哥25500人学习过

小白轻松学Redis 4.x NoSQL数据库

小白轻松学Redis 4.x NoSQL数据库

讲师:齐毅37881人学习过

读 书 +更多

SQL Server 2005实现与维护(MCTS教程)

本书是微软认证技术专家(MCTS) 70-431考试的专用教材,全书共21章,围绕着考查目标,通过详尽的描述、大量课程和课后测试,全面介绍了SQL S...

订阅51CTO邮刊

点击这里查看样刊

订阅51CTO邮刊

51CTO服务号

51CTO官微