程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> JAVA編程 >> JAVA綜合教程 >> spring注解配置啟動過程,spring注解過程

spring注解配置啟動過程,spring注解過程

編輯:JAVA綜合教程

spring注解配置啟動過程,spring注解過程


  最近看起spring源碼,突然想知道沒有web.xml的配置,spring是怎麼通過一個繼承於AbstractAnnotationConfigDispatcherServletInitializer的類來啟動自己的。鑒於能力有限以及第一次看源碼和發博客,不到之處請望諒~

 

  我用的IDE是IntelliJ IDEA,這個比myEclipse看源碼方便一點,而且黑色背景挺喜歡。然後項目是在maven下的tomcat7插件運行。spring版本是4.3.2.RELEASE。

 

  如果寫過純注解配置的spring web,應該知道需要繼承一個初始化類來裝載bean,然後從這個類開始就會加載我們自定義的功能和bean了,下面是我的一個WebInitializer

 1 @Order(1)
 2 public class WebMvcInit extends AbstractAnnotationConfigDispatcherServletInitializer {
 3     protected Class<?>[] getRootConfigClasses() {
 4         return new Class[]{RootConfig.class,WebSecurityConfig.class};
 5     }
 6 
 7     protected Class<?>[] getServletConfigClasses() {
 8         return new Class[]{WebConfig.class};
 9     }
10 
11     protected String[] getServletMappings() {
12         return new String[]{"/"};
13     }
14 
15     @Override
16     protected Filter[] getServletFilters() {
17         return new Filter[]{new HiddenHttpMethodFilter()};
18     }
19 
20 }

 

  首先看下AbstractAnnotationConfigDispatcherServletInitializer類的結構,這個也是IDEA的一個uml功能,在類那裡右鍵Diagrams->show Diagrams就有啦

  然後我們直接點進AbstractAnnotationConfigDispatcherServletInitializer,可以看到這個類很簡單,只有四個方法,然後我們關注下createRootApplicationContext()

 1 @Override
 2     protected WebApplicationContext createRootApplicationContext() {
 3         Class<?>[] configClasses = getRootConfigClasses();
 4         if (!ObjectUtils.isEmpty(configClasses)) {
 5             AnnotationConfigWebApplicationContext rootAppContext = new AnnotationConfigWebApplicationContext();
 6             rootAppContext.register(configClasses);
 7             return rootAppContext;
 8         }
 9         else {
10             return null;
11         }
12     }

 

  這個方法大概意思是獲取用戶(程序員)傳過來的RootClasses,然後注冊裡面的bean,這些都不是我們關注的,不過這個方法應該是要在啟動後執行的,所以我們可以從這個方法往上找

  IDEA下Ctrl+G可以找調用某個方法或類,然後設置尋找范圍為project and library

  我們找到,AbstractContextLoaderInitializer下registerContextLoaderListener(ServletContext servletContext)方法調用子類的createRootApplicationContext()獲取WebApplicationContext,繼續找registerContextLoaderListener(ServletContext servletContext)方法的調用者,結果發現就是該類下的onStartup(ServletContext servletContext),下面貼下AbstractContextLoaderInitializer類

 1 public abstract class AbstractContextLoaderInitializer implements WebApplicationInitializer {
 2 
 3     /** Logger available to subclasses */
 4     protected final Log logger = LogFactory.getLog(getClass());
 5 
 6 
 7     @Override
 8     public void onStartup(ServletContext servletContext) throws ServletException {
 9         registerContextLoaderListener(servletContext);
10     }
11 
12     /**
13      * Register a {@link ContextLoaderListener} against the given servlet context. The
14      * {@code ContextLoaderListener} is initialized with the application context returned
15      * from the {@link #createRootApplicationContext()} template method.
16      * @param servletContext the servlet context to register the listener against
17      */
18     protected void registerContextLoaderListener(ServletContext servletContext) {
19         WebApplicationContext rootAppContext = createRootApplicationContext();
20         if (rootAppContext != null) {
21             ContextLoaderListener listener = new ContextLoaderListener(rootAppContext);
22             listener.setContextInitializers(getRootApplicationContextInitializers());
23             servletContext.addListener(listener);
24         }
25         else {
26             logger.debug("No ContextLoaderListener registered, as " +
27                     "createRootApplicationContext() did not return an application context");
28         }
29     }
30 
31     /**
32      * Create the "<strong>root</strong>" application context to be provided to the
33      * {@code ContextLoaderListener}.
34      * <p>The returned context is delegated to
35      * {@link ContextLoaderListener#ContextLoaderListener(WebApplicationContext)} and will
36      * be established as the parent context for any {@code DispatcherServlet} application
37      * contexts. As such, it typically contains middle-tier services, data sources, etc.
38      * @return the root application context, or {@code null} if a root context is not
39      * desired
40      * @see org.springframework.web.servlet.support.AbstractDispatcherServletInitializer
41      */
42     protected abstract WebApplicationContext createRootApplicationContext();
43 
44     /**
45      * Specify application context initializers to be applied to the root application
46      * context that the {@code ContextLoaderListener} is being created with.
47      * @since 4.2
48      * @see #createRootApplicationContext()
49      * @see ContextLoaderListener#setContextInitializers
50      */
51     protected ApplicationContextInitializer<?>[] getRootApplicationContextInitializers() {
52         return null;
53     }
54 
55 }

 

  注意的是這裡我們跳過了AbstractDispatcherServletInitializer抽象類(看uml圖),這個類主要配置DispatcherServlet,這裡就是spring mvc等功能的實現了。

 

  那誰來加載AbstractContextLoaderInitializer?WebApplicationInitializer已經是接口,不會再有一個抽象類來調用了,於是我嘗試性地搜WebApplicationInitializer接口,因為spring這種大項目肯定是面向接口的,所以調用的地方一般是寫接口,然後我們找到了SpringServletContainerInitializer類,它實現了ServletContainerInitializer接口,這個類大概是說把所有WebApplicationInitializer都startUp一遍,可以說這個類很接近我們的目標了。下面貼下SpringServletContainerInitializer

 1 @HandlesTypes(WebApplicationInitializer.class)
 2 public class SpringServletContainerInitializer implements ServletContainerInitializer {
 3     @Override
 4     public void onStartup(Set<Class<?>> webAppInitializerClasses, ServletContext servletContext)
 5             throws ServletException {
 6 
 7         List<WebApplicationInitializer> initializers = new LinkedList<WebApplicationInitializer>();
 8 
 9         if (webAppInitializerClasses != null) {
10             for (Class<?> waiClass : webAppInitializerClasses) {
11                 // Be defensive: Some servlet containers provide us with invalid classes,
12                 // no matter what @HandlesTypes says...
13                 if (!waiClass.isInterface() && !Modifier.isAbstract(waiClass.getModifiers()) &&
14                         WebApplicationInitializer.class.isAssignableFrom(waiClass)) {
15                     try {
16                         initializers.add((WebApplicationInitializer) waiClass.newInstance());
17                     }
18                     catch (Throwable ex) {
19                         throw new ServletException("Failed to instantiate WebApplicationInitializer class", ex);
20                     }
21                 }
22             }
23         }
24 
25         if (initializers.isEmpty()) {
26             servletContext.log("No Spring WebApplicationInitializer types detected on classpath");
27             return;
28         }
29 
30         servletContext.log(initializers.size() + " Spring WebApplicationInitializers detected on classpath");
31         AnnotationAwareOrderComparator.sort(initializers);
32         for (WebApplicationInitializer initializer : initializers) {
33             initializer.onStartup(servletContext);
34         }
35     }
36 
37 }

  在最後的foreach把所有的WebApplicationInitializer都啟動一遍。那麼問題來了,誰來啟動SpringServletContainerInitializer,spring肯定不能自己就能啟動的,在

web環境下,就只有web容器了。我們可以在上面某一個地方打個斷點,然後Debug一下(事實上,完全可以全程Debug = =,這樣准確又快捷,不過這樣少了點尋找的意味,沿路風景還是挺不錯的)

 

  可以看到包org.apache.catalina.core下的StandardContext類的startInternal方法,這個已經是tomcat的范圍了,所以我們的目標算是達到了。注意的是ServletContainerInitializer接口並不是spring包下的,而是javax.servlet

  我猜測,tomcat通過javax.servlet的ServletContainerInitializer接口來找容器下實現這個接口的類,然後調用它們的OnStartUp,然後spring的SpringServletContainerInitializer就可以把所有WebApplicationInitializer都啟動一遍,其中就有我們自己寫的WebInitializer,另外spring security用注解配置也是實現WebApplicationInitializer啟動的,所以這樣spring的擴展性很強。這幾天再看下tomcat源碼,了解下tomcat的機制。

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved