springmvc处理器及前端控制器介绍

未分类 发表评论

处理器

(1)非注解的处理器映射器和适配器

处理器映射器

第一种非注解的映射器

<bean class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping"/>

另一种非注解的映射器

<bean class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">

具体配置如下

<!-- 配置Handler -->  
<bean name="/queryItems.action" 
    class="com.amuxia.controller.ItemsController" />  
<!-- 配置另外一个Handler -->  
<!-- 
     处理器映射器 将bean的name作为url进行查找,
     需要在配置Handler时指定beanname(就是url)   
     所有的映射器都实现 HandlerMapping接口。  
--> 
<bean class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping" />

也可以多个多个映射器并存,由DispatcherServlet(前端控制器)指定URL被哪个映射器处理,如下:

<!-- 配置Handler -->
<bean id="itemsController" name="/queryItems.action" 
    class="com.amuxia.ItemsController"/>
<!-- 
处理器映射器 将bean的name作为url进行查找,
需要在配置Handler时指定URL 
-->
<bean class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping"/>
<!-- 简单url映射-->
<bean class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">     
<property name="mappings">         
    <props>             
<!-- 对 itemsController进行url映射-->             
        <prop key="/queryItems1.action">items1</prop>             
        <prop key="/queryItems2.action">items2</prop>         
    </props>     
</property>
</bean>

处理器适配器

第一种非注解的适配器

<bean class="org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter" />

(要求编写的Handler实现Controller接口)

public class ItemsController implements Controller{      
    public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) 
                throws Exception {         
    ModelAndView modelAndView = new ModelAndView();         
    modelAndView.setViewName("view/list");         
    modelAndView.addObject("name", "张三");        
    return modelAndView;     
    }   
}

另一种非注解的适配器

<bean class="org.springframework.web.servlet.mvc.HttpRequestHandlerAdapter"/>

(要求编写的Handler实现HttpRequestHandler接口)

public class ItemsController implements HttpRequestHandler{
     public void handleRequest(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) 
               throws ServletException, IOException {      
    List<Items> itemsList = new ArrayList<Items>();             
    Items items = new Items();         
    items.setName("阿木侠");                      
    itemsList.add(items);                   
    httpServletRequest.setAttribute("list",itemsList);              
    httpServletRequest.getRequestDispatcher("view/list").forward(httpServletRequest,httpServletResponse);
    } 
 }

这里可以使用response设置响应格式,字符编码等

response.setCharacterEncoding("utf-8");

response.setContentType("application/json;charset=utf-8");

 

(2)基于注解的处理器映射器和适配器

注解映射器

<bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping"/> 


注解适配器

<bean  class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter"/>

注解的映射器和注解的适配器必须配对使用,spring对其有一个更简化的配置,可以使用下面的这段代码代替上面的两段配置

<mvc:annotation-driven></mvc:annotation-driven>

使用注解的映射器和注解的适配器,在具体的Java代码实现上有很大的不同,不再需要实现特定的接口,代码风格更加简化。

@Controllerpublic class ItemsController{    
    @RequestMapping("/items")    
    public ModelAndView items() throws Exception{
        List<Items> itemsList = new ArrayList<Items>();
        Items items = new Items();
        items.setName("阿木侠");
        itemsList.add(items);
        ModelAndView modelAndView = new ModelAndView();
        modelAndView.addObject("list",itemsList);
        modelAndView.setViewName("view/list");        return modelAndView;
    }
}


这里使用到了两个注解,也是最最基本的两个注解

        @Controller修饰类,用来标识它是一个控制器。

        @RequestMapping(“”)修饰方法或者类,这里表示实现对items方法和url进行映射,一个方法对应一个url。注意这里@RequestMapping(“”)中的名称一般和方法同名,但不是必须。

最后,还需要在spring容器中加载Handler,指定扫描controller。

<bean class="com.amuxia.controller.ItemsController"></bean>

这里需要对用到的所有的控制器类都需要在spring容器中加载Handler,但我们在实际开发中,所有的Controller一般都会放在某个包下,所以可以使用扫描组件扫描Controller包文件

<context:component-scan base-package="com.amuxia.controller"></context:component-scan>  

扩展

注解的处理器映射器和适配器在spring3.1之前和之后使用略有不同,现在一般使用spring3.1之后的,但对之前的需要有一个大概的认识,避免在旧项目中见到之后一脸懵逼。

在spring3.1之前使用(注解映射器)

org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping


在spring3.1之后使用(注解映射器)

org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping


在spring3.1之前使用(注解适配器)

org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter


在spring3.1之后使用(注解适配器)

org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter

前端控制器

配置

        第一种:*.action,访问以.action结尾 由DispatcherServlet进行解析

        第二种:/,所以访问的地址都由DispatcherServlet进行解析,对于静态文件的解析需要配置不让DispatcherServlet进行解析,使用此种方式可以实现RESTful风格的url

        在平常开发中使用第一种配置方法较多。

DispatcherServlet解析请求的过程:

        ①:DispatcherServlet是springmvc中的前端控制器,负责接收request并将request转发给对应的处理组件。

        ②:HanlerMapping是springmvc中完成url到controller映射的组件。DispatcherServlet接收request,然后从HandlerMapping查找处理request的controller。

        ③:Cntroller处理request,并返回ModelAndView对象,Controller是springmvc中负责处理request的组件(类似于struts2中的Action),ModelAndView是封装结果视图的组件。

        ④ ⑤ ⑥:视图解析器解析ModelAndView对象并返回对应的视图给客户端

        总的来说,前端控制器用来维护url和controller的映射,具体做法是对标记@Controller类中标识有@RequestMapping的方法进行映射。在@RequestMapping里边定义映射的url。

DispatcherServlet作用分析

        DispatcherServlet 是web服务器的入口,它继承自抽象类FrameworkServlet,也就是间接继承了HttpServlet。我们大家都知道,Servlet的生命周期是:初始化阶段——响应客户请求阶段——销毁。

        DispatcherServlet也是一种Servlet。所以,它的生命周期也分为这三个阶段,借助DispatcherServlet的生命周期的源码,我们可以对它有一个更好的理解。

初始化阶段

        DispatcherServlet继承FrameworkServlet类,使用initStrategies()方法初始化。

protected void initStrategies(ApplicationContext context) {
     initMultipartResolver(context); 
     //文件上传解析
     initLocaleResolver(context);    
     //本地化解析         
     initThemeResolver(context);        //主题解析         
     initHandlerMappings(context);   
     //通过HandlerMapping,将请求映射到处理器         
     initHandlerAdapters(context);   
     //通过HandlerAdapter支持多种类型的处理器         
     initHandlerExceptionResolvers(context); 
     //如果执行过程中遇到异常将交给HandlerExceptionResolver来解析         
     initRequestToViewNameTranslator(context); 
     //直接解析请求到视图名         
     initViewResolvers(context);      
     //通过ViewResolver解析逻辑视图名到具体视图实现         
     initFlashMapManager(context);   
     //flash映射管理器     
  }

响应客户请求阶段

/**
     * Exposes the DispatcherServlet-specific request attributes and delegates to {@link #doDispatch}
     * for the actual dispatching.
     */@Overrideprotected void doService(HttpServletRequest request, HttpServletResponse response) 
             throws Exception {        
    if (logger.isDebugEnabled()) {
        String resumed = WebAsyncUtils.getAsyncManager(request).hasConcurrentResult() ? " resumed" : "";
        logger.debug("DispatcherServlet with name '" + getServletName() + "'" + resumed +                    
        " processing " + request.getMethod() + " request for [" + getRequestUri(request) + "]");
     }        
    // Keep a snapshot of the request attributes in case of an include,
        // to be able to restore the original attributes after the include.
        Map<String, Object> attributesSnapshot = null;        
     if (WebUtils.isIncludeRequest(request)) {
         attributesSnapshot = new HashMap<String, Object>();
         Enumeration<?> attrNames = request.getAttributeNames();            
         while (attrNames.hasMoreElements()) {
            String attrName = (String) attrNames.nextElement();                
            if (this.cleanupAfterInclude || attrName.startsWith("org.springframework.web.servlet")) {
                  attributesSnapshot.put(attrName, request.getAttribute(attrName));
              }
           }
        }        
// Make framework objects available to handlers and view objects.
        request.setAttribute(WEB_APPLICATION_CONTEXT_ATTRIBUTE, getWebApplicationContext());
        request.setAttribute(LOCALE_RESOLVER_ATTRIBUTE, this.localeResolver);
        request.setAttribute(THEME_RESOLVER_ATTRIBUTE, this.themeResolver);
        request.setAttribute(THEME_SOURCE_ATTRIBUTE, getThemeSource());
        FlashMap inputFlashMap = this.flashMapManager.retrieveAndUpdate(request, response);        
            if (inputFlashMap != null) {
            request.setAttribute(INPUT_FLASH_MAP_ATTRIBUTE, Collections.unmodifiableMap(inputFlashMap));
        }
        request.setAttribute(OUTPUT_FLASH_MAP_ATTRIBUTE, new FlashMap());
        request.setAttribute(FLASH_MAP_MANAGER_ATTRIBUTE, this.flashMapManager);        
        try {
            doDispatch(request, response);
        }finally {            
            if (WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) {               
             return;
            }            // Restore the original attribute snapshot, in case of an include.
            if (attributesSnapshot != null) {
                restoreAttributesAfterInclude(request, attributesSnapshot);
            }
        }
    }

其中,doDispatch(request, response);实现请求的分发,流程是:

        1. 把请求分发到handler(按照配置顺序获取servlet的映射关系获取handler);

        2. 根据servlet已安装的  HandlerAdapters 去查询第一个能处理的handler;

        3. handler激发处理请求

doDispatch() 方法中的几个重要方法

getHandler(processedRequest) 

获取类HandlerExecutionChain ,存放这个 url 请求的各种信息的类(bean , method ,参数 , 拦截器 ,beanFactory 等等)

getHandlerAdapter(mappedHandler.getHandler())  

获取请求处理类 handlerAdapter ( 通过handlerAdapter类的 supports() 方法判断)

ha.handle(processedRequest, response, mappedHandler.getHandler()) 

返回 ModelAndView 视图(以及response)

processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException) 

结果处理方法以及异常处理

销毁阶段

public void destroy() {
        getServletContext().log("Destroying Spring FrameworkServlet '" + getServletName() + "'");        
// Only call close() on WebApplicationContext if locally managed...
        if (this.webApplicationContext instanceof 
                ConfigurableApplicationContext && !this.webApplicationContextInjected) {
            ((ConfigurableApplicationContext) this.webApplicationContext).close();
        }
    }

最后,展示基于注解的完整的springMVC配置文件

<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:mvc="http://www.springframework.org/schema/mvc"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans 
http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
http://www.springframework.org/schema/mvc 
http://www.springframework.org/schema/mvc/spring-mvc-4.0.xsd
http://www.springframework.org/schema/context 
http://www.springframework.org/schema/context/spring-context-4.0.xsd">

    <context:component-scan base-package="com.amuxia.controller">
    </context:component-scan>

    <mvc:annotation-driven></mvc:annotation-driven>

    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <!-- 配置jsp路径的前缀 -->
        <property name="prefix" value="/WEB-INF/jsp/"/>
        <!-- 配置jsp路径的后缀 -->
        <property name="suffix" value=".jsp"/>
    </bean>
</beans>

总结

        前端控制器的基本工作流程是:一个http请求到达服务器,被DispatcherServlet接收,DispatcherServlet将请求委派给合适的处理器Controller。

        此时处理控制权到达Controller对象。Controller内部完成请求的数据模型的创建和业务逻辑的处理,然后再将填充了数据后的模型(model)和控制权一并交还给DispatcherServlet,委派DispatcherServlet来渲染响应。

        DispatcherServlet再将这些数据和适当的数据模版视图结合,向Response输出响应。

发表评论

电子邮件地址不会被公开。 必填项已用*标注

昵称 *