Spring 5源码阅读系列(3):再说spring集成

1. 前文回顾

在上一篇中,说到传统web项目与Spring集成:从web.xml中的ContextLoaderListener开始,从源码层面分析,追踪底层逻辑,发现web项目通过xml集成spring时,通过配置文件ContextLoader.properties中配置的ApplicationContext的实现类XmlWebApplicationContext来承担Spring容器的引入。源码阅读的进度停在XmlWebApplicationContextrefresh()方法这里了。

按理说,今天应该接着从这里开始,但现在还不是继续追refresh()方法的时候,为什么?

因为,我还想说说Spring集成:通过注解的方式,来集成Spring.


2. 注解方式集成Spring

首先, 以注解方式如何集成spring? 答案是spring提供的Java Config技术.

来看一个官方提供的以注解方式实现spring集成的例子:

它实现spring集成, 只需要两个类:

  • MyWebApplicationContextInitializer
public class MyWebApplicationInitializer implements WebApplicationInitializer {

    @Override
    public void onStartup(ServletContext servletCxt) {

        // Load Spring web application configuration
        AnnotationConfigWebApplicationContext ac = new AnnotationConfigWebApplicationContext();
        ac.register(AppConfig.class);
        ac.refresh();

        // Create and register the DispatcherServlet
        DispatcherServlet servlet = new DispatcherServlet(ac);
        ServletRegistration.Dynamic registration = servletCxt.addServlet("app", servlet);
        registration.setLoadOnStartup(1);
        registration.addMapping("/app/*");
    }
}
  • AppConfig
@Configuration
@ComponentScan("com.springstudy.web")
public class AppConfig {
	@Bean
	public DataSource getDataSource() {
		...
	}

	@Bean
	public SqlSessionFactory getSqlSessionFactory(DataSource datasource) {
		...
	}

	//other bean defintions
	...
}

除了直接使用ServletContext API之外, 还可以通过继承AbstractAnnotationConigDispatcherServletInitializer并覆写其指定的方法, 也能做到实现Spring的集成.

public class MyWebAppInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {

    @Override
    protected Class<?>[] getRootConfigClasses() {
        return null;
    }

    @Override
    protected Class<?>[] getServletConfigClasses() {
        return new Class<?>[] { AppConfig.class };
    }

    @Override
    protected String[] getServletMappings() {
        return new String[] { "/" };
    }

	/*
	 * 如果需要Servlet Filter,通过该方法进行Java Config进行注册
	 */
	@Override
    protected Filter[] getServletFilters() {
        return new Filter[] {
            new HiddenHttpMethodFilter(), new CharacterEncodingFilter() };
    }
}

官方解释到, 通过这种Java Config配置方法,可以注册和实例化DispatcherServlet.

为什么呢? 因为Servlet容器能自动探测到. 然后引入spring容器, 实现容器初始化.


3. 注解集成的原理

为什么Servlet容器就能自动探测到上面说到的这个类? 答案是: Servlet 3协议规定的.

Servlet 3.0+环境中, 通过Spring Config注解方式集成spring时. 用到了Servlet 3协议规范

在上述协议中, 对于Servlet容器的加载过程, 有专门的规定和限制要求, 这些约定使得Servlet容器在启动时, 能调用到上面提到的那个WebApplicationInitializer类.

3.1. Servlet 3协议内容

针对注解集成spring时,用到的协议相关内容, 有如下内容:
ServletContainnerInnitializer

ServletContainerInitializer onStartup

3.2. 文件: javax.servlet.ServletContainerInitializer

根据Servlet 3协议中的说法, 存在一个文件javax.servlet.ServletContainerInitializer, 定义了一个ServletContainerinitializer的实现类.

OK, 找出这个文件, 看看它的内容具体是什么:
在这里插入图片描述

org.springframework.web.SpringServletContainerInitializer

3.3. 实现类: SpringServletContainerInitializer

既然找到了这个实现类, 不妨看看它的代码, 有没有协议中说到的onStartup方法:

@HandlesTypes(WebApplicationInitializer.class)
public class SpringServletContainerInitializer implements ServletContainerInitializer {

	@Override
	public void onStartup(@Nullable Set<Class<?>> webAppInitializerClasses, ServletContext servletContext)
			throws ServletException {

		List<WebApplicationInitializer> initializers = new LinkedList<>();

		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)
								ReflectionUtils.accessibleConstructor(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);
		}
	}
}

果然有!

而且, 根据协议中的说法, 如果这个类上存在@HandleTypes注解, 则表示对其注解中引用的类感兴趣: 被@HandlesTypes标注的类, 或其实现类, 或其子类, 会被当作参数, 传入onStartup()

结合现在看到的代码, 就是: SpringServletContainerInitializer在调用onStartup()方法时, 会传入一个装有WebApplicationInitializer的实现类, 或其子类的类对象Set, 然后进行判断:

如果传入的WebApplicationInitializer类对象,是其子类, 或者是其实现类对象, 
同时又不是接口类,也不是抽象类, 则加入initializers list中 

然后, 遍历这个initializers list, 调用各自的onStartup(), 并传入ServletContext 对象


3.4. 方法: WebApplicationInitializer.onStartup()

现在回到之前最开始集成spring时, 写的两个类: AppConfigMyWebApplicationInitializer.

AppConfig暂时别管, MyWebApplicationInitializer正是实现了WebApplicationInitializer接口的实现类, 所以在Servlet容器启动时, 能调用其onStartup()方法.

嗯, 不对啊? 之前不是说, 还有一种通过继承类的方式, 也可以集成spring吗? 那它就不是接口WebApplicationInitializer的实现类了啊?

好问题! 答案是,其实它也是该接口的实现类!
请看继承类的层级:

public class MyWebAppInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {
...//需要再找父类
}

public abstract class AbstractAnnotationConfigDispatcherServletInitializer
		extends AbstractDispatcherServletInitializer {
...//需要再找父类
}

public abstract class AbstractDispatcherServletInitializer extends AbstractContextLoaderInitializer {
...//需要再找父类
}

public abstract class AbstractContextLoaderInitializer implements WebApplicationInitializer {
...//终于看到最终的接口类了
}

回到前面说到的onStartup()方法, 如果按方式1(实现接口), spring容器的引入, 就是通过AnnotationConfigWebApplicationContext引入的, 因为又看到了熟悉的refresh()方法了

//通过AnnotationConfigWebApplicationContext来加载spring容器
AnnotationConfigWebApplicationContext ac = new AnnotationConfigWebApplicationContext();
ac.register(AppConfig.class);
ac.refresh();
...

那如果是方式2呢(通过继承抽象类)? 答案也是AnnotationConfigWebApplicationContext:

//简称这个类为ADSI
public abstract class AbstractDispatcherServletInitializer extends AbstractContextLoaderInitializer {
	...
	@Override
	public void onStartup(ServletContext servletContext) throws ServletException {
		super.onStartup(servletContext);//调用父类的onStartup方法
		registerDispatcherServlet(servletContext);
	}

}

需要继续追踪父类的onStartup方法

//父类的onStartup方法,简称这个类为ACLI
public abstract class AbstractContextLoaderInitializer implements WebApplicationInitializer {
	...
	@Override
	public void onStartup(ServletContext servletContext) throws ServletException {
		registerContextLoaderListener(servletContext);
	}

	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");
		}
	}
	...
}

父类的onStartup()方法,调用registerContextLoaderListener(servletContext)方法;在此方法中,会继续调用createRootApplicationContext()方法。

但是在类AbstractContextLoaderInitializer, 包括AbstractDispatcherServletInitializer中, 方法createRootApplicationContext()都没有实现, 所以需要找其子类中的实现.

AbstractAnnotationConfigDispatcherServletInitializer中存在其实现:

//简称这个类为AACDSI
public abstract class AbstractAnnotationConfigDispatcherServletInitializer
		extends AbstractDispatcherServletInitializer {
	...
	@Override
	@Nullable
	protected WebApplicationContext createRootApplicationContext() {
		Class<?>[] configClasses = getRootConfigClasses();
		if (!ObjectUtils.isEmpty(configClasses)) {
			//不为空:有顶层Config配置类
			AnnotationConfigWebApplicationContext context = new AnnotationConfigWebApplicationContext();
			context.register(configClasses);
			return context;
		}
		else {
			//为空:如果没有顶层Config配置类
			return null;
		}
	}
	...
}

由代码可知, 如果没有顶层Config配置类,就返回空;如果有,则通过AnnotationConfigWebApplicationContext来注册配置类, 从而加载spring容器。

那,如果没有顶层Config配置类呢?AnnotationConfigWebApplicationContext不是就没有机会引入了吗?

所以,需要再看代码!再回到之前说到的、会调用父类onStartup()的那段代码处:

//简称这个类为ADSI
public abstract class AbstractDispatcherServletInitializer extends AbstractContextLoaderInitializer {
	...
	@Override
	public void onStartup(ServletContext servletContext) throws ServletException {
		super.onStartup(servletContext);//调用父类的onStartup方法
		registerDispatcherServlet(servletContext);
	}

}

如果调用完父类的onStartup()方法,没有发现顶层Config配置类,就不会有顶层上下文,也就不会有AnnotationConfigWebApplicationContext引入。

但是,不是后面还有一个registerDispatcherServlet(servletContext)方法吗?继续追呀:

protected void registerDispatcherServlet(ServletContext servletContext) {
	String servletName = getServletName();
	Assert.hasLength(servletName, "getServletName() must not return null or empty");

	//1、创建Servlet应用上下文对象
	WebApplicationContext servletAppContext = createServletApplicationContext();
	Assert.notNull(servletAppContext, "createServletApplicationContext() must not return null");

	//2、在Servlet应用上下文中,创建DispatcherSerlvet
	FrameworkServlet dispatcherServlet = createDispatcherServlet(servletAppContext);
	Assert.notNull(dispatcherServlet, "createDispatcherServlet(WebApplicationContext) must not return null");

	//3、在DispatcherSerlvet中注册Initializers
	dispatcherServlet.setContextInitializers(getServletApplicationContextInitializers());

	//4、在Servlet应用上下文中,注册Servlet
	ServletRegistration.Dynamic registration = servletContext.addServlet(servletName, dispatcherServlet);
	if (registration == null) {
		throw new IllegalStateException("Failed to register servlet with name '" + servletName + "'. " +
				"Check if there is another servlet registered under the same name.");
	}

	//5、配置Servlet参数
	registration.setLoadOnStartup(1);
	registration.addMapping(getServletMappings());
	registration.setAsyncSupported(isAsyncSupported());

	//6、注册ServletFilters
	Filter[] filters = getServletFilters();
	if (!ObjectUtils.isEmpty(filters)) {
		for (Filter filter : filters) {
			registerServletFilter(servletContext, filter);
		}
	}

	//7、其它额外的自定义
	customizeRegistration(registration);
}

现在重点关注下步骤1:
因为createServletApplicationContext()在当前类AbstractDispatcherServletInitializer中是个抽象方法,需要从其子类中寻找实现;

在其子类AbstractAnnotationConfigDispatcherServletInitializer中,它的实现是这样的:

@Override
	protected WebApplicationContext createServletApplicationContext() {
		AnnotationConfigWebApplicationContext servletAppContext = new AnnotationConfigWebApplicationContext();
		Class<?>[] configClasses = getServletConfigClasses();
		if (!ObjectUtils.isEmpty(configClasses)) {
			servletAppContext.register(configClasses);
		}
		return servletAppContext;
	}

这下AnnotationConfigWebApplicationContext的引入,再也没有什么多余的判断逻辑了;终于又看到我们熟悉的……

等等,但是,这里没有看到我们期望的`refresh()·方法啊?在哪里调用的呢?

难道是在其它步骤中?


3.5. 寻找消失的refresh()方法

但是,追踪了其它步骤,也没有发现refresh()被调用的地方?

怎么回事?刚才是在追没有顶层Config类的代码,如果有顶层Config类的时候呢?
回到判断顶层Config类的那段代码,再看下:

//简称这个类为AACDSI
public abstract class AbstractAnnotationConfigDispatcherServletInitializer
		extends AbstractDispatcherServletInitializer {
	...
	@Override
	@Nullable
	protected WebApplicationContext createRootApplicationContext() {
		Class<?>[] configClasses = getRootConfigClasses();
		if (!ObjectUtils.isEmpty(configClasses)) {
			//有顶层Config配置类
			AnnotationConfigWebApplicationContext context = new AnnotationConfigWebApplicationContext();
			
			//奇怪了,不是应该还有个refresh方法的吗?
			context.register(configClasses);
			
			return context;
		}
		else {
			//如果没有顶层Config配置类
			return null;
		}
	}
	...
}

我去?不是吧?这里也只有register(),没有调用refresh(),那这个方法在哪里被调用呢?

到这里,好像没有思路了:

  • Servlet容器启动后,从配置文件中获取到ServletContainerInitializer的实现类SpringServletContainerInitializer
  • ·Servlet·容器调用·SpringServletContainerInitializer·的onStartup()方法;
  • onStartup()方法根据项目配置,创建ContextLoaderListenerAnnotationConfigWebApplicationContextDispatcherServletServletFilter等;

整个过程,整理一下,是这样的:

Servlet容器启动类的onStartup()调用过程

//Servlet容器调用容器启动类的onStartup方法
onStartup(Set<Class<?>>, ServletContext) : SpringServletContainerInitializer
	|//1、启动:
    |--onStartup(ServletContext) : AbstractDispatcherServletInitializer
    |    |--onStartup(ServletContext servletContext) : AbstractContextLoaderInitializer
	|	      |//注册:上下文加载监听器
	|         |--registerContextLoaderListener(ServletContext servletContext) : AbstractContextLoaderInitializer
	|              |//根据是否存在顶层Config配置类,决定是否创建顶层应用上下文对象
	|              |--createRootApplicationContext() : [$1] : AbstractAnnotationConfigDispatcherServletInitializer
	|	  	   	   |    |--getRootConfigClasses() : MyWebAppInitializer (自己写的类: 是否指定顶层Config配置类)
    |	  	   	   |//如果存在顶层上下文对象,则创建上下文加载监听器
	|	 	   	   |--new ContextLoaderListener(rootAppContext) : [$1]+
	|
    |//2、注册:DispatcherServlet
	|--registerDispatcherServlet(ServletContext) : AbstractDispatcherServletInitializer
    |    |//2.1、创建servletContext
    |    |--createServletApplicationContext() :
    |    |    |--new AnnotationConfigWebApplicationContext() : [servletAppContext]
	|    |    |--getServletConfigClasses() : [$2] : MyWebAppInitializer
	|    |    |--servletAppContext.register(configClasses) : [$2]+
	|    |
    |    |//2.2、创建DipatcherServlet
    |    |--createDispatcherServlet(servletAppContext) : 
	|    |
    |    |//2.3、向servletContext中添加servlet
    |    |--addServlet(servletName, dispatcherServlet)
	|    |
    |    |//2.4、向servletContext中添加servlet
    |    |--registerServletFilter(servletContext, filter)

整个过程中,都没有地方存在调用refresh()的地方;那么,是否这个refresh()不在·onStartup()`的作用范围内?

带着这个疑问,看看最初调用onStartup()方法的后面,是否存在调用refresh()的可能。

onStartup()方法上,·Ctrl + Alt + H`,显示其调用链路:
onStartup_callStack

可见,Servlet容器——确实是Tomcat调用onStartup(),这与之前提到的Servlet 3协议中的说法是一致的。现在只能去看一下Tomcat容器的这个startInternal()方法:

@Override
protected synchronized void startInternal() throws LifecycleException {
	...
	// Set up the context init params
	mergeParameters();

	// Call ServletContainerInitializers
	//就是从这里开始调用 ServletContainerInitializers 实现类的onStartup()
	//那么后续的代码,就有可能存在调用refresh()的可能
	for (Map.Entry<ServletContainerInitializer, Set<Class<?>>> entry : initializers.entrySet()) {
		try {
			entry.getKey().onStartup(entry.getValue(), getServletContext());
		} catch (ServletException e) {
			log.error(sm.getString("standardContext.sciFail"), e);
			ok = false;
			break;
		}
	}

	//疑点代码1
	//配置和调用web应用的Listener;联想到在xml集成时,通过ContextLoaderListener来完成spring容器的引入,
	//这里涉及到的listenerStart()方法可能需要看看
	// Configure and call application event listeners
	if (ok) {
		if (!listenerStart()) {
			log.error(sm.getString("standardContext.listenerFail"));
			ok = false;
		}
	}
	... ...

	// Configure and call application filters
	//配置和应用web应用的Filter
	if (ok) {
    	if (!filterStart()) {
        	log.error(sm.getString("standardContext.filterFail"));
            ok = false;
        }
    }
	
	// Load and initialize all "load on startup" servlets
	//疑点代码2
	//加载和实例化所有"启动时加载"的Servlet
    if (ok) {
    	if (!loadOnStartup(findChildren())){
	        log.error(sm.getString("standardContext.servletFail"));
            ok = false;
    	}
	}

	... ...

}

通过对上面的代码的简单分析,我们可以认定,spring容器有可能在两处代码被引入进来。

为什么?因为:
1)之前xml集成spring时,容器是通过listener监听web上下文启动事件而引入的;
2)启动servletstartInternal()方法中的最后一个与容器组件相关的事件了;

OK,先看`listenerStart()·:

public boolean listenerStart() {
	... ...
    String listeners[] = findApplicationListeners();
    Object results[] = new Object[listeners.length];
    ... ...
    for (int i = 0; i < results.length; i++) {
    	... ...
    	String listener = listeners[i];
        results[i] = getInstanceManager().newInstance(listener);
    }
	... ...
	for (int i = 0; i < results.length; i++) {
		... ...
		lifecycleListeners.add(results[i]);
	}

	for (Object lifecycleListener: getApplicationLifecycleListeners()) {
		... ... 
		lifecycleListeners.add(lifecycleListener);
		... ...
	}

	setApplicationLifecycleListeners(lifecycleListeners.toArray());
	... ...
	Object instances[] = getApplicationLifecycleListeners();
	... ...

	ServletContextEvent event = new ServletContextEvent(getServletContext());
	... ...
    for (int i = 0; i < instances.length; i++) {
    	... ...
    	ServletContextListener listener = (ServletContextListener) instances[i];
        ... ...
		listener.contextInitialized(event);

    }

	... ...
}

可见,如果容器注册了listener就会调用其contextInitialized(event)

结合前面分析的代码:只有存在顶层Config类时,才会创建ContextLoadListener,这里才有可能引入spring容器。
但很多时候,有可能是真的没有顶层Config配置类,就比如上面那个AppConfig类;所以,容器spring的引入,应该不在这里。


再看疑点代码2:loadOnStartup(findChildren())

	//参数 children由方法findChildren()返回
	public boolean loadOnStartup(Container children[]) {
		TreeMap<Integer, ArrayList<Wrapper>> map = new TreeMap<>();
		for (int i = 0; i < children.length; i++) {
			Wrapper wrapper = (Wrapper) children[i];
			... ...
			int loadOnStartup = wrapper.getLoadOnStartup();
			... ...
			Integer key = Integer.valueOf(loadOnStartup);
			... ...
			map.put(key, list);
			... ...
			list.add(wrapper);
		}
		... ...
		
		... ...
		for (ArrayList<Wrapper> list : map.values()) {
			for (Wrapper wrapper : list) {
				wrapper.load();
				... ...
			}
		}
		... ...
	}

	//children由addChild()方法加入到对象属性中
	public Container[] findChildren() {
		... ...
		Container results[] = new Container[children.size()];
		return children.values().toArray(results);
	}

    @Override
    public ServletContext getServletContext() {
        if (context == null) {
            context = new ApplicationContext(this);
            if (altDDName != null)
                context.setAttribute(Globals.ALT_DD_ATTR,altDDName);
        }
        return context.getFacade();
    }

这里分析Tomcat的源码有点恶心,我就不说过多的细节,大致的流程说下:

  • 1、tomcat调用SpringServletContainerInitializeronStartup()方法,传入ServletContext对象作为servlet上下文:ApplicationContextFacade
  • 2、在随后的registerDispatcherServlet()方法中,通过这个上下文调用addServlet(String, Servlet)注册DispatcherServlet
  • 3、ApplicationContextFacade底层会调用ApplicationContext.addServlet(String servletName, Servlet servlet)注册Servlet;
  • 4、进而将传入的Servlet,调用createWrapper()封装成Wrapper : StandardWrapper,调用StandardContext.addChild(wrapper),装入其children[]属性中;
  • 然后,容器在loadOnStartup(children)时,遍历children[],调用其wrapper.load()方法

然后,来看一看wrapper.load()方法:因为wrapper对应的类对象是StandardWrapper,所以要看StandardWrapper.load()的代码实现:

public synchronized void load() throws ServletException {
	instance = loadServlet();
	if (!instanceInitialized) {
		initServlet(instance);
	}
	... ...
	
}

public synchronized Servlet loadServlet() throws ServletException {
	... ... 
	Servlet servlet = (Servlet) instanceManager.newInstance(servletClass);
	... ...
	initServlet(servlet);
	... ...
	return servlet;
}


private synchronized void initServlet(Servlet servlet) throws ServletException {
	... ... 
	//protected final StandardWrapperFacade facade = new StandardWrapperFacade(this);
	servlet.init(facade);
	... ...
}

终于,追到最后,看到调用servlet.init()方法了,感觉spring容器要现身了:

    public void init(ServletConfig config) throws ServletException {
		this.config = config;
		this.init();//this是DispatcherServlet对象,其init()在父类HttpServletBean中
    }

	public final void init() throws ServletException {
		... ...
		initServletBean();
	}

	protected final void initServletBean() throws ServletException {
		... ...
		this.webApplicationContext = initWebApplicationContext(); 
		initFrameworkServlet();
		... ...
	}

啊,又近了下一步,看到有点熟悉的initWebApplicationContext方法了;进去确认下:

protected WebApplicationContext initWebApplicationContext() {
		WebApplicationContext rootContext =
				WebApplicationContextUtils.getWebApplicationContext(getServletContext());
		WebApplicationContext wac = null;

		if (this.webApplicationContext != null) {
			//之前在onStartup()被调用时,那会后面创建过web上下文对象
			wac = this.webApplicationContext;
			if (wac instanceof ConfigurableWebApplicationContext) {
				ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) wac;
				if (!cwac.isActive()) {
					if (cwac.getParent() == null) {
						cwac.setParent(rootContext);
					}
					configureAndRefreshWebApplicationContext(cwac);//就是这个方法,会刷新上下文对象
				}
			}
		}
		if (wac == null) {
			wac = findWebApplicationContext();
		}
		if (wac == null) {
			wac = createWebApplicationContext(rootContext);
		}

		if (!this.refreshEventReceived) {
			synchronized (this.onRefreshMonitor) {
				onRefresh(wac);
			}
		}

		if (this.publishContext) {
			String attrName = getServletContextAttributeName();
			getServletContext().setAttribute(attrName, wac);
		}

		return wac;
	}

	//DispatcherServlet的实现
	protected void configureAndRefreshWebApplicationContext(ConfigurableWebApplicationContext wac) {
		if (ObjectUtils.identityToString(wac).equals(wac.getId())) {
			// The application context id is still set to its original default value
			// -> assign a more useful id based on available information
			if (this.contextId != null) {
				wac.setId(this.contextId);
			}
			else {
				// Generate default id...
				wac.setId(ConfigurableWebApplicationContext.APPLICATION_CONTEXT_ID_PREFIX +
						ObjectUtils.getDisplayString(getServletContext().getContextPath()) + '/' + getServletName());
			}
		}

		wac.setServletContext(getServletContext());
		wac.setServletConfig(getServletConfig());
		wac.setNamespace(getNamespace());
		wac.addApplicationListener(new SourceFilteringListener(wac, new ContextRefreshListener()));

		ConfigurableEnvironment env = wac.getEnvironment();
		if (env instanceof ConfigurableWebEnvironment) {
			((ConfigurableWebEnvironment) env).initPropertySources(getServletContext(), getServletConfig());
		}

		postProcessWebApplicationContext(wac);
		applyInitializers(wac);
		wac.refresh();
	}

终于,看到最后的refresh()了!

真心不容易,ServletContainerInitializer.onStartup() + Tomcat.startInternal(),配合起来,中间再穿插一个addServlet() + Wrapper.load()的注册方法,整个调用栈太深了;能追到头,好累啊!


自此, 集成spring的方式都说清楚了. 下一步, 就是开始分析spring容器的加载过程了.

加油, 少年! 长征只走完了一小步呐!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值