14个编写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中的控制器类代码。希望对大家的学习有所帮助,也希望大家多多支持我们。

(0)

相关推荐

  • 详解springMVC—三种控制器controller

    在springmvc中提供了三种controller的配置,1.针对不需要controller代码的,也就是只起到跳转页面的作用.2.可以接受实体类型的controller.3.可以接受表单数据的controller,它只允许POST提交,在配置文件中需要指定提交FORM,请求成功的FORM. 1.直接转发到页面,不需要添加controller代码. <bean id="toLogin" name="/toLogin.do" class="org.s

  • 编写Spring MVC控制器的14个技巧(小结)

    通常,在Spring MVC中,我们编写一个控制器类来处理来自客户端的请求.然后,控制器调用业务类来处理与业务相关的任务,然后将客户端重定向到逻辑视图名称,该名称由Spring的调度程序Servlet解析,以呈现结果或输出.这样就完成了典型的请求-响应周期的往返.今天整理了一下编写Spring MVC控制器的14个技巧,你今天get到了吗? \(≧▽≦)/ 1.使用@Controller构造型 这是创建可以处理一个或多个请求的控制器类的最简单方法.仅通过用构造型注释一个类@Controller 

  • spring mvc DispatcherServlet之前端控制器架构详解

    前端控制器是整个MVC框架中最为核心的一块,它主要用来拦截符合要求的外部请求,并把请求分发到不同的控制器去处理,根据控制器处理后的结果,生成相应的响应发送到客户端.前端控制器既可以使用Filter实现(Struts2采用这种方式),也可以使用Servlet来实现(spring MVC框架). DispatcherServlet 作为前置控制器是web服务器的入口,是spring mvc最重要的一个类,通过它的生命周期可以加深对web服务器的理解. servlet的生命周期 首先我们回忆一下ser

  • SpringMVC编程使用Controller接口实现控制器实例代码

    Controller简介 Controller控制器,是MVC中的部分C,为什么是部分呢?因为此处的控制器主要负责功能处理部分: 1.收集.验证请求参数并绑定到命令对象: 2.将命令对象交给业务对象,由业务对象处理并返回模型数据: 3.返回ModelAndView(Model部分是业务对象返回的模型数据,视图部分为逻辑视图名). DispatcherServlet,主要负责整体的控制流程的调度部分: 1.负责将请求委托给控制器进行处理: 2.根据控制器返回的逻辑视图名选择具体的视图进行渲染(并把

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

    本文介绍了编写Spring MVC框架的控制器(controller)的基础技巧和最佳操作.在Spring MVC框架中,编写控制器类通常是为了处理用户提出的请求. 编写完成后,控制器会调用一个业务类来处理业务相关任务,进而重定向客户到逻辑视图名.Springdispatcher servlet会对逻辑视图名进行解析,并渲染结果或输出.这就是一个典型的"请求-响应"的完整流程. 1.使用@controllerstereotype 创建一个能够处理单个或多个请求的控制器类,最简单的方法就

  • 如何使用新方式编写Spring MVC接口

    1. 前言 通常我们编写 Spring MVC 接口的范式是这样的: @RestController @RequestMapping("/v1/userinfo") public class UserInfoController { @GetMapping("/foo") public String foo() { return "felord.cn"; } } 这种我都写吐了,今天换个口味,使用 Spring 5 新引入的函数式端点(Funct

  • 基于Spring MVC 简介及入门小例子(推荐)

    一.什么是 Spring MVC Spring MVC 属于 SpringFrameWork 的后续产品,已经融合在 Spring Web Flow 里面,是一个强大灵活的 Web 框架.Spring MVC 提供了一个 DispatcherServlet 作为前端控制器来分配请求.通过策略接口,Spring 框架是高度可配置的.Spring MVC 还包含多种视图技术,如 Java Server Pages(JSP).Velocity.Tiles.iText 和 POI 等.Spring MV

  • Kotlin开发的一些实用小技巧总结

    前言 随着Google I/O大会的召开,Google宣布将支持Kotlin作为Android的开发语言,最近关于Kotlin的文章.介绍就异常的活跃. 本文主要给大家介绍了关于Kotlin开发的一些实用小技巧,分享出来供大家参考学习,下面话不多说了,来一起看看详细的介绍吧. 1.Lazy Loading(懒加载) 延迟加载有几个好处.延迟加载能让程序启动时间更快,因为加载被推迟到访问变量时. 这在使用 Kotlin 的 Android 应用程序而不是服务器应用程序中特别有用.对于 Androi

  • JavaScript编程的10个实用小技巧

    在这篇文章中,我将列出10个Javascript实用小技巧,主要面向Javascript新手和中级开发者.希望每个读者都能至少从中学到一个有用的技巧. 1.变量转换 看起来很简单,但据我所看到的,使用构造函数,像Array()或者Number()来进行变量转换是常用的做法.始终使用原始数据类型(有时也称为字面量)来转换变量,这种没有任何额外的影响的做法反而效率更高. 复制代码 代码如下: var myVar   = "3.14159",str     = ""+ m

  • 常用的10个Python实用小技巧

    大家好,都说追女孩方法大于态度,学Python也是,今天就给大家分享的是我在用Python编写程序时常用的一些小技巧. 1.多次打印同一个字符 在Python中,不用特地写一个函数来重复打印同一个字符,直接使用Print就可以 tem = 'I Love Python ' print(tem * 3) I Love Python I Love Python I Love Python 2.在函数内部使用生成器 在写Python程序时,我们可以在函数内部直接使用生成器,这样可以使代码更简洁. su

  • JavaScript开发的七个实用小技巧(很有用)

    目录 1. 数组求和 2. 使用 length 属性更改数组 3. 数组元素随机打乱 4. 过滤唯一值 5. 逗号运算符 6. 使用数组解构交换数据元素 7. 使用 && 代替 If 条件判断为真的条件 总结 本文译文,采用意译. 下面这些方法对于我来说很有作用,自从我发现了这些操作. 1. 数组求和 假设你有下面的数字数组:let numbers = [2,52,55,5]. 计算求和,我们会想到使用 for,是吧. 但是我们可以使用这行代码完成let sum = numbers.red

  • Android Jetpack Compose开发实用小技巧

    目录 前言 实用小技巧 如何移除View点击阴影 Text文本如何垂直居中 如何移除Button的点击阴影 Dialog宽度如何全屏 如何提升编码效率 前言 在Compose开发的过程中,我们会经常遇到一些看起来很简单却不知道如何处理的小问题,比如去除点击阴影.Dialog全屏等问题,本文记录了这些常见小问题的处理方式.如有更好方案欢迎大佬们交流探讨- 实用小技巧 如何移除View点击阴影 这里的View指的是除了Button系列的之外,如Button.TextButton等,也就是自身没有on

  • 关于Go你不得不知道的一些实用小技巧

    目录 Go 箴言 Go 之禅 代码 使用 go fmt 格式化 多个 if 语句可以折叠成 switch 用 chan struct{} 来传递信号, chan bool 表达的不够清楚 30 * time.Second 比 time.Duration(30) * time.Second 更好 用 time.Duration 代替 int64 + 变量名 按类型分组 const 声明,按逻辑和/或类型分组 var 不要在你不拥有的结构上使用 encoding/gob 不要依赖于计算顺序,特别是在

随机推荐