全文共4248字,预计学习时长9分钟
编写Spring MVC控制器的最佳技巧
本文介绍了编写Spring MVC框架的控制器(controller)的基础技巧和最佳操作。在Spring MVC框架中,编写控制器类通常是为了处理用户提出的请求。
编写完成后,控制器会调用一个业务类来处理业务相关任务,进而重定向客户到逻辑视图名。Springdispatcher servlet会对逻辑视图名进行解析,并渲染结果或输出。这就是一个典型的“请求—响应”的完整流程。

1.使用@controllerstereotype

创建一个能够处理单个或多个请求的控制器类,最简单的方法就是使用@controllerstereotype注解一个类,如:
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
@Controller
publicclassHomeController { @RequestMapping("/") publicString visitHome() {  // do something before returning view name  return"home"; }
}

如上所示,visitHome()方法通过重定向跳转到视图名home来处理应用程序内容路径(/)收到的请求。
注意:只有在Spring配置文件中启用了注解驱动,才能使用@controllerstereotype。
启用注解驱动后,Spring的容器(container)会自动扫描如下包中的类:
带有@controller注解的类会被标记成控制器。由于其简单方便,且不再需要对配置文件中的控制器声明beans,这一方法非常实用。
注意:使用@controller注解可以创建一个多动作控制器类,可同时处理多个不同的请求。如:
@Controller
publicclassMultiActionController {  @RequestMapping("/listUsers")    public ModelAndView listUsers() {   }   @RequestMapping("/saveUser") public ModelAndView saveUser(User user) {   }   @RequestMapping("/deleteUser")   public ModelAndView deleteUser(User user) { }
}
如上所示,有三个处理器(handler)在分别处理三个请求,/listUsers,/saveUser,和/deleteUser。

2.实现控制器接口

在Spring MVC中创建控制器还可以用另一个经典的方法,即对一个类实现Controller接口。如:
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.Controller;
publicclassMainControllerimplements Controller {    @Override  public ModelAndView handleRequest(HttpServletRequest request,   HttpServletResponse response) throws Exception {    System.out.println("Welcome main");   returnnew ModelAndView("main");   }
}

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

3.继承AbstractController类

如果想要轻松控制受支持的HTTP方法、会话和内容缓存,让控制类继承AbstractController类是理想的方法。如:
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.AbstractController;
publicclassBigControllerextends AbstractController {    @Override  protected ModelAndView handleRequestInternal(HttpServletRequest request,    HttpServletResponse response) throws Exception {    System.out.println("You're big!");   returnnew ModelAndView("big");    }
}
上例创建了一个配置了受支持的方法、会话和缓存的单动作控制器,能够在控制器的bean声明中被指明。如:
<beanname="/big"class="net.codejava.spring.BigController">  <propertyname="supportedMethods"value="POST"/>
</bean>
这一配置表明该控制器handler方法仅支持POST方法。了解更多配置(如会话、缓存),参见AbstractController。
SpringMVC还提供了多个支持特定目的的控制器类,包括:
  • AbstractUrlViewController
  • MultiActionController
  • ParameterizableViewController
  • ServletForwardingController
  • ServletWrappingController
  • UrlFilenameViewController

4.为处理器指定URL映射

这是编写控制器类必不可少的一步,旨在处理一个及以上特定请求。Spring MVC提供了@RequestMapping注解,用于指定URL映射。如:
这一步映射了URL模式/login,并用注解或注解类对其进行了处理。@RequestMapping注解用于类上时,类变成了单动作控制器。如:
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
@Controller
@RequestMapping("/hello")
publicclassSingleActionController { @RequestMapping(method = RequestMethod.GET)   publicString sayHello() {   return"hello";    }
}
@RequestMapping注解用于方法上时,则可生成多动作控制器。如:
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
@Controller
publicclassUserController { @RequestMapping("/listUsers")    publicString listUsers() {  return"ListUsers";    }   @RequestMapping("/saveUser") publicString saveUser() {   return"EditUser"; }   @RequestMapping("/deleteUser")   publicString deleteUser() { return"DeleteUser";   }
}
@RequestMapping注解也可用于指定多个URL模式,并用单一方法对其进行处理。如:
此外,该注解还有其他的属性,在一些情况下能发挥作用,如下一小节将讲到的method属性。

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

使用@RequestMapping注解的method属性,可以指定处理器方法支持的HTTP方法(包括GET、POST、PUT等)。如:
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping
import org.springframework.web.bind.annotation.RequestMethod;
@Controller
publicclassLoginController {    @RequestMapping(value = "/login", method = RequestMethod.GET)  publicString viewLogin() {  return"LoginForm";    }   @RequestMapping(value = "/login", method = RequestMethod.POST) publicString doLogin() {    return"Home"; }
}
如上所示,对于同一个URL模式/login,该控制器有两个处理方法。第一个方法用于GET方法,第二个则用于POST方法。
了解更多@RequestMapping注解相关知识,参见@RequestMapping注解。

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

SpringMVC的特征之一,就是可以使用@RequestParam注解将请求参数作为处理器方法的常规参数取回。这是一个将控制器从ServletAPI的HttpServletRequest接口中解耦出来的好方法。
如:
@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类型的参数声明如下:
则Spring会在处理方法中自动将请求参数的值(String类型)转换为指定类型(integer)。
为防止参数名与变量名不同,可将参数实名指定如下:
@RequestParam注解还有另外两个属性,可在一些情况下发挥作用。其中一个属性是required,可指定一个参数是强制参数还是可选参数。如:
这就意味着参数country是可选的,在请求中可略去。当请求中没有参数country时,则变量country为空值。
另一个属性是defaultValue,可在请求参数为空时充当回退值(fallbackvalue)。如:
当方法参数类型为Map<String,String>时,Spring也支持将所有参数作为Map对象。如:
则映射参数包含所有键值对形式的请求参数。了解更多@RequestParam注解相关知识,参见@RequestParam注解。

7.返回模型和视图

处理器方法在处理完业务逻辑后,会返回一个视图,该视图随后由Springdispatcher servlet进行解析。Spring支持handler方法返回String对象或ModelAndView对象。如下所示,handler方法返回了一个String对象,并表示了视图名LoginForm:
@RequestMapping(value = "/login", method = RequestMethod.GET)
public String viewLogin() { return"LoginForm";
}
这是返回视图名最简单的方法。但是如果想要发送其他数据到视图,则必须返回ModelAndView对象。如:
@RequestMapping("/listUsers")
public ModelAndView listUsers() {   List<User> listUser = new ArrayList<>();   // get user list from DAO...    ModelAndView modelView = new ModelAndView("UserList");   modelView.addObject("listUser", listUser);    return modelView;
}
如上所示,该处理器方法返回了一个ModelAndView对象,该对象视图名为UserList,并有一个可用在视图中的User对象集。
Spring是一个非常灵活的框架,支持将ModelAndView对象声明为处理器方法的参数,而无需再重新创建一个。因此,上例可以重写为:
@RequestMapping("/listUsers")
public ModelAndView listUsers(ModelAndView modelView) { List<User> listUser = new ArrayList<>();   // get user list from DAO...    modelView.setViewName("UserList");    modelView.addObject("listUser", listUser);    return modelView;
}
了解更多ModelAndView类相关知识,参见ModelAndView类。

8.将对象放入模型

在MVC架构的应用程序中,控制器将数据输入到模型中,该模型则被用在视图中。从上一节中的举例中可以看到,ModelAndView类的addObject()用于将对象以名值对的形式放入模型中:
modelView.addObject("listUser", listUser);
modelView.addObject("siteName", newString("CodeJava.net"));
modelView.addObject("users", 1200000);
Spring同样支持声明处理器方法中的Map类型参数。Spring使用这一映射存储将放入模型的对象。如:
@RequestMapping(method = RequestMethod.GET)
publicStringviewStats(Map<String, Object> model) {    model.put("siteName", "CodeJava.net");  model.put("pageviews", 320000);   return"Stats";
}
这一方法比使用ModelAndView对象更加简单。Spring支持用户灵活选择Map对象和ModelAndView对象。

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

当条件允许时,只需在URL前加上redirect:/就可将用户重定向跳转到另一个URL。如:
// check login status....
if (!isLogin) { returnnew ModelAndView("redirect:/login");
}
// return a list of Users
在上述代码中,没有登陆的用户将会跳转到/loginURL。

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

Spring中的@ModelAttribute注解支持将表单字段绑定到表单返回对象,BingingRequest接口则支持验证表单字段。这使得处理表单提交变得非常简单。一个处理和验证表单数据的典型处理器方法的代码如下所示:
@Controller
publicclassRegistrationController { @RequestMapping(value = "/doRegister", method = RequestMethod.POST)    publicString doRegister(    @ModelAttribute("userForm") User user, BindingResult bindingResult) {    if (bindingResult.hasErrors()) {    // form validation error    } else {    // form input is OK }   // process registration...  return"Success";  }
}
了解更多@ModelAttribute注解和BindingResult接口相关知识,参见Spring官方文档:
  • Using @ModelAttribute on a method argument
  • Using @ModelAttribute on a method
  • Interface BindingResult

11.处理文件上传

Spring支持自动将上传数据绑定到CommonsMultiparFile数组对象,这使得在处理器方法中处理文件上传变得非常简单。Spring使用Apache CommonsFileUpload作为深层多部分解析器(underlyingmultipart resolver)。
简单上传用户文件的代码如下所示:
@RequestMapping(value = "/uploadFiles", method = RequestMethod.POST)
publicStringhandleFileUpload(   @RequestParam CommonsMultipartFile[] fileUpload) throws Exception {    for (CommonsMultipartFile aFile : fileUpload){  // stores the uploaded file aFile.transferTo(new File(aFile.getOriginalFilename()));    }   return"Success";
}
了解Spring MVC处理文件上传的完整方法,参见Spring MVC 文件上传教程。

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

为了让控制器将业务逻辑处理委托到相关业务类,可以使用@Autowired注解,让Spring自动将业务类的实际实现注入到控制器中。如:
@Controller
publicclassUserController { @Autowired private UserDAO userDAO;    publicString listUser() {   // handler method to list all users userDAO.list(); }   publicString saveUser(User user) {  // handler method to save/update a user userDAO.save(user); }   publicString deleteUser(User user) {    // handler method to delete a user  userDAO.delete(user);   }   publicString getUser(int userId) {  // handler method to get a user userDAO.get(userId);    }
}
本例中所有与用户管理相关的业务逻辑都由UserDAO接口的实现提供。如:
interfaceUserDAO {  List<User> list();    void save(User user);   void checkLogin(User user);
}
如上所示,使用@Autowired注解使处理器方法可以将任务委托到业务类:
了解更多@Autowired注解相关知识,参见Annotation TypeAutowired。

13.获取HttpServletRequest和HttpServletResponse

有些情况要求在处理器方法中直接获取HttpServletRequest或HttpServletResponse对象。在Spring灵活的框架中,仅需给处理器方法加上一个相关参数就可以完成此任务。如:
@RequestMapping("/download")
publicStringdoDownloadFile( HttpServletRequest request, HttpServletResponse response) { // access the request   // access the response  return"DownloadPage";
}
Spring支持检测并自动将HttpServletRequest和HttpServletResponse对象注入到方法中。这样一来,就可以直接获取请求和响应,如获取InputStream、OutputStream或返回特定的HTTP代码。

14.遵守单一职责原则

在Spring MVC中设计和编写控制器时,应遵循以下两个非常实用的操作:
  • 不要用控制器类来执行业务逻辑,应该用控制器类将业务处理委托到相关的业务类。这可以保证控制器专注于其指定职责,即控制应用程序的工作流。如:
@Controller
publicclassUserController { @Autowired private UserDAO userDAO;    publicString listUser() {   // handler method to list all users userDAO.list(); }   publicString saveUser(User user) {  // handler method to save/update a user userDAO.save(user); }   publicString deleteUser(User user) {    // handler method to delete a user  userDAO.delete(user);   }   publicString getUser(int userId) {  // handler method to get a user userDAO.get(userId);    }
}
  • 给每个业务领域创建一个独立的控制器。如,用UserController控制用户管理的工作流,用OrderController控制订单处理的工作流,等等:
@Controller
publicclassUserController {
}
@Controller
publicclassProductController {
}
@Controller
publicclassOrderController {
}
@Controller
publicclassPaymentController {
}
以上就是本文全部内容,希望这14个小技巧可以帮助读者准确且高效地编写Spring MVC中的控制器类代码。有任何技巧分享或建议,欢迎在评论区留言。
推荐阅读专题
留言 点赞 发个朋友圈
我们一起分享AI学习与发展的干货
编译组:王努铱、莫菲菲
相关链接:
https://dzone.com/articles/14-tips-for-writing-spring-mvc-controller
如需转载,请后台留言,遵守转载规范
推荐文章阅读

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

  1. 编写 Spring MVC 控制器的 14 个技巧

    欢迎关注方志朋的博客,回复"666"获面试宝典 通常,在Spring MVC中,我们编写一个控制器类来处理来自客户端的请求.然后,控制器调用业务类来处理与业务相关的任务,然后将客户 ...

  2. requestmapping默认是get还是post_编写Spring MVC控制器的14个技巧,你今天get到了吗?...

    Java面试笔试面经.Java技术每天学习一点 公众号Java面试 关注我不迷路 原文:http://dzone.com/articles/14-tips-for-writing-spring-mvc ...

  3. 换一种方式编写 Spring MVC 接口

    点击上方蓝色"程序猿DD",选择"设为星标" 回复"资源"获取独家整理的学习资料! 1. 前言 通常我们编写 Spring MVC 接口的范 ...

  4. Spring MVC控制器的单元测试:“普通”控制器

    本教程的第一部分描述了如何配置使用Spring MVC Test框架的单元测试. 现在是时候动手做,学习如何为"常规"控制器编写单元测试了. 显而易见的下一个问题是: 什么是普通控 ...

  5. Spring MVC控制器的单元测试:REST API

    Spring MVC提供了一种创建REST API的简便方法. 但是,为这些API编写全面而快速的单元测试一直很麻烦. Spring MVC测试框架的发布使我们可以编写可读,全面且快速的单元测试. 这 ...

  6. Spring MVC控制器的单元测试:配置

    传统上,为Spring MVC控制器编写单元测试既简单又成问题. 尽管编写调用控制器方法的单元测试非常简单,但问题是这些单元测试不够全面. 例如,我们不能仅通过调用已测试的控制器方法来测试控制器映射, ...

  7. Spring MVC控制器JUnit测试

    JUnit测试Spring MVC控制器并非易事 . 但是最近,一个新项目 (即将在Spring推出)提供了新工具来简化此工作. 这篇文章说明了如何通过JUnit测试来测试一个简单的控制器. 该代码是 ...

  8. Spring Controller – Spring MVC控制器

    Spring Controller annotation is a specialization of @Component annotation. Spring Controller annotat ...

  9. Java项目:(小程序)前台+后台相结合在线点餐系统(spring+spring mvc+mybatis+layui+微信小程)

    源码获取:博客首页 "资源" 里下载! 一.项目简述 本系统功能包括: 1 .微信小程序扫码点单 2 .微信小程序外卖点单 3 .后台可对微信小程序主页进行自定义(如颜色.布局. ...

最新文章

  1. CocosCreator TOUCH_MOVE事件
  2. 面试收集--卡特兰数(Catalan数)应用
  3. VIM入门必读(转)
  4. BAT无线工程师面试流程详细解析
  5. C#中数据类型的安全转换(is,as)
  6. c现代方法8.2节 deal.c程序自己编写
  7. postman断言之常用函数
  8. android 空白占位符,android textview空格占位符以及一些其他占位符汇总
  9. Windows环境下通过lynx查看隐藏链接识别黑链方法
  10. 也谈谈Javascript中的几个怪异特性(上)
  11. 日志平台查询异常,没有打印异常信息
  12. 数据规划(python实现数独自动算法之三)
  13. 关于正则表达式的补充(贪婪和懒惰)
  14. 干掉Google Base? 微软欲推Fremont服务 (转自donews.com)
  15. Linux测试主机之间连通性和端口是否开放的方法
  16. 电子签名屏什么牌子好
  17. win10共享打印机链接失败错误代码0x0000011b
  18. 【HbuilderX+微信小程序开发者工具解决报错问题】
  19. Linux下使用云笔记及OneNote
  20. 数据分析 之 渠道质量分析

热门文章

  1. “悟空”来也!未上市就签几亿订单,看优必选机器人梦想的超级路径
  2. Cartographer源码阅读
  3. 找工作,要做就做最好的自己,大平台去闯闯,一定不要让未来后悔!~附简历...
  4. 什么样的程序员才是牛逼的程序员?
  5. c语言牛逼,C语言牛逼代码
  6. 虚拟数字人市场现状研究分析-
  7. python的os删除文件或者文件夹
  8. K8S的 CNI 详细原理以及解释
  9. python 社区-谈谈Python社区7种治理方案
  10. ECCV2022细粒度图像检索SEMICON代码学习记录