第五步:适配器执行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编码

results matching ""

    No results matching ""