SpringMVC之二:配置 Spring MVC
Servlet 3.0规范在2009年12月份就发布了,因此很有可能你会将应用部署到支持Servlet 3.0的Servlet容器之中,如tomcat7.0及以上。在Servlet 3 规范中,可以使用 javaConfig
来配置 servlet,而不仅仅是 xml 文件。这里主要介绍如何使用 javaConfig
配置 web 应用和 spring MVC。
开启 Spring MVC 支持
Spring 使用如下方法开启 MVC 的支持:
@EnableWebMvc
注解(JavaConfig):和@Configuration
注解一起使用<mvc:annotation-driven />
元素(XML 配置)
@EnableWebMvc
注解(JavaConfig)
1、新建一个springboot工程
2、Spring DispatcherServlet 配置
package com.dxz.mvcdemo1.config; import org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer; public class SpitterWebInitializer extends AbstractAnnotationConfigDispatcherServletInitializer { @Override protected Class<?>[] getRootConfigClasses() { return new Class<?>[] { RootConfig.class }; } @Override protected Class<?>[] getServletConfigClasses() { return new Class<?>[] { WebConfig.class }; } @Override protected String[] getServletMappings() { return new String[] { "/" }; } }
最简单的Spring MVC配置就是一个带有@EnableWebMvc注解的类:
package com.dxz.mvcdemo1.config; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; import org.springframework.web.servlet.ViewResolver; import org.springframework.web.servlet.config.annotation.DefaultServletHandlerConfigurer; import org.springframework.web.servlet.config.annotation.EnableWebMvc; import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter; import org.springframework.web.servlet.view.InternalResourceViewResolver; @Configuration @EnableWebMvc //启动Spring MVC @ComponentScan("com.dxz.mvcdemo1.web") //启动组件扫描 public class WebConfig extends WebMvcConfigurerAdapter { }
这可以运行起来,它的确能够启用Spring MVC,但还有不少问题要解决:
- 没有配置视图解析器。如果这样的话,Spring默认会使用BeanNameView-Resolver,这 个视图解析器会查找ID与视图名称匹配的bean,并且查找的bean要实现View接口,它以 这样的方式来解析视图。
- 没有启用组件扫描。这样的结果就是,Spring只能找到显式声明在配置类中的控制器。 这样配置的话,DispatcherServlet会映射为应用的默认Servlet,所以它会处理所有 的请求,包括对静态资源的请求,如图片和样式表(在大多数情况下,这可能并不是你想 要的效果)。
开启 MVC 支持,它会从 WebMvcConfigurationSupport
导入 Spring MVC 的配置,会在处理请求时加入注解的支持(比如 @RequestMapping
,@ExceptionHandler
等注解)。
如果需要自定义配置,从 @EnableWebMvc
的文档上来看,需要继承 @WebMvcConfigurer
接口或者继承基类 WebMvcConfigurerAdapter
(它继承了 @WebMvcConfigurer
接口,但是用空方法实现)。所以,覆盖相应的方法就能实现 mvc 配置的自定义。
那么,我们需要在 web mvc 配置中做哪些事情呢:
- 开启 ComponentScan
- View Resolver(视图解析)
- 静态文件处理
View Resolver 将在后面介绍,这里先讨论如何处理静态文件(html, css, js)
package com.dxz.mvcdemo1.config; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; import org.springframework.web.servlet.ViewResolver; import org.springframework.web.servlet.config.annotation.DefaultServletHandlerConfigurer; import org.springframework.web.servlet.config.annotation.EnableWebMvc; import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter; import org.springframework.web.servlet.view.InternalResourceViewResolver; @Configuration @EnableWebMvc //启动Spring MVC @ComponentScan("com.dxz.mvcdemo1.web") //启动组件扫描 public class WebConfig extends WebMvcConfigurerAdapter { //配置JSP视图解析器 @Bean public ViewResolver viewResolver() { InternalResourceViewResolver resolver = new InternalResourceViewResolver(); resolver.setPrefix("/WEB-INF/views/"); resolver.setSuffix(".jsp"); return resolver; } //配置静态资源的处理 @Override public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) { //对"静态文件"开启默认转发给servlet容器(如tomcat)处理 configurer.enable(); } }
静态文件处理
Spring 可以有两种方式处理静态文件:
- 转发到默认的 web 服务器的 servlet 处理(比如 tomcat 来处理)
- 使用 Spring ResourceHandler 处理
使用这两种办法都需要继承 WebMvcConfigurerAdapter
基类,覆盖其中相应的方法实现。
默认 Servlet 处理
@Override public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) { //对"静态文件"开启默认转发给servlet容器(如tomcat)处理 configurer.enable(); }
如此配置后,如果 Sping 遇到没有 mapping 的 url 地址,就会转发到默认的 Servlet 处理(如 tomcat)。这其中就包括静态文件(前提是你没有为静态文件设置 RequestMapping)。
Spring ResourceHandler
使用 Spring ResourceHandler 可以使用 Spring 来处理静态文件:
@Override public void addResourceHandlers(ResourceHandlerRegistry registry) { registry .addResourceHandler("/resources/**") .addResourceLocations("/resources/", "classpath:/resources/"); }
我们为 url 地址符合 /resource/**
的文件设置了指定的文件路径,spring 会按照配置的先后顺序在指定的路径中查找文件是否存在并返回。
Spring 4.1 提供了新的静态资源的特性 ResourceResolvers
和 ResourceTransformers
,具体用法请参考 Spring Framework 4.1 - handling static web resources。
因为本章聚焦于Web开发,而Web相关的配置通 过DispatcherServlet创建的应用上下文都已经配置好了,因此现在的RootConfig相对 很简单:
RootConfig
配置:
package com.dxz.mvcdemo1.config; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.ComponentScan.Filter; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.FilterType; import org.springframework.web.servlet.config.annotation.EnableWebMvc; @Configuration @ComponentScan(basePackages = { "com.dxz.mvcdemo1" }, excludeFilters = { @Filter(type = FilterType.ANNOTATION, value = EnableWebMvc.class) }) public class RootConfig { }
配置很简单,因为还没有配置数据库等,所以只是开启了 ComponentScan,通过注解排除了 WebConfig
文件。唯一需要注意的是RootConfig使用了@ComponentScan注解。这样的话,在本书中,我们 就有很多机会用非Web的组件来充实完善RootConfig。
Spring 控制器
在 Spring MVC 中,控制器就是一个类,其中有很多被 @RequestMapping
注解的方法,标明它处理的请求类型。
package com.dxz.mvcdemo1.web; import static org.springframework.web.bind.annotation.RequestMethod.*; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.web.bind.annotation.RequestMapping; @Controller public class HomeController { @RequestMapping(value = "/", method = GET) public String home(Model model) { return "home"; } }
@Controller
注解基于 @Component
注解,标明这是一个控制器,但是完全可以使用 @Component
注解,只是 @Controller
更明确。
@RequestMapping
的 value 值表示这个控制器处理的请求路径,而 methos
属性标明它能够处理的 HTTP 方法是 GET
方法。
在 home
方法中,参数 model
可用于给 ViewResolver 传递数据。Model
也可用 Map
代替。
home
方法返回的是一个字符串 home
,标明用于处理该视图的视图名称为 home
。可能是 jsp,也可能是 velocity 模板,取决于你使用的视图。前面我们说过,Spring MVC 最后都会有一个视图解析的过程,它始终需要解析到一个视图上,然后返回 html 页面给 client。所以,视图解析就可能给这个 home
视图名称加上前缀和后缀,然后找到他的位置,然后处理数据(也就是控制器传入的 Model
),然后把处理过后得到的页面返回给 client。
如果使用前面配置的 InternalResourceViewResolver
,那么 home
视图就会被解析到 /WEB-INF/views/home.jsp。然后在 jsp 中就可以访问 Model
中的数据。如果返回的不是字符串指定视图名,那么 Spring 会使用方法名称作为视图名称。
home.jsp
<html> <head> <title>Spittr</title> <link rel="stylesheet" type="text/css" href="http://localhost:8080/resources/style.css"> </head> <body> <h1>Welcome to Spittr</h1> <a href="http://localhost:8080/spittles">Spittles</a> | <a href="http://localhost:8080/spittles/register">Register</a> </body> </html>
home.jsp所在位置
不过,你也可以把 @RequestMapping
注解加在类上,它会应用在所有的方法的 @RequestMapping
之上。
package com.dxz.mvcdemo1.web; import static org.springframework.web.bind.annotation.RequestMethod.*; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.web.bind.annotation.RequestMapping; @Controller @RequestMapping("/home") public class HomeController { // 这会处理 /home/page 的GET请求 @RequestMapping(value = "/page", method = GET) public String home(Model model) { return "home"; } }
测试一把,用浏览器访问:http://localhost:8080/home/page
二、XML配置web及SpringMVC
2.1、配置分发器
DispatcherServlet 是Spring MVC 的入口,所有进入Spring Web 的 Request 都经过 DispatcherServlet来分发。
需要在 web.xml 中注册 DispatcherServlet
<servlet> <servlet-name>dispatherContext</servlet-name> <servlet-class> org.springframework.web.servlet.DispatcherServlet </servlet-class> <load-on-startup>1</load-on-startup> </servlet>
加载 DispatcherServlet 时 Spring 会尝试读取配置文件
默认的配置文件位于 web.xml 相同的路径下,文件名与注册的 Servlet名有关 Servlet注册名跟上 -servlet.xml
例如:上面的 Servlet 注册名为 dispatcherContext 那么默认的配置文件名位:dispatcherContext-servlet.xml
当然 也可以明确配置文件 需要在注册 servlet 时 设定初始化参数
<init-param> <param-name>contextConfigLocation</param-name> <param-value> <!-- 配置文件名 --> </param-value> </init-param>
注册 DispatcherServlet 后 还应指定有 Spring 处理的 url 模板
<servlet-mapping> <servlet-name>dispatherContextServlet</servlet-name> <url-pattern>*.do</url-pattern> </servlet-mapping>
这样 请求 .do 的处理 就全部交由 Spring 处理了
当程序越来越大 配置文件中的 <bean> 越来越多 而且变得关系错综复杂,难于维护 此时应该考虑 将配置文件拆分成多个
为了让 Spring 能够读到这些配置文件,并察觉到他们的变化
需要注册配置文件读取器
对于 Servlet 2.3 以上标准 且 web 容器支持监听器,可以 在 web.xml 中注册监听
<listener> <listener-class> org.springframework.web.context.ContextLoaderListener </listener-class> </listener>
对于 Servlet 2.3 以下版本 由于不支持监听器 所以需要注册 Servlet
<servlet> <servlet-name>contextLoader</servlet-name> <servlet-class> org.springframework.web.context.ContextLoaderServlet </servlet-class> <load-on-startup>1</load-on-startup> </servlet>
配置文件读取器 注册成功后 需要设定配置文件列表
设置全局参数 contextConfigLocation
置为 配置文件列表 以逗号分隔 注意路径
<context-param> <param-name>contextConfigLocation</param-name> <param-value> /WEB-INF/dispatcherContext-servlet.xml, <!-- classpath*: 指定编译后的class目录 在ide中 与src根目录相同 --> classpath*:hibernateContext.xml </param-value> </context-param>
2.2、配置映射响应器(HandlerMapping)
当 DispatcherServlet 接到请求后会向 HandlerMapping询问,请求所对应的控制器
BeanNameUrlHandlerMapping:Spring 默认的映射响应器,根据 <bean> 的 name 属性查找控制器处理请求
<bean id="urlMapping" class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping" />
SimpleUrlHandlerMapping:Spring 中最常用的映射响应器,通过对其 mappings 进行设置从而获得更为灵活的控制器查找机制:
<bean id="urlMapping" class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping"> <property name="mappings"> <props> <prop key="/<!-- url 模板 -->.do"><!-- 控制器 <bean> 的 id --></prop> </props> </property> </bean>
CommonsPathMapHandlerMapping: 应用了 jdk1.5 后的新特性,通过 Controller 中的注释进行映射,在类的主是中加入 @@org.springframework.web.servlet.handler.commonsattributes.PathMap("/path.do")
<bean id="urlMapping" class="org.springframework.web.servlet.handler.metadata.CommonsPathMapHandlerMapping" />
2.3、配置控制器(Controller)
当 DispatcherServlet 接到请求后,通过 HandlerMapping 询问请求所对应的处理控制器后,在 dispatcherContext-servlet.xml 中查找相对应得 <bean> 处理请求.当选用了 BeanNameUrlHandlerMapping 映射响应器时,各个处理控制器应保证 <bean> 的 name属性即为请求的 url 模板.
例如:
<bean name="/home.do" class="<!-- 包名 -->.HomeController" />
当选用了 SimpleUrlHandlerMapping 映射响应器时,各个处理控制器应保证 <bean> 的 id属性与SimpleUrlHandlerMapping 中的 mappings 对应.
例如:
<bean id="homeAction" class="<!-- 包名 -->.HomeController" /> <bean id="urlMapping" class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping"> <property name="mappings"> <props> <prop key="/hello.do">homeAction</prop> </props> </property> </bean>
当选用了 CommonsPathMapHandlerMapping 映射响应器时
/** * @@org.springframework.web.servlet.handler.commonsattributes.PathMap("/hello.do") */ public class HelloController extends AbstractCommandController { ... }
2.4、配置视图解析器(ViewResolver)
<bean id="viewResolver" 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>