第五步:适配器执行Handler;Handler是后端控制器,可以当成模型。
DispatcherServlet:前端控制器,由SpringMVC提供。
HandlerMapping:处理器映射器,由SpringMVC提供。
HandlerAdapter:处理器适配器,由SpringMVC提供。j解析方法上的参数,返回参数也会解析
Handler:处理器,需要程序员开发。
ViewResolver:视图解析器,由SpringMVC提供。
view:真正的视图页面,由程序员编写。
DispatcherServlet前端控制器通过HandlerMapping处理器映射器根据url找到Handler。
DispatcherServlet前端控制器通过HandlerAdapter处理器适配器执行Handler。
DispatcherServlet前端控制器得到Handler返回的ModelAndView通过视图解析器ViewResolver进行视图解析。
一.web.xml的配置
<servlet>
<servlet-name>springmvc</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<!-- 加载SpringMVC配置 -->
<init-param>
<!-- 配置文件的位置 -->
<!-- 如果不配置contextConfigLocation,
默认查找的配置文件名称classpath下的: servlet名称+"servlet.xml"即springmvc-servlet.xml
-->
<param-name>contextConfigLocation</param-name>
<param-value>classpath:springmvc.xml</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>springmvc</servlet-name>
<!--
可以配置/, 此工程 所有请求全部由springmvc解析,此种方式可以实现
RESTful方式,需要特殊处理对静态文本的解析不能由springmvc解析
可以配置*.do或*.action, 所有请求的url扩展名为.do或.action由springmvc解析,此种方法
不可以使用/*,如果配置/*,返回的jsp也由springmvc解析,这是不对的
-->
<url-pattern>*.action</url-pattern>
</servlet-mapping>
二、springmvc.xml的配置
在springmvc.xml中配置springmvc架构的三大组件(处理器映射器、处理器适配器、视图解析器)
1.处理器映射器
在springmvc.xml中进行配置:BeanNameURLHandlerMapping:根据url( xxx.action ) 匹配Spring容器的name。
找到对应的Bean(程序员编写的Handler)
<!-- 根据bean的name进行查找Handler 将action的url配置在bean的name中 -->
<bean class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping"/>
注意:所有处理器映射器都实现HandlerMapping接口。
其它映射器
SimpleUrlHandlerMapping(映射器)
<!-- 简单url映射 集中配置bean的id对应的url -->
<bean class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
<property name="mappings">
<props>
<prop key="itemsTest1.action">itemController1</prop>
<prop key="itemsTest2.action">itemController1</prop>
</props>
</property>
</bean>
注意:在springmvc.xml中配置了多个处理器映射器,多个处理器映射器就可以共存,不会排斥。
2.处理器适配器
在springmvc.xml中配置:
<!-- 配置处理器适配器 springmvc框架根据HandlerAdapter接口判断是否是处理器适配器 -->
<bean class="org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter"/>
注意:所有的适配器都是实现了HandlerAdapter接口。
程序员编写Handler要根据所配置的适配器的要求编写。
SimpleControllerHandlerAdapter适配器要求:通过supports方法知道Handler必须要实现哪个接口
查看HandlerAdapter(适配器接口)源代码如下:
public interface HandlerAdapter {
boolean supports(Object handler);
ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception;
long getLastModified(HttpServletRequest request, Object handler);
}
查看SimpleControllerHandlerAdapter适配器源码如下:
public class SimpleControllerHandlerAdapter implements HandlerAdapter {
public boolean supports(Object handler) {
return (handler instanceof Controller);
}
public ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
return ((Controller) handler).handleRequest(request, response);
}
public long getLastModified(HttpServletRequest request, Object handler) {
if (handler instanceof LastModified) {
return ((LastModified) handler).getLastModified(request);
}
return -1L;
}
}
可以发现SimpleControllerHandlerAdapter类型的处理器适配器通过support方法根据Handler对象是否实现了Controller接口来进行判断是否支持Handler,凡是实现了Controller接口的类都可以被简单类型的处理器适配器匹配调用,简单处理器适配器通过在handle方法中调用Handler实现Controller接口的HandleRequest方法向前端控制器返回ModelAndView。
Handler的编写
public class ItemController1 implements Controller {
public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception {
// 使用静态数据将商品信息展示在jsp页面上
// 商品列表
List<Items> itemsList = new ArrayList<Items>();
Items items_1 = new Items();
items_1.setName("联想笔记本");
items_1.setPrice(6000f);
items_1.setCreatetime(new Date());
items_1.setDetail("ThinkPad T430 联想笔记本电脑!");
Items items_2 = new Items();
items_2.setName("苹果手机");
items_2.setPrice(5000f);
items_2.setDetail("iphone6苹果手机!");
itemsList.add(items_1);
itemsList.add(items_2);
ModelAndView modelAndView = new ModelAndView();
// 将数据填充到request
// request.setAttribute("itemList", itemsList);
modelAndView.addObject("itemsList", itemsList);
// 指定转发到JSP页面
modelAndView.setViewName("/WEB-INF/jsp/itemsList.jsp");
return modelAndView;
}
}
配置Handler
在springmvc.xml中配置Handler,由Spring管理Handler。
<!-- 配置Handler 由于使用了BeanNameUrlHandlerMapping处理器映射器,name配置为url -->
<bean name="/itemList.action" class="lx.springmvc.first.ItemController1"/>
其它
HttpRequestHandlerAdapter(适配器)
在springmvc.xml中配置:HttpRequestHandlerAdapter
要求编写的Handler实现 HttpRequestHandler接口
3.配置视图解析器
<!-- 配置视图解析器 -->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"></bean>
三、注解映射器和适配器
注解映射器
Spring3.1之前默认加载器是org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping,3.1之后要使用org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping。
使用RequestMappingHandlerMapping 注解映射器需要在Handler中使用@Controller标识此类是一个控制器,使用@RequestMapping指定Handler方所对应的url。
注解适配器
Spring3.1之前默认加载映射器是org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter,3.1之后要使用org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter
RequestMappingHandlerAdapter 注解适配器不要求Handler实现任何接口,但它需要和RequestMappingHandlerMapping注解映射器配对使用,主要解析Handler方法中的形参。
注意:使用<mvc:annotation-driven/>可以替代上边的注解处理器映射器和注解处理器适配器的配置。
小结
DispatcherServlet:前端控制器,相当于中央调度器,可以降低组件之间的耦合度。
HandlerMapping:处理器映射器,负责根据url查找Handler。
HandlerAdapter:处理器适配器,负责根据适配器要求的规则去执行处理器。可以通过扩展适配器支持不同类型的Handler。
ViewResolver:视图解析器,根据逻辑视图名解析成真正的视图。
四、默认支持的参数类型
处理器方法形参中添加如下类型的参数,处理器适配器会默认识别并进行赋值的。
HttpServletRequest
通过request对象获取请求信息
HttpServletResponse
通过response处理响应信息
HttpSession
通过Session对象得到Session中存放的对象
Model
五、@RequestParam
如果request请求的参数名和controller方法的形参参数名一致,适配器自动进行参数绑定。如果不一致可以通过@RequestParam指定request请求的参数名(value属性)绑定到哪个方法形参上。对于必须要传入的参数,通过@RequestParam中的属性required设置为true,设置后如果不传入此参数则会报错。对于有些参数如果不传入,还需要设置默认值,使用@RequestParam中属性defaultvalue设置默认值。
可以绑定简单类型
可以绑定整型、字符串、单精/双精度、日期、布尔型
可以绑定简单pojo类型
简单pojo类型只包括简单类型的属性。
绑定过程:request请求的参数名称和pojo的属性名一致,就可以绑定成功。
六、自定义绑定使用属性编辑器
1.使用WebBindingInitializer(了解)
自定义WebBindingInitializer,注入到处理器适配器中。
如果想使多个controller共用属性编辑器,需要共同注册相同的属性编辑器,可以实现PropertyEditorRegistrar接口,并且注入到webBindingInitializer中。
public class CustomPropertyEditor implements PropertyEditorRegistrar {
public void registerCustomEditors(PropertyEditorRegistry binder) {
binder.registerCustomEditor(Date.class,
new CustomDateEditor(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"), true));
}
}
在springmvc.xml中添加如下配置
<!-- 注解适配器 -->
<bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter">
<!-- 在webBindingInitializer中注入自定义属性编辑器,自定义转换器 -->
<property name="webBindingInitializer" ref="customBinder"/>
</bean>
<!-- 自定义webBinder -->
<bean id="customBinder" class="org.springframework.web.bind.support.ConfigurableWebBindingInitializer">
<!-- propertyEditorRegistrars用于属性编辑器 -->
<property name="propertyEditorRegistrars">
<list>
<ref bean="customPropertyEditor"/>
</list>
</property>
</bean>
<!-- 注册属性编辑器 -->
2.自定义参数绑定使用转换器(重点)
实现Converter接口
配置转换器
配置方式:
自定义转换器实现Converter<K,V> 接口 K:原始数据类型 V:转换后的数据类型
自定义日期转换器
/**
* 自定义日期转换器
*/
public class CustomDateConverter implements Converter<String, Date> {
@Override
public Date convert(String source) {
// 进行日期转换
try {
return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").parse(source);
} catch (ParseException e) {
e.printStackTrace();
}
return null;
}
}
自定义去空格转换器
/**
* 自定义去除字符串前后空格的转换器
*/
public class StringTrimConverter implements Converter<String, String>{
@Override
public String convert(String source) {
//去掉字符串两边空格,如果去除后为空设置为null
if (source!=null) {
source = source.trim();
if (source.equals("")) {
return null;
}
}
return source;
}
}
配置如下
注册FormattingConversionServiceFactoryBean,并将自定义的转换器注入到converters数组属性中
<!-- 注册转换器 -->
<bean id="conversionService" class="org.springframework.format.support.FormattingConversionServiceFactoryBean">
<property name="converters">
<list>
<bean class="liuxun.ssm.controller.converter.CustomDateConverter"/>
<bean class="liuxun.ssm.controller.converter.StringTrimConverter"/>
</list>
</property>
</bean>
并将注册的转换器注入到webBinder中
<!-- 自定义webBinder -->
<bean id="customBinder" class="org.springframework.web.bind.support.ConfigurableWebBindingInitializer">
<!-- 使用converter进行参数转换 -->
<property name="conversionService" ref="conversionService"/>
</bean>
最后将其webBinder注入到注解适配器中
<!-- 注解适配器 -->
<bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter">
<!-- 在webBindingInitializer中注入自定义属性编辑器,自定义转换器 -->
<property name="webBindingInitializer" ref="customBinder"/>
</bean>
七、问题处理
post乱码在web.xml中加入
<!-- post乱码处理 -->
<filter>
<filter-name>CharacterEncodingFilter</filter-name>
<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
<init-param>
<param-name>encoding</param-name>
<param-value>utf-8</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>CharacterEncodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
get乱码
对于get请求中文参数出现乱码解决方法有两个
修改Tomcat配置文件添加编码与工程一致 如下
<Connector URIEncoding="utf-8" connectionTimeout="20000" port="8080" protocol="HTTP/1.1" redirectPort="8443"/>
另外一种方法对参数进行重新编码:
String userName=new String(request.getParamter("userName").getBytes("ISO8859-1"),"utf-8")
ISO8859-1是tomcat默认编码,需要将tomcat编码后的内容按utf-8编码