源码解析
org.springframework.web.context.WebApplicationContext
接口来为web应用程序提供配置。这个接口将getServletContext()方法添加到通用的ApplicationContext接口,并定义一个众所周知的应用程序属性名,根上下文必须在引导过程中绑定到这个属性名。与通用应用程序上下文一样,web应用程序上下文也是分层的。每个应用程序都有一个根上下文,而应用程序中的每个servlet(包括MVC框架中的dispatcher servlet)都有自己的子上下文。除了标准的应用程序上下文生命周期功能之外,WebApplicationContext实现还需要检测ServletContextAware bean,并相应地调用setServletContext方法。
String ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE = WebApplicationContext.class.getName() + ".ROOT";
上下文属性,以便在成功启动时将根WebApplicationContext绑定到。
String SCOPE_REQUEST = "request";
请求范围的范围标识符,一次请求一次响应结束。
String SCOPE_SESSION = "session";
会话范围的范围标识符,当前会话所有的请求响应有效。
String SCOPE_GLOBAL_SESSION = "globalSession";
全局session有效。
String SCOPE_APPLICATION = "application";
应用上下文有效。
String SERVLET_CONTEXT_BEAN_NAME = "servletContext";
servletContext的bean名称。
String CONTEXT_PARAMETERS_BEAN_NAME = "contextParameters";
BeanFactory中init-params环境bean的名称。
String CONTEXT_ATTRIBUTES_BEAN_NAME = "contextAttributes";
BeanFactory中属性环境bean的名称。
ServletContext getServletContext();
返回此应用程序的标准Servlet API ServletContext。
org.springframework.web.context.ConfigurableWebApplicationContext
接口由可配置的web应用程序上下文实现。由ContextLoader和org.springframe .web.servlet. frameworkservlet支持。注意:在调用从ConfigurableApplicationContext继承的刷新方法之前,需要调用此接口的设置器。它们本身不会导致上下文的初始化。
String APPLICATION_CONTEXT_ID_PREFIX = WebApplicationContext.class.getName() + ":";
引用上下文路径和/或servlet名称的ApplicationContext id的前缀。
String SERVLET_CONFIG_BEAN_NAME = "servletConfig";
servletConfig bean名称。
void setServletContext(ServletContext servletContext);
为这个web应用程序上下文设置ServletContext。
void setServletConfig(ServletConfig servletConfig);
这个web应用程序上下文设置ServletConfig。
void setNamespace(String namespace);
设置此web应用程序上下文的名称空间,用于构建默认上下文配置位置。根web应用程序上下文没有名称空间。
void setConfigLocation(String configLocation);
以int -param样式设置此web应用程序上下文的配置位置,即使用逗号、分号或空格分隔不同的位置。如果没有设置,则实现应该为给定的名称空间或根web应用程序上下文使用默认值(视情况而定)。
void setConfigLocations(String... configLocations);
为这个web应用程序上下文设置配置位置。如果没有设置,则实现应该为给定的名称空间或根web应用程序上下文使用默认值(视情况而定)。
org.springframework.web.context.support.AbstractRefreshableWebApplicationContext
org.springframework.context.support。AbstractRefreshableApplicationContext子类,它为web环境实现了ConfigurableWebApplicationContext接口。提供“configLocations”属性,在web应用程序启动时通过ConfigurableWebApplicationContext接口进行填充。这个类和AbstractRefreshableApplicationContext一样容易子类化:您只需要实现loadbeandefinition方法;实现应该从getConfigLocations方法返回的位置指定的文件加载bean定义。将资源路径解释为servlet上下文资源,即web应用程序根目录下的路径。绝对路径,例如web应用根目录之外的文件,可以通过“file:”url访问,由org.springframe .core.io. defaultresourceloader实现。这是要为不同bean定义格式子类化的web上下文。这样的上下文实现可以指定为org.springframe .web.context的“contextClass”上下文-param。对于org.springframe .web.servlet, ContextLoader或作为“contextClass”的init-param。FrameworkServlet,替换默认的XmlWebApplicationContext。然后它将自动接收“contextConfigLocation”上下文-param或int -param。
private ServletContext servletContext;
servletContext
private ServletConfig servletConfig;
servletConfig
private String namespace;
namespace
org.springframework.web.context.support.AbstractRefreshableWebApplicationContext#postProcessBeanFactory重写了父类org.springframework.context.support.AbstractApplicationContext#postProcessBeanFactory
在应用程序上下文的标准初始化之后修改它的内部bean工厂。所有bean定义都已加载,但还没有实例化bean。这允许在特定的ApplicationContext实现中注册特殊的beanpostprocessor等。
这里的处理是添加ServletContextAwareProcessor,添加BeanFactory忽略依赖解析的接口ServletContextAware ServletConfigAware,注册application scope,注册environment bean。
@Override
protected void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {
// 添加ServletContextAwareProcessor
beanFactory.addBeanPostProcessor(new ServletContextAwareProcessor(this.servletContext, this.servletConfig));
// BeanFactory忽略依赖解析的接口ServletContextAware ServletConfigAware
beanFactory.ignoreDependencyInterface(ServletContextAware.class);
beanFactory.ignoreDependencyInterface(ServletConfigAware.class);
// 注册application scope
WebApplicationContextUtils.registerWebApplicationScopes(beanFactory, this.servletContext);
// 注册environment bean
WebApplicationContextUtils.registerEnvironmentBeans(beanFactory, this.servletContext, this.servletConfig);
}
org.springframework.web.context.support.ServletContextAwareProcessor
将ServletContext传递给实现ServletContextAware接口的bean的BeanPostProcessor实现。Web应用程序上下文将自动将其注册到其底层bean工厂。应用程序不会直接使用它。
org.springframework.web.context.support.ServletContextAwareProcessor#postProcessBeforeInitialization重写了org.springframework.beans.factory.config.BeanPostProcessor#postProcessBeforeInitialization方法,在任何bean初始化回调之前(如InitializingBean的afterPropertiesSet或自定义init-method),将此BeanPostProcessor应用于给定的新bean实例。bean已经被填充了属性值。返回的bean实例可能是原始bean的包装器。
这里的处理是执行org.springframework.web.context.ServletContextAware.setServletContext(),执行org.springframework.web.context.ServletConfigAware.setServletConfig()。
org.springframework.web.context.ServletContextAware
接口由任何希望被通知其运行的ServletContext(通常由WebApplicationContext决定)的对象实现
void setServletContext(ServletContext servletContext);
设置此对象运行的ServletContext。在普通bean属性填充之后调用,但在初始化回调之前调用,如InitializingBean的afterPropertiesSet或定制的init方法。在ApplicationContextAware的setApplicationContext之后调用。
org.springframework.web.context.ServletConfigAware
接口由任何希望被通知其运行的ServletConfig(通常由WebApplicationContext决定)的对象实现。注意:只有在特定于servlet的WebApplicationContext中实际运行时才会满足。否则,将不设置ServletConfig。
void setServletConfig(ServletConfig servletConfig);
设置此对象运行的ServletConfig。在普通bean属性填充之后调用,但在初始化回调之前调用,如InitializingBean的afterPropertiesSet或定制的init方法。在ApplicationContextAware的setApplicationContext之后调用。
org.springframework.web.context.support.ServletContextAwareProcessor#postProcessAfterInitialization
重写了org.springframework.beans.factory.config.BeanPostProcessor#postProcessAfterInitialization。在任何bean初始化回调之后(如InitializingBean的afterPropertiesSet或自定义init-method),将此BeanPostProcessor应用于给定的新bean实例。bean已经被填充了属性值。返回的bean实例可能是原始bean的包装器。
对于FactoryBean,这个回调将被FactoryBean实例和FactoryBean创建的对象调用(从Spring 2.0开始)。后处理器可以决定是应用于FactoryBean还是创建的对象,或者通过相应的bean instanceof FactoryBean检查应用于两者。
这个回调也将在实例化awarebeanpostprocessor触发的短路之后调用。与所有其他BeanPostProcessor回调不同,postProcessBeforeInstantiation方法。
这里的处理是不做任何处理。
返回org.springframework.web.context.support.AbstractRefreshableWebApplicationContext#postProcessBeanFactory
WebApplicationContextUtils.registerWebApplicationScopes(beanFactory, this.servletContext);
注册application scope
org.springframework.web.context.support.WebApplicationContextUtils#registerWebApplicationScopes(org.springframework.beans.factory.config.ConfigurableListableBeanFactory, javax.servlet.ServletContext)
使用给定的BeanFactory注册特定于web的作用域("request", "session", "globalSession", "application")。
public static void registerWebApplicationScopes(ConfigurableListableBeanFactory beanFactory, ServletContext sc) {
// 注册scope到BeanFactory中
beanFactory.registerScope(WebApplicationContext.SCOPE_REQUEST, new RequestScope());
beanFactory.registerScope(WebApplicationContext.SCOPE_SESSION, new SessionScope(false));
beanFactory.registerScope(WebApplicationContext.SCOPE_GLOBAL_SESSION, new SessionScope(true));
if (sc != null) {
ServletContextScope appScope = new ServletContextScope(sc);
beanFactory.registerScope(WebApplicationContext.SCOPE_APPLICATION, appScope);
// Register as ServletContext attribute, for ContextCleanupListener to detect it.
sc.setAttribute(ServletContextScope.class.getName(), appScope);
}
// 注册BeanFactory解析的依赖
beanFactory.registerResolvableDependency(ServletRequest.class, new RequestObjectFactory());
beanFactory.registerResolvableDependency(ServletResponse.class, new ResponseObjectFactory());
beanFactory.registerResolvableDependency(HttpSession.class, new SessionObjectFactory());
beanFactory.registerResolvableDependency(WebRequest.class, new WebRequestObjectFactory());
if (jsfPresent) {
FacesDependencyRegistrar.registerFacesDependencies(beanFactory);
}
}
org.springframework.web.context.request.RequestScope
依赖于一个线程绑定的RequestAttributes实例,它可以通过RequestContextListener、org.springframe .web.filter导出。RequestContextFilter或org.springframework.web.servlet.DispatcherServlet。
org.springframework.web.context.request.SessionScope
依赖于一个线程绑定的RequestAttributes实例,它可以通过RequestContextListener、org.springframe .web.filter导出。RequestContextFilter或org.springframework.web.servlet.DispatcherServlet。
org.springframework.web.context.request.RequestAttributes
用于访问与请求关联的属性对象的抽象。通过可选的“global session”概念,支持对请求范围属性和会话范围属性的访问。可以为任何类型的请求/会话机制实现
Object getAttribute(String name, int scope);
获取属性值
void setAttribute(String name, Object value, int scope);
设置属性值
void removeAttribute(String name, int scope);
删除属性值
void registerDestructionCallback(String name, Runnable callback, int scope);
注册一个回调函数,该回调函数将在销毁给定范围中的指定属性时执行。
org.springframework.web.context.support.ServletContextScope
servlet上下文的作用域包装器,即全局web应用程序属性的作用域包装器。这与传统的Spring单例有所不同,因为它在ServletContext中公开属性。当整个应用程序关闭时,这些属性将被销毁,这可能早于或晚于包含Spring ApplicationContext的关闭。相关的销毁机制依赖于org.springframe .web.context。ContextCleanupListener在web.xml中注册。注意,org.springframework.web.context。ContextLoaderListener包含ContextCleanupListener的功能。此作用域注册为默认作用域,密钥为“application”。
org.springframework.web.context.ContextCleanupListener实现javax.servlet.ServletContextListener
Web应用程序侦听器,用于清理ServletContext中剩余的一次性属性,即实现DisposableBean且以前未被删除的属性。这通常用于销毁“应用程序”范围内的对象,生命周期意味着在web应用程序关闭阶段的最后阶段销毁这些对象。
@Override
public void contextDestroyed(ServletContextEvent event) {
cleanupAttributes(event.getServletContext());
}
应用上下文关闭时执行。org.springframework.web.context.ContextCleanupListener#cleanupAttributes 找到所有实现DisposableBean的ServletContext属性并销毁它们,最终删除所有受影响的ServletContext属性。
static void cleanupAttributes(ServletContext sc) {
Enumeration<String> attrNames = sc.getAttributeNames();
while (attrNames.hasMoreElements()) {
String attrName = attrNames.nextElement();
if (attrName.startsWith("org.springframework.")) {
Object attrValue = sc.getAttribute(attrName);
if (attrValue instanceof DisposableBean) {
try {
// 执行org.springframework.beans.factory.DisposableBean.destroy()
((DisposableBean) attrValue).destroy();
}
catch (Throwable ex) {
logger.error("Couldn't invoke destroy method of attribute with name '" + attrName + "'", ex);
}
}
}
}
}
这里会执行这个方法org.springframework.web.context.support.ServletContextScope#destroy。
返回org.springframework.web.context.support.AbstractRefreshableWebApplicationContext#postProcessBeanFactory
WebApplicationContextUtils.registerEnvironmentBeans(beanFactory, this.servletContext, this.servletConfig);
注册environment bean。
org.springframework.web.context.support.WebApplicationContextUtils#registerEnvironmentBeans(org.springframework.beans.factory.config.ConfigurableListableBeanFactory, javax.servlet.ServletContext, javax.servlet.ServletConfig)
public static void registerEnvironmentBeans(
ConfigurableListableBeanFactory bf, ServletContext servletContext, ServletConfig servletConfig) {
// BenFactory中不包括servletContext
if (servletContext != null && !bf.containsBean(WebApplicationContext.SERVLET_CONTEXT_BEAN_NAME)) {
// 注册单例bean servletContext bf.registerSingleton(WebApplicationContext.SERVLET_CONTEXT_BEAN_NAME, servletContext);
}
// BeanFactory中不包括servletConfig
if (servletConfig != null && !bf.containsBean(ConfigurableWebApplicationContext.SERVLET_CONFIG_BEAN_NAME)) {
// 注册单例bean servletConfig bf.registerSingleton(ConfigurableWebApplicationContext.SERVLET_CONFIG_BEAN_NAME, servletConfig); }
// BeanFactory中不包括contextParameters
if (!bf.containsBean(WebApplicationContext.CONTEXT_PARAMETERS_BEAN_NAME)) { Map<String, String> parameterMap = new Hash
Map<String, String>();
if (servletContext != null) {
// 从servletContext中获取servlet上下文初始化参数
Enumeration<?> paramNameEnum = servletContext.getInitParameterNames();
while (paramNameEnum.hasMoreElements()) {
String paramName = (String) paramNameEnum.nextElement();
parameterMap.put(paramName, servletContext.getInitParameter(paramName));
}
}
// 从servletConfig中获取servlet上下文初始化参数
if (servletConfig != null) {
Enumeration<?> paramNameEnum = servletConfig.getInitParameterNames();
while (paramNameEnum.hasMoreElements()) {
String paramName = (String) paramNameEnum.nextElement();
parameterMap.put(paramName, servletConfig.getInitParameter(paramName));
}
}
// 注册单例bean contextParameters bf.registerSingleton(WebApplicationContext.CONTEXT_PARAMETERS_BEAN_NAME,
Collections.unmodifiableMap(parameterMap));
}
// BeanFactory中不包括contextAttributes
if (!bf.containsBean(WebApplicationContext.CONTEXT_ATTRIBUTES_BEAN_NAME)) {
Map<String, Object> attributeMap = new HashMap<String, Object>();
// 从servletContext中获取参数
if (servletContext != null) {
Enumeration<?> attrNameEnum = servletContext.getAttributeNames();
while (attrNameEnum.hasMoreElements()) {
String attrName = (String) attrNameEnum.nextElement();
attributeMap.put(attrName, servletContext.getAttribute(attrName));
}
}
// 注册contextAttributes 单例bean bf.registerSingleton(WebApplicationContext.CONTEXT_ATTRIBUTES_BEAN_NAME,
Collections.unmodifiableMap(attributeMap));
}
}
org.springframework.web.context.support.AbstractRefreshableWebApplicationContext#onRefresh重写org.springframework.context.support.AbstractApplicationContext#onRefresh。模板方法,可以覆盖该方法以添加特定于上下文的刷新工作。在单例实例化之前,在初始化特殊bean时调用。
这个实现是空的。这里的处理是初始化themeSource。
org.springframework.ui.context.support.UiApplicationContextUtils#initThemeSource
为给定的应用程序上下文初始化主题资源,自动检测名为“themeSource”的bean。如果没有找到这样的bean,将使用默认(空)的ThemeSource。
public static ThemeSource initThemeSource(ApplicationContext context) {
// BeanFactory中包含themeSource BeanDefinition
if (context.containsLocalBean(THEME_SOURCE_BEAN_NAME)) {
// 从BeanFactory中获取themeSource bean
ThemeSource themeSource = context.getBean(THEME_SOURCE_BEAN_NAME,ThemeSource.class);
// Make ThemeSource aware of parent ThemeSource.
if (context.getParent() instanceof ThemeSource && themeSource instanceof
HierarchicalThemeSource) { HierarchicalThemeSource hts = (HierarchicalThemeSource) themeSource;
if (hts.getParentThemeSource() == null) {
// Only set parent context as parent ThemeSource if no parent ThemeSource
// registered already.//如果没有父源,则只将父上下文设置为父源
//注册了。
hts.setParentThemeSource((ThemeSource) context.getParent());
}
}
if (logger.isDebugEnabled()) {
logger.debug("Using ThemeSource [" + themeSource + "]");
}
return themeSource;
}
else {
// Use default ThemeSource to be able to accept getTheme calls, either
// delegating to parent context's default or to local ResourceBundleThemeSource.//也可以使用默认的ThemeSource来接受getTheme调用
//委托给父上下文的默认值或本地的ResourceBundleThemeSource。
HierarchicalThemeSource themeSource = null;
if (context.getParent() instanceof ThemeSource) {
themeSource = new DelegatingThemeSource();
themeSource.setParentThemeSource((ThemeSource) context.getParent());
}
else {
themeSource = new ResourceBundleThemeSource();
}
if (logger.isDebugEnabled()) {
logger.debug("Unable to locate ThemeSource with name '" + THEME_SOURCE_BEAN_NAME +
"': using default [" + themeSource + "]");
}
return themeSource;
}
}
org.springframework.web.context.support.AbstractRefreshableWebApplicationContext#initPropertySources重写org.springframework.context.support.AbstractApplicationContext#initPropertySources 用实际实例替换任何存根属性源。
@Override
protected void initPropertySources() {
ConfigurableEnvironment env = getEnvironment();
if (env instanceof ConfigurableWebEnvironment) {
((ConfigurableWebEnvironment) env).initPropertySources(this.servletContext, this.servletConfig);
}
}
这里的处理是初始化property资源。
org.springframework.web.context.support.StandardServletEnvironment#initPropertySources
@Override
public void initPropertySources(ServletContext servletContext, ServletConfig servletConfig) {
WebApplicationContextUtils.initServletPropertySources(getPropertySources(), servletContext, servletConfig);
}
org.springframework.web.context.support.WebApplicationContextUtils#initServletPropertySources(org.springframework.core.env.MutablePropertySources, javax.servlet.ServletContext, javax.servlet.ServletConfig)
将基于servlet的存根属性源替换为使用给定的servletContext和servletConfig对象填充的实际实例。这个方法是幂等的,因为它可以被调用很多次,但是将用它们对应的实际属性源替换存根属性源一次,而且只有一次。
public static void initServletPropertySources(
MutablePropertySources propertySources, ServletContext servletContext, ServletConfig servletConfig) {
Assert.notNull(propertySources, "'propertySources' must not be null");
if (servletContext != null && propertySources.contains(StandardServletEnvironment.SERVLET_CONTEXT_PROPERTY_SOURCE_NAME) &&
propertySources.get(StandardServletEnvironment.SERVLET_CONTEXT_PROPERTY_SOURCE_NAME) instanceof StubPropertySource) {
propertySources.replace(StandardServletEnvironment.SERVLET_CONTEXT_PROPERTY_SOURCE_NAME,
new ServletContextPropertySource(StandardServletEnvironment.SERVLET_CONTEXT_PROPERTY_SOURCE_NAME, servletContext));
}
if (servletConfig != null &&propertySources.contains(StandardServletEnvironment.SERVLET_CONFIG_PROPERTY_SOURCE_NAME) &&
propertySources.get(StandardServletEnvironment.SERVLET_CONFIG_PROPERTY_SOURCE_NAME) instanceof StubPropertySource) {
propertySources.replace(StandardServletEnvironment.SERVLET_CONFIG_PROPERTY_SOURCE_NAME,
new ServletConfigPropertySource(StandardServletEnvironment.SERVLET_CONFIG_PROPERTY_SOURCE_NAME, servletConfig));
}
}
org.springframework.web.context.support.AnnotationConfigWebApplicationContext
WebApplicationContext实现,它接受带注释的类作为输入——特别是@ configuration带注释的类,但也接受普通的@Component类和使用javax兼容JSR-330的类。注入注解。允许逐个注册类(指定类名作为配置位置)以及类路径扫描(指定基包作为配置位置)。这本质上相当于web环境的AnnotationConfigApplicationContext。要使用此应用程序上下文,必须将ContextLoader的“contextClass”上下文-param和/或FrameworkServlet的“contextClass”init-param设置为该类的全限定名称。从Spring 3.1开始,当使用基于web.xml的基于代码的新WebApplicationInitializer替代方案时,这个类也可以直接实例化并注入Spring的DispatcherServlet或ContextLoaderListener。与XmlWebApplicationContext不同的是,没有默认的配置类位置。相反,需要为ContextLoader设置“contextConfigLocation”上下文-param,为FrameworkServlet设置“contextConfigLocation”init-param。参数值可以包含完全限定的类名和用于扫描组件的基包。作为设置“contextConfigLocation”参数的替代方法,用户可以实现ApplicationContextInitializer并设置“contextInitializerClasses”上下文-param / init-param。在这种情况下,用户应该更喜欢refresh()和scan(String…)方法,而不是setConfigLocation(String)方法,后者主要供ContextLoader使用。对于多个@Configuration类,后面的@Bean定义将覆盖先前加载的文件中定义的类。
private final Set<Class<?>> annotatedClasses = new LinkedHashSet<Class<?>>();
带@Configuration注解的类名
private final Set<String> basePackages = new LinkedHashSet<String>();
扫描的包名
public void register(Class<?>... annotatedClasses) {
Assert.notEmpty(annotatedClasses, "At least one annotated class must be specified");
this.annotatedClasses.addAll(Arrays.asList(annotatedClasses));
}
注册一个或多个要处理的注释类。
public void scan(String... basePackages) {
Assert.notEmpty(basePackages, "At least one base package must be specified");
this.basePackages.addAll(Arrays.asList(basePackages));
}
在指定的基本包中执行扫描。
@Override
protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) {
// 查询AnnotatedBeanDefinitionReader AnnotatedBeanDefinitionReader reader = get
AnnotatedBeanDefinitionReader(beanFactory);
// 查询ClassPathBeanDefinitionScanner
ClassPathBeanDefinitionScanner scanner = getClassPathBeanDefinitionScanner(beanFactory);
BeanNameGenerator beanNameGenerator = getBeanNameGenerator();
if (beanNameGenerator != null) {
reader.setBeanNameGenerator(beanNameGenerator);
scanner.setBeanNameGenerator(beanNameGenerator);
// 注册单例bean beanNameGenerator
beanFactory.registerSingleton(AnnotationConfigUtils.CONFIGURATION_BEAN_NAME_GENERATOR, beanNameGenerator);
}
// 获取scopeMetadataResolver
ScopeMetadataResolver scopeMetadataResolver = getScopeMetadataResolver();
if (scopeMetadataResolver != null) {
reader.setScopeMetadataResolver(scopeMetadataResolver);
scanner.setScopeMetadataResolver(scopeMetadataResolver);
}
// 带有@Configuration类可以有多个
if (!this.annotatedClasses.isEmpty()) {
if (logger.isInfoEnabled()) {
logger.info("Registering annotated classes: [" +
StringUtils.collectionToCommaDelimitedString(this.annotatedClasses) + "]");
}
// 注册@Configuration指定类的BeanDefinition
reader.register(ClassUtils.toClassArray(this.annotatedClasses));
}
// 包名可以有多个,用,分开
if (!this.basePackages.isEmpty()) {
if (logger.isInfoEnabled()) {
logger.info("Scanning base packages: [" +
StringUtils.collectionToCommaDelimitedString(this.basePackages) + "]");
}
// 按指定的包名扫描解析beanDefinition并注册
scanner.scan(StringUtils.toStringArray(this.basePackages));
}
// 获取spring配置文件,可以是多个
String[] configLocations = getConfigLocations();
if (configLocations != null) {
for (String configLocation : configLocations) {
try {
// 从配置文件加载class
Class<?> clazz = getClassLoader().loadClass(configLocation);
if (logger.isInfoEnabled()) {
logger.info("Successfully resolved class for [" + configLocation + "]");
}
// 注册class的bean reader.register(clazz);
}
catch (ClassNotFoundException ex) {
if (logger.isDebugEnabled()) {
logger.debug("Could not load class for config location [" + configLocation +
"] - trying package scan. " + ex);
}
// 扫描配置文件中配置的包名的BeanDefinition
int count = scanner.scan(configLocation);
if (logger.isInfoEnabled()) {
if (count == 0) { logger.info("No annotated classes found for specified class/package [" + configLocation + "]");
}
else {
logger.info("Found " + count + " annotated classes in package [" + configLocation + "]");
}
}
}
}
}
}
用AnnotatedBeanDefinitionReader读取BeanDefinition并解析注册到BeanFactory中。用ClassPathBeanDefinitionScanner按指定的包名扫描BeanDefinition。
先解析带有@Configuration注解的类并注册BeanDefinition到BeanFactory中,按basePackages指定的包名扫描BeanDefinition并注册到BeanFactory中。解析configLocations指定的配置文件,先解析指定的class注册BeanDefinition然后再按包名扫描BeanDefinition注册到BeanFactory中。
org.springframework.web.context.support.XmlWebApplicationContext
org.springframework.web.context。WebApplicationContext实现从XML文档中获取配置,XmlBeanDefinitionReader可以理解它。这本质上相当于org.springframe .context.support。web环境的GenericXmlApplicationContext。默认情况下,配置将取自“/WEB-INF/applicationContext”。xml“用于根上下文”和“/WEB-INF/test-servlet”。对于名称空间为“test-servlet”的上下文(例如对于使用servlet名称为“test”的DispatcherServlet实例)。置位置默认值可以通过org.springframe .web.context的“contextConfigLocation”上下文-param覆盖。frameworkservlet的ContextLoader和servlet的init-param。配置位置既可以表示具体的文件,如“/WEB-INF/context”。或者ant风格的模式,比如“/WEB-INF/*-context”。xml”,对于多个配置位置,后面的bean定义将覆盖前面加载的文件中定义的配置位置。对于读取不同bean定义格式的WebApplicationContext,创建AbstractRefreshableWebApplicationContext的类似子类。这样的上下文实现可以指定为ContextLoader的“contextClass”上下文-param或FrameworkServlet的“contextClass”init-param。
public static final String DEFAULT_CONFIG_LOCATION = "/WEB-INF/applicationContext.xml";
spring默认配置文件。
public static final String DEFAULT_CONFIG_LOCATION_PREFIX = "/WEB-INF/";
spring配置文件默认路径。
public static final String DEFAULT_CONFIG_LOCATION_SUFFIX = ".xml";
spring配置文件默认后缀。
@Override
protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {
// Create a new XmlBeanDefinitionReader for the given BeanFactory.
XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);
// Configure the bean definition reader with this context's
// resource loading environment.
beanDefinitionReader.setEnvironment(getEnvironment());
beanDefinitionReader.setResourceLoader(this);
beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));
// Allow a subclass to provide custom initialization of the reader,
// then proceed with actually loading the bean definitions.
//允许子类为读写器提供自定义初始化,
//然后继续实际加载bean定义。
// 模板方法
initBeanDefinitionReader(beanDefinitionReader);
// 加载bean定义
loadBeanDefinitions(beanDefinitionReader);
}
初始化beanDefinitionReader从指定的配置文件中加载BeanDefinition。
org.springframework.web.context.support.XmlWebApplicationContext#initBeanDefinitionReader
初始化用于加载此上下文的bean定义的bean定义阅读器。默认实现为空。可以在子类中重写,例如用于关闭XML验证或使用不同的XmlBeanDefinitionParser实现。
org.springframework.web.context.support.XmlWebApplicationContext#loadBeanDefinitions(org.springframework.beans.factory.xml.XmlBeanDefinitionReader)
使用给定的XmlBeanDefinitionReader加载bean定义。bean工厂的生命周期由refreshBeanFactory方法处理;因此,这个方法应该只加载和/或注册bean定义。委托给ResourcePatternResolver将位置模式解析为资源实例。
protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws IOException {
// 获取指定的BeanDefinition配置文件
String[] configLocations = getConfigLocations();
if (configLocations != null) {
for (String configLocation : configLocations) {
// 读取配置文件加载BeanDefinition
reader.loadBeanDefinitions(configLocation);
}
}
}
读取配置文件并加载BeanDefinition。
org.springframework.web.context.support.XmlWebApplicationContext#getDefaultConfigLocations
根上下文的默认位置是“/WEB-INF/applicationContext”。xml”和“/ web - inf /测试servlet。对于名称空间为“test-servlet”的上下文(例如对于使用servlet名称为“test”的DispatcherServlet实例)。
@Override
protected String[] getDefaultConfigLocations() {
if (getNamespace() != null) {
return new String[] {DEFAULT_CONFIG_LOCATION_PREFIX + getNamespace() + DEFAULT_CONFIG_LOCATION_SUFFIX};
}
else {
return new String[] {DEFAULT_CONFIG_LOCATION};
}
}
如果namespace不为空,如果配置了一个DispatcherServlet的servlet-name属性是test,namespace值是test-servlet,就会从/WEB-INF/test-servlet.xml加载spring配置文件,否则就从/WEB-INF/applicationContext.xml加载spring配置文件,我们一般使用<context-param>标签在web.xml中这样配置指定spring配置文件。
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:applicationContext/applicationContext.xml</param-value>
</context-param>
org.springframework.web.context.ContextLoader
执行根应用程序上下文的实际初始化工作。被ContextLoaderListener叫调用。在web.xml上下文-param级别上查找“contextClass”参数,以指定上下文类类型,返回到对org.springframe .web.context.support的支持。如果没有找到XmlWebApplicationContext。使用默认的ContextLoader实现,指定的任何上下文类都需要实现ConfigurableWebApplicationContext接口。处理“contextConfigLocation”上下文参数,并将其值传递给上下文实例,将其解析为多个文件路径,这些文件路径可以由任意数量的逗号和空格分隔。“- inf / applicationContext1。xml、web - inf / applicationContext2.xml”。也支持ant样式的路径模式,例如。“web - inf / * Context.xml - inf /春*。xml”或“web - inf / * * / * Context.xml”。如果没有明确指定,上下文实现应该使用默认位置(使用XmlWebApplicationContext: "/WEB-INF/applicationContext.xml")。注意:对于多个配置位置,以后的bean定义将覆盖以前加载的文件中定义的那些,至少在使用Spring的默认ApplicationContext实现时是这样。可以利用这一点通过一个额外的XML文件有意地覆盖某些bean定义。除了加载根应用程序上下文之外,该类还可以选择加载或获取共享父上下文并将其关联到根应用程序上下文。有关更多信息,请参见loadParentContext(ServletContext)方法。从Spring 3.1开始,ContextLoader支持通过ContextLoader(WebApplicationContext)构造函数注入根web应用程序上下文,允许在Servlet 3.0+环境中进行编程配置。看到org.springframework.web。使用示例的WebApplicationInitializer。
public static final String CONFIG_LOCATION_PARAM = "contextConfigLocation";
servlet上下文参数的名称(即),它可以为根上下文指定配置位置,否则返回到实现的默认位置。
public static final String CONTEXT_CLASS_PARAM = "contextClass";
要使用的根WebApplicationContext实现类的配置参数:contextClass
public static final String CONTEXT_INITIALIZER_CLASSES_PARAM = "contextInitializerClasses";
用于初始化根web应用程序上下文的ApplicationContextInitializer类的配置参数:“contextInitializerClasses”
public static final String GLOBAL_INITIALIZER_CLASSES_PARAM = "globalInitializerClasses";
用于初始化当前应用程序中的所有web应用程序上下文的全局ApplicationContextInitializer类的配置参数
private static final String INIT_PARAM_DELIMITERS = ",; \t\n";
任何数量的这些字符都被认为是单个int -param字符串值中多个值之间的分隔符。
private static final String DEFAULT_STRATEGIES_PATH = "ContextLoader.properties";
定义ContextLoader默认策略名称的类路径资源的名称(相对于ContextLoader类)。
ContextLoader.properties
org.springframework.web.context.WebApplicationContext=org.springframework.web.context.support.XmlWebApplicationContext
这里ContextLoader默认加载的是XmlWebApplicationContext。
private final List<ApplicationContextInitializer<ConfigurableApplicationContext>> contextInitializers = new ArrayList<ApplicationContextInitializer<ConfigurableApplicationContext>>();
applicationContextInitializers
说在最后
关于源码阅读还是建议看视频讲解,一起跟着操作,培养出这种阅读源码的能力。
下面是小编学习源码的时候学习的视频,有需要的小伙伴(转发+关注后私信“666”免费获取)