很好的一篇文章,拿到博客和大家共享一下
(转)0Spring中MVC框架的底层实现
Written by Tony Jiang @ 20120119
Spring中的MVC
Spring MVC的流程
Spring的Sample这里就不讲了,大家自己上网google
Spring 在Web环境下的启动
ServletContextListener。
(问题: ContextListener如何启动? 随着web容器启动而启动?是单线程的?线程安全的?)
ContextListener是随着Tomcat的启动而启动,并且只启动这一次,为整个WebContext的启动做准备。
Spring在Web环境下启动的监听器是:
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中初期化
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类同
入口的DispatchServlet
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. new XmlBeanDefinitionReader(beanFactory);
11.
12. // Configure the bean definition reader with this context's
13. // resource loading environment.
14. this.getEnvironment());
15. this);
16. 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的初期化和生成,我认为不是由Spring容器负责的,应该是由Web容器自己管理的。可以参考《How tomcat works》这本书
DispatchServlet的init方法在父类的父类的HttpServletBean中
是一个final方法哦
1. public final void init() throws ServletException {
2. if (logger.isDebugEnabled()) {
3. "Initializing servlet '" + getServletName() + "'");
4. }
5.
6. // Set bean properties from init parameters.
7. try {
8. new ServletConfigPropertyValues(getServletConfig(), this.requiredProperties);
9. this);
10. new ServletContextResourceLoader(getServletContext());
11. class, new ResourceEditor(resourceLoader, this.environment));
12. initBeanWrapper(bw);
13. true);
14. }
15. catch (BeansException ex) {
16. "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. "Servlet '" + getServletName() + "' configured successfully");
25. }
26. }
真正的servlet的初期化在子类中执行,又是模板模式~~~
于是我们在子类的FrameworkServlet中看到
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. "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. " ms");
30. }
31. }
接下来最后将自己sevlet对应上具体的ServletContext
先回顾一下Servlet的生存周期
Servlet的init方法是在容器的启动中被启动,只执行这一次。
那就意味着,Servlet的所需要的资源,内存空间,instance的预实例化都要在init内完成。
作为Spring Servlet的init,那么相对应ServletName -servlet.xml中所有的定义类都必须在init中被成功初期化。
我们拿一个简单ServletName -servlet.xml来举例
1. 1 <?xml version="1.0" encoding="UTF-8" ?>
2. >
3. 3
4. <beans>
5. 5
6. <bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
7. <property name="prefix" value="/WEB-INF/jsp/" />
8. <property name="suffix" value=".jsp" />
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方法内执行(通过父类模板方法的调用)
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中
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. class, true, false);
13. if (!matchingBeans.isEmpty()) {
14. this.handlerMappings = new ArrayList<HandlerMapping>(matchingBeans.values());
15. // We keep HandlerMappings in sorted order.
16. this.handlerMappings);
17. }
18. }
19. else {
20. try {
21. 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. "No HandlerMappings found in servlet '" + getServletName() + "': using default");
35. }
36. }
37. }
它的被执行是在DispatchServlet中的doService中
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(责任链模式)
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中就能找到自己所要执行的控制器。
其他细节在此打住。