Spring中MVC框架的底层实现

很好的一篇文章,拿到博客和大家共享一下

(转)0Spring中MVC框架的底层实现

Written by Tony Jiang @ 20120119

Spring中的MVC

Spring MVC的流程

Spring的Sample这里就不讲了,大家自己上网google

Spring 在Web环境下的启动

                按照javaEE标准,Web应用启动web.xml,之后的启动root是ServletContextListener。

                (问题: ContextListener如何启动? 随着web容器启动而启动?是单线程的?线程安全的?)

ContextListener是随着Tomcat的启动而启动,并且只启动这一次,为整个WebContext的启动做准备。

                Spring在Web环境下启动的监听器是:

[java]  view plain copy print ?
  1. public class ContextLoaderListener extends ContextLoader implements ServletContextListener {  
  2. /** 
  3.      * Initialize the root web application context. 
  4.      */  
  5.     public void contextInitialized(ServletContextEvent event) {  
  6.         this.contextLoader = createContextLoader();  
  7.         if (this.contextLoader == null) {  
  8.             this.contextLoader = this;  
  9.         }  
  10.         this.contextLoader.initWebApplicationContext(event.getServletContext());  
  11.     }  


其中WebApplication的上下文在ContextLoader中初期化

[java]  view plain copy print ?
  1. /** 
  2.      * Initialize Spring's web application context for the given servlet context, 
  3.      * using the application context provided at construction time, or creating a new one 
  4.      * according to the "{@link #CONTEXT_CLASS_PARAM contextClass}" and 
  5.      * "{@link #CONFIG_LOCATION_PARAM contextConfigLocation}" context-params. 
  6.      * @param servletContext current servlet context 
  7.      * @return the new WebApplicationContext 
  8.      * @see #ContextLoader(WebApplicationContext) 
  9.      * @see #CONTEXT_CLASS_PARAM 
  10.      * @see #CONFIG_LOCATION_PARAM 
  11.      */  
  12.     public WebApplicationContext initWebApplicationContext(ServletContext servletContext) {  
  13. try {  
  14.             // Store context in local instance variable, to guarantee that  
  15.             // it is available on ServletContext shutdown.  
  16.             if (this.context == null) {  
  17.                 this.context = createWebApplicationContext(servletContext);  
  18.             }     

最后的启动委托给XmlWebApplicationContext

这个类中使用了大量的模板设计模式!!

最终的容器启动和我们编程式启动Spring类同

[java]  view plain copy print ?
  1. /** 
  2.      * Loads the bean definitions via an XmlBeanDefinitionReader. 
  3.      * @see org.springframework.beans.factory.xml.XmlBeanDefinitionReader 
  4.      * @see #initBeanDefinitionReader 
  5.      * @see #loadBeanDefinitions 
  6.      */  
  7.     @Override  
  8.     protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {  
  9.         // Create a new XmlBeanDefinitionReader for the given BeanFactory.  
  10.         XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);  
  11.   
  12.         // Configure the bean definition reader with this context's  
  13.         // resource loading environment.  
  14.         beanDefinitionReader.setEnvironment(this.getEnvironment());  
  15.         beanDefinitionReader.setResourceLoader(this);  
  16.         beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));  
  17.   
  18.         // Allow a subclass to provide custom initialization of the reader,  
  19.         // then proceed with actually loading the bean definitions.  
  20.         initBeanDefinitionReader(beanDefinitionReader);  
  21.         loadBeanDefinitions(beanDefinitionReader);  
  22.     }  

入口的DispatchServlet

DispatchServlet的初期化和生成,我认为不是由Spring容器负责的,应该是由Web容器自己管理的。可以参考《How tomcat works》这本书

DispatchServlet的init方法在父类的父类的HttpServletBean中

是一个final方法哦

[java]  view plain copy print ?
  1. public final void init() throws ServletException {  
  2.         if (logger.isDebugEnabled()) {  
  3.             logger.debug("Initializing servlet '" + getServletName() + "'");  
  4.         }  
  5.   
  6.         // Set bean properties from init parameters.  
  7.         try {  
  8.             PropertyValues pvs = new ServletConfigPropertyValues(getServletConfig(), this.requiredProperties);  
  9.             BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(this);  
  10.             ResourceLoader resourceLoader = new ServletContextResourceLoader(getServletContext());  
  11.             bw.registerCustomEditor(Resource.classnew ResourceEditor(resourceLoader, this.environment));  
  12.             initBeanWrapper(bw);  
  13.             bw.setPropertyValues(pvs, true);  
  14.         }  
  15.         catch (BeansException ex) {  
  16.             logger.error("Failed to set bean properties on servlet '" + getServletName() + "'", ex);  
  17.             throw ex;  
  18.         }  
  19.   
  20.         // Let subclasses do whatever initialization they like.  
  21.         initServletBean();  
  22.   
  23.         if (logger.isDebugEnabled()) {  
  24.             logger.debug("Servlet '" + getServletName() + "' configured successfully");  
  25.         }  
  26.     }  

 

真正的servlet的初期化在子类中执行,又是模板模式~~~

于是我们在子类的FrameworkServlet中看到

[java]  view plain copy print ?
  1. /** 
  2.      * Overridden method of {@link HttpServletBean}, invoked after any bean properties 
  3.      * have been set. Creates this servlet's WebApplicationContext. 
  4.      */  
  5.     @Override  
  6.     protected final void initServletBean() throws ServletException {  
  7.         getServletContext().log("Initializing Spring FrameworkServlet '" + getServletName() + "'");  
  8.         if (this.logger.isInfoEnabled()) {  
  9.             this.logger.info("FrameworkServlet '" + getServletName() + "': initialization started");  
  10.         }  
  11.         long startTime = System.currentTimeMillis();  
  12.   
  13.         try {  
  14.             this.webApplicationContext = initWebApplicationContext();  
  15.             initFrameworkServlet();  
  16.         }  
  17.         catch (ServletException ex) {  
  18.             this.logger.error("Context initialization failed", ex);  
  19.             throw ex;  
  20.         }  
  21.         catch (RuntimeException ex) {  
  22.             this.logger.error("Context initialization failed", ex);  
  23.             throw ex;  
  24.         }  
  25.   
  26.         if (this.logger.isInfoEnabled()) {  
  27.             long elapsedTime = System.currentTimeMillis() - startTime;  
  28.             this.logger.info("FrameworkServlet '" + getServletName() + "': initialization completed in " +  
  29.                     elapsedTime + " ms");  
  30.         }  
  31.     }  

 

接下来最后将自己sevlet对应上具体的ServletContext

先回顾一下Servlet的生存周期

Servlet的init方法是在容器的启动中被启动,只执行这一次。

那就意味着,Servlet的所需要的资源,内存空间,instance的预实例化都要在init内完成。

作为Spring Servlet的init,那么相对应ServletName -servlet.xml中所有的定义类都必须在init中被成功初期化。

我们拿一个简单ServletName -servlet.xml来举例

[html]  view plain copy print ?
  1. <?xml version="1.0" encoding="UTF-8" ?>  
  2.  2 <!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd">  
  3.  3   
  4.  4 <beans>  
  5.  5   
  6.  6     <bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">  
  7.  7         <property name="prefix" value="/WEB-INF/jsp/" />  
  8.  8         <property name="suffix" value=".jsp" />  
  9.  9     </bean>  
  10. 10   
  11. 11     <bean id="simpleUrlHandlerMapping" class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">  
  12. 12         <property name="mappings">  
  13. 13             <props>  
  14. 14                 <prop key="/hello.do">helloController</prop>  
  15. 15             </props>  
  16. 16         </property>  
  17. 17     </bean>  
  18. 18   
  19. 19     <bean id="helloController" class="com.ideawu.HelloController">  
  20. 20         <!--  
  21. 21         <property name="helloManager" ref="helloManager" />  
  22. 22         -->  
  23. 23     </bean>  
  24. 24   
  25. 25 </beans>  

 

其中包括viewResolver(采用何种视图模板),HandlerMapping(采用何种http拦截器),Controller(对应每种http请求采用何种控制器)。

所有的这些,都是通过Spring容器本身进行加载的。

DispatchServlet中这些资源的加载是在本身的initStrategies方法内执行(通过父类模板方法的调用)

[java]  view plain copy print ?
  1. /** 
  2.      * Initialize the strategy objects that this servlet uses. 
  3.      * <p>May be overridden in subclasses in order to initialize further strategy objects. 
  4.      */  
  5.     protected void initStrategies(ApplicationContext context) {  
  6.         initMultipartResolver(context);  
  7.         initLocaleResolver(context);  
  8.         initThemeResolver(context);  
  9.         initHandlerMappings(context);  
  10.         initHandlerAdapters(context);  
  11.         initHandlerExceptionResolvers(context);  
  12.         initRequestToViewNameTranslator(context);  
  13.         initViewResolvers(context);  
  14.         initFlashMapManager(context);  
  15.     }  

 

关于HandlerMapping

                handlerMaper是一个单例的(相对于一个JVM而言)的ArrayList

                它的初期化在DispatchServlet中

[java]  view plain copy print ?
  1. /** 
  2.      * Initialize the HandlerMappings used by this class. 
  3.      * <p>If no HandlerMapping beans are defined in the BeanFactory for this namespace, 
  4.      * we default to BeanNameUrlHandlerMapping. 
  5.      */  
  6.     private void initHandlerMappings(ApplicationContext context) {  
  7.         this.handlerMappings = null;  
  8.   
  9.         if (this.detectAllHandlerMappings) {  
  10.             // Find all HandlerMappings in the ApplicationContext, including ancestor contexts.  
  11.             Map<String, HandlerMapping> matchingBeans =  
  12.                     BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerMapping.classtruefalse);  
  13.             if (!matchingBeans.isEmpty()) {  
  14.                 this.handlerMappings = new ArrayList<HandlerMapping>(matchingBeans.values());  
  15.                 // We keep HandlerMappings in sorted order.  
  16.                 OrderComparator.sort(this.handlerMappings);  
  17.             }  
  18.         }  
  19.         else {  
  20.             try {  
  21.                 HandlerMapping hm = context.getBean(HANDLER_MAPPING_BEAN_NAME, HandlerMapping.class);  
  22.                 this.handlerMappings = Collections.singletonList(hm);  
  23.             }  
  24.             catch (NoSuchBeanDefinitionException ex) {  
  25.                 // Ignore, we'll add a default HandlerMapping later.  
  26.             }  
  27.         }  
  28.   
  29.         // Ensure we have at least one HandlerMapping, by registering  
  30.         // a default HandlerMapping if no other mappings are found.  
  31.         if (this.handlerMappings == null) {  
  32.             this.handlerMappings = getDefaultStrategies(context, HandlerMapping.class);  
  33.             if (logger.isDebugEnabled()) {  
  34.                 logger.debug("No HandlerMappings found in servlet '" + getServletName() + "': using default");  
  35.             }  
  36.         }  
  37.     }  

 

它的被执行是在DispatchServlet中的doService中

[java]  view plain copy print ?
  1. /** 
  2.      * Return the HandlerExecutionChain for this request. 
  3.      * <p>Tries all handler mappings in order. 
  4.      * @param request current HTTP request 
  5.      * @return the HandlerExecutionChain, or <code>null</code> if no handler could be found 
  6.      */  
  7.     protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {  
  8.         for (HandlerMapping hm : this.handlerMappings) {  
  9.             if (logger.isTraceEnabled()) {  
  10.                 logger.trace(  
  11.                         "Testing handler map [" + hm + "] in DispatcherServlet with name '" + getServletName() + "'");  
  12.             }  
  13.             HandlerExecutionChain handler = hm.getHandler(request);  
  14.             if (handler != null) {  
  15.                 return handler;  
  16.             }  
  17.         }  
  18.         return null;  
  19.     }  

 

最后会调用到父类AbstractHandlerMapping中的getHandler,返回一个HandlerChain(责任链模式)

[java]  view plain copy print ?
  1. /** 
  2.      * Look up a handler for the given request, falling back to the default 
  3.      * handler if no specific one is found. 
  4.      * @param request current HTTP request 
  5.      * @return the corresponding handler instance, or the default handler 
  6.      * @see #getHandlerInternal 
  7.      */  
  8.     public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {  
  9.         Object handler = getHandlerInternal(request);  
  10.         if (handler == null) {  
  11.             handler = getDefaultHandler();  
  12.         }  
  13.         if (handler == null) {  
  14.             return null;  
  15.         }  
  16.         // Bean name or resolved handler?  
  17.         if (handler instanceof String) {  
  18.             String handlerName = (String) handler;  
  19.             handler = getApplicationContext().getBean(handlerName);  
  20.         }  
  21.         return getHandlerExecutionChain(handler, request);  
  22.     }  

 

HandlerChain中包含了一系列封装Controller的HandlerAdapte

在接到http请求之后,在HandlerChain中就能找到自己所要执行的控制器。

其他细节在此打住。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值