天天看点

SpringMVC源码解读 --- 处理器适配器 -- 1 (HandlerAdapter)的使用(以及这些处理器与适配器等是怎样被SpringMVC自动加载注入到容器中的)

1、HandlerAdapter的使用:

       我们在前一章讲了Handler(从HanlderMapping获取的是Handler),现在我们来梳理来HandlerAdapter是怎样被使用以及怎样被加载到Spring的bean容器中的(其他没有提到的内容一般也是本文下面介绍的几种加载方法)

    1、SimpleServletHandlerAdapter

    xml文件配置

<?xml version="1.0" encoding="UTF-8"?>
<beans
        xmlns="http://www.springframework.org/schema/beans"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xmlns:context="http://www.springframework.org/schema/context"
        xmlns:mvc="http://www.springframework.org/schema/mvc"
        xsi:schemaLocation="http://www.springframework.org/schema/beans
    http://www.springframework.org/schema/beans/spring-beans-3.2.xsd
    http://www.springframework.org/schema/context
    http://www.springframework.org/schema/context/spring-context-3.2.xsd
    http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.2.xsd
      http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.2.xsd">

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

    <bean id="beanNameUrlHandlerMapping"
          class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping">
    </bean>
    <bean id="myServlet" name="/myServletName" class="web.servlet.MyServlet"/>
    <alias name="myServlet" alias="/myServletAlias"/>
</bean>
           

  然后简单实现一个Serlvet

public class MyServlet extends HttpServlet{
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        System.out.println("Hello MyServlet");
    }
}
           

我们在浏览器输入地址:http://localhost:8080/myServletName  或http://localhost:8080/myServletAlias ,然后在后台就会打印"Hello MyServlet"

   2、SimpleControllerHandlerAdapter

     xml配置:

<?xml version="1.0" encoding="UTF-8"?>
<beans
        xmlns="http://www.springframework.org/schema/beans"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xmlns:context="http://www.springframework.org/schema/context"
        xmlns:mvc="http://www.springframework.org/schema/mvc"
        xsi:schemaLocation="http://www.springframework.org/schema/beans
    http://www.springframework.org/schema/beans/spring-beans-3.2.xsd
    http://www.springframework.org/schema/context
    http://www.springframework.org/schema/context/spring-context-3.2.xsd
    http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.2.xsd
      http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.2.xsd">

    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver" id="jspViewResolver">
        <property value="org.springframework.web.servlet.view.JstlView" name="viewClass"/>
        <property value="/WEB-INF/" name="prefix"/>
        <property value=".jsp" name="suffix"/>
    </bean>
    <bean id="welcomeController"
          class="web.SimpleController" />
</bean>
           

实现一个Controller接口: 

public class SimpleController implements Controller {
   public ModelAndView handleRequest(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws Exception {
      ModelAndView modelAndView = new ModelAndView();
      modelAndView.setViewName("springMethod");
      return modelAndView;
   }
}
           

jsp页面:

<%@ page language="java" contentType="text/html; charset=UTF-8"
         pageEncoding="UTF-8" isELIgnored="false"%>

<h1>Hello World</h1>
           

不过其实这里要注意的是,你应该也可以将这个Controller实现当做Servlet去用(也理解如果web容器用的是tomcat,其就不能脱离tomcat的标准,最终都会调用到tomcat那套标准)(不需要使用到视图的话,其实你是可以将handler(处理器)与View(视图)分开来看,视图是可有可无的),因为这里的入参就是httpServletRequest、httpServletResponse。如果将handler、与View分开来看的话,在上面的xml就不用注入InternalResourceViewResolver(视图解析器)。

    例如:

<?xml version="1.0" encoding="UTF-8"?>
<beans
        xmlns="http://www.springframework.org/schema/beans"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xmlns:context="http://www.springframework.org/schema/context"
        xmlns:mvc="http://www.springframework.org/schema/mvc"
        xsi:schemaLocation="http://www.springframework.org/schema/beans
    http://www.springframework.org/schema/beans/spring-beans-3.2.xsd
    http://www.springframework.org/schema/context
    http://www.springframework.org/schema/context/spring-context-3.2.xsd
    http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.2.xsd
      http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.2.xsd">

    <bean id="beanNameUrlHandlerMapping"
          class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping">
    </bean>
    <bean id="welcomeController" name="/simpleController"
          class="web.SimpleController" />
</bean>
           
public class SimpleController implements Controller {
   public ModelAndView handleRequest(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws Exception {
      PrintWriter out = httpServletResponse.getWriter();
      out.println("Hello SimpleController");
      out.close();
      return null;
   }
}
           

  然后在页面请求: http://localhost:8080/simpleController,就会在页面输出"Hello SimpleController"。

  3、RequestMappingHandlerAdapter

    这个是与前面HandlerMapping的应的。其用法:

    xml文件:

<?xml version="1.0" encoding="UTF-8"?>
<beans
        xmlns="http://www.springframework.org/schema/beans"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xmlns:context="http://www.springframework.org/schema/context"
        xmlns:mvc="http://www.springframework.org/schema/mvc"
        xsi:schemaLocation="http://www.springframework.org/schema/beans
    http://www.springframework.org/schema/beans/spring-beans-3.2.xsd
    http://www.springframework.org/schema/context
    http://www.springframework.org/schema/context/spring-context-3.2.xsd
    http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.2.xsd
      http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.2.xsd">
    <context:component-scan base-package="web" />   //对应java文件扫描的包名
    <mvc:annotation-driven/>
</bean
           
@RestController
public class SpringController{

    @RequestMapping(value = "/spring/{name}", method = RequestMethod.GET)
    public String springMethod(@PathVariable("name") String name) {
        return "springMethod23";
    }
}
           

然后在包web下面创建这个java文件就可以了。

请求就会再页面输出"springMethod23",因为加的@RestController,如果是@Controller,你也可以与视图解析器一起使用来处理视图(就像前面使用一样,可以将适配器与视图解析器分开来看,不一定都需要视图解析器)。

2、HandlerAdapter、HandlerMappng等容器本身的类是怎样被注入到SpringMVC bean容器中的

    1、我们在前面章节将DispatcherServlet的init方法讲过BeanDefinitionParser接口,这里再来粗略回顾下其的用法。

public class MvcNamespaceHandler extends NamespaceHandlerSupport {

   @Override
   public void init() {
      registerBeanDefinitionParser("annotation-driven", new AnnotationDrivenBeanDefinitionParser());
      registerBeanDefinitionParser("default-servlet-handler", new DefaultServletHandlerBeanDefinitionParser());
      registerBeanDefinitionParser("interceptors", new InterceptorsBeanDefinitionParser());
      registerBeanDefinitionParser("resources", new ResourcesBeanDefinitionParser());
      registerBeanDefinitionParser("view-controller", new ViewControllerBeanDefinitionParser());
      registerBeanDefinitionParser("redirect-view-controller", new ViewControllerBeanDefinitionParser());
      registerBeanDefinitionParser("status-controller", new ViewControllerBeanDefinitionParser());
      registerBeanDefinitionParser("view-resolvers", new ViewResolversBeanDefinitionParser());
      registerBeanDefinitionParser("tiles-configurer", new TilesConfigurerBeanDefinitionParser());
      registerBeanDefinitionParser("freemarker-configurer", new FreeMarkerConfigurerBeanDefinitionParser());
      registerBeanDefinitionParser("groovy-configurer", new GroovyMarkupConfigurerBeanDefinitionParser());
      registerBeanDefinitionParser("script-template-configurer", new ScriptTemplateConfigurerBeanDefinitionParser());
      registerBeanDefinitionParser("cors", new CorsBeanDefinitionParser());
   }
}
           

      简单来说,就是你在xml配置的<mvc:annotation-driven/>、<context:component-scan base-package="web" />标签,在进行xml文件节点解析的时候,如果扫描到例如"annotation-driven",就会运用AnnotationDrivenBeanDefinitionParser去处理这个标签,并且去注册一些与之相关联的类到容器中。这里的RequestMappingHandlerAdapter就是通过这种方式注入的:

class AnnotationDrivenBeanDefinitionParser implements BeanDefinitionParser {

   @Override
   @Nullable
   public BeanDefinition parse(Element element, ParserContext parserContext) {
      ..........
   RootBeanDefinition handlerAdapterDef = new RootBeanDefinition(RequestMappingHandlerAdapter.class);
  .  ..............
    readerContext.getRegistry().registerBeanDefinition(HANDLER_ADAPTER_BEAN_NAME , handlerAdapterDef); 
    ........
}
           

  2、但我们讲SimpleControllerHandlerAdapter的时候,在xml中并没有加一些特殊的标签内容,那其是怎样注入的呢?

   只是因为DispatcherServlet类主动加载了一个DispatcherServlet.properties文件:

public class DispatcherServlet extends FrameworkServlet {
     ...........
   private static final String DEFAULT_STRATEGIES_PATH = "DispatcherServlet.properties";
       ...........
   static {
      try {
         ClassPathResource resource = new ClassPathResource(DEFAULT_STRATEGIES_PATH, DispatcherServlet.class);
         defaultStrategies = PropertiesLoaderUtils.loadProperties(resource);
      }
        ........
}
           

文件中的部分内容:

org.springframework.web.servlet.ThemeResolver=org.springframework.web.servlet.theme.FixedThemeResolver

org.springframework.web.servlet.HandlerMapping=org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping,\
   org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping

org.springframework.web.servlet.HandlerAdapter=org.springframework.web.servlet.mvc.HttpRequestHandlerAdapter,\
   org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter,\
   org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter
           

然后通过这个注入里面的内容到容器中:

protected <T> List<T> getDefaultStrategies(ApplicationContext context, Class<T> strategyInterface) {
   String key = strategyInterface.getName();
   String value = defaultStrategies.getProperty(key);
   if (value != null) {
      String[] classNames = StringUtils.commaDelimitedListToStringArray(value);
      List<T> strategies = new ArrayList<>(classNames.length);
      for (String className : classNames) {
            ......
            Class<?> clazz = ClassUtils.forName(className, DispatcherServlet.class.getClassLoader());
            Object strategy = createDefaultStrategy(context, clazz);
             .........
        }
}
protected Object createDefaultStrategy(ApplicationContext context, Class<?> clazz) {
   return context.getAutowireCapableBeanFactory().createBean(clazz);
}
public <T> T createBean(Class<T> beanClass) throws BeansException {
   RootBeanDefinition bd = new RootBeanDefinition(beanClass);
   bd.setScope(SCOPE_PROTOTYPE);
   bd.allowCaching = ClassUtils.isCacheSafe(beanClass, getBeanClassLoader());
   return (T) createBean(beanClass.getName(), bd, null);
}
           

    3、第三种。就是像前面的SimpleServletHandlerAdapter,一些冷门的类,SpringMVC就不会主动通过DispatcherServlet.properties文件去注入,就需要我们主动配置到xml文件中来完成注入。