SpringMVC的请求和响应过程
图片来自网络)
1.客户端请求提交到DispatcherServlet
2.DispatcherServlet查询一个或多个HandlerMapping,找到处理请求的Controller
3.DispatcherServlet将请求提交到Controller
4.Controller调用业务逻辑处理后,返回ModelAndView给DispatcherServlet
5.DispatcherServlet查询一个或多个ViewResolver视图解析器,找到ModelAndView指定的视图
6.视图负责将结果显示到客户端
SpringMVC的核心组件DispatcherServlet、HandlerMapping、Controller、HandlerInterceptor、ViewResolver、View、LocalResolver、HandlerExceptionResolver、ModelAndView、HandlerAdapter
Didspatcher作为前端,也是核心控制器,它拦截匹配的请求,依据HandlerMapping实现类配置的规则转发到相应的Controller中去。
Dispatcher的配置例子:
- <!-- 配置前段控制器 -->
- <servlet>
- <servlet-name>dispatcher</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
- <load-on-startup>1</load-on-startup>
- </servlet>
- <servlet-mapping>
- <servlet-name>dispatcher</servlet-name>
- <url-pattern>*.html</url-pattern>
- </servlet-mapping>
配置DispatcherServlet只需要像配置正常的Servlet即可,<servlet-name>表示该Dispatcher的名称,可以配置多个,但是要用不同的名称进行区分,而<url-pattern>则表示会拦截以.html结尾的请求。
在默认情况下不需要指定特定的配置文件,当应用服务器启动时,DidspatcherServlet它会在web应用的WEB-INF目录下去寻找在web.xml中配置DispatcherServlet的<servlet-name>中名称一致的[servlet-name]-servlet.xml文件,去初实例化文件中配置的Bean,如果此文件不存在,则会抛出异常信息。
信息如下:
- org.springframework.beans.factory.BeanDefinitionStoreException: IOException parsing XML document from ServletContext resource [/WEB-INF/dispatcher-servlet.xml]; nested exception is java.io.FileNotFoundException: Could not open ServletContext resource [/WEB-INF/dispatcher-servlet.xml]
如果不想使用默认的配置,也可以进行指定配置文件名称
配置如下:
- <!-- 配置前段控制器 -->
- <servlet>
- <servlet-name>dispatcher</servlet-name>
- <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
- <init-param>
- <param-name>contextConfigLocation</param-name>
- <param-value>/WEB-INF/extends-bean.xml</param-value>
- </init-param>
- <load-on-startup>1</load-on-startup>
- </servlet>
- <servlet-mapping>
- <servlet-name>dispatcher</servlet-name>
- <url-pattern>*.html</url-pattern>
- </servlet-mapping>
如果放置在类路径下,那么也可以使用相应的配置:
配置如下:
- <param-value>classpath:/extends-bean.xml</param-value>
**如果以上两种方式都有多个配置文件,那么可以使用逗号隔开!
**DispatcherServlet的初始化过程可以参考:http://jinnianshilongnian.iteye.com/blog/1602617
DispatcherServlet是一个Servlet,它有自己的上下文,它的上下文继承自Spring容器的上下文。DispatcherServlet的是子上下文,而Spring容器的上下文是父上下文,子上下文可以访问并且重新覆盖父上下文的信息,但是父上下文无法访问子上下文的信息。多个DispatcherServlet的上下文之间也无法互相范文。
子上下文覆盖父上下文的例子:
1.web.xml中的配置
- <context-param>
- <param-name>contextConfigLocation</param-name>
- <param-value>/WEB-INF/dao-bean.xml,/WEB-INF/service-bean.xml</param-value>
- </context-param>
- <!-- 监听spring配置文件 -->
- <listener>
- <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
- </listener>
- <!-- 配置前段控制器 -->
- <servlet>
- <servlet-name>dispatcher</servlet-name>
- <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
- <init-param>
- <param-name>contextConfigLocation</param-name>
- <param-value>/WEB-INF/extends-bean.xml</param-value>
- </init-param>
- <load-on-startup>1</load-on-startup>
- </servlet>
使用ContextLoaderListener加载初始化配置文件。
2.service-bean.xml文件的配置:
- <bean id="service" class="cn.changluo.service.impl.UserServiceImpl">
- <property name="userDao" ref="dao"></property>
- <property name="username" value="lisi"></property>
- </bean>
此service Bean属于spring容器的上下文,为bean的username属性设置值为lisi
3.extends-bean.xml文件的配置:
- <bean id="service" class="cn.changluo.service.impl.UserServiceImpl">
- <property name="username" value="zhangsan"></property>
- <property name="userDao" ref="dao"></property>
- </bean>
此service Bean属于DispatcherServlet的上下文,同时为username属性设置值为zhangsan。
执行了相应的方法之后,发现输出为:
也就是说,子上下文中的bean覆盖了父上下文中的bean。
正确的理解和使用上下文可以帮助我们较好的分层,当分为多个层时,各个层之间该放置于那个上下文中,可以更好的管理和维护。如果不想使用父上下文,那么只需要使用子上下文即可。
SpringMVC将请求映射到处理程序的方法
在SpringMVC中,Web请求被应用程序的上下文中声明的一个或者多个处理程序映射到处理程序上。这些Bean都必须实现了HandlerMapping接口,这样DispatcherServlet才能自动侦测他们。
1.按Bean名称映射请求
最简单的策略就是按照处理程序的Bean名称进行映射,这也是默认的策略。为了让这个策略生效,必须将每个处理程序的Bean名称声明成URL模式的形式。可以使用通配符。
XML配置文件示例:
- <bean name="/hello" class="mvcdemo.controller.HelloWorldController"/>
SpringMVC提供了几个HandlerMapping实现,可以根据不同的策略来映射。默认情况下,如果你没有显示的配置处理程序映射,DispatcherServlet将利用BeanNameUrlHandlerMapping作为它的默认处理程序,它根据Bean名称中指定的Url模式将请求映射到处理程序上。
使用此映射策略是时,必须通过name属性设置处理程序的名称。
2.按控制器类名称映射请求
它根据Web应用的上下文中声明的控制器的类名称,自动生成映射。Spring有提供一个专门的类ControllerClassNameHandlerMapping,它实现了Controller接口的处理程序生成处理程序的映射。
XML配置文件示例:
- <bean class="org.springframework.web.servlet.mvc.support.ControllerClassNameHandlerMapping">
- <!—让URL遵守JAVA的变量命名约定-->
- <property name="caseSensitive" value="true"></property>
- <!--
- <property name="pathPrefix" value="/testproject"></property>
- <property name="basePackage" value="mvcdemo.controller"></property>
- -->
- </bean>
- <bean class="mvcdemo.controller.HelloWorldController"/>
对于HelloWorldController这个Bean,ControllerClassNameHandlerMapping会移除类名称中的Controller后缀,并将其他部分转换成小写字母,以此来生成处理程序映射。
HelloWorldController---------/helloworld*
加上了以上的property后,变为
HelloWorldController---------/helloWorld*
使用此方法时,必须在XML中显示注册ControllerClassNameHandlerMapping这个类,并且控制处理程序必须以Controller结尾。
3.用户定制的映射定义来映射请求
将请求映射到处理程序最直接最灵活的策略,是显式的指定URL模式和处理程序之间的映射定义,可以通过SimpleUrlHandlerMapping来做到。
XML配置文件示例:
- <!-- 配置请求的Controller的业务类 -->
- <bean id="loginController" class="cn.changluo.controller.HelloWorldController">
- <property name="failView" value="login"></property>
- <property name="successView" value="success"></property>
- </bean>
- <!-- 配置Controller和URL的映射关系 -->
- <bean id="urlMapping" class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
- <property name="mappings">
- <props>
- <prop key="/login.html">loginController</prop>
- </props>
- </property>
- </bean>
SimpleUrlHandlerMapping接受Properties对象形式的映射定义。属性是URL模式,而属性值则是处理器ID或者名称。URL模式也可以接受通配符,让处理程序处理多个URL。
HandlerInterceptor是SpringMVC提供的拦截器,你可以通过处理拦截器,拦截Web请求,进行前置处理和后置处理。拦截器是在SpringMVC应用程序的上下文中配置的,因此它们可以利用各种容器特性,并引用容器中声明的任何bean。处理拦截是针对特殊的处理程序映射进行注册的,因此它只拦截通过这些处理程序映射的请求。
每个拦截器都必须实现HandlerInterceptor这个接口,它有三个方法:preHandler()、postHandler()、afterCompletion()。第一个和第二个方法分别是在处理程序处理请求之前和之后被调用的。第二个方法还允许访问返回的ModelAndView对象。最后一个方法是在所有请求处理完之后被调用的,也就是呈现视图之后。
preHandler()方法返回true时,继续处理请求,否则直接将响应返回给用户。
SpringMVC允许拦截器继承自HandlerInterceptorAdapter,实现HandlerInterceptor需要实现所有的方法,当我们仅仅需要实现其中的某个方法时,则可以继承HandlerInterceptorAdapter
拦截器示例:
1.拦截器类:
- public class MyInterceptor implements HandlerInterceptor {
- @Override
- public void afterCompletion(HttpServletRequest request,
- HttpServletResponse response, Object handler, Exception exception)
- throws Exception {
- System.out.println("afterCompletion");
- }
- @Override
- public void postHandle(HttpServletRequest request, HttpServletResponse response,
- Object handler, ModelAndView mv) throws Exception {
- System.out.println("postHandle");
- }
- @Override
- public boolean preHandle(HttpServletRequest request, HttpServletResponse response,
- Object handler) throws Exception {
- System.out.println("preHandle");
- return true;
- }
- }
第二个拦截器类同上,只是输出内容不同
2.interceptor-bean.xml配置
- <!-- 配置Controller和URL的映射关系 -->
- <bean id="urlMapping" class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
- <property name="interceptors">
- <list>
- <ref bean="myInterceptor1"/>
- <ref bean="myInterceptor3"/>
- </list>
- </property>
- <property name="mappings">
- <props>
- <!-- 通配符 -->
- <prop key="/context.html">contextController</prop>
- </props>
- </property>
- </bean>
- <bean id="myInterceptor1" class="cn.changluo.interceptor.MyInterceptor"></bean>
- <bean id="myInterceptor3" class="cn.changluo.interceptor.MyInterceptor3"></bean>
执行之后的结果如下:
- preHandle
- preHandle3
- contextcontroller
- postHandle3
- postHandle
- afterCompletion3
- afterCompletion
如果你在控制器返回的视图中输出一句话,就会发现,它是在afterCompletion这个方法输出之前调用
SpringMVC的重定向和转发
转发:将请求转发到另外一个Action中进行处理
重定向:重定向到应用或者其他外部资源,可以防止表单重复提交
转发和重定向的区别在于:
1.重定向时,浏览器上的地址栏改变,而转发时,浏览器的地址栏不变
2.重定向其实是产生了两次请求,没有,而转发只有一次请求
3.重定向可以到任何网址,转发必须是本站点的网址
在SpringMVC中,对于转发和重定向的写法有如下几种:
转发:
- return new ModelAndView("forward:/context.html")
重定向:
- ModelMap model = new ModelMap();
- model.addAttribute("param1", "param1");
- model.addAttribute("param2", "param2");
- return new ModelAndView("redirect:/index.jsp",model); //http://localhost:8083/mvcdemo/index.jsp?param1=param1¶m2=param2
- //添加了参数之后,会在URL重定向到相应的界面,并且后面加了一串参数
- //下面的这种方式结果和上面的一致
- RedirectView rv = new RedirectView("index.jsp");
- ModelMap model = new ModelMap();
- model.addAttribute("param1", "data1");
- model.addAttribute("param2", "data2");
- rv.setAttributesMap(model);
- return new ModelAndView(rv);
SpringMVC使用PDF和Excel视图
具体的解释可以参考Spring的文档
示例:
1.controller控制类
1)ExcelController类:
- public class ExcelController extends AbstractController{
- @SuppressWarnings("unchecked")
- @Override
- protected ModelAndView handleRequestInternal(HttpServletRequest request,
- HttpServletResponse response) throws Exception {
- List<String> list = getModelData();
- Map model = new HashMap();
- model.put("data", list);
- return new ModelAndView("exl",model);
- }
- public List<String> getModelData(){
- List<String> list = new ArrayList<String>();
- for(int i = 0 ;i<10;i++){
- list.add("this demo"+i);
- }
- return list;
- }
- }
2)PdfController类:
- public class PdfController extends AbstractController {
- @SuppressWarnings("unchecked")
- @Override
- protected ModelAndView handleRequestInternal(HttpServletRequest arg0,
- HttpServletResponse arg1) throws Exception {
- List<String> list = getModelData();
- Map model = new HashMap();
- model.put("data", list);
- return new ModelAndView("pdf", model);
- }
- public List<String> getModelData() {
- List<String> list = new ArrayList<String>();
- for (int i = 0; i < 10; i++) {
- list.add("this demo" + i);
- }
- return list;
- }
- }
2.demoview-bean.xml的配置
- <bean id="viewRosolver" class="org.springframework.web.servlet.view.ResourceBundleViewResolver">
- <!--
- ResourceBundleViewResolver视图解析器会在classpath中寻找properties属性文件,根据此bean配置的
- <property name="basename" value="view" />
- 说明寻找的properties文件名为view.properties
- -->
- <property name="basename" value="view"></property>
- <property name="order" value="2"></property>
- </bean>
- <!-- 配置Controller和URL的映射关系 -->
- <bean id="urlMapping" class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
- <property name="mappings">
- <props>
- <prop key="/show_excel.html">excelController</prop>
- <prop key="/show_pdf.html">pdfController</prop>
- </props>
- </property>
- </bean>
- <bean id="excelController" class="cn.changluo.controller.ExcelController"/>
- <bean id="pdfController" class="cn.changluo.controller.PdfController"/>
4.view.properties属性文件的配置
- exl.(class)=cn.changluo.view.ExcelHomepage
- pdf.(class)=cn.changluo.view.PdfHomepage
5.view.properties文件中配置的两个类的具体实现
1)ExcelHomepage类:
- public class ExcelHomepage extends AbstractExcelView {
- @SuppressWarnings("unchecked")
- @Override
- protected void buildExcelDocument(Map model, HSSFWorkbook wb,
- HttpServletRequest request, HttpServletResponse response)
- throws Exception {
- HSSFSheet sheet = null;
- HSSFCell cell = null;
- sheet = wb.createSheet("springMvc excel view");
- sheet.setDefaultColumnWidth((short)10);
- cell = getCell(sheet, 0, 0);
- setText(cell, "springMVC-excel");
- List<String> list = (List<String>)model.get("data");
- for(int i=0;i<list.size();i++){
- cell = getCell(sheet, 2, i);
- setText(cell, list.get(i));
- }
- }
- }
2)PdfHomepage类:
- public class PdfHomepage extends AbstractPdfView {
- @SuppressWarnings("unchecked")
- @Override
- protected void buildPdfDocument(Map model, Document doc, PdfWriter pw,HttpServletRequest request, HttpServletResponse response)
- throws Exception {
- List<String> words = (List<String>) model.get("data");
- for (int i = 0; i < words.size(); i++)
- doc.add(new Paragraph((String) words.get(i)));
- }
- }