1.ApplicationContextInitializer接口
/**
* Callback interface for initializing a Spring {@link ConfigurableApplicationContext}
* prior to being {@linkplain ConfigurableApplicationContext#refresh() refreshed}.
*
* <p>Typically used within web applications that require some programmatic initialization
* of the application context. For example, registering property sources or activating
* profiles against the {@linkplain ConfigurableApplicationContext#getEnvironment()
* context's environment}. See {@code ContextLoader} and {@code FrameworkServlet} support
* for declaring a "contextInitializerClasses" context-param and init-param, respectively.
*
* <p>{@code ApplicationContextInitializer} processors are encouraged to detect
* whether Spring's {@link org.springframework.core.Ordered Ordered} interface has been
* implemented or if the @{@link org.springframework.core.annotation.Order Order}
* annotation is present and to sort instances accordingly if so prior to invocation.
*
* @author Chris Beams
* @since 3.1
* @see org.springframework.web.context.ContextLoader#customizeContext
* @see org.springframework.web.context.ContextLoader#CONTEXT_INITIALIZER_CLASSES_PARAM
* @see org.springframework.web.servlet.FrameworkServlet#setContextInitializerClasses
* @see org.springframework.web.servlet.FrameworkServlet#applyInitializers
*/
public interface ApplicationContextInitializer<C extends ConfigurableApplicationContext> {
/**
* Initialize the given application context.
* @param applicationContext the application to configure
*/
void initialize(C applicationContext);
}复制代码
这是一个Spring接口,实现该接口后,spring会自动加载实现类并执行initialize方法。除了实现这个接口,还需要在web.xml中配置一个<context-param>参数,<param-name>必须是contextInitializerClasses。
<context-param>
<param-name>contextInitializerClasses</param-name>
<param-value>com.xxx.XXXApplicationContextInitializer</param-value>
</context-param>复制代码
那这个流程是怎么一回事呢?
于是我写了一个demo开始debug:
public class MySimpleContextListener implements ApplicationContextInitializer<ConfigurableApplicationContext> {
@Override
public void initialize(ConfigurableApplicationContext applicationContext) {
System.out.println("开始加载了");
}
}复制代码
这是调用栈,从下往上,是代码的执行顺序。
首先肯定是Tomcat被启动,然后bla……bla……bla……boom……da...da...da(此处略过我不懂的启动流程)。
Tomcat启动完之后开始启动web程序,并按照java web的启动顺序,依次加载,如下:
ServletContext -> context-param -> listener -> filter -> servlet
一般用springMVC构建的web程序,web.xml里都会设置这个监听器
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>复制代码
也就是调用栈里的ContextLoaderListener类,该类又继承org.springframework.web.context.ContextLoader。在调用ContextLoaderListener类的contextInitialized()方法时,又会调用其父类的initWebApplicationContext()方法,如下:
public void contextInitialized(ServletContextEvent event) {
initWebApplicationContext(event.getServletContext());
}复制代码
然后这个方法再调别的方法,一层一层,总之,就是调用栈里的那个顺序。
最后,目光放到ContextLoader类的customizeContext()方法:
protected void customizeContext(ServletContext sc, ConfigurableWebApplicationContext wac) {
List<Class<ApplicationContextInitializer<ConfigurableApplicationContext>>> initializerClasses =
determineContextInitializerClasses(sc);
for (Class<ApplicationContextInitializer<ConfigurableApplicationContext>> initializerClass : initializerClasses) {
Class<?> initializerContextClass =
GenericTypeResolver.resolveTypeArgument(initializerClass, ApplicationContextInitializer.class);
if (initializerContextClass != null && !initializerContextClass.isInstance(wac)) {
throw new ApplicationContextException(String.format(
"Could not apply context initializer [%s] since its generic parameter [%s] " +
"is not assignable from the type of application context used by this " +
"context loader: [%s]", initializerClass.getName(), initializerContextClass.getName(),
wac.getClass().getName()));
}
this.contextInitializers.add(BeanUtils.instantiateClass(initializerClass));
}
AnnotationAwareOrderComparator.sort(this.contextInitializers);
for (ApplicationContextInitializer<ConfigurableApplicationContext> initializer : this.contextInitializers) {
initializer.initialize(wac);
}
}复制代码
全在这里面了,现在可以一行行的看。
List<Class<ApplicationContextInitializer<ConfigurableApplicationContext>>> initializerClasses = determineContextInitializerClasses(sc);
它的作用是从web.xml里提取名为“globalInitializerClasses”及“contextInitializerClasses”的<context-param>参数的值。这些值在web.xml里都是字符串,所以还需要转成Class<?>对象,转换的过程中会检查,配置的这个类名是不是实现了ApplicationContextInitializer接口的类,不是的话会抛出异常。
紧接着的for循环是检查配置的这些实现类的泛型,是不是ConfigurableApplicationContext。不是的话,会抛出异常。是的话,会被实例化成对象,并放入List中。
最后,for循环执行List中ApplicationContextInitializer接口的对象,调用initialize()方法。
// 所有代码看完,发现这一切的流程早被写在接口的注释里,所以说英语好是多么重要。。。。。。
2.BeanPostProcessor接口
// 待续