Spring MVC 简介
Spring Web MVC是Spring框架中用于Web开发的一个模块。它是一个基于Spring提供的MVC架构设计模式的Web开发框架。它本质上也是一个Servlet。
在MVC架构中,控制器负责持久层(控制器还可以细化dao层和service层);模型负责ORM映射;而视图则负责视图的渲染。
普通MVC框架
在web项目中三层架构:
controller目录下都是servlet处理业务逻辑,(也可以对servlet封装,细化为dao和service层);model是POJO,view是html,css,jsp等静态资源。
//servlet处理业务逻辑
package controller;
import com.alibaba.fastjson.JSON;
import factory.SqlsessionFactory;
import mapper.CommentMapper;
import model.Comment;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import javax.servlet.*;
import javax.servlet.http.*;
import javax.servlet.annotation.*;
import java.io.BufferedReader;
import java.io.IOException;
import java.net.URLDecoder;
@WebServlet(name = "AddCommentServlet", value = "/AddCommentServlet")
public class AddCommentServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
request.setCharacterEncoding("utf-8");
BufferedReader reader=request.getReader();
String params=reader.readLine();
String json=JSON.toJSONString(params);
json=URLDecoder.decode(json,"utf-8");
String list[]= json.split("&");
/*mybatis对数据库的操作 SqlSessionFactory sqlSessionFactory=new SqlsessionFactory().SqlsessionFactory(); SqlSession session = sqlSessionFactory.openSession(); CommentMapper mapper=session.getMapper(CommentMapper.class); Comment comment=new Comment(); comment.setNickname(list[0].substring(10)); comment.setTime(list[1].substring(5)); comment.setPhone(list[2].substring(6)); comment.setContent(list[3].substring(8,list[3].length()-1)); mapper.addComment(comment); session.commit(); session.close(); */
response.getWriter().write("success");
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.doGet(request, response);
}
}
每个http请求都会有一个对应的servlet,它实现持久化操作。然后将处理后的数据发送到jsp生成动态网页或发送响应数据。
Servlet封装(controller层的dao)
Spring的核心IoC容器可以管理bean,servlet作为Java web中开发的类,也可以由IoC容器管理。由于servlet数量众多,bean的生产和组装会遇到很大的困难。这就需要对servlet进行封装:
//例如:
addBookServlet
updateBookServlet
deleteBookServlet
//优化
/* 优化写具体的servlet只获取路径 book/* 在该路径下有以下方法 add update delete */
//优化的servlet就不能仅仅重写doGet等方法了,而要重写service方法,具体式:获取url,判断方法对应调用函数
public BaseServlet extends HttpServlet{
@Override
public void service(HttpServiceRequest req,HttpServiceResponse resp) throws ServletException{
//获取请求路径
String uri=req.getRequestURI();
//根据请求路径的最后一段调用方法
int index=uri.lastIndexOf("/");
String dirname=uri.substring(index+1);
//现在已经获取到了方法名,问题是不同子类如何通过继承获取不同的方法名并调用方法,通过反射实现。
/*this关键字,谁调用我,我代表谁 */
//获取字节码文件
Class<?extends BaseServlet> cls=this.getClass();
//通过字节码文件获取方法
Method method = cls.getMethod(dirname,HttpServiceRequest.class,HttpServiceResponse.class) ;
//执行方法
method.invoke(this,req,resp);
//上面便于好看省略了异常
}
//其继承类
@WebSevlet("/book/*")
public bookServlet extends BaseServlet{
public void add(HttpServiceRequest req,HttpServiceResponse resp) throws ServletException{
...
}
public void show(HttpServiceRequest req,HttpServiceResponse resp) throws ServletException{
...
}
...
//这样http请求发送过来就会自动调用book后面同路径名的方法,不分get或post。
}
业务逻辑层封装(controller层的service)
业务逻辑层也可以直接放在servlet中,即对数据库的操作在servlet中实现。但为了解耦,降低代码复杂度一般都是分开的。
BookService:
public interface Service{
public List<Book> selectAll();
}
BookServiceImple:
public BookServiceImple implements Service{
//实现类中实现持久层操作
static{
SqlSessionFactory factory=SqlSessionFactoryUtils.getSqlSessionFactory();
}
@Override
public List<Book> selectAll(){
/* 该部分式mybatis框架 */
//新建数据库交互线程
SqlSession session=factory.openSession();
BookMapper mapper=session.getMapper(BookMapper.class);
List<Book> list = mapper.selectAll();
session.close();
return list;
}
}
在dao层调用service层方法实现持久化操作:
//servlet中的show方法
private BrandService service=new ServiceImple();
...
public void show(HttpServiceRequest req,HttpServiceResponse resp) throws ServletException{
List<Book> list=service.selectAll();
//至此已经获取到数据库中的数据了,然后处理后发送给前端就可以了。
...
}
对于servlet的封装减少众多HttpServlet实现类的编写,也跟符合Resultfull设计风格,与spring框架结合式不用配置众多的servlet只需要配置,父类servlet即可更加方便IoC容器的管理。对于serive的封装也是满足与spring框架结合的需要,由上面的代码可以看出每个service的实现类都是通过new BookServiceImple()
实例化的,如果没有接口Service
IoC容器就要管理众多的XXXServiceImple()
既不容易管理也不容易装配,基于java的多态性用父类声明子类:
Service service=new BookServiceImple();
您可以有不同的实现类。 BoxServiceImpl、PhoneServiceImpl等都可以用service来声明,而IoC容器只能管理service。开发被简化。
Spring MVC框架
普通的MVC框架都是由程序员封装的,包括Spring,也需要程序员进行配置。 Spring正式退出Spring MVC框架,以解决MVC框架与Spring结合的一系列问题。使用Spring MVC框架只需要程序员进行简单的配置即可实现快速的Web开发。
Spring MVC是一个基于Spring提供的MVC设计模式的轻量级Web开发框架。是结构最清晰的Servlet+JSP+JavaBean的实现。
既然Spring MVC是一个实用的MVC框架,Servet和Service层以及由官方封装好了,提供了各种接口来调用。其中DispatcherServlet
是顶级Servlet它负责对请求的解析和分发并调用对应方法。IoC容器只管理这个顶级Servlet,其实现类通过注解和配置都可以,但一般是注释更方便。service层也定义了众多接口实现各种功能。
框架和第一个程序的部署:
-
导入依赖包
spring核心包、web和web-mvc的包日志包。 -
配置
DispatcherServlet
顶级类web.xml:
<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.4" xmlns="http://java.sun.com/xml/ns/j2ee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://JAVA.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd">
<servlet>
<servlet-name>SpringMVC</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:spring-mvc.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>SpringMVC</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
</web-app>
- 配置spring配置文件
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xmlns:mvc="http://www.springframework.org/schema/mvc" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.0.xsd">
<!--开启注解-->
<context:annotation-config></context:annotation-config>
<context:component-scan base-package="springdemo"/>
<mvc:annotation-driven></mvc:annotation-driven>
</beans>
- 编写控制器实现类
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
@Controller
@RequestMapping("/book")
public class BookCtrl {
@RequestMapping("/add")
@ResponseBody
public String add(){
return "add";
}
@RequestMapping("/list")
public void list(){
System.out.println("list");
}
}
Spring MVC 执行流程
SpringMVC 的执行流程如下。
- 用户点击某个请求路径发起HTTP请求,该请求会提交给DispatcherServlet(前端控制器);
- DispatcherServlet 请求一个或多个 HandlerMapping(处理器映射器),并返回一条执行链(HandlerExecutionChain)。
- DispatcherServlet将执行链返回的Handler信息发送给HandlerAdapter(处理器适配器);
- HandlerAdapter根据Handler信息找到并执行对应的Handler(通常称为Controller);
- Handler执行后,会向HandlerAdapter返回一个ModelAndView对象(Spring MVC的底层对象,包括Model数据模型和View视图信息);
- HandlerAdapter接收到ModelAndView对象后,返回给DispatcherServlet;
- DispatcherServlet接收到ModelAndView对象后,会请求ViewResolver(视图解析器)解析视图;
- ViewResolver根据View信息匹配对应的视图结果并返回给DispatcherServlet;
- DispatcherServlet接收到具体的View后,渲染视图,将Model中的模型数据填充到View中的请求字段中,生成最终的View;
- 视图负责将结果显示给浏览器(客户端)。
Spring Web MVC框架的特点
-
明确的角色划分:控制器、验证器、命令对象、表单对象、模型对象、Servlet分发器(DispatcherServlet)、处理程序映射)、视图解析器等。每个角色都可以由专门的对象来实现。
-
强大而直接的配置方法:框架类和应用程序类都可以配置为JavaBean,支持跨多个上下文的引用,例如对Web控制器中的业务对象和验证器的引用。
-
适应性强且非侵入性:您可以根据不同的应用场景选择合适的控制器子类型(简单、命令、表单、向导、多操作或自定义),而不是从单个控制器(如Action/ActionForm)继承中进行选择。
-
可重用的业务代码:您可以使用现有的业务对象作为命令或表单对象,而无需扩展特定框架的基类。
-
可定制的处理程序映射和视图解析:Spring 提供了从最简单的 URL 映射到复杂的专用定制策略的一切。与一些强制开发人员使用单一特定技术的Web MVC框架相比,Spring更加灵活。
-
灵活的模型转换:在Spring Web框架中,使用基于Map的键/值对可以轻松地与各种视图技术集成。
-
JSP表单标签库:Spring 2.0中引入的表单标签库使得在JSP中编写表单变得更加容易。
-
Spring Bean 的生命周期可以限制为当前的 HTTP 请求或 HTTP 会话。准确的说,这并不是 Spring MVC 框架本身的特性,而是属于 Spring MVC 使用的 WebApplicationContext 容器。
Spring的web框架围绕调度程序Servlet设计。 DispatcherServlet的作用是将请求分发到不同的处理器。 Spring的web框架包括可配置的Handler映射、视图解析、本地解析、主题解析和文件上传的支持。Spring的Web框架中缺省的处理器是==Controller 接口,这是一个非常简单的接口,只包含ModelAndView handleRequest(request, response) ==方法。可以通过实现这个接口来创建自己的控制器(也可以称之为处理器),但是更推荐继承Spring提供的一系列控制器, 比如AbstractController、AbstractCommandController 和SimpleFormController。注意,需要选择正确的基类:如果没有表单,就不需要一个FormController。
DispatcherServlet
DispatcherServlet实际上是一个Servlet(它继承了HttpServlet)。与其他 Servlet 一样,DispatcherServlet 是在 Web 应用程序的 web.xml 文件中定义的。由 DispatcherServlet 处理的请求必须使用同一 web.xml 文件中的 url 映射进行映射。
Spring 的 Web MVC 框架是一个请求驱动的 Web 框架,围绕中央 Servlet 设计,该 Servlet 将请求分发到控制器并提供其他功能来帮助 Web 应用程序开发。然而,Spring 的 DispatcherServlet 的作用远不止于此。它与 Spring 的 IoC 容器完全集成,允许您使用 Spring 的其他功能。
配置DispatcherServlet
网络.xml:
<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.4" xmlns="http://java.sun.com/xml/ns/j2ee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://JAVA.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd">
<servlet>
<servlet-name>Spring-mvc</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>spring-mvc</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
</web-app>
上面的配置中所有<url-pattern>/</url-pattern>
请求都会由请求的名为spring-mvc 的DispatcherServlet处理。这只是配置Spring Web MVC 的第一步。接下来需要配置DispatcherServlet本身和Spring Web MVC 框架用到的其他的bean。他们都由IoC容器管理。
在DispatcherServlet的初始化过程中,框架会在web应用的 WEB-INF文件夹下寻找名为[servlet-name]-servlet.xml 的配置文件,生成文件中定义的bean。这些bean会覆盖在全局范围(global cope)中定义的同名的bean。在控制器中就可以使用这些对象。
可以在WEB-INF下创建spring-mvc.xml的spring配置文件同上spring配置文件
在beans中进行bean生产和和装配。
也可以Spring MVC 的配置文件存放在应用程序目录中的任何地方,默认是WEB-INF。但需要使用 servlet 的 init-param 元素加载配置文件,通过 contextConfigLocation 参数来指定 Spring MVC 配置文件的位置,示例代码如下(这样可以将配置文件放在maven项目的resources中)。**/"表示的是任意目录
<param-value>classpath*:**/applicationContext-*.xml</param-value>
表示任意目录下的以"applicationContext-"开头的XML文件。最好把所有Spring配置文件都放在一个统一的目录下:
<!-- 部署 DispatcherServlet -->
<servlet>
<servlet-name>springmvc</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet </servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:springmvc-servlet.xml</param-value>
</init-param>
<!-- 表示容器再启动时立即加载servlet -->
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>springmvc</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
此处使用 Spring 资源路径的方式进行指定,即 classpath:springmvc-servlet.xml
。该 Servlet 是 DispatcherServlet 类型,它就是 Spring MVC 的入口,并通过 1 配置标记容器在启动时就加载此 DispatcherServlet,即自动启动。然后通过 servlet-mapping 映射到“/”,即 DispatcherServlet 需要截获并处理该项目的所有 URL 请求。
默认 DispatcherServlet 配置
如上一节中所述,对每种特殊的bean,DispatcherServlet都会维护一个默认可用的实现的列表。此信息保存在包org.springframework.web.servlet中的文件DispatcherServlet.properties中。
所有特殊 bean 都有一些合理的默认值,尽管您迟早需要调整这些 bean 提供的一个或多个属性。例如,通常配置 InternalResourceViewResolver 将其前缀属性设置为视图文件的父目录。
不管细节如何,这里需要理解的重要概念是,一旦在 WebApplicationContext 中配置了特殊 bean(例如 InternalResourceViewResolver),它就会有效地覆盖默认实现列表,否则这些默认实现将用于该特殊 bean 类型。例如,如果配置了InternalResourceViewResolver,则默认的ViewResolver实现列表将被忽略。
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/jsp/" />
<property name="suffix" value=".jsp" />
</bean>
WebApplicationContext
使用spring MVC框架后,上下文连接不再是ApplicationContext对象,而是WebApplicationContext。它只是一个普通的ApplicationContext,具有Web应用程序的必要功能。配置完Bean.xml文件后,通过ApplicationContext获取bean对象进行管理。 DispatcherServlet生成的WebApplicationContext是获取DispatcherServlet对象的上下文连接。
豆类 | 功能 |
---|---|
控制器 | 实现dao层 |
处理程序映射 | 处理器映射包含预处理器、后处理器和控制器的列表,仅在满足某些条件(例如匹配控制器指定的URL)时才执行 |
查看解析器 | 视图解析器可以将视图名称解析为相应的视图,或者将基于字符串的逻辑视图名称解析为实际的View类型。 |
本地化解析器(LocaleResolver & LocaleContextResolver) | 解析客户正在使用的区域设置和可能的时区,以便能够提供国际化视图 |
处理程序异常解析器 | 处理器异常解析器可以将异常映射到视图或实现更复杂的异常处理逻辑。 |
多部分文件解析器 | 文件上传解析器提供HTML表单文件上传功能 |
主题解析器 | 主题解析器可以解析您的Web应用程序使用的主题以提供个性化布局 |
FlashMap管理器(FlashMapManager) | 存储和检索“输入”和“输出”FlashMap,可用于将属性从一个请求传递到另一个请求(通常通过重定向) |
处理器适配器 | 帮助 DispatcherServlet 调用映射到请求的处理程序,无论实际调用的处理程序的详细信息如何。例如,调用一个带注解的控制器需要解析各种注解,所以HandlerAdapter的主要目的就是屏蔽DispatcherServlet这些细节。 |
下面是对应于 DispatcherServlet 传入 HTTP 请求的事件序列:
-
DispatcherServlet收到HTTP请求后,根据HandlerMapping选择并调用合适的控制器。
-
控制器接受请求并根据所使用的 GET 或 POST 方法调用适当的服务方法。 Service方法将根据定义的业务逻辑设置模型数据,并将视图名称返回给DispatcherServlet。
-
DispatcherServlet 从 ViewResolver 获取帮助来检索请求的已定义视图。
-
一旦确定了视图,DispatcherServlet就会将模型数据传递给视图,并最终在浏览器中渲染。
所有上述组件,即 HandlerMapping、Controller 和 ViewResolver 都是 WebApplicationContext 的一部分,WebApplicationContext 是 ApplicationContext 的扩展,具有 Web 应用程序所需的一些额外功能。
DispatcherServlet初始化参数
范围 | 描述 |
---|---|
上下文类 | 实现当前 servlet 用于创建上下文的 WebApplicationContext 接口的类。如果不指定该参数,则默认使用XmlWebApplicationContext。 |
上下文配置位置 | 传递给上下文实例(由 contextClass 指定)的字符串,用于指定上下文的位置。该字符串可以拆分为多个字符串(使用逗号作为分隔符)以支持多个上下文(多个上下文的情况下,如果同一个bean被定义两次,则后一个优先) |
名称空间 | WebApplicationContext 命名空间。默认值为 [服务器名称]-servlet |
DispatcherServlet的处理顺序
设置 DispatcherServlet 并接收到对该特定 DispatcherServlet 的请求后,DispatcherServlet 将开始按以下顺序处理请求:
- 搜索 WebApplicationContext 并将其绑定为请求中的属性,以便控制器和流程中的其他元素可以使用它。默认情况下,绑定位于 DispatcherServlet.WEB_APPLICATION_CONTEXT_ATTRIBUTE 键下。
- 区域解析器绑定到请求,以便流程中的元素可以解析处理请求(呈现视图、准备数据等)时使用的区域。如果不需要区域解析,则不需要此解析器。
- 主题解析器与请求相关联,以允许视图等元素确定要使用的主题。如果您不使用主题,可以忽略它。
- 如果指定了多部分文件解析器,则检查请求是否为多部分;如果找到,则该请求将包装在 MultipartHttpServletRequest 中,以便由流程中的其他元素进行后续处理。
- 寻找合适的处理程序。如果找到,则执行与处理程序(预处理器、后处理器和控制器)关联的执行链以准备模型或渲染。
- 如果返回模型,则渲染视图。如果未返回模型(可能是因为预处理器或后处理器出于安全原因拦截了请求),则不会呈现任何视图,因为请求可能已完成。
WebApplicationContext 中声明的程序异常解析器收集请求处理期间抛出的异常。使用这些异常解析器允许您定义处理异常的自定义行为。
控制器(Controller)
控制器的概念是 MVC 设计模式的一部分(准确地说是 MVC 中的 C)。应用程序的行为通常被定义为服务接口,控制器允许用户访问应用程序提供的服务。控制器解析用户输入并将其转换为合理的模型数据,可以通过视图进一步显示给用户。
Spring本身包含了表单控制器、命令控制器、向导控制器等多种控制器。 SpringWeb框架的所有控制器都会返回一个ModelAndView实例来实现Model接口。
定义控制器
@Controller
@RequestMapping("/hello")
public class HelloController{
@RequestMapping(method = RequestMethod.GET)
public String printHello(ModelMap model) {
model.addAttribute("message", "Hello Spring MVC Framework!");
return "hello";
}
}
@Controller & @RequestMapping
@Controller 注释定义该类作为一个 Spring MVC 控制器。在这里,第一次使用的 @RequestMapping 表明在该控制器中处理的所有方法都是相对于 /hello 路径的。下一个注释 @RequestMapping(method = RequestMethod.GET) 用于声明 printHello() 方法作为控制器的默认 service 方法来处理 HTTP GET 请求。你可以在相同的 URL 中定义其他方法来处理任何 POST 请求。
你可以用另一种形式编写上面的控制器,你可以在@RequestMapping中添加额外的属性,如下所示:
@Controller
public class HelloController{
@RequestMapping(value = "/hello", method = RequestMethod.GET)
public String printHello(ModelMap model) {
model.addAttribute("message", "Hello Spring MVC Framework!");
return "hello";
}
}
@Controller 注解表明一个类作为控制器存在。 Spring不要求您继承任何控制器基类,也不要求您实现Servlet API。注解可以认为是被注解类的原型(stereotype),表明了这个类所扮演的角色。调度程序(DispatcherServlet)将扫描所有使用 @Controller 注解的类,并检测通过 @RequestMapping 注解配置的方法。这也可以通过java继承或者xml配置来实现。
@RequestMapping属性
@RequestMapping注解来将请求URL,映射到整个类上或某个特定的处理器方法上。servlet封装
章节由讲解。
-
值属性
value 属性是 @RequestMapping 注解的默认属性,因此如果只有 value 属性时,可以省略该属性名,如果有其它属性,则必须写上 value 属性名称。 -
路径属性
path 属性和 value 属性都用来作为映射使用。 -
名称属性
name属性相当于方法的注释,使方法更易理解。 -
方法属性
method 属性用于表示该方法支持哪些 HTTP 请求。如果省略 method 属性,则说明该方法支持全部的 HTTP 请求。@RequestMapping(value = “toUser”,method = RequestMethod.GET) 表示该方法只支持 GET 请求。也可指定多个 HTTP 请求,如 @RequestMapping(value = “toUser”,method = {RequestMethod.GET,RequestMethod.POST}),说明该方法同时支持 GET 和 POST 请求。 -
参数属性
params 属性用于指定请求中规定的参数。
@RequestMapping(value = "toUser",params = "type")
public String toUser() {
return "showUser";
}
/* 以上代码表示请求中必须包含 type 参数时才能执行该请求。 即 http://localhost:8080/toUser?type=xxx 能够正常访问 toUser() 方法, 而 http://localhost:8080/toUser 则不能正常访问 toUser() 方法。 */
@RequestMapping(value = "toUser",params = "type=1")
public String toUser() {
return "showUser";
}
/* 以上代码表示请求中必须包含 type 参数,且 type 参数为 1 时才能够执行该请求。 即 http://localhost:8080/toUser?type=1 能够正常访问 toUser() 方法, 而 http://localhost:8080/toUser?type=2 则不能正常访问 toUser() 方法。 */
-
标头属性
header 属性表示请求中必须包含某些指定的 header 值。@RequestMapping(value = “toUser”,headers = “Referer=http://www.xxx.com”) 表示请求的 header 中必须包含了指定的“Referer”请求头,以及值为“http://www.xxx.com”时,才能执行该请求。 -
消费者属性
consumers 属性用于指定处理请求的提交内容类型(Content-Type),例如:application/json、text/html。如 @RequestMapping(value = “toUser”,consumes = “application/json”)。 -
产生属性
produces 属性用于指定返回的内容类型,返回的内容类型必须是 request 请求头(Accept)中所包含的类型。如 @RequestMapping(value = “toUser”,produces = “application/json”)。
除此之外,produces 属性还可以指定返回值的编码。如 @RequestMapping(value = “toUser”,produces = “application/json,charset=utf-8”),表示返回 utf-8 编码。
使用 @RequestMapping 来完成映射,具体包括 4 个方面的信息项:请求 URL、请求参数、请求方法和请求头。 -
一个特别重要的类型是 org.springframework.ui.Model 类型
该类型是一个包含 Map 的 Spring MVC类型。在每次调用请求处理方法时 Spring MVC 都将创建 org.springframework.ui.Model 对象。还有ViewModel和Map。 -
请求处理方法的常见返回类型
请求处理方法可以返回如下类型的对象:
- 模型与视图
- 模型
- 包含模型属性的映射
- 看法
- 表示逻辑视图名称的字符串
- 空白
- 任何其他 Java 类型
@RequestMapping注解方法参数
@路径变量
URI模板是一个类似于URI的字符串,只不过其中包含了一个或多个的变量名。当你使用实际的值去填充这些变量名的时候,模板就退化成了一个URI。
比如说,一个这个URI模板http://www.example.com/users/{userId}
就包含了一个变量名userId。将值fred赋给这个变量名后,它就变成了一个URI:http://www.example.com/users/fred
。
@RequestMapping(path="/owners/{ownerId}", method=RequestMethod.GET)
public String findOwner(@PathVariable String ownerId, Model model) {
Owner owner = ownerService.findOwner(ownerId);
model.addAttribute("owner", owner);
return "displayOwner";
}
为了处理@PathVariables注释,Spring MVC必须使用变量名在URI模板中查找相应的变量。可以直接在注解中声明:
@RequestMapping(path="/owners/{ownerId}}", method=RequestMethod.GET)
public String findOwner(@PathVariable("ownerId") String theOwner, Model model) {
// 具体的方法代码…
}
或者,如果 URI 模板中的变量名称与方法参数名称相同,则不必再次指定。只要在编译时留下调试信息,Spring MVC 就可以自动匹配 URL 模板中与方法参数名称相同的变量名称。
@RequestMapping(path="/owners/{ownerId}", method=RequestMethod.GET)
public String findOwner(@PathVariable String ownerId, Model model) {
// 具体的方法代码…
}
一个方法可以有任意数量的@PathVariable注释:
@RequestMapping(path="/owners/{ownerId}/pets/{petId}", method=RequestMethod.GET)
public String findPet(@PathVariable String ownerId, @PathVariable String petId, Model model) {
Owner owner = ownerService.findOwner(ownerId);
Pet pet = owner.getPet(petId);
model.addAttribute("pet", pet);
return "displayPet";
}
当 @PathVariable 注释应用于 Map<String, String> 类型的参数时,框架将使用所有 URI 模板变量来填充映射。
带有正则表达式的 URI 模板
@RequestMapping注解支持你在URI模板变量中使用正则表达式。语法是{varName:regex},其中第一部分定义了变量名,第二部分就是你所要应用的正则表达式。比如下面的代码样例:
@RequestMapping("/spring-web/{symbolicName:[a-z-]+}-{version:\\d\\.\\d\\.\\d}{extension:\\.[a-z]+}")
public void handle(@PathVariable String version, @PathVariable String extension) {
// 代码部分省略...
}
}
后缀模式匹配
Spring MVC默认采用"." 后缀模式匹配来执行路径匹配,因此,映射到 /person 路径的控制器也会隐式映射到 /person。。这使得通过URL来请求同一资源文件的不同格式变得更简单(比如/person.pdf,/person.xml)。
矩阵变量
矩阵变量可以在任何路径段落中出现,每对矩阵变量之间使用一个分号“;”隔开。比如这样的URI:"/cars;color=red;year=2012"。多个值可以用逗号隔开"color=red,green,blue",或者重复变量名多次"color=red;color=green;color=blue"。
// GET /pets/42;q=11;r=22
@RequestMapping(path = "/pets/{petId}", method = RequestMethod.GET)
public void findPet(@PathVariable String petId, @MatrixVariable int q) {
// petId == 42
// q == 11
}
由于任何路径段落都可以包含矩阵变量,因此在某些场景下您需要使用更精确的信息来指定矩阵变量的位置:
// GET /owners/42;q=11/pets/21;q=22
@RequestMapping(path = "/owners/{ownerId}/pets/{petId}", method = RequestMethod.GET)
public void findPet(
@MatrixVariable(name="q", pathVar="ownerId") int q1,
@MatrixVariable(name="q", pathVar="petId") int q2) {
// q1 == 11
// q2 == 22
}
您还可以通过 Map 存储所有矩阵变量:
// GET /owners/42;q=11;r=12/pets/21;q=22;s=23
@RequestMapping(path = "/owners/{ownerId}/pets/{petId}", method = RequestMethod.GET)
public void findPet(
@MatrixVariable Map<String, String> matrixVars,
@MatrixVariable(pathVar="petId") Map<String, String> petMatrixVars) {
// matrixVars: ["q" : [11,22], "r" : 12, "s" : 23]
// petMatrixVars: ["q" : 11, "s" : 23]
}
@RequestMapping支持的方法参数类型
使用@RequestMapping注解的处理方法可以具有非常灵活的方法签名、支持的方法参数和返回值类型。
- HttpSession 类型的会话对象(Servlet API)。使用这种类型的参数需要存在这样的会话,因此这样的参数永远不会为空。
- 当前请求的区域设置信息 java.util.Locale 由配置的最相关的区域设置解析器进行解析。在MVC环境中,是应用程序中配置的LocaleResolver或LocaleContextResolver。
- 当前请求绑定的时区信息java.util.TimeZone(java 6以上)/java.time.ZoneId(java 8)由LocaleContextResolver解析。
- 用于访问请求正文的 java.io.InputStream 或 java.io.Reader。该对象与通过Servlet API获取的输入流/Reader相同。
- 用于生成响应正文的 java.io.OutputStream 或 java.io.Writer。该对象与通过Servlet API获取的输出流/Writer相同。
- org.springframework.http.HttpMethod。您可以获取 HTTP 请求方法。
- java.security.Principal 包装当前经过身份验证的用户信息。
- 用@PathVariable注解的方法参数将值存储在URI模板变量中。
- 使用 @MatrixVariable 注解的方法参数将键值对存储在 URI 路径段中。
- 用@RequestParam注解的方法参数存储Servlet请求中指定的参数。参数值被转换为方法参数的声明类型。
- 用@RequestHeader注释的方法参数存储Servlet请求中指定的HTTP请求头的值。参数值被转换为方法参数的声明类型。
- 使用 @RequestBody 注释的参数提供对 HTTP 请求正文的访问。参数的值通过HttpMessageConverter转换为方法参数声明的类型。
- 用 @RequestPart 注释的参数提供对“multipart/form-data 请求部分”内容的访问。
- HttpEntity<?> 类型的参数,提供对 HTTP 请求标头和请求内容的访问。请求流通过HttpMessageConverter转换为实体对象。
- java.util.Map/org.springframework.io.Model/org.springframework.ui.ModelMap类型的参数用于增强默认暴露给视图层的模型(model)的功能。
- org.springframework.web.util.UriComponentsBuilder 构造函数对象用于构造与当前请求 URL 相关的信息,例如主机名、端口号、资源类型(方案)、上下文路径、Servlet 映射中的相对部分(文字部分) , ETC。 。
- org.springframework.web.util.UriComponentsBuilder 构造函数对象用于构造与当前请求 URL 相关的信息,例如主机名、端口号、资源类型(方案)、上下文路径、servlet 映射中的相对部分(文字部分) , ETC。 。
@RequestMapping(method = RequestMethod.POST)
public String processSubmit(@ModelAttribute("pet") Pet pet, BindingResult result, Model model) {
... }
@RequestMapping支持的方法返回值类型
- ModelAndView对象,其中模型隐式填充命令对象,并调用@ModelAttribute字段注解的访问器返回的值。
- 模型对象,其中视图名称默认由RequestToViewNameTranslator确定,模型隐式填充命令对象和调用时用@ModelAttribute字段注释的访问器返回的值。
- Map对象,用于暴露模型,其中视图名称默认由RequestToViewNameTranslator确定。该模型隐式填充了命令对象和调用时用 @ModelAttribute 字段注释的访问器返回的值。
- 查看对象。该模型隐式填充了命令对象和调用时用 @ModelAttribute 字段注释的访问器返回的值。 handler方法还可以添加Model类型方法参数来增强模型。
- String对象,它的值将被解析为逻辑视图名称。其中,模型默认会填充命令对象和用@ModelAttribute字段注释的访问器返回的值。 handler方法还可以添加Model类型方法参数来增强模型。
- 空白。如果响应数据已经在处理器方法中处理过(例如,在方法参数中定义ServletResponse或HttpServletResponse类型参数并直接向其响应主体写入内容),则该方法可以返回void。 handler方法还可以添加Model类型方法参数来增强模型。
- HttpEntity<?> 或 ResponseEntity<?> 对象,用于提供对 Servlet HTTP 响应标头和响应内容的访问。对象主体将被 HttpMessageConverters 转换为响应流。
- HttpHeaders 对象,返回不带响应正文的响应。
- 可调用<?> 对象。当应用程序想要异步返回方法值时使用。这个过程是由Spring MVC自己的线程来管理的。
- ResponseBodyEmitter 对象,可用于同时异步写入多个对象到响应主体。
- SseEmitter 对象,可用于将服务器端事件异步写入响应正文。
- 一个 StreamingResponseBody 对象,可用于异步写入响应对象的输出流。
任何其他返回类型都将被处理为模型的属性并返回到视图。属性的名称是方法级别用@ModelAttribute注释的字段名称(或者使用返回类型的类名作为默认属性名称)。该模型隐式填充了命令对象和调用 @ModelAttribute 字段注释的访问器返回的值。
上面描述了注解定义的方法的参数和返回值类型。如何使用这些命令来实现西安的特定功能?
@RequestParam 获取请求行参数
@Controller
@RequestMapping("/pets?petId=1")
public class EditPetForm {
// ...
@RequestMapping(method = RequestMapping.GET)
public String setupForm(@RequestParam("petId") int petId, ModelMap model) {
model.addAttribute("petId", petId);
return "petForm";
}
// ,..
}
如果注解的方法参数类型不是String,则会自动进行类型转换。
@RequestBody获取请求体参数
请求体到方法参数的转换是由HttpMessageConverter完成的。HttpMessageConverter负责将HTTP请求信息转换成对象,以及将对象转换回一个HTTP响应体。对于@RequestBody注解,RequestMappingHandlerAdapter提供了以下几种默认的HttpMessageConverter支持:
ByteArrayHttpMessageConverter
用以转换字节数组StringHttpMessageConverter
用以转换字符串FormHttpMessageConverter
用以将表格数据转换成MultiValueMap<String, String>或从MultiValueMap<String, String>中转换出表格数据
@ResponseBody注释映射响应主体
@ResponseBody注解可被应用于方法上,标志该方法的返回值被直接写回到HTTP响应体中去(而不会被被放置到Model中或被解释为一个视图名,没有该注解会将返回值解析为jsp文件,除String会自动匹配)
@RestController注解创建一个REST控制器
当今让控制器实现一个REST API是非常常见的,这种场景下控制器只需要提供JSON、XML或其他自定义的媒体类型内容即可。你不需要在每个@RequestMapping方法上都增加一个@ResponseBody注解,更简明的做法是,给你的控制器加上一个@RestController的注解。
它与普通的@Controller没有什么不同,@RestController也可以与@ControllerAdvicebean一起使用。
使用 HTTP 实体 HttpEntity
HttpEntity与@RequestBody和@ResponseBody很相似。除了能获得请求体和响应体中的内容之外,HttpEntity(以及专门负责处理响应的ResponseEntity子类)还可以存取请求头和响应头。
@RequestMapping("/something")
public ResponseEntity<String> handle(HttpEntity<byte[]> requestEntity) throws UnsupportedEncodingException {
String requestHeader = requestEntity.getHeaders().getFirst("MyRequestHeader");
byte[] requestBody = requestEntity.getBody();
// do something with request header and body
HttpHeaders responseHeaders = new HttpHeaders();
responseHeaders.set("MyResponseHeader", "MyValue");
return new ResponseEntity<String>("Hello World", responseHeaders, HttpStatus.CREATED);
}
@ModelAttribute注解
@ModelAttribute注解可被应用在方法或方法参数上。说明了方法的作用是用于添加一个或多个属性到model上。@ModelAttribute方法通常被用来填充一些公共需要的属性或数据。
@ModelAttribute 方法的两种样式。第一种写法,该方法会通过返回值的方式默认添加一个属性;在第二种编写方式中,该方法接收一个 Model 对象,然后您可以向它添加任意数量的属性。
方法参数上注解的@ModelAttribute表示方法参数的值将从模型中获取。当它存在于模型中后,请求中所有名称匹配的参数都会填充到该参数中。一般都会携带模型参数。
数据绑定
数据的绑定。WebDataBinder类能将请求参数——包括字符串的查询参数和表单字段等——通过名称匹配到model的属性上。成功匹配的字段在需要的时候会进行一次类型转换(从String类型到目标字段的类型),然后被填充到model对应的属性中。
数据绑定后,可能会出现一些错误,例如未提供必需的字段、类型转换过程中出现错误等。如果要检查这些错误,可以在@ModelAttribute注解的参数后立即声明一个BindingResult参数。
@RequestMapping(path = "/owners/{ownerId}/pets/{petId}/edit", method = RequestMethod.POST)
public String processSubmit(@ModelAttribute("pet") Pet pet, BindingResult result) {
if (result.hasErrors()) {
return "petForm";
}
// ...
}
在请求之间使用@SessionAttributes注解并使用HTTP会话保存模型数据
类型级别的@SessionAttributes注解声明了某个特定处理器所使用的会话属性。通常它会列出该类型希望存储到session。该注解通过model对象获取参数,必须存在model参数。除了用该注解快速存储session外,由于控制器继承HttpServlet,可以同普通Servlet的getSession等方法获取及存储。此时蚕食中就要传递HttpSession了
@CookieValue注解映射cookie值
@CookieValue注解能将一个方法参数与一个HTTP cookie的值进行绑定。若注解的目标方法参数不是String类型,则类型转换会自动进行。
@RequestMapping("/display")
public void displayHeaderInfo(@CookieValue("info") String cookie) {
//...
}
使用@RequestHeader注解映射请求头属性
@RequestMapping("/header")
public void displayHeaderInfo(@RequestHeader("Accept-Encoding") String encoding,
@RequestHeader("Keep-Alive") long keepAlive) {
//...
}
控制器类型
控制器
Spring控制器架构的基础是org.springframework.mvc.Controller接口:
public interface Controller {
/** * Process the request and return a ModelAndView object which the DispatcherServlet * will render. */
ModelAndView handleRequest(
HttpServletRequest request,
HttpServletResponse response) throws Exception;
}
可以发现Controller接口只声明了一个方法,负责处理请求并返回合适的模型和视图。
抽象控制器
为提供一套基础设施,所有的Spring控制器都继承了 AbstractController ,AbstractController 提供了诸如缓存支持和mimetype设置这样的功能。
方法 | 描述 |
---|---|
支持的方法 | 指定该控制器应接受哪些请求方法。通常它被设置为同时支持 GET 和 POST,但您可以选择您想要支持的方法。如果控制器不支持请求发送方法,则会通知客户端(通常通过抛出ServletException) |
需要会话 | 指示该控制器是否需要 HTTP 会话才能正常运行。如果控制器在没有会话的情况下接收到请求,则会通过抛出 ServletException 来通知客户端 |
同步会话 | 指定控制器是否同步用户的HTTP会话 |
缓存秒数 | 指定控制器通知客户端缓存数据内容的秒数,通常是大于零的整数。默认值为-1,表示不缓存 |
使用ExpiresHeader | 指定Controller在响应请求时是否兼容HTTP 1.0 Expires header。默认值是true |
使用缓存头 | 指定Controller在发出相应请求时是否兼容HTTP 1.1 Cache-Control标头。默认值是true |
public class SampleController extends AbstractController {
public ModelAndView handleRequestInternal(
HttpServletRequest request,
HttpServletResponse response) throws Exception {
ModelAndView mav = new ModelAndView("hello");
mav.addObject("message", "Hello World!");
return mav;
}
}
<bean id="sampleController" class="samples.SampleController">
<property name="cacheSeconds" value="120"/>
</bean>
多动作控制器
Spring提供了多动作控制器来将多个请求处理方法合并在一个控制器里,这样可以把相关功能组合在一起。它可以定义页面请求到控制器方法名的映射, 然后在处理相应请求时调用该方法。当你有很多比较小的且相关的功能时使用MultiActionController很方便,这样就不必为每个小功能创建 一个单独的Controller了。
其他简单控制器
管可以继承AbstractController来实现自己的控制器,不过Spring提供的众多控制器减轻了我们开发简单MVC应用时的负担。
ParameterizableViewController
基本上和上面例子中的一样,不同的是,可以在application context中指定返回的视图名称(从而 避免了在Java代码中的硬编码)。UrlFilenameViewController
会检查URL,获取文件请求的文件名,并把它作为视图名加以使用。。例如, http://www.springframework.org/index.html对应的视图文件名是index。
命令控制器
命令控制器提供了一种和数据对象交互的方式,并将 HttpServletRequest 的参数动态绑定到指定的数据对象上。
AbstractCommandController
- 可以使用该抽象命令控制器来创建自己的命令控制器,它能够将请求参数绑定到指定的命令对象。 这个类并不提供任何表单功能,但是它提供验证功能,并且让你在控制器中去实现如何处理由请求参数值产生的命令对象。AbstractFormController
- 一个支持表单提交的抽象控制器类。 使用这个控制器,可以定义表单,并使用从控制器获取的数据对象构建表单。 当用户输入表单内容,AbstractFormController将用户输入的内容绑定到命令对象,验证表单内容, 并将该对象交给控制器,完成相应的操作。它支持的功能有防止重复提交、表单验证以及一般的表单处理流程。 子类需要实现自己的方法来指定采用哪个视图来显示输入表单,哪个视图显示表单正确提交后的结果。 如果需要表单,但不想在应用上下文中指定显示给用户的视图,可使用该控制器。SimpleFormController
- 这是一个form controller,当需要根据命令对象来创建相应的form的时候,该类可以提供更多的支持。 可以为其指定一个命令对象,显示表单的视图名,当表单提交成功后显示给用户的视图名等.
控制器传递参数
- 通过实体bean接收请求参数
@RequestMapping("/login")
public String login(User user, Model model) {
if ("bianchengbang".equals(user.getName())
&& "123456".equals(user.getPwd())) {
model.addAttribute("message", "登录成功");
return "main"; // 登录成功,跳转到 main.jsp
} else {
model.addAttribute("message", "用户名或密码错误");
return "login";
}
}
- 通过处理方法的形参接收请求参数
@RequestMapping("/login")
public String login(String name, String pwd, Model model) {
if ("bianchengbang".equals(user.getName())
&& "123456".equals(user.getPwd())) {
model.addAttribute("message", "登录成功");
return "main"; // 登录成功,跳转到 main.jsp
} else {
model.addAttribute("message", "用户名或密码错误");
return "login";
}
}
- 通过HttpServletRequest接收请求参数
@RequestMapping("/login")
public String login(HttpServletRequest request, Model model) {
String name = request.getParameter("name");
String pwd = request.getParameter("pwd");
if ("bianchengbang".equals(name)
&& "123456".equals(pwd)) {
model.addAttribute("message", "登录成功");
return "main"; // 登录成功,跳转到 main.jsp
} else {
model.addAttribute("message", "用户名或密码错误");
return "login";
}
}
- 通过@PathVariable接收URL中的请求参数
@RequestMapping("/login/{name}/{pwd}")
public String login(@PathVariable String name, @PathVariable String pwd, Model model) {
if ("bianchengbang".equals(name)
&& "123456".equals(pwd)) {
model.addAttribute("message", "登录成功");
return "main"; // 登录成功,跳转到 main.jsp
} else {
model.addAttribute("message", "用户名或密码错误");
return "login";
}
}
- 通过@RequestParam接收请求参数
@RequestMapping("/login")
public String login(@RequestParam String name, @RequestParam String pwd, Model model) {
if ("bianchengbang".equals(name)
&& "123456".equals(pwd)) {
model.addAttribute("message", "登录成功");
return "main"; // 登录成功,跳转到 main.jsp
} else {
model.addAttribute("message", "用户名或密码错误");
return "login";
}
}
- 通过@ModelAttribute接收请求参数
@RequestMapping("/login")
public String login(@ModelAttribute("user") User user, Model model) {
if ("bianchengbang".equals(name)
&& "123456".equals(pwd)) {
model.addAttribute("message", "登录成功");
return "main"; // 登录成功,跳转到 main.jsp
} else {
model.addAttribute("message", "用户名或密码错误");
return "login";
}
}
处理器映射(Handler mapping)
通过处理器映射,Web 请求可以映射到正确的处理程序。 Spring有许多内置的处理器映射策略,例如SimpleUrlHandlerMapping或BeanNameUrlHandlerMapping。现在我们来看看HandlerMapping的基本概念。
HandlerMapping的基本功能就是将请求传递给HandlerExecutionChain。首先,这个HandlerExecutionChain必须包含一个可以处理请求的处理器。其次,这条链还可以包含一系列可以拦截请求的拦截器。收到请求后,DispatcherServlet 将请求交给处理程序映射,允许其检查请求并找到合适的 HandlerExecutionChain。然后 DispatcherServlet 执行链中定义的处理程序和拦截器。
Spring 中最常用的两种处理器映射。它们都是AbstractHandlerMapping的子类并继承以下属性:
-
interceptors
: 在映射中使用的拦截器列表。 -
defaultHandler
: 缺省的处理器。 当没有合适的处理器可以匹配请求时,该处理器就会被使用。 -
order
: 根据每个映射的order属性值 (由org.springframework.core.Ordered 接口定义),Spring 将上下文中可用的映射进行排序,然后选用第一个和请求匹配的处理器。 -
alwaysUseFullPath
:如果这个属性被设成true,Spring 将会使用绝对路径在当前的servlet context中寻找合适的处理器。 这个属性的默认值是false,在这种情况下,Spring会使用当前servlet context中的相对路径。 例如,如果一个servlet在servlet-mapping中用的值是/testing/*,当alwaysUseFullPath 设成true时, 处理器映射中的URL格式应该使用/testing/viewPage.html,当这个属性设成false,同一个URL应该写成 /viewPage.html。 -
urlDecode
:这个属性的默认值是true。 如果想比较编码后的路径,可以把这个属性设为false。 不过,需要注意的是,HttpServletRequest总是返回解码后的servlet路径, 与编码后的格式进行比较时可能不会匹配。 -
lazyInitHandlers
:这个属性允许设置是否延迟singleton处理器的初始化工作(prototype处理器的初始化都是延迟的)。 这个属性的默认值是false。
BeanNameUrlHandlerMapping
BeanNameUrlHandlerMapping 是一个简单但功能强大的处理程序映射,它将传入的 HTTP 请求映射到 bean 的名称(这些 bean 需要在 Web 应用程序上下文中定义)。
<beans>
<bean id="handlerMapping" class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping"/>
<bean name="/editaccount.form" class="org.springframework.web.servlet.mvc.SimpleFormController">
<property name="formView" value="account"/>
</bean>
<beans>
<web-app>
...
<servlet>
<servlet-name>sample</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<!-- maps the sample dispatcher to *.form -->
<servlet-mapping>
<servlet-name>sample</servlet-name>
<url-pattern>*.form</url-pattern>
</servlet-mapping>
...
</web-app>
SimpleUrlHandlerMapping
另一个更强大的处理程序映射是 SimpleUrlHandlerMapping。它可在应用程序上下文中进行配置,并具有 Ant 风格的路径匹配功能。
<web-app>
...
<servlet>
<servlet-name>sample</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<!-- maps the sample dispatcher to *.form -->
<servlet-mapping>
<servlet-name>sample</servlet-name>
<url-pattern>*.form</url-pattern>
</servlet-mapping>
<!-- maps the sample dispatcher to *.html -->
<servlet-mapping>
<servlet-name>sample</servlet-name>
<url-pattern>*.html</url-pattern>
</servlet-mapping>
...
</web-app>
上述 web.xml 设置允许此示例 DispatcherServlet 处理所有以 .html 和 .form 结尾的请求。
<beans>
<!-- no 'id' required, HandlerMapping beans are automatically detected by the DispatcherServlet -->
<bean class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
<property name="mappings">
<value>
/*/account.form=editAccountFormController
/*/editaccount.form=editAccountFormController
/ex/view*.html=helpController
/**/help.html=helpController
</value>
</property>
</bean>
<bean id="helpController" class="org.springframework.web.servlet.mvc.UrlFilenameViewController"/>
<bean id="editAccountFormController" class="org.springframework.web.servlet.mvc.SimpleFormController">
<property name="formView" value="account"/>
</bean>
<beans>
该处理程序映射首先将所有目录中名为 help.html 的文件的请求传递给 helpController 。 helpController 是一个 UrlFilenameViewController(要了解有关控制器的更多信息,请参阅第 13.3 节“控制器”)。 ex 目录中所有以 view 开头并以 .html 结尾的请求都将传递给 helpController。
拦截器(HandlerInterceptor)
Spring的处理器映射支持拦截器。当您想要为某些请求提供特殊功能(例如验证用户身份)时,这非常有用。
处理器映射中的拦截器必须实现org.springframework.web.servlet包中的HandlerInterceptor接口。 这个接口定义了三个方法,preHandle(..)
,它在处理器实际执行 之前 会被执行; postHandle(..)
,它在处理器执行 完毕 以后被执行; afterCompletion(..)
,它在 整个请求处理完成 之后被执行。
拦截器可以通过interceptors属性进行配置。该选项在继承AbstractHandlerMapping的所有处理程序映射类HandlerMapping中提供配置接口。
<beans>
<bean id="handlerMapping" class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping">
<property name="interceptors">
<list>
<ref bean="officeHoursInterceptor"/>
</list>
</property>
</bean>
<bean id="officeHoursInterceptor" class="samples.TimeBasedAccessInterceptor">
</bean>
<beans>
package samples;
public class TimeBasedAccessInterceptor extends HandlerInterceptorAdapter {
public boolean preHandle(HttpServletRequest request, HttpServletResponse response,
Object handler) throws Exception {
response.sendRedirect("http://host.com/outsideOfficeHours.html");
return false;
}
}
在上面的例子中,该处理器处理的所有请求都会被TimeBasedAccessInterceptor拦截器拦截。
Spring的拦截器适配器HandlerInterceptorAdapter使得继承HandlerInterceptor接口变得更加容易。
视图与视图解析器 (View & View resolver)
所有 Web 应用程序的 MVC 框架都有自己的定位视图的方式。 Spring 提供了一个视图解析器,允许您在浏览器中显示模型数据,而无需绑定到特定的视图技术。 Spring 内置了对 JSP、Velocity 模板和 XSLT 视图的支持。
视图解析器(ViewResolver)
ViewResolver和View是Spring的视图处理方式中特别重要的两个接口。 ViewResolver提供了从视图名称到实际视图的映射。 View处理请求的准备工作,并将该请求提交给某种具体的视图技术。
SpringWeb框架的所有控制器都返回一个ModelAndView实例。 Sprnig中的视图以名字为标识,视图解析器通过名字来解析视图。Spring提供了多种视图解析器。
视图解析器 | 描述 |
---|---|
抽象缓存视图解析器 | 抽象视图解析器实现视图的缓存。在使用视图之前,通常需要做一些准备工作。从它继承的视图解析器将缓存要解析的视图。 |
XmlView解析器 | XmlViewResolver实现了ViewResolver并支持XML格式的配置文件。配置文件必须使用与 Spring XML Bean Factory 相同的 DTD。默认配置文件是/WEB-INF/views.xml。 |
资源包视图解析器 | ResourceBundleViewResolver 实现 ViewResolver 并在 ResourceBundle 中查找所需 bean 的定义。该包通常在类路径上的属性文件中定义。默认属性文件是views.properties。 |
基于Url的视图解析器 | UrlBasedViewResolver 实现了 ViewResolver,直接将视图名称解析为对应的 URL,无需显式映射定义。如果您的视图名称和视图资源名称一致,则无需映射即可使用此解析器。 |
内部资源视图解析器 | 作为 UrlBasedViewResolver 的子类,它支持 InternalResourceView(Servlet 和 JSP 的包装器)及其子类 JstlView 和 TilesView。通过setViewClass方法,您可以指定该解析器用来生成视图的视图类。 |
VelocityViewResolver/FreeMarkerViewResolver | 作为 UrlBasedViewResolver 的子类,它支持 VelocityView(Velocity 模板的包装器)和 FreeMarkerView 及其子类。 |
当使用JSP作为视图层技术时,可以使用UrlBasedViewResolver。该视图解析器会将视图名称解析为 URL,并将请求传递给 RequestDispatcher 以显示视图。依赖于jstl,需要导入依赖并注入到IoC容器中。
<bean id="viewResolver" class="org.springframework.web.servlet.view.UrlBasedViewResolver">
<property name="viewClass" value="org.springframework.web.servlet.view.InternalResourceViewResolver"/> <!--不能省略-->
<property name="prefix" value="/WEB-INF/jsp/"/>
<property name="suffix" value=".jsp"/>
</bean>
当返回的视图名为test时,该视图解析器将请求传递给RequestDispatcher,RequestDispatcher将请求传递给/WEB-INF/jsp/test.jsp。
ResourceBundleViewResolver
上述解析器特定于 ResourceBundleViewResolver,可在 Web 应用程序中混合不同的视图技术时使用。
<bean id="viewResolver" class="org.springframework.web.servlet.view.ResourceBundleViewResolver">
<property name="basename" value="views"/>
<property name="defaultParentView" value="parentView"/>
</bean>
ResourceBundleViewResolver通过basename指定的ResourceBundle解析视图名称。对于每个要解析的视图,ResourceBundle中的[视图名称].class对应的值就是实现该视图的类。同样,[视图名称].url对应的值为视图对应的URL。
视图解析链
Spring 支持多个视图解析器一起使用。将它们视为解析链。这有很多好处,例如在特定情况下重新定义某些视图。定义视图解析链很简单,只需在应用程序上下文中定义多个解析器即可。如果需要,还可以通过 order 属性声明每个解析器的顺序。需要记住的是,解析器的阶数越高,它在解析链中的位置就越靠下。
<bean id="jspViewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="viewClass" value="org.springframework.web.servlet.view.JstlView"/>
<property name="prefix" value="/WEB-INF/jsp/"/>
<property name="suffix" value=".jsp"/>
</bean>
<bean id="excelViewResolver" class="org.springframework.web.servlet.view.XmlViewResolver">
<property name="order" value="1"/>
<property name="location" value="/WEB-INF/views.xml"/>
</bean>
<!-- in views.xml -->
<beans>
<bean name="report" class="org.springframework.example.ReportExcelView"/>
</beans>
如果解析器没有找到合适的视图,Spring 将在上下文中查找是否配置了其他解析器。如果是,则继续解析,否则,Srping将抛出异常。
请记住,当视图解析器找不到合适的视图时,它可能会返回 null。然而,并不是每个解析器都这样做。这是因为,在某些情况下,解析器可能无法检测是否存在匹配的视图。例如,InternalResourceViewResolver在内部调用RequestDispatcher。请求调度是检查 JSP 文件是否存在的唯一方法。不幸的是,这种方法只能使用一次。 VelocityViewResolver 和其他解析器也存在同样的问题。使用这些解析器时,最好仔细阅读它们的 Javadoc,看看所需的解析器是否无法发现不存在的视图。这个问题的一个副作用是,如果InternalResourceViewResolver解析器没有放在链的末尾,那么InternalResourceViewResolver后面的那些解析器根本不会被使用,因为InternalResourceViewResolver总是返回一个视图!
重定向(Rediret)到另一个视图
控制器通常返回一个视图名称,然后由视图解析器将其解析为某个视图实现。对于像JSP这样实际由Servlet/JSP引擎处理的视图,我们通常使用InternalResourceViewResolver和InternalResourceView。该视图实现最终将调用 Servlet API 的 RequestDispatcher.forward(...) 方法或 RequestDispatcher.include() 方法将用户指向最终页面。对于其他视图技术(例如Velocity、XSLT等),视图本身生成返回给用户的内容。
重定向视图
在控制器中强制重定向的方法之一是让控制器创建并返回一个Spring的RedirectView的实例。 在这种情况下,DispatcherServlet不会使用通常的视图解析机制, 既然它已经拿到了一个(重定向)视图,它就让这个视图去完成余下的工作。
RedirectView 调用 HttpServletResponse.sendRedirect() 方法,这会导致 HTTP 重定向发送回用户的浏览器。所有模型属性都转换为 HTTP 请求的访问参数。这意味着该模型只能包含可以轻松转换为字符串形式的 HTTP 请求访问参数的对象,例如 String 或可以转换为 String 的类型。
如果您使用的是 RedirectView 并且它是由控制器创建的,则最好使用 Spring 提供的 IoC 功能将重定向的 URL 注入到控制器中。这样,URL 可以与视图名称一起在上下文中声明,而不是固定在控制器中。
重定向:前缀
尽管使用RedirectView帮我们达到了目的,但是如果控制器生成RedirectView的话, 控制器不可避免地要知道某个请求的结果是让用户重定向到另一个页面。这不是最佳的实现,因为这使得系统不同模块之间结合得过于紧密。 其实控制器不应该过问返回结果是如何生成的,通常情况下,它应该只关心注入给它的视图名称。
解决上述问题的方法是依靠redirect:前缀。如果返回的视图名称包含redirect:前缀,UrlBasedViewResolver(及其子类)将知道系统将生成HTTP重定向。视图名称的其余部分被视为重定向 URL。
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
@Controller
public class WebController {
@RequestMapping(value = "/index", method = RequestMethod.GET)
public String index() {
return "index";
}
@RequestMapping(value = "/redirect", method = RequestMethod.GET)
public String redirect() {
return "redirect:finalPage";
}
@RequestMapping(value = "/finalPage", method = RequestMethod.GET)
public String finalPage() {
return "final";
}
}
转发:前缀
类似的,我们也可以使用包含有forward:前缀的视图名。 这些视图名会被UrlBasedViewResolver和它的子类正确解析。 解析的内部实现是生成一个InternalResourceView, 这个视图最终会调用RequestDispatcher.forward()方法,将forward视图名的其余部分作为URL。 所以,当使用InternalResourceViewResolver/InternalResourceView, 并且你所用的视图技术是JSP时,你没有必要使用这个前缀。 但是,当你主要使用其它的视图技术,但仍需要对Servlet/JSP engine处理的页面强制forward时, 这个forward前缀还是很有用的。
转发
控制器类中处理方法的return语句默认是转发实现,但是实现的是转发到视图。 return 将相当于response。
@RequestMapping("/register")
public String register() {
return "register"; //转发到register.jsp
}
在Spring MVC框架中,无论是重定向还是转发,都需要遵守视图解析器的配置。如果直接转发到不需要DispatcherServlet的资源,例如:
return "forward:/html/my.html";
<mvc:resources location="/html/" mapping="/html/**" />
这样配置后html目录下的文件不需要经过DispatcherServlet 处理。
Spring MVC 使用闪存属性
Flash 属性为一个请求提供了一种为另一个请求存储有用属性的方法。这在重定向时最常用,例如常见的 POST/REDIRECT/GET 模式。 Flash属性在重定向之前会被临时保存(通常保存在会话中)。重定向后,它们将被下一个请求再次访问,并立即从原始存储位置删除。
为了支持 flash 属性,Spring MVC 提供了两个抽象。 FlashMap用于存储Flash属性,FlashMapManager用于存储、检索和管理FlashMap实例。
默认启用对 flash 属性的支持,无需显式声明,但在不使用时绝不会主动创建 HTTP 会话。对于每个请求,框架都会“传入”一个FlashMap,其中存储了上一个请求保存的属性(如果有);同时,每个请求还会“输出”一个FlashMap,其中存储了要传递给下一个请求的属性。请求使用的属性。通过 RequestContextUtils 实用程序类的静态方法,可以在 Spring MVC 应用程序中的任何位置获取两个 FlashMap 实例。
控制器通常不需要直接接触FlashMap。一般使用@RequestMapping方法接受RedirectAttributes类型的参数,然后直接为其添加flash属性。通过 RedirectAttributes 对象添加的 Flash 属性将自动填充到请求的“输出”FlashMap 对象中。同样,重定向后“传入”的FlashMap属性会自动添加到服务重定向URL的控制器参数Model中。
本地化解析器(LocaleResolver)
Spring架构的绝大部分都支持国际化,Spring的web MVC框架也不例外。 DispatcherServlet 允许使用客户端本地化信息自动解析消息。 这个工作由LocaleResolver
对象完成。
当收到请求时,DispatcherServlet查找一个本地化解析器,如果找到,就使用它设置本地化信息。 通过RequestContext.getLocale()
方法,总可以获取由本地化解析器解析的客户端的本地化信息。
AcceptHeaderLocaleResolver
该本地化解析器检查客户端浏览器在请求中发送的接受语言标头信息。通常此 HTTP 标头包含客户端操作系统的本地化信息。
CookieLocaleResolver
该本地化解析器检查客户端中的 cookie 是否包含本地化信息。如果可用,请使用它。配置此解析器时,您可以指定 cookie 名称和 cookie 的最大生存期(Max Age)。
<bean id="localeResolver" class="org.springframework.web.servlet.i18n.CookieLocaleResolver">
<property name="cookieName" value="clientlanguage"/>
<!-- in seconds. If set to -1, the cookie is not persisted (deleted when browser shuts down) -->
<property name="cookieMaxAge" value="100000">
</bean>
SessionLocaleResolver
SessionLocaleResolver 允许从与用户请求相关的会话中获取本地化信息。它可以检测请求中的特定参数,然后调用上下文中LocaleResolver中的setLocale()方法,并相应地修改本地化信息。 SessionLocaleResolver 会简单地取出与当前请求 HttpServletRequest 相关的相应 HttpSession 对象。属性并修改它们的值,仅此而已。
<bean id="localeChangeInterceptor" class="org.springframework.web.servlet.i18n.LocaleChangeInterceptor">
<property name="paramName" value="siteLanguage"/>
</bean>
<bean id="localeResolver" class="org.springframework.web.servlet.i18n.CookieLocaleResolver"/>
<bean id="urlMapping" class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
<property name="interceptors">
<list>
<ref bean="localeChangeInterceptor"/>
</list>
</property>
<property name="mappings">
<value>/**/*.view=someController</value>
</property>
</bean>
LocaleChangeInterceptor
可以使用 LocaleChangeInterceptor 修改本地化信息。该拦截器需要添加到处理器映射中
主题解析器(Theme resolver)
Spring的Web MVC框架允许您通过主题来控制网页的样式,这将进一步改善用户体验。简而言之,主题是一组影响应用程序视觉外观的静态资源(例如样式表和图像)。
为了在Web应用程序中使用主题,您需要设置org.springframework.ui.context.ThemeSource。 WebApplicationContext 扩展自 ThemeSource,但它不实现 ThemeSource 本身定义的方法。它将这些任务转移到其他专用模块。如果没有显式设置,实际实现ThemeSource的类是org.springframework.ui.context.support.ResourceBundleThemeSource。该类在类路径的根目录(例如,在 /WEB-INF/classes 目录中)查找适当的属性文件以完成配置。如果你想自己实现ThemeSource接口,或者需要配置ResourceBundleThemeSource所需的属性文件的前缀(basename前缀),可以在应用上下文中定义一个名为“themeSource”的bean(注意必须使用这个名字) )。 Web 应用程序上下文将自动检测并使用此 bean。
使用 ResourceBundleThemeSource 时,每个主题都配置有一个属性文件。该属性文件列出了形成主题所需的资源。
处理器异常解析器(Handler exception resolver)
Spring的处理器异常解析器HandlerExceptionResolver
接口的实现负责处理各类控制器执行过程中出现的异常。
实现HandlerExceptionResolver接口并非实现异常处理的唯一方式,它只是提供了resolveException(Exception, Hanlder)
方法的一个实现而已,方法会返回一个ModelAndView
。
SimpleMappingExceptionResolver 允许您获取可能抛出的异常类的名称并将其映射到视图名称。这在功能上等同于Servlet API提供的异常映射功能,但你也可以基于此实现更细粒度的异常映射。
而@ExceptionHandler
注解的方法则会在异常抛出时被调用以处理该异常。这样的方法可以定义在@Controller
注解的控制器类里,也可以定义在@ControllerAdvice
类中,后者可以使该异常处理方法被应用到更多的@Controller控制器中。
如果@ExceptionHandler
方法是在控制器内部定义的,那么它会接收并处理由控制器(或其任何子类)中的@RequestMapping
方法抛出的异常。如果你将@ExceptionHandler方法定义在@ControllerAdvice
类中,那么它会处理相关控制器中抛出的异常。
@Controller
public class SimpleController {
// @RequestMapping methods omitted ...
@ExceptionHandler(IOException.class)
public ResponseEntity<String> handleIOException(IOException ex) {
// prepare responseEntity
return responseEntity;
}
}
业务异常可以使用@ResponseStatus
来注解。当异常被抛出时,ResponseStatusExceptionResolver会设置相应的响应状态码。DispatcherServlet会默认注册一个ResponseStatusExceptionResolver 以供使用。
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.ResponseStatus;
@ResponseStatus(value=HttpStatus.FORBIDDEN,reason="用户不匹配")
public class UserNotMatchException extends RuntimeException{
}
文件上传解析器(Multipart File resolver)
Spring支持Web应用程序中的分段文件上传。此支持由即插即用的 MultipartResolver 提供。这些解析器在 org.springframework.web.multipart 包中定义。 Spring提供了一个现成的MultipartResolver,支持Commons FileUpload。
通常,Spring不处理文件上传,因为一些开发人员希望自己处理它们。如果你想使用Spring的这个功能,你需要在Web应用程序的上下文中添加一个分段文件解析器。这样,每个请求都会被检查以查看它是否包含文件上传。如果不是,则正常处理请求,否则调用应用程序上下文中定义的 MultipartResolver。然后,像其他属性一样处理请求中的文件属性。
styleSheet=/themes/cool/style.css
background=/themes/cool/img/coolBg.jpg
<%@ taglib prefix="spring" uri="http://www.springframework.org/tags"%>
<html>
<head>
<link rel="stylesheet" href="<spring:theme code="styleSheet"/>" type="text/css"/>
</head>
<body background="<spring:theme code="background"/>">
...
</body>
</html>
DispatcherServlet会查找一个名称为themeResolver的bean以确定使用哪个ThemeResolver的实现。主题解析器的工作原理与地区解析器LocaleResolver的工作原理大同小异。
MultipartResolver
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
<!-- file size in bytes -->
<property name="maxUploadSize" value="100000"/>
</bean>
CosMultipartResolver
<bean id="multipartResolver" class="org.springframework.web.multipart.cos.CosMultipartResolver">
<!-- file size in bytes -->
<property name="maxUploadSize" value="100000"/>
</bean>
当然,需要在classpath中为分段文件解析器提供正确的jar文件。 如果是CommonsMultipartResolver, 需要使用commons-fileupload.jar
,如果是CosMultipartResolver, 则使用cos.jar
。
当Spring的DispatcherServlet发现文件上传请求时,它会激活上下文中定义的解析器来处理该请求。然后这个解析器将当前的 HttpServletRequest 封装成 MultipartHttpServletRequest,它支持分段文件上传。使用MultipartHttpServletRequest,你可以获取请求中包含的上传信息,甚至可以获取控制器中分段文件的内容。
<html>
<head>
<title>Upload a file please</title>
</head>
<body>
<h1>Please upload a file</h1>
<form method="post" action="upload.form" enctype="multipart/form-data">
<input type="file" name="file"/>
<input type="submit"/>
</form>
</body>
</html>
表单中有一个输入元素。该元素(“文件”)的名称与在服务器端处理表单的 bean(下面提到)中 byte[] 类型的属性名称相同。在这个表单中,我们还声明了编码参数(enctype="multipart/form-data"),让浏览器知道如何对这个文件上传表单进行编码。
<beans>
<!-- lets use the Commons-based implementation of the MultipartResolver interface -->
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver"/>
<bean id="urlMapping" class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
<property name="mappings">
<value>
/upload.form=fileUploadController
</value>
</property>
</bean>
<bean id="fileUploadController" class="examples.FileUploadController">
<property name="commandClass" value="examples.FileUploadBean"/>
<property name="formView" value="fileuploadform"/>
<property name="successView" value="confirmation"/>
</bean>
</beans>
public class FileUploadController extends SimpleFormController {
protected ModelAndView onSubmit(
HttpServletRequest request,
HttpServletResponse response,
Object command,
BindException errors) throws ServletException, IOException {
// cast the bean
FileUploadBean bean = (FileUploadBean) command;
let's see if there's content there
MultipartFile file = bean.getFile();
if (file == null) {
// hmm, that's strange, the user did not upload anything
}
// well, let's do nothing with the bean for now and return
return super.onSubmit(request, response, command, errors);
}
}
public class FileUploadBean {
private MultipartFile file;
public void setFile(MultipartFile file) {
this.file = file;
}
public MultipartFile getFile() {
return file;
}
}
为了将上传的二进制数据保存为bean属性,必须通过ServletRequestDatabinder注册属性编辑器。 Spring 中内置了几个这样的编辑器,它们可以处理文件,然后将结果保存为 bean 属性。例如,StringMultipartEditor 可以将文件转换为字符串(使用用户声明的字符集)。 ByteArrayMultipartEditor 可以将文件转换为字节数组。它们的功能与CustomDateEditor类似。
总而言之,为了使用(HTML)表单上传文件,您需要声明一个解析器和一个控制器,然后将文件上传URL映射到控制器来处理请求。