第二章 SpringMVC配置式开发
所谓配置式开发是指,“处理器类是程序员手工定义的、实现了特定接口的类,然后再在SpringMVC配置文件中对该类进行显式的、明确的注册”的开发方式。
2.1.1处理器映射器 HandlerMapping 通过注册
HandlerMapping 接口负责根据request 请求找到对应的Handler处理器及Interceptor拦截器,并将它们封装在HandlerExecutionChain对象中,返回给中央调度器。
其常用的实现类有两种:
BeanNameUrlHandlerMapping
SimpleUrlHandlerMapping
(1)BeanNameUrlHandlerMapping默认的
第一个程序里使用的就是这个。
BeanNameUrlHandlerMapping处理器映射器,会根据请求的url与spring容器中定义的处理器bean的name属性值进行匹配,从而在spring 容器中找到处理器bean实例。
其中第一行代码可以省略不写。
打开类的源码,从处理器映射器的方法中可以看出,对于处理器的Bean的名称,必须以“/”开头,否则无法加入到urIs数组中。
(2)SimpleUrlHandlerMapping
/11-simpleUrlHandlerMapping
使用BeanNameUrlHandlerMapping映射器有两点明显不足:
1、处理器Bean的id为一个url请求路径,而不是Bean的名称,有些不伦不类。
2、处理器Bean的定义与请求url绑定在了一起。若出现多个url请求同一个处理器的情况,就需要在Spring容器中配置多该处理器类的。这将导致容器会创建多个该处理器类实例。
SimpleUrlHandlerMapping处理器映射器,不仅可以将url与处理器的定义分离,还可以对url 进行统一映射管理。
SimpleUrlHandlerMapping处理器映射器,会根据请求的url与Spring容器中定义的处理器映射器子标签的key属性进行匹配。匹配上后,再将该key的value值与处理器bean的id值进行匹配,从而在spring容器中找到处理器bean。
两种方法
<!-- 注册处理器映射器 -->
<bean class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
<!--
<property name="mappings">
<props>
<prop key="/hello.do">myController</prop>
<prop key="/world.do">myController</prop>
<prop key="/my.do">myController</prop>
</props>
</property>
-->
<property name="urlMap">
<map>
<entry key="/hello.do" value="myController"></entry>
<entry key="/world.do" value="myController"></entry>
<entry key="/my.do" value="myController"></entry>
</map>
</property>
</bean>
<!-- 注册处理器对处理的请求做出限制,限制.do -->
<bean id="myController" class="com.bjpowernode.handlers.MyController"/>
2.1.2 处理器适配器 HandlerAdapter 通过实现接口
适配器模式解决的间题是,使得原本由于接口不兼容而不能一起工作的那些类可以在一起工作。所以处理器适配器所起的作用是,将多种处理器(实现了不同接口的处理器),通过处理器适配器的适配,使它们可以进行统一标准的工作,对请求进行统一方式的处理。
只所以要将Handler定义为Controller接口的实现类,就是因为这里使用的处理器适配器是SimpleControllerHandlerAdapter。打开其源码,可以看到将handler 强转为了Controller。
在定义Handler时,若不将其定义为Controller接口的实现类,这里的强转要出错的。
当然,中央调度器首先会调用该适配器的supports()方法,判断该Handler是否与Controller具有is-a关系。在具有is-a关系的前提下,才会强转。
(1)SimpleControllerHandlerAdapter
所有实现了Controller接口的处理器Bean,均是通过此适配器进行适配、执行的。Controller接口中有一个方法:就是我们在第一个程序中写过的
该方法用于处理用户提交的请求。通过调用Service层代码,实现对用户请求的计算响应,并最终将计算所得数据及要响应的页面,封装为一个对象ModelAndView,返回给中央调度器。
(2)HttpRequestHandlerAdapter
/12-httpRequestHandlerAdapter
所有实现了HttpRequestHandler接口的处理器Bean,均是通过此适配器进行适配、执行的。HttpRequestHandler接口中有一个方法:‘
该方法没有返回值,不能像ModelAndView一样,将数据及目标视图封装为一个对象。但可以将数据直接放入到request、session等域属性中,并由request或response完成到目标页面的跳转。
package com.bjpowernode.handlers;
public class MyController implements HttpRequestHandler {
@Override
public void handleRequest(HttpServletRequest request,
HttpServletResponse response)
throws ServletException, IOException {
request.setAttribute("message", "Hello SpringMVC World!");
request.getRequestDispatcher("/WEB-INF/jsp/welcome.jsp").forward(request, response);
}
}
当然,此时视图解析器也无需再配置前辍与后辍了。即在springmvc.xml中无需声明视图解析器的Bean了。即springmvc.xml文件中不用修改。
<!-- 注册处理器对处理的请求做出限制,限制.do -->
<bean id="/my.do" class="com.bjpowernode.handlers.MyController"/>
2.1.3 处理器 通过继承
处理器除了实现Controller接口外,还可以继承自一些其它的类来完成些特殊的功能。
(1)继承自AbstractController类
/13-abstractController 项目位置
若处理器继承自AbstractController类,那么该控制器就具有了一些新的功能。因为AbstractController类还继承自一个父类WebContentGenerator
WebContentGenerator 类具有supportedMethods属性,可以设置支持的HTTP数据提交方式。默认支持GET、POST。
若处理器继承自AbstractController类,那么处理器就可以通过属性supportedMethods来限制HTTP请求提交方式了。例如,指定只支持POST的HTTP请求提交方式。
<!-- 注册处理器 -->
<bean id="/my.do" class="com.bjpowernode.handlers.MyController">
<proprty name="supportedMethods" value="POST"></proprty>
</bean>
注意,这里的POST必须写为大写。
那就意味着该请求只能通过表单或AlAX请求方式进行提交,而不能通过地址栏、超链接、Html标签中的src方式进行提交。因为地址栏、超链接、Html标签中的src方式都是GET提交。否则,会给出请求方法不允许的405错误。
客户端访问浏览器常用的提交方式:
指定提交方法要么不写,则默认都可以;要么只写POST,支持一种。
(2)继承自MultiActionController类
/14-multiActionController 项目名称
MultiActionController 类继承自AbstractController,所以继承自MultiActionController类的子类也可以设置HTTP请求提交方式。
除此之外,继承自该类的处理器中可以定义多个处理方法。这些方法的签名为公共的方法,返回值为ModelAndView,包含参数HttpServletRequest 与HttpServletResponse,抛出Exception异常,方法名随意
1修改处理器类
要执行两个方法
public class MyController extends MultiActionController {
public ModelAndView doFirst(HttpServletRequest request,
HttpServletResponse response) {
ModelAndView mv = new ModelAndView();
mv.addObject("message", "执行doFirst()方法");
mv.setViewName("/WEB-INF/jsp/welcome.jsp");
return mv;
}
public ModelAndView doSecond(HttpServletRequest request,
HttpServletResponse response) {
ModelAndView mv = new ModelAndView();
mv.addObject("message", "执行doSecond()方法");
mv.setViewName("/WEB-INF/jsp/welcome.jsp");
return mv;
}
}
修改springmvc.xml
汪意处埋器类的映格的与法:要求必须以/xxx/*
的路径万式定又映剔路径。其中*为通配符,在访问时使用要访问的方法名代替。
因为有多个方法,所以采用SimpleUrlHandlerMapping处理器映射器
<bean class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
<property name="urlMap">
<map>
<entry key="/my/*.do" value-ref="myController"/>
</map>
</property>
</bean>
<!-- 注册处理器对处理的请求做出限制,限制.do -->
<bean id="myController" class="com.bjpowernode.handlers.MyController"/>
3访问
对doFirst()方法的访问请求是:
对doSecond()方法的访问请求是:
只所以通过在请求URI中写上方法名就可以访问到指定方法,是因为在MultiActionController 类中有一个专门处理方法名称的解析器MethodNameResolver。该解析器作为一个属性出现,具有get与set方法。MethodNameResolver是一个接口,不同的解析器实现类,其对方法名在URI中的写法要求也是不同的。
A、InternalPathMethodNameResolver 方法名解析器(默认)
该方法名解析器要求方法名以URI中资源名称的身份出现,即方法作为一种可以被请求的资源出现。也就是前面的写法:/xxx/方法名。
B、PropertiesMethodNameResolver 方法名解析器
该方法名解析器中的方法名是作为URI资源名称中的一部分出现的,即方法名并非单独作为一种资源名称出现。例如请求时可以写为/xxx_doFirst,则会访问xxx所映射的处理器的doFirst()方法。
/15-multiActionController-2
其他不变,只修改springmvc.xml
1、处理器映射器的请求格式发生了改变。
2、另外,还需要配置一个PropertiesMethodNameResolver 方法名解析器,并指定请求与要执行的方法名之间的映射关系。注意,这里的指定的请求,必须要加上.do,否则,无法完成匹配,将报404错误。
3、最后,将配置好的方法名解析器,注入给处理器。
其实完成的就是更多修饰访问名称限制的操作
<bean class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
<property name="urlMap">
<map>
<entry key="/my_*.do" value-ref="myController"/>
</map>
</property>
</bean>
<!-- 注册方法名称解析器 -->
<bean id="propertiesMethodNameResolver" class="org.springframework.web.servlet.mvc.multiaction.PropertiesMethodNameResolver">
<property name="mappings">
<props>
<prop key="/my_first.do">doFirst</prop>
<prop key="/my_second.do">doSecond</prop>
</props>
</property>
</bean>
<!-- 注册处理器 -->
<bean id="myController" class="com.bjpowernode.handlers.MyController">
<property name="methodNameResolver" ref="propertiesMethodNameResolver"/>
</bean>
对doFirst()方法的请求是:
http://localhost:8080/15-multiActionController-2/my_first.do
C、ParameterMethodNameResolver 方法名解析器
该方法名解析器中的方法名作为请求参数的值出现。例如请求时可以写为/xxx?ooo=doFirst,则会访问xxx所映射的处理器的doFirst()方法。其中ooo为该请求所携带的参数名,而doFirst则作为其参数值出现。
/16-multiActionController-3
直接修改springmvc.xml文件即可
<bean class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
<property name="urlMap">
<map>
<entry key="/my.do" value-ref="myController"/>
</map>
</property>
</bean>
<!-- 注册方法名称解析器 -->
<bean id="parameterMethodNameResolver" class="org.springframework.web.servlet.mvc.multiaction.ParameterMethodNameResolver">
<property name="paramName" value="xxx"/>
</bean>
<!-- 注册处理器 -->
<bean id="myController" class="com.bjpowernode.handlers.MyController">
<property name="methodNameResolver" ref="parameterMethodNameResolver"/>
</bean>
对doFirst()方法的访问请求是:
http://localhost:8080/16-multiActionController-3/my.do?xxx=doSecond
不过,打开ParameterMethodNameResolver源码,发现该类中有一个默认的参数action。即若不指定参数名称,则可以使用action作为参数。
2.1.4 视图解析器ViewResolver
springmvc.xml
注册XmlViewResolver视图解析器顺序1和ResourceBundleViewResolver视图解析器顺序3与优先级
<!-- ResourceBundle注册视图解析器1 -->
<bean class="org.springframework.web.servlet.view.ResourceBundleViewResolver">
<property name="basename" value="myViews"/>
<property name="order" value="3"/>
</bean>
<!-- xml注册视图解析器2 -->
<bean class="org.springframework.web.servlet.view.XmlViewResolver">
<property name="location" value="classpath:myViews.xml"/>
<property name="order" value="1"/>
</bean>
<!-- 默认的视图解析器优先级最低 -->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"/>
<!-- 注册处理器对处理的请求做出限制,限制.do -->
<bean id="/my.do" class="com.bjpowernode.handlers.MyController"/>
myViews.xml
定义XmlViewResolver视图解析器
<!-- 定义外部资源视图 -->
<bean id="ecommerce" class="org.springframework.web.servlet.view.RedirectView">
<property name="url" value="http://www.taobao.com"/>
</bean>
myViews.properties
定义ResourceBundleViewResolver视图解析器
ecommerce.(class)=org.springframework.web.servlet.view.RedirectView
ecommerce.url=http://www.jd.com
接口
public class MyController implements Controller {
@Override
public ModelAndView handleRequest(HttpServletRequest request,
HttpServletResponse response) throws Exception {
return new ModelAndView("ecommerce");
}
}