你的位置:首页 > Java教程

[Java教程]spring注解配置启动过程


  最近看起spring源码,突然想知道没有web.

 

  我用的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   @Override16   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. The14    * {@code ContextLoaderListener} is initialized with the application context returned15    * from the {@link #createRootApplicationContext()} template method.16    * @param servletContext the servlet context to register the listener against17   */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 the33    * {@code ContextLoaderListener}.34    * <p>The returned context is delegated to35    * {@link ContextLoaderListener#ContextLoaderListener(WebApplicationContext)} and will36    * be established as the parent context for any {@code DispatcherServlet} application37    * 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 not39    * desired40    * @see org.springframework.web.servlet.support.AbstractDispatcherServletInitializer41   */42   protected abstract WebApplicationContext createRootApplicationContext();43 44   /**45    * Specify application context initializers to be applied to the root application46    * context that the {@code ContextLoaderListener} is being created with.47    * @since 4.248    * @see #createRootApplicationContext()49    * @see ContextLoaderListener#setContextInitializers50   */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 = =,这样准确又快捷,不过这样少了点寻找的意味,沿路风景还是挺不错的)

7月泰国旅游泰国旅游多少钱一人泰国游费用泰国旅游团购泰国蜜月旅游二桅尖——美不可言的“冰帘”美景 青岛夜晚出游攻略 不得不看的青岛夜景 河南旅游的十个之最,来数数你知道几个? 揭秘粉笔的制作过程(组图) 珠海梦幻水城什么时候关门?梦幻水城游玩时间多长? 中秋节去哪里旅游好? 阳朔菩萨水岩学生证优惠吗?菩萨水岩学生票价? 清新县古龙峡国际漂流怎么样?古龙峡国际漂流攻略? 惠州川龙峡漂流坐车怎么去?川龙峡漂流坐几路公交车? 香港西贡怎么去?在香港怎么去西贡? 南京有哪些小清新景点? 大丰门漂流电话?增城大丰门漂流预约优惠吗? 趣谈重庆小面 成都丽江自助游攻略 致青春带热大学旅游_《致青春》与大学旅游 三亚蜈支洲岛一日游记 VVY40-16I01--C568 Datasheet VVY40-16I01--C568 Datasheet VVY40-16i01 Datasheet VVY40-16i01 Datasheet VY21246-SGI 099-0117-004 Datasheet VY21246-SGI 099-0117-004 Datasheet 九龙壁在哪 九龙壁在哪 九龙壁在哪 成都国色天香门票 成都国色天香门票 成都国色天香门票 西藏土特产 西藏土特产 西藏土特产