国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 編程 > Java > 正文

詳解spring注解配置啟動過程

2019-11-26 13:42:18
字體:
來源:轉載
供稿:網友

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

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

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

@Order(1)public class WebMvcInit extends AbstractAnnotationConfigDispatcherServletInitializer { protected Class<?>[] getRootConfigClasses() {  return new Class[]{RootConfig.class,WebSecurityConfig.class}; } protected Class<?>[] getServletConfigClasses() {  return new Class[]{WebConfig.class}; } protected String[] getServletMappings() {  return new String[]{"/"}; } @Override protected Filter[] getServletFilters() {  return new Filter[]{new HiddenHttpMethodFilter()}; }}

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

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

@Override protected WebApplicationContext createRootApplicationContext() {  Class<?>[] configClasses = getRootConfigClasses();  if (!ObjectUtils.isEmpty(configClasses)) {   AnnotationConfigWebApplicationContext rootAppContext = new AnnotationConfigWebApplicationContext();   rootAppContext.register(configClasses);   return rootAppContext;  }  else {   return null;  } } 

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

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

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

public abstract class AbstractContextLoaderInitializer implements WebApplicationInitializer { /** Logger available to subclasses */ protected final Log logger = LogFactory.getLog(getClass()); @Override public void onStartup(ServletContext servletContext) throws ServletException {  registerContextLoaderListener(servletContext); } /**  * Register a {@link ContextLoaderListener} against the given servlet context. The  * {@code ContextLoaderListener} is initialized with the application context returned  * from the {@link #createRootApplicationContext()} template method.  * @param servletContext the servlet context to register the listener against  */ protected void registerContextLoaderListener(ServletContext servletContext) {  WebApplicationContext rootAppContext = createRootApplicationContext();  if (rootAppContext != null) {   ContextLoaderListener listener = new ContextLoaderListener(rootAppContext);   listener.setContextInitializers(getRootApplicationContextInitializers());   servletContext.addListener(listener);  }  else {   logger.debug("No ContextLoaderListener registered, as " +     "createRootApplicationContext() did not return an application context");  } } /**  * Create the "<strong>root</strong>" application context to be provided to the  * {@code ContextLoaderListener}.  * <p>The returned context is delegated to  * {@link ContextLoaderListener#ContextLoaderListener(WebApplicationContext)} and will  * be established as the parent context for any {@code DispatcherServlet} application  * contexts. As such, it typically contains middle-tier services, data sources, etc.  * @return the root application context, or {@code null} if a root context is not  * desired  * @see org.springframework.web.servlet.support.AbstractDispatcherServletInitializer  */ protected abstract WebApplicationContext createRootApplicationContext(); /**  * Specify application context initializers to be applied to the root application  * context that the {@code ContextLoaderListener} is being created with.  * @since 4.2  * @see #createRootApplicationContext()  * @see ContextLoaderListener#setContextInitializers  */ protected ApplicationContextInitializer<?>[] getRootApplicationContextInitializers() {  return null; }}

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

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

@HandlesTypes(WebApplicationInitializer.class)public class SpringServletContainerInitializer implements ServletContainerInitializer { @Override public void onStartup(Set<Class<?>> webAppInitializerClasses, ServletContext servletContext)   throws ServletException {  List<WebApplicationInitializer> initializers = new LinkedList<WebApplicationInitializer>();  if (webAppInitializerClasses != null) {   for (Class<?> waiClass : webAppInitializerClasses) {    // Be defensive: Some servlet containers provide us with invalid classes,    // no matter what @HandlesTypes says...    if (!waiClass.isInterface() && !Modifier.isAbstract(waiClass.getModifiers()) &&      WebApplicationInitializer.class.isAssignableFrom(waiClass)) {     try {      initializers.add((WebApplicationInitializer) waiClass.newInstance());     }     catch (Throwable ex) {      throw new ServletException("Failed to instantiate WebApplicationInitializer class", ex);     }    }   }  }  if (initializers.isEmpty()) {   servletContext.log("No Spring WebApplicationInitializer types detected on classpath");   return;  }  servletContext.log(initializers.size() + " Spring WebApplicationInitializers detected on classpath");  AnnotationAwareOrderComparator.sort(initializers);  for (WebApplicationInitializer initializer : initializers) {   initializer.onStartup(servletContext);  } }}
  

         在最后的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的機制。

以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持武林網。

發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
主站蜘蛛池模板: 庆安县| 抚宁县| 敦化市| 通榆县| 淮阳县| 繁峙县| 涿州市| 青田县| 弋阳县| 那曲县| 千阳县| 广河县| 修文县| 雅江县| 海南省| 崇仁县| 乌兰察布市| 平远县| 铁岭市| 弥渡县| 义马市| 白水县| 化隆| 鄂州市| 泰来县| 万载县| 江川县| 石阡县| 永川市| 比如县| 宝鸡市| 澄江县| 措勤县| 湟中县| 吉木乃县| 西林县| 延庆县| 鲁山县| 鄂温| 浦北县| 富宁县|