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檔案中來完成注入。