spring注解配置启动过程
最近看起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的机制。
spring注解配置启动过程的更多相关文章
- 关于Spring注解配置的步骤
今天分享一下 关于Spring注解配置的流程 1 导包:如下图所示 2 书写User和Car类 代码如下 package cn.lijun.bean; public class Car { priv ...
- Spring注解配置和xml配置优缺点比较
Spring注解配置和xml配置优缺点比较 编辑 在昨天发布的文章<spring boot基于注解方式配置datasource>一文中凯哥简单的对xml配置和注解配置进行了比较.然后朋 ...
- Spring MVC的启动过程
一.概述 下面一个基本的运用springMVC的的web.xml的配置,这里要注意两个地方,一个是ContextLoadListener,一个是DispatcherServlet.web容器正是通过这 ...
- 【Spring实战】Spring注解配置工作原理源码解析
一.背景知识 在[Spring实战]Spring容器初始化完成后执行初始化数据方法一文中说要分析其实现原理,于是就从源码中寻找答案,看源码容易跑偏,因此应当有个主线,或者带着问题.目标去看,这样才能最 ...
- Spring注解配置Aop
之前学习了SpringAop的基本原理.http://www.cnblogs.com/expiator/p/7977975.html 现在尝试使用注解来配置SpringAop. Aop,面向切面编程. ...
- 【转】【Spring实战】Spring注解配置工作原理源码解析
一.背景知识 在[Spring实战]Spring容器初始化完成后执行初始化数据方法一文中说要分析其实现原理,于是就从源码中寻找答案,看源码容易跑偏,因此应当有个主线,或者带着问题.目标去看,这样才能最 ...
- Spring注解配置、Spring aop、整合Junit——Spring学习 day2
注解配置: 1.为主配置文件引入新的命名空间(约束) preference中引入文件 2.开启使用注解代理配置文件 <?xml version="1.0" encoding= ...
- spring容器的启动过程
spring的启动过程: 首先,对于一个web应用,其部署在web容器中,web容器提供其一个全局的上下文环境,这个上下文就是ServletContext,其为后面的spring IoC容器提供宿主环 ...
- SpringBoot:Spring容器的启动过程
一.简述 Spring的启动过程就是IoC容器的启动过程,本质上就是创建和初始化Bean的工厂(BeanFactory),BeanFactory是整个SpringIoC的核心,Spring使用Bean ...
随机推荐
- searchableselect不支持onchange的问题
1.找到jquery.searchableSelect.js 2.找到selectItem函数 修改里面的方法,加入自定义你要回调的函数 selectItem: function(item){ //L ...
- lua学习项目笔记
这几天草草的浏览了一下电子版的<lua程序设计>,没有懂的地方就自动忽略了,挑拣了一些可以理解的部分一直在推进.推进至后面的时候已经浑浑噩噩的了,有种想看完这本书的强迫症的感觉.推进CAP ...
- [论文笔记] Methodologies for Data Quality Assessment and Improvement (ACM Comput.Surv, 2009) (1)
Carlo Batini, Cinzia Cappiello, Chiara Francalanci, and Andrea Maurino. 2009. Methodologies for data ...
- ios 同步Get请求的实现
//第一步,创建URL NSURL *url = [NSURL URLWithString:@"http://itunes.apple.com/search?term=微信&enti ...
- 一起做RGB-D SLAM 第二季 (一)
小萝卜:师兄!过年啦!是不是很无聊啊!普通人的生活就是赚钱花钱,实在是很没意思啊! 师兄:是啊…… 小萝卜:他们都不懂搞科研和码代码的乐趣呀! 师兄:可不是嘛…… 小萝卜:所以今年过年,我们再做一个S ...
- wow 各职业体验(pvp)
玩过职业 近战 武器战,冰DK,惩戒骑,增强萨,踏风 法系远程 鸟德,痛苦术,火法,奥法 治疗 奶德,奶骑,奶萨 三板甲职业就冰DK 最轻松,增强萨操作最频繁 机动性最好就武器战,踏风最差的,踏风群攻 ...
- Struts2知多少(2) Struts2 是什么
Struts2是流行和成熟的基于MVC设计模式的Web应用程序框架. Struts2不只是Struts1下一个版本,它是一个完全重写的Struts架构. WebWork框架开始以Struts框架为基础 ...
- Popup 显示阴影
WPF Popup: How to put a border around the popup? 通过设置 Border 的 margin 来为阴影留出位置,并设置 Popup: AllowsTran ...
- Spring中加载配置文件的方式
原文:http://blog.csdn.net/snowjlz/article/details/8158560 Spring 中加载XML配置文件的方式,好像有3种, XML是最常见的Spring 应 ...
- AVL树(一)之 图文解析 和 C语言的实现
概要 本章介绍AVL树.和前面介绍"二叉查找树"的流程一样,本章先对AVL树的理论知识进行简单介绍,然后给出C语言的实现.本篇实现的二叉查找树是C语言版的,后面章节再分别给出C++ ...