标签:base cti -- active 是什么 view java 内容 http
在spring官网有这样一幅图(如下),它讲述了web项目中父子容器的概念,从图中可以看到在进行web项目开发的时候我们可以把跟前端比较靠近的一部分(如:Controller,ViewResolver,HandlerMapping)组件放入servlet webApplicationContext这个子容器中,把跟业务逻辑相关的组件(如:Service,Repositories)放在Root webApplicationContext父容器中。当我们需要这些组件的时候,只需要通过子容器就可以拿到,如果在子容器中没找到就会去父容器中找。父子容器并不是web项目特有的,在使用spring作为Bean容器的项目中都可以为一个容器设置父容器。
这样做的好处在于将组件配置文件分离,不必在一个xml文件中配置,分工明确,减少不必要的错误和麻烦。在web项目中ContextLoaderListener就承担着创建父容器的任务,DispatcherServlet承担创建子容器的任务。
public class ContextLoaderListener extends ContextLoader implements ServletContextListener
可以看到ContextLoaderListener继承ContextLoader类并实现了ServletContextListener接口。
public interface ServletContextListener extends EventListener {
public void contextInitialized(ServletContextEvent sce);
public void contextDestroyed(ServletContextEvent sce);
}
可以看到ServletContextListener有两个方法,它们的作用是在servletContext初始化之后和销毁之前做一些事情。
public class ContextLoader {
public WebApplicationContext initWebApplicationContext(ServletContext servletContext);
protected WebApplicationContext createWebApplicationContext(ServletContext sc);
protected Class<?> determineContextClass(ServletContext servletContext);
protected void configureAndRefreshWebApplicationContext(ConfigurableWebApplicationContext wac, ServletContext sc)
......
}
ContextLoader中方法主要是实例化一个webApplicationContext对象即IOC容器。
<!-- 初始化spring容器 -->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:spring/applicationContext*.xml</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
在web.xml文件中进行这样的简单的配置就可以在启动一个web项目的时候创建一个webApplicationContext对象即在web项目中使用的IOC容器,容器会加载你配置的xml文件。
如我们在web.xml配置的一样,ContextLoaderListener在tomcat中只是一个Listener,从它实现的接口来看它是一个ServletContextListener,前面也介绍了实现了这个接口的类会在一个servletContext初始化的时候被调用。所以从contextInitialized方法跟踪ContextLoaderListener的初始化。
@Override
public void contextInitialized(ServletContextEvent event) {
initWebApplicationContext(event.getServletContext());
}
这个方法调用了initWebApplication方法并将一个servletContext作为参数传入。
public WebApplicationContext initWebApplicationContext(ServletContext servletContext) {
// 第一步: 查看 servletContext是否已经有了webApplicationContext
// WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE = WebApplicationContext.ROOT
if (servletContext.getAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE)!= null) {
throw new IllegalStateException();
}
if (this.context == null) {
// 第二步:创建一个xmlwebpApplicationContext对象
this.context = createWebApplicationContext(servletContext);
}
if (this.context instanceof ConfigurableWebApplicationContext) {
ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) this.context;
if (!cwac.isActive()) {
if (cwac.getParent() == null) {
ApplicationContext parent = loadParentContext(servletContext);
cwac.setParent(parent);
}
//第三步 配置webApplicationContext并调用其onfresh方法加载xml中的单例bean
configureAndRefreshWebApplicationContext(cwac, servletContext);
}
}
//第四步:将application Context 放入servletContext 中
servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, this.context);
return this.context;
}
可以看到在上面方法中做了这个几件事情:
1.查看servletContext中是否已经了webApplicationContext(通过查看servletContext是否有属性名为WebApplicationContext.ROOT的属性值来判断的),有就抛出异常,没有则进行下一步。
2.如果没有通过构造方法传递一个WebApplicationContext对象也就是说this.context == null 的时候就调用createWebApplicationContext方法实例化一个XmlWebApplicationContext对象(ContextLoader 可以通过构造方法传入一个webApplicationContext对象,但是一般我们在web.xml配置的都是使用默认构造方法,所以这里this.context == null)
3.调用configureAndRefreshWebApplicationContext方法配置webApplicationContext并加载xml中单例Bean。
4.将webApplicationContext对象放入ServletContext中。
看看是如何create一个WebApplicationContext对象的。
protected WebApplicationContext createWebApplicationContext(ServletContext sc) {
//获得WebApplicationContext的class
Class<?> contextClass = determineContextClass(sc);
.....
//利用反射实例化一个contextClass对象
return (ConfigurableWebApplicationContext) BeanUtils.instantiateClass(contextClass);
}
接着看是如何获得WebApplicationContext的class对象的。
protected Class<?> determineContextClass(ServletContext servletContext) {
// 从servletContext中获得参数名为contextclass的参数
String contextClassName = servletContext.getInitParameter(CONTEXT_CLASS_PARAM);
if (contextClassName != null) {
try {
//返回在web.xml中配置的contextClass的Class对象
return ClassUtils.forName(contextClassName, ClassUtils.getDefaultClassLoader());
}
catch (ClassNotFoundException ex) {
throw new ApplicationContextException(
"Failed to load custom context class [" + contextClassName + "]", ex);
}
}
else {
//如果没有在web.xml中设置,则使用默认的。
contextClassName = defaultStrategies.getProperty(WebApplicationContext.class.getName());
try {
//返回一个org.springframework.web.context.support.XmlWebApplicationContext Class对象
return ClassUtils.forName(contextClassName, ContextLoader.class.getClassLoader());
}
catch (ClassNotFoundException ex) {
throw new ApplicationContextException(
"Failed to load default context class [" + contextClassName + "]", ex);
}
}
}
首先从servletContext中获得属性名为contextclass的属性值,如果没有配置这样的属性,则使用默认的WebApplicationContext类型。
那么默认的又是如何设置的呢?设置的WebApplicationContext的类型又是什么呢?
从ContextLoaderListener一段静态代码块看出是加载了ContextLoader.properites文件,
文件位于spring-web\src\main\resources\org\springframework\web\context\ContextLoader.properties
文件内容是org.springframework.web.context.WebApplicationContext=org.springframework.web.context.support.XmlWebApplicationContext。
所以通过defaultStrategies.getProperty(WebApplicationContext.class.getName())就获得了webApplicationContext的类型。
static {
try {
// 加载了 ContextLoader.properties 中的属性
ClassPathResource resource = new ClassPathResource(DEFAULT_STRATEGIES_PATH, ContextLoader.class);
defaultStrategies = PropertiesLoaderUtils.loadProperties(resource);
}
catch (IOException ex) {
throw new IllegalStateException("Could not load ‘ContextLoader.properties‘: " + ex.getMessage());
}
}
从determineContextClass方法中可以看出,我们可以自己配置WebApplicationContext的类型,但是配置的类型一定要是ConfigurableWebApplicationContext 的子类!!!!!
<context-param>
<param-name>contextclass</param-name>
<param-value>....</param-value>
</context-param>
在执行第三步之前,首先判断WebApplicationContext容器isActive,意思是确定此容器是否处于活动状态,即是否已至少刷新一次并且尚未关闭。刷新指执行容器的onfresh方法,没有关闭是 没有执行close方法。然后在容器的父容器为空的情况下使用loadParentContext方法找出父容器,这个方法直接返回null。
protected ApplicationContext loadParentContext(ServletContext servletContext) {
return null;
}
看看第三步到底做了什么
protected void configureAndRefreshWebApplicationContext(ConfigurableWebApplicationContext wac, ServletContext sc) {
// 1. 设置id
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
String idParam = sc.getInitParameter(CONTEXT_ID_PARAM);
if (idParam != null) {
wac.setId(idParam);
}
else {
// Generate default id...
wac.setId(ConfigurableWebApplicationContext.APPLICATION_CONTEXT_ID_PREFIX +
ObjectUtils.getDisplayString(sc.getContextPath()));
}
}
//2. 设置servletContext
wac.setServletContext(sc);
//3.设置xml文件
String configLocationParam = sc.getInitParameter(CONFIG_LOCATION_PARAM);
// 设置configLocation
if (configLocationParam != null) {
wac.setConfigLocation(configLocationParam);
}
//4.将servletContext作为属性放入Environment中,可以通过getProperty来获取你配置在servletContext中的initParam参数值
ConfigurableEnvironment env = wac.getEnvironment();
if (env instanceof ConfigurableWebEnvironment) {
((ConfigurableWebEnvironment) env).initPropertySources(sc, null);
}
//5.执行实现了ApplicationContextInitializer接口的类
customizeContext(sc, wac);
//6. 执行refresh方法在单例bean
wac.refresh();
}
可以看到通过ServletContext获得初始化参数来设置了id,配置文件地址等信息,所以我们也可以自己来自定义这些信息。值得注意的步骤是4,5,6步骤。
4步骤就是将servletContext作为属性设置到Environment中。
5步骤主要是执行实现了ApplicationContextInitializer接口的类,这个接口只有一个方法
public interface ApplicationContextInitializer<C extends ConfigurableApplicationContext> {
void initialize(C applicationContext);
}
可以看到initialize 方法传入一个ApplicationContext参数,可以方便实现这个接口的类在执行refresh方法即初始化bean容器之前做一些事情。
在看看具体是怎么执行的吧
protected void customizeContext(ServletContext sc, ConfigurableWebApplicationContext wac) {
// 获得配置的实现了ApplicationContextInitializer接口的类的class对象
List<Class<ApplicationContextInitializer<ConfigurableApplicationContext>>> initializerClasses =
determineContextInitializerClasses(sc);
for (Class<ApplicationContextInitializer<ConfigurableApplicationContext>> initializerClass : initializerClasses) {
...
// 利用反射将他们实例化,然后放入contextInitializers中
this.contextInitializers.add(BeanUtils.instantiateClass(initializerClass));
}
AnnotationAwareOrderComparator.sort(this.contextInitializers);
//一一执行其initialize方法
for (ApplicationContextInitializer<ConfigurableApplicationContext> initializer : this.contextInitializers) {
initializer.initialize(wac);
}
}
在看看determineContextInitializerClasses方法是如何找到我们配置的类的。
protected List<Class<ApplicationContextInitializer<ConfigurableApplicationContext>>>
determineContextInitializerClasses(ServletContext servletContext) {
List<Class<ApplicationContextInitializer<ConfigurableApplicationContext>>> classes =
new ArrayList<>();
String globalClassNames = servletContext.getInitParameter(GLOBAL_INITIALIZER_CLASSES_PARAM);
if (globalClassNames != null) {
for (String className : StringUtils.tokenizeToStringArray(globalClassNames, INIT_PARAM_DELIMITERS)) {
classes.add(loadInitializerClass(className));
}
}
String localClassNames = servletContext.getInitParameter(CONTEXT_INITIALIZER_CLASSES_PARAM);
if (localClassNames != null) {
for (String className : StringUtils.tokenizeToStringArray(localClassNames, INIT_PARAM_DELIMITERS)) {
classes.add(loadInitializerClass(className));
}
}
return classes;
}
可以看到 比较重要的两句话 :
servletContext.getInitParameter(GLOBAL_INITIALIZER_CLASSES_PARAM);
servletContext.getInitParameter(CONTEXT_INITIALIZER_CLASSES_PARAM);
也就是说 他是从servletContext的初始化参数中查找contextInitializerClasses和globalInitializerClasses参数名的配置
<context-param>
<param-name>contextInitializerClasses</param-name>
<param-value></param-value>
</context-param>
<context-param>
<param-name>globalInitializerClasses</param-name>
<param-value></param-value>
</context-param>
6步骤就是让ApplicationContext启动起来,配置一些信息,加载xml文件,实例化一些singleton bean。
springMVC源码(1) ContextLoaderListener
标签:base cti -- active 是什么 view java 内容 http
原文地址:https://www.cnblogs.com/zhangchenwei/p/12512195.html