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文件中来完成注入。