总概
ApplicationContext继承结构图
ApplicationContext整体结构解析
1、非web应用(可配置执行期上下文)
如图:
此时,再看结构图:
2、web应用(web环境执行期上下文)
顶层接口
ApplicationContext接口
结构图
作用
- BeanFactory:Spring 管理 Bean 的顶层接口,ApplicationContext 继承了 BeanFactory 的两个子类:HierarchicalBeanFactory 和 ListableBeanFactory。HierarchicalBeanFactory 是一个具有层级关系的 BeanFactory,拥有属性 parentBeanFactory。 ListableBeanFactory 实现了枚举方法可以列举出当前 BeanFactory 中所有的 bean 对象而不必根据 name 一个一个的获取。
- ApplicationEventPublisher(事件发布):用于封装事件发布功能的接口,向事件监听器(Listener)发送事件消息。
- ResourceLoader(让其有加载文件资源的能力):Spring 加载资源的顶层接口,用于加载资源文件。ApplicationContext 继承 ResourceLoader 的子类 ResourcePatternResolver,该接口是将 location 解析为 Resource 对象的策略接口。
- MessageSource(消息,一般用于国际化的支持) :解析 message 的策略接口,用于支撑国际化等功能。
- EnvironmentCapable (环境感知):用于获取 Environment 的接口。
关键词
执行期上下文
解析
二级接口-ConfigurableApplicationContext接口
结构图
作用
- 配置应用上下文的能力
- 启动/停止生命周期
- 关闭应用上下文,释放所有的资源和锁,这也包括摧毁所有缓存的单例的bean
关键词
Configurable:可配置的
解析
ApplicationContext接口本身是 read-only
的,所以子接口 ConfigurableApplicationContext就提供了如setID()、setParent()、setEnvironment()等方法,用来配置ApplicationContext。如图:
我们可以看到,ApplicationContext里面都是get方法,所以ApplicationContext接口本身是 read-only
的。ConfigurableApplicationContext
在ApplicationContext
的基础上提供了配置应用上下文的能力。如图:
而且ConfigurableApplicationContext
不仅如此,他还继承了Lifecycle
和Closeable
:
(1)Lifecycle
定义了启动/停止生命周期等一些方法
(2)Closeable
接口用于关闭应用上下文
,释放所有的资源和锁,这也包括摧毁所有缓存的单例的bean
二级接口-WebApplicationContext接口
结构图
作用
- 它允许从相对于Web根目录的路径中加载配置文件完成初始化工作。从WebApplicationContext中可以获取ServletContext引用,整个Web应用上下文对象将作为属性放置在ServletContext中,以便Web应用环境可以访问Spring上下文。
- WebApplicationContext还为Bean提供了三个新的作用域,request、session和application。其中两个参数HttpServletRequest:服务器从客户端拿去数据 ,HttpServletResponse:服务器向前台传送数据
关键词
web:web环境
解析
WebApplicationContext是实现ApplicationContext接口的子类。是专门为WEB应用准备的。
WebApplicationContext的初始化方式和BeanFactory、ApplicationContext有所区别,因为WebApplicationContext需要ServletContext实例,也就是说它必须拥有Web容器的前提下才能完成启动的工作。WebApplicationContext提供了在web应用中的配置,接口提供了一个ServletContext getServletContext()
用来获取ServletContext
对象:
注:有过Web开发经验的读者都知道可以在web.xml中配置自启动的Servlet或定义Web容器监听器(ServletContextListener),借助着两者中的任何一个,我们就可以启动Spring Web应用上下文的工作。
非web应用
三级抽象类-AbstractApplicationContext
JavaDoc
1.它是对ApplicationContext接口的抽象实现,不强制指定用于配置的存储类型(就是注解或者xml文件配置等的类型),只实现公共上下文功能。
2.它使用了模板设计模式,需要具体的子类去实现抽象方法
3.与纯BeanFactory不同,ApplicationContext用于检测在其内部bean工厂中定义的特殊bean:因此,他会自动注册在context中定义为bean的BeanFactoryPostProcessors、BeanPostProcessors和ApplicationListeners。
4.在context中MessageSource也可能作为bean被提供,其名字为"messageSource",否则,消息解析将会被委托给父类上下文。此外,在context中,对应用程序事件的广播由ApplicationEventMulticaster类型的"applicationEventMulticaster"的bean提供,否则,默认会使用SimpleApplicationEventMulticaster。
5.通过继承DefaultResourceLoader实现了资源的加载功能,主要作用:将非URL资源路径视为类路径资源(支持包含包路径的完整类路径资源名称),除非在子类重写getResourceByPath方法。
方法解析
ApplicationContext方法实现
省略
ConfigurableApplicationContext方法实现
在这里,实现了一个很重要的方法refresh(),并实现了里面绝大多数步骤的方法,但是也遗留了一些模板方法留给子类实现
BeanFactory方法实现
省略
ListableBeanFactory方法实现
省略
HierarchicalBeanFactory方法实现
@Override
@Nullable
public BeanFactory getParentBeanFactory() {
return getParent();
}
@Override
public boolean containsLocalBean(String name) {
return getBeanFactory().containsLocalBean(name);
}
//找到父的,若存在就返回 若存在父容器就存在父的BeanFactory
@Nullable
protected BeanFactory getInternalParentBeanFactory() {
return (getParent() instanceof ConfigurableApplicationContext ?
((ConfigurableApplicationContext) getParent()).getBeanFactory() : getParent());
}
MessageSource方法实现
省略
ResourcePatternResolver方法实现
省略
Lifecycle方法实现
@Override
public void start() {
getLifecycleProcessor().start();
publishEvent(new ContextStartedEvent(this));
}
@Override
public void stop() {
getLifecycleProcessor().stop();
publishEvent(new ContextStoppedEvent(this));
}
@Override
public boolean isRunning() {
return (this.lifecycleProcessor != null && this.lifecycleProcessor.isRunning());
}
自定义的抽象方法
// 子类必须实现这个方法:子类可以创建一个新的BeanFactory,或者直接指向自己一个已经hold的引用
// (GenericApplicationContext就是指向自己已经持有的)
protected abstract void refreshBeanFactory() throws BeansException, IllegalStateException;
// 销毁BeanFactory this.beanFactory=null
protected abstract void closeBeanFactory();
// refreshBeanFactory这里肯定已经有工厂实例了,这里直接返回呗
@Override
public abstract ConfigurableListableBeanFactory getBeanFactory() throws IllegalStateException;
-----------------------------------------------------------------------------------------
四级类-GenericApplicationContext
JavaDoc
1. 通用的ApplicationContext实现,它拥有一个单独的内部DefaultListableBeanFactory实例,并且不假设特定的bean定义格式。
2. 实现BeanDefinitionRegistry接口,以便允许对其应用任何bean definition reader(也可以不是XmlBeanDefinitionReader)。典型的用法是通过BeanDefinitionRegistry接口注册各种bean定义,然后调用AbstractApplicationContext。
3. 使用应用程序上下文语义(处理ApplicationContextAware,自动检测BeanFactoryPostProcessors等)初始化这些bean。
4. 与为每次刷新创建一个新的内部BeanFactory实例的其他ApplicationContext实现相反,此上下文的内部BeanFactory从一开始就可用,以便能够在其上注册bean定义。AbstractApplicationContext.refresh()只能被调用一次。
关键词
Generic:表现出它的通用
解析
在这里,GenericApplicationContext 持有一个DefaultListableBeanFactory实例
GenericApplicationContext基本就是对DefaultListableBeanFactory 做了个简易的封装,几乎所有方法都是使用了DefaultListableBeanFactory的方法去实现。
基于这个特性,GenericApplicationContext能够混合搭配从不同源头获取bean的定义信息,能加载各种配置文件,例如xml,properties等等,如图:
最后在统一调用refresh()方法刷新,如下示例:
GenericApplicationContext ctx = new GenericApplicationContext();
XmlBeanDefinitionReader xmlReader = new XmlBeanDefinitionReader(ctx);
xmlReader.loadBeanDefinitions(new ClassPathResource("applicationContext.xml"));
PropertiesBeanDefinitionReader propReader = new PropertiesBeanDefinitionReader(ctx);
propReader.loadBeanDefinitions(new ClassPathResource("otherBeans.properties"));
ctx.refresh();
MyBean myBean = (MyBean) ctx.getBean("myBean");
..
注:详见官方文档
方法解析
ResourceLoader / ResourcePatternResolver方法实现
省略
AbstractApplicationContext模板方法的实现
@Override
protected final void refreshBeanFactory() throws IllegalStateException {
if (!this.refreshed.compareAndSet(false, true)) {
throw new IllegalStateException(
"GenericApplicationContext does not support multiple refresh attempts: just call 'refresh' once");
}
this.beanFactory.setSerializationId(getId());
}
@Override
protected void cancelRefresh(BeansException ex) {
this.beanFactory.setSerializationId(null);
super.cancelRefresh(ex);
}
@Override
protected final void closeBeanFactory() {
this.beanFactory.setSerializationId(null);
}
@Override
public final ConfigurableListableBeanFactory getBeanFactory() {
return this.beanFactory;
}
public final DefaultListableBeanFactory getDefaultListableBeanFactory() {
return this.beanFactory;
}
@Override
public AutowireCapableBeanFactory getAutowireCapableBeanFactory() throws IllegalStateException {
assertBeanFactoryActive();
return this.beanFactory;
}
BeanDefinitionRegistry方法实现
可以看到注册BeanDefinition时,使用的是DefaultListableBeanFactory实例
@Override
public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition) throws BeanDefinitionStoreException {
this.beanFactory.registerBeanDefinition(beanName, beanDefinition);
}
@Override
public void removeBeanDefinition(String beanName) throws NoSuchBeanDefinitionException {
this.beanFactory.removeBeanDefinition(beanName);
}
@Override
public BeanDefinition getBeanDefinition(String beanName) throws NoSuchBeanDefinitionException {
return this.beanFactory.getBeanDefinition(beanName);
}
@Override
public boolean isBeanNameInUse(String beanName) {
return this.beanFactory.isBeanNameInUse(beanName);
}
@Override
public void registerAlias(String beanName, String alias) {
this.beanFactory.registerAlias(beanName, alias);
}
@Override
public void removeAlias(String alias) {
this.beanFactory.removeAlias(alias);
}
@Override
public boolean isAlias(String beanName) {
return this.beanFactory.isAlias(beanName);
}
注册单个bean的方法
public <T> void registerBean(Class<T> beanClass, Object... constructorArgs) {
registerBean(null, beanClass, constructorArgs);
}
public <T> void registerBean(@Nullable String beanName, Class<T> beanClass, Object... constructorArgs) {
registerBean(beanName, beanClass, (Supplier<T>) null,
bd -> {
for (Object arg : constructorArgs) {
bd.getConstructorArgumentValues().addGenericArgumentValue(arg);
}
});
}
public final <T> void registerBean(Class<T> beanClass, BeanDefinitionCustomizer... customizers) {
registerBean(null, beanClass, null, customizers);
}
public final <T> void registerBean(
@Nullable String beanName, Class<T> beanClass, BeanDefinitionCustomizer... customizers) {
registerBean(beanName, beanClass, null, customizers);
}
public final <T> void registerBean(Class<T> beanClass, Supplier<T> supplier, BeanDefinitionCustomizer... customizers) {
registerBean(null, beanClass, supplier, customizers);
}
/**
* Register a bean from the given bean class, using the given supplier for
* obtaining a new instance (typically declared as a lambda expression or
* method reference), optionally customizing its bean definition metadata
* (again typically declared as a lambda expression).
* <p>This method can be overridden to adapt the registration mechanism for
* all {@code registerBean} methods (since they all delegate to this one).
* @param beanName bean 名字。如果是 null 则会使用类名。
* @param beanClass 要创建的 bean 类。
* @param supplier 如果为 null,这会通过 beanClass 构造函数自动装配,否则会直接回调获取 bean 实例。
* @param customizers 定制的意思。也就是说在 bean 还没有被注册之前可以通过该接口做定制化,例如:强制要求该 Bean 为单例或多例。
*/
public <T> void registerBean(@Nullable String beanName, Class<T> beanClass,@Nullable Supplier<T> supplier, BeanDefinitionCustomizer... customizers) {
ClassDerivedBeanDefinition beanDefinition = new ClassDerivedBeanDefinition(beanClass);
if (supplier != null) {
beanDefinition.setInstanceSupplier(supplier);
}
for (BeanDefinitionCustomizer customizer : customizers) {
// 如果你是使用某个构造方法来创建 Bean,这里的回调就会
// 按照你的参数顺序将参数类型和值保存到当前的 BeanDefinition 中。
//
// 参数顺序指的是 constructorArgs 的顺序。
// registerBean(@Nullable String beanName, Class<T> beanClass, Object... constructorArgs)
customizer.customize(beanDefinition);
}
String nameToUse = (beanName != null ? beanName : beanClass.getName());
// 该方法中会调用 DefaultListableBeanFactory#registerBeanDefinition 方法完成 Bean 注册。
registerBeanDefinition(nameToUse, beanDefinition);
}
四级抽象类-AbstractRefreshableApplicationContext
JavaDoc
基础类 ApplicationContext,这个实现支持多次调用 refresh(),每次调用都创建一个新的内部 bean factory 实例。通常(但也不必然),例如一个 context 将凭借 配置地点被驱动,从而去加载bean definitions。
方法 loadBeanDefinitions 仅凭借子类实现,在每一次被刷新时调用。一个具体的实现应当去加载 bean definitions 到 DefaultListableBeanFactory 中
通常会委派到一个或多个 bean definition 加载器。
关键词
Refreshable:可重复刷新
解析
如果你追求定制化的ApplicationContext实现,可从AbstractRefreshableApplicationContext继承来实现从不同地方去加载Bean定义信息进来。
AbstractRefreshableApplicationContext实现了父类的方法refreshBeanFactory(),执行BeanFactory的“刷新”。
与GenericApplicationContext不同,AbstractRefreshableApplicationContext是可以多次刷新的。当然,每次刷新的时候,都会重新创建DefaultListableBeanFactory实例:
当然,在这个方法里面加载BeanDefinition的方法,还需子类实现
-----------------------------------------------------------------------------------------
五级类-AnnotationConfigApplicationContext
解析
AnnotationConfigApplicationContext
继承自GenericApplicationContext
,提供了注解配置(例如:Configuration、Component、inject等)和类路径扫描(scan方法)的支持,可以使用register(Class<?>... annotatedClasses)
来注册一个一个的进行注册。实现了AnnotationConfigRegistry接口,来完成对注册配置的支持,只有两个方法:register和scan。内部使用AnnotatedBeanDefinitionReader
来完成注解配置的解析,使用ClassPathBeanDefinitionScanner
来完成类路径下的bean定义的扫描。
方法解析
构造器
private final AnnotatedBeanDefinitionReader reader;
private final ClassPathBeanDefinitionScanner scanner;
//AnnotatedBeanDefinitionReader是一个读取注解的Bean读取器,这里将this传了进去。
public AnnotationConfigApplicationContext() {
// 创建读取器,传入 bean 定义信息的注册中心
this.reader = new AnnotatedBeanDefinitionReader(this);
this.scanner = new ClassPathBeanDefinitionScanner(this);
}
//从上面这个构造函数可以顺便提一句:如果你仅仅是这样ApplicationContext applicationContext = new AnnotationConfigApplicationContext()
// 容器是不会启动的(也就是不会执行refresh()的),这时候需要自己之后再手动启动容器
/**
* Create a new AnnotationConfigApplicationContext with the given DefaultListableBeanFactory.
* @param beanFactory the DefaultListableBeanFactory instance to use for this context
*/
public AnnotationConfigApplicationContext(DefaultListableBeanFactory beanFactory) {
super(beanFactory);
this.reader = new AnnotatedBeanDefinitionReader(this);
this.scanner = new ClassPathBeanDefinitionScanner(this);
}
/**
* Create a new AnnotationConfigApplicationContext, deriving bean definitions
* from the given component classes and automatically refreshing the context.
* @param componentClasses one or more component classes — for example,
* {@link Configuration @Configuration} classes
*/
public AnnotationConfigApplicationContext(Class<?>... componentClasses) {
// 调用空参构造
this();
// 注册主配置类,也就是 new AnnotationConfigApplicationContext(Config.class); 时传入的配置类
register(componentClasses);
// 刷新工厂十二大步
refresh();
}
public AnnotationConfigApplicationContext(String... basePackages) {
this();
scan(basePackages);
refresh();
}
AnnotationConfigRegistry方法实现
@Override
public void register(Class<?>... componentClasses) {
Assert.notEmpty(componentClasses, "At least one component class must be specified");
this.reader.register(componentClasses);
}
@Override
public void scan(String... basePackages) {
Assert.notEmpty(basePackages, "At least one base package must be specified");
this.scanner.scan(basePackages);
}
将超类registerBean调用改编为AnnotatedBeanDefinitionReader
@Override
public <T> void registerBean(@Nullable String beanName, Class<T> beanClass,
@Nullable Supplier<T> supplier, BeanDefinitionCustomizer... customizers) {
this.reader.registerBean(beanClass, beanName, supplier, customizers);
}
五级类-GenericXmlApplicationContext
JavaDoc
内置XML支持,方便的应用程序上下文。这是ClassPathXmlApplicationcontext和FileSystemXmlApplicationContext的灵活替代方案,可以通过设置器进行配置,并最终调用AbstractApplicationcontext.refresh()来激活上下文。对于多个配置文件,后面文件中的bean定义将覆盖前面文件中定义的bean定义。可以利用这一点,通过附加到列表中的额外配置文件,有意地覆盖某些bean定义。
解析
如图:
我们可以看到,GenericXmlApplicationContext和GenericApplicationContext的区别就在于,GenericApplicationContext可以自定义使用想要的BeanDefinitionReader,即可以混合搭配;而GenericXmlApplicationContext则内置了XmlBeanDefinitionReader,针对xml做了特异性处理,正如官方文档所说,是ClassPathXmlApplicationcontext和FileSystemXmlApplicationContext的灵活替代方案。
五级类-GenericGroovyApplicationContext
JavaDoc
GenericGroovyApplicationContext继承自GenericApplicationContext,实现了GroovyObject接口以便能够使用点的语法(.xx)取代getBean方法来获取bean。它主要用于Groovy bean的定义,与GenericXmlApplicationContext一样,它也能解析XML格式定义的bean。内部使用GroovyBeanDefinitionReader来完成groovy脚本和XML的解析。
解析
如图:
和GenericXmlApplicationContext类似,针对Groovy语言做了特异性处理
五级类-ResourceAdapterApplicationContext
JavaDoc
ResourceAdapterApplicationContext继承自GenericApplicationContext,是为JCA(J2EE Connector Architecture)的ResourceAdapter设计的,主要用于传递BootstrapContext的实例给实现了BootstrapContextAware接口且由spring管理的bean。覆盖了postProcessBeanFactory方法来实现此功能。
解析
public class ResourceAdapterApplicationContext extends GenericApplicationContext {
private final BootstrapContext bootstrapContext;
/**
* Create a new ResourceAdapterApplicationContext for the given BootstrapContext.
* @param bootstrapContext the JCA BootstrapContext that the ResourceAdapter
* has been started with
*/
public ResourceAdapterApplicationContext(BootstrapContext bootstrapContext) {
Assert.notNull(bootstrapContext, "BootstrapContext must not be null");
this.bootstrapContext = bootstrapContext;
}
@Override
protected void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
beanFactory.addBeanPostProcessor(new BootstrapContextAwareProcessor(this.bootstrapContext));
beanFactory.ignoreDependencyInterface(BootstrapContextAware.class);
beanFactory.registerResolvableDependency(BootstrapContext.class, this.bootstrapContext);
// JCA WorkManager resolved lazily - may not be available.
beanFactory.registerResolvableDependency(WorkManager.class,
(ObjectFactory<WorkManager>) this.bootstrapContext::getWorkManager);
}
}
----------------------------------------------------------------------------------------
五级抽象类-AbstractRefreshableConfigApplicationContext
结构图
作用
对指定的配置文件路径的公共的处理
解析
AbstractRefreshableConfigApplicationContext
继承自AbstractRefreshableApplicationContext
,添加了对指定的配置文件路径的公共的处理,可以把他看作基于XML的应用上下文的基类。实现了如下的两个接口:
BeanNameAware
用于设置上下文的bean的名称,只有一个方法:void setBeanName(String name)
InitializingBean
用于上下文一切就绪后,如果还未刷新,那么就执行刷新操作,只有一个方法:void afterPropertiesSet()
六级抽象类-AbstractXmlApplicationContext
JavaDoc
方便的ApplicationContext实现的基类,它从包含XmlBeanDefinitionReader的bean定义的XML文档中提取配置。
子类只需要实现getConfigResources和/或getConfigLocations方法。
此外,它们可能会覆盖getResourceByPath钩子,以特定于环境的方式解释相对路径,和/或getResourcePatternResolver来扩展模式解析
解析
AbstractXmlApplicationContext这个类主要实现了对xml配置文件的解析,主要是由两个函数loadBeanDefinitions(DefaultListableBeanFactory beanFactory)和loadBeanDefinitions(XmlBeanDefinitionReader reader)中实现。
(1)loadBeanDefinitions(DefaultListableBeanFactory beanFactory)
在这里面使用了xml专属的读取器XmlBeanDefinitionReader
//主要是对spring注入配置文件的解析,主要使用到BeanDefinitionReader和BeanDefinitionDocumentReader两个接口相关的实现类
@Override
protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {
//创建XmlBeanDefinitionReader,即创建Bean读取器,并通过回调设置到容器中去,容 器使用该读取器读取Bean定义资源
XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);
//为Bean读取器设置Spring资源加载器,AbstractXmlApplicationContext的
//祖先父类AbstractApplicationContext继承DefaultResourceLoader,因此,容器本身也是一个资源加载器
beanDefinitionReader.setEnvironment(this.getEnvironment());
beanDefinitionReader.setResourceLoader(this);
beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));
//当Bean读取器读取Bean定义的Xml资源文件时,启用Xml的校验机制
initBeanDefinitionReader(beanDefinitionReader);
//Bean读取器真正实现加载的方法
loadBeanDefinitions(beanDefinitionReader);
}
(2)loadBeanDefinitions(XmlBeanDefinitionReader reader)
//Xml Bean读取器加载Bean定义资源
protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws BeansException, IOException {
//获取Bean定义资源的定位
Resource[] configResources = getConfigResources();
if (configResources != null) {
//Xml Bean读取器调用其父类AbstractBeanDefinitionReader读取定位的Bean定义资源
reader.loadBeanDefinitions(configResources);
}
String[] configLocations = getConfigLocations();
if (configLocations != null) {
//Xml Bean读取器调用其父类AbstractBeanDefinitionReader读取定位的Bean定义资源
reader.loadBeanDefinitions(configLocations);
}
}
其他
//这里又使用了一个委托模式,调用子类的获取Bean定义资源定位的方法
//getConfigResources() 用来获取程序启动读取的xml ,内部实现交给子类ClassPathXmlApplicationContext 来实现的
protected Resource[] getConfigResources() {
return null;
}
七级类-FileSystemXmlApplicationContext
解析
FileSystemXmlApplicationContext是独立的 XML application context,从文件系统或URL得到上下文定义文件。URL可以是绝对或者相对路径。普通路径将始终被解释为相对于当前VM工作目录,即使它们以斜杠/开头。原因:
使用方式
1.没有盘符的是项目工作路径,即项目的根目录;
2.有盘符表示的是文件绝对路径.(若是绝对路径,“file:” 前缀可以缺省)
示例:
ApplicationContext ctx = newFileSystemXmlApplicationContext("spring-config.xml"); //当前路径加载单个配置文件
String[] locations = {"bean1.xml", "bean2.xml", "bean3.xml"};
ApplicationContext ctx = new FileSystemXmlApplicationContext(locations ); //同时加载多个配置文件
ApplicationContext ctx = new FileSystemXmlApplicationContext("D:/project/bean.xml");//根据具体路径加载文件
使用注意
配置文件路径可以是一个具体的文件比如/myfiles/context.xml也可以是带有通配符的如/myfiles/*-context.xml,其可以通过getConfigLocations方法被覆盖。
在多个配置位置的情况下,后面的bean定义将覆盖前面加载的文件中定义的bean。这可以通过一个额外的XML文件故意覆盖某些bean定义。
方法解析
public class FileSystemXmlApplicationContext extends AbstractXmlApplicationContext {
public FileSystemXmlApplicationContext() {
}
public FileSystemXmlApplicationContext(ApplicationContext parent) {
super(parent);
}
// 这个构造函数的configLocation包含的是BeanDefinition所在的文件路径
// 从给定的xml 文件中加载BeanDefinition,然后自动刷新context
public FileSystemXmlApplicationContext(String configLocation) throws BeansException {
this(new String[] {configLocation}, true, null);
}
// 这个构造函数允许configLocations包含多个BeanDefinition的文件路径
public FileSystemXmlApplicationContext(String... configLocations) throws BeansException {
this(configLocations, true, null);
}
// 这个构造函数在允许configLocation包含多个BeanDefinition的文件路径同时,还允许指定自己的双亲IOC容器
public FileSystemXmlApplicationContext(String[] configLocations, ApplicationContext parent) throws BeansException {
this(configLocations, true, parent);
}
//这个构造函数在允许configLocation包含多个BeanDefinition的文件路径同时,还允许指定是否刷新context
public FileSystemXmlApplicationContext(String[] configLocations, boolean refresh) throws BeansException {
this(configLocations, refresh, null);
}
// 允许多个路径、允许指定是否刷新、允许指定自己的双亲IOC容器
public FileSystemXmlApplicationContext(String[] configLocations, boolean refresh, @Nullable ApplicationContext parent)throws BeansException {
super(parent);
setConfigLocations(configLocations);
if (refresh) {
refresh();
}
}
//将资源路径解析为文件系统路径 即使一个文件路径以/ 开头,它也会被视作为一个相对当前VM工作路径的相对路径
@Override
protected Resource getResourceByPath(String path) {
if (path.startsWith("/")) {
path = path.substring(1);
}
return new FileSystemResource(path);
}
}
七级类-ClassPathXmlApplicationContext
解析
ClassPathXmlApplicationContext 默认会去 classpath 路径下找。
使用方式
1. classpath:前缀是不需要的,默认就是指项目的classpath路径下面;
2. 如果要使用绝对路径,需要加上“file:”前缀表示这是绝对路径;
使用注意:
classPath 路径指的就是编译后的 classes 目录。可以理解读取resource目录下的配置文件
方法解析
@Nullable
private Resource[] configResources;
public ClassPathXmlApplicationContext(String[] configLocations, boolean refresh, @Nullable ApplicationContext parent)
throws BeansException {
super(parent);
setConfigLocations(configLocations);
if (refresh) {
refresh();
}
}
public ClassPathXmlApplicationContext(String[] paths, Class<?> clazz, @Nullable ApplicationContext parent)throws BeansException {
super(parent);
Assert.notNull(paths, "Path array must not be null");
Assert.notNull(clazz, "Class argument must not be null");
this.configResources = new Resource[paths.length];
for (int i = 0; i < paths.length; i++) {
this.configResources[i] = new ClassPathResource(paths[i], clazz);
}
refresh();
}
@Override
@Nullable
protected Resource[] getConfigResources() {
return this.configResources;
}
web应用
三级接口-ConfigurableWebApplicationContext
JavaDoc
由可配置的web应用程序上下文实现的接口。由ContextLoader和FrameworkServlet支持。
注意:这个接口的setter需要在调用继承自ConfigurableApplicationcontext的ConfigurableApplicationcontext refresh()方法之前调用。它们本身不会导致上下文的初始化。
描述
web应用上下文使用的接口,并没有定义太多的操作,主要和Servlet上下文及配置文件相关。
它一次性都继承了上面的两个二级接口,可以说是他俩的结合体
方法解析
public interface ConfigurableWebApplicationContext extends WebApplicationContext, ConfigurableApplicationContext {
//引用上下文路径和/或servlet名称的ApplicationContext id的前缀。
String APPLICATION_CONTEXT_ID_PREFIX = WebApplicationContext.class.getName() + ":";
//工厂中的ServletConfig环境bean的名称。
String SERVLET_CONFIG_BEAN_NAME = "servletConfig";
//为这个web应用程序上下文设置ServletContext。不会导致上下文的初始化:需要在设置所有配置属性后调用refresh。
void setServletContext(@Nullable ServletContext servletContext);
//为这个web应用程序上下文设置ServletConfig。仅为属于特定Servlet的WebApplicationContext调用。
void setServletConfig(@Nullable ServletConfig servletConfig);
@Nullable
ServletConfig getServletConfig();
//设置此web应用程序上下文的名称空间,用于构建默认上下文配置位置。根web应用程序上下文没有名称空间。
void setNamespace(@Nullable String namespace);
@Nullable
String getNamespace();
// Spring配置文件的地方 比如默认位置为:/WEB-INF/applicationContext.xml
//以init-param风格设置web应用程序上下文的配置位置,即使用逗号、分号或空格分隔不同的位置。如果没有设置,实现将根据需要对给定的名称空间或根web应用程序上下文使用默认值。
void setConfigLocation(String configLocation);
void setConfigLocations(String... configLocations);
@Nullable
String[] getConfigLocations();
}
四级类-GenericWebApplicationContext
JavaDoc
GenericApplicationcontext的子类,适用于web环境。
实现ConfigurablewebApplicationcontext,但不用于web中的声明性设置。xml。相反,它是为编程设置而设计的,例如用于构建嵌套上下文或在WebApplicationInitializers中使用。
如果您打算实现一个从配置文件读取bean定义的WebApplicationContext,请考虑从AbstractRefreshableWebApplicationContext派生,在loadBeanDefinitions方法的实现中读取bean定义。
将资源路径解释为servlet上下文资源,即web应用程序根目录下的路径。绝对路径,例如web应用根目录外的文件,可以通过“file:”URs访问,由AbstractApplicationContext实现。除了由AbstractApplicationContext检测到的特殊bean之外,该类还在上下文中检测一个名为“ThemeSource”的ThemeSource bean。
解析
如图:
我们可以看到里面有ServletContext,这是针对web做出的处理
四级抽象类-AbstractRefreshableWebApplicationContext
描述
GenericWebApplicationContext继承自AbstractRefreshableConfigApplicationContext,实现了ConfigurableWebApplicationContext和ThemeSource接口,主要是用于web环境下。在web程序启动的时候,提供一个configLocations属性,通过ConfigurableWebApplicationContext接口来进行填充。子类化这个接口是很简单的,所有你所需要做的事情就是实现loadBeanDefinitions方法,来实现你自己的bean定义的加载逻辑。
方法解析
@Override
protected void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {
//注册ServletContextAwareProcessor 这样任意Bean都可以很方便的获取到ServletContext了 同时忽略另外两个,
// 因为ServletContextAwareProcessor 都把事情都做了
beanFactory.addBeanPostProcessor(new ServletContextAwareProcessor(this.servletContext, this.servletConfig));
beanFactory.ignoreDependencyInterface(ServletContextAware.class);
beanFactory.ignoreDependencyInterface(ServletConfigAware.class);
//注册web环境,包括request、session、golableSession、application
WebApplicationContextUtils.registerWebApplicationScopes(beanFactory, this.servletContext);
//注册servletContext、contextParamters、contextAttributes 、servletConfig单例bean
WebApplicationContextUtils.registerEnvironmentBeans(beanFactory, this.servletContext, this.servletConfig);
}
五级类-XmlWebApplicationContext
JavaDoc
webApplicationcontext实现,它从XML文档中获取配置,由XmlBeanDefinitionReader理解。这本质上相当于web的GenericmlApplicationcontext环境。
默认情况下,根上下文的配置将取自“/WEB-INF/applicationContext.xml”,命名空间为“test-servlet”的上下文的配置将取自“/WEB-INF/test-servlet.xml”(类似于servlet-name为“test”的DispatcherServlet实例)。
配置位置默认值可以通过ContextLoader的contextConfigLocation" context-param和FrameworkServlet的servlet init-param来覆盖。配置位置既可以表示具体文件,如/WEB-INF/context.xml”,也可以表示Ant风格的模式,如“/WEB-INF/*-context.xml”(模式细节参见PathMatcher javadoc)。
注意:如果有多个配置位置,后面的bean定义将覆盖前面加载文件中定义的bean定义。可以利用这一点,通过额外的ML文件故意覆盖某些bean定义。
对于以不同bean定义格式读取的WebApplicationContext,创建AbstractRefreshablewebApplicationContext的类似子类。可以指定这样的上下文实现作为 ContextLoader 的 “contextClass” context-param 或 FrameworkServlet 的 “contextClass” init-param。
解析
XmlWebApplicationContext
继承自AbstractRefreshableWebApplicationContext
,接受能被XmlBeanDefinitionReader
所理解的XML文档配置。对于根上下文,默认的配置文件路径是/WEB-INF/applicationContext.xml
,对于命名空间为test-servlet的上下文,默认的配置文件路径是/WEB-INF/test-servlet.xml
(就像servlet-name为test的DispatcherServlet实例)。
默认的配置文件路径处理的代码如下:
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};
}
}
和其他的上下文一样,bean定义的加载也是在void loadBeanDefinitions(DefaultListableBeanFactory beanFactory)
方法中,使用的是XmlBeanDefinitionReader
。
方法解析
public class XmlWebApplicationContext extends AbstractRefreshableWebApplicationContext {
/** Default config location for the root context. */
public static final String DEFAULT_CONFIG_LOCATION = "/WEB-INF/applicationContext.xml";
/** Default prefix for building a config location for a namespace. */
public static final String DEFAULT_CONFIG_LOCATION_PREFIX = "/WEB-INF/";
/** Default suffix for building a config location for a namespace. */
public static final String DEFAULT_CONFIG_LOCATION_SUFFIX = ".xml";
@Override
protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {
XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);
beanDefinitionReader.setEnvironment(getEnvironment());
beanDefinitionReader.setResourceLoader(this);
beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));
initBeanDefinitionReader(beanDefinitionReader);
loadBeanDefinitions(beanDefinitionReader);
}
protected void initBeanDefinitionReader(XmlBeanDefinitionReader beanDefinitionReader) {
}
protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws IOException {
String[] configLocations = getConfigLocations();
if (configLocations != null) {
for (String configLocation : configLocations) {
reader.loadBeanDefinitions(configLocation);
}
}
}
@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};
}
}
}
五级类-GroovyWebApplicationContext
解析
GroovyWebApplicationContext
继承自AbstractRefreshableWebApplicationContext
,实现了GroovyObject
接口,接受能被GroovyBeanDefinitionReader
所理解的groovy bean定义脚本和XML文档配置。对于web环境,基本上是和GenericGroovyApplicationContext
是等价的。对于根上下文,默认的配置文件路径是/WEB-INF/applicationContext.groovy
,对于命名空间为test-servlet的上下文,默认的配置文件路径是/WEB-INF/test-servlet.xml
(就像servlet-name为test的DispatcherServlet实例)。
默认的配置文件路径处理的代码如下:
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};
}
}
和其他的上下文一样,bean定义的加载也是在void loadBeanDefinitions(DefaultListableBeanFactory beanFactory)
方法中,使用的是GroovyBeanDefinitionReader
。
五级类-AnnotationConfigWebApplicationContext
JavaDoc
WebApplicationcontext实现,它接受组件类作为输入——特别是@configuration注释的类,还包括使用javax.inject注释的普通@Component类和JSR-330兼容类。允许一个一个地注册类(指定类名作为配置位置)以及类路径扫描(指定基本包作为配置位置)。这本质上相当于web环境中的AnnotationconfigApplicationcontext。要使用这个应用程序上下文,ContextLoader的“contextClass”上下文参数和/或FrameworkServlet的“contextClass”初始化参数必须设置为该类的全限定名。
解析
AnnotationConfigWebApplicationContext
继承自AbstractRefreshableWebApplicationContext
,接受注解的类作为输入(特殊的@Configuration注解类,一般的@Component注解类,与JSR-330兼容的javax.inject注解)。允许一个一个的注入,同样也能使用类路径扫描。对于web环境,基本上是和AnnotationConfigApplicationContext
等价的。使用AnnotatedBeanDefinitionReader
来对注解的bean进行处理,使用ClassPathBeanDefinitionScanner
来对类路径下的bean进行扫描。
方法解析
@Override
protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) {
// 初始化这个脚手架 其实就是直接new出实例。
AnnotatedBeanDefinitionReader reader = getAnnotatedBeanDefinitionReader(beanFactory);
ClassPathBeanDefinitionScanner scanner = getClassPathBeanDefinitionScanner(beanFactory);
// 生成Bean的名称的生成器,如果自己没有setBeanNameGenerator(可以自定义),这里目前为null
BeanNameGenerator beanNameGenerator = getBeanNameGenerator();
if (beanNameGenerator != null) {
reader.setBeanNameGenerator(beanNameGenerator);
scanner.setBeanNameGenerator(beanNameGenerator);
//若我们注册了beanName生成器,那么就会注册进容器里面
beanFactory.registerSingleton(AnnotationConfigUtils.CONFIGURATION_BEAN_NAME_GENERATOR, beanNameGenerator);
}
//这是给reader和scanner注册scope的解析器 此处为null
ScopeMetadataResolver scopeMetadataResolver = getScopeMetadataResolver();
if (scopeMetadataResolver != null) {
reader.setScopeMetadataResolver(scopeMetadataResolver);
scanner.setScopeMetadataResolver(scopeMetadataResolver);
}
// 此处注意了:annotatedClasses和basePackages一般是选其一(当然看到此处,他们是可以并存的)
//我们可以自己指定annotatedClasses 配置文件,同时也可以交给下面扫描
if (!this.componentClasses.isEmpty()) {
// 这里会把所有的配置文件输出=======info日志 请注意观察控制台
if (logger.isDebugEnabled()) {
logger.debug("Registering component classes: [" +
StringUtils.collectionToCommaDelimitedString(this.componentClasses) + "]");
}
// 若是指明的Bean,就交给reader去处理,至于怎么处理,见上篇博文的doRegisterBean去怎么解析每一个Config Bean的
reader.register(ClassUtils.toClassArray(this.componentClasses));
}
// 也可以是包扫描的方式,扫描配置文件的Bean
if (!this.basePackages.isEmpty()) {
// 输出对应的info日志
if (logger.isDebugEnabled()) {
logger.debug("Scanning base packages: [" +
StringUtils.collectionToCommaDelimitedString(this.basePackages) + "]");
}
// 这里重要了,scan方法具体做了什么事,上篇博文也有详细的介绍,请参阅
scanner.scan(StringUtils.toStringArray(this.basePackages));
}
// 此处的意思是,也可以以全类名的形式注册。比如可以调用setConfigLocations设置(这在xml配置中使用较多) 可以是全类名,也可以是包路径
String[] configLocations = getConfigLocations();
if (configLocations != null) {
for (String configLocation : configLocations) {
try {
Class<?> clazz = ClassUtils.forName(configLocation, getClassLoader());
if (logger.isTraceEnabled()) {
logger.trace("Registering [" + configLocation + "]");
}
reader.register(clazz);
}
catch (ClassNotFoundException ex) {
if (logger.isTraceEnabled()) {
logger.trace("Could not load class for config location [" + configLocation +
"] - trying package scan. " + ex);
}
int count = scanner.scan(configLocation);
if (count == 0 && logger.isDebugEnabled()) {
logger.debug("No component classes found for specified class/package [" + configLocation + "]");
}
}
}
}
}
引用
https://docs.spring.io/spring-framework/docs/5.2.x/javadoc-api/org/springframework/web/context/support/AnnotationConfigWebApplicationContext.html
https://www.cnblogs.com/coder-qi/p/11192054.html#configurablewebapplicationcontext
备用资料
FileSystemXmlApplicationContext,从磁盘路径查找 XML 配置文件,创建容器(旧)
XmlWebApplicationContext,传统 SSM 整合时,基于 XML 配置文件的容器(旧)
AnnotationConfigWebApplicationContext,传统 SSM 整合时,基于 java 配置类的容器(旧)
AnnotationConfigApplicationContext,Spring boot 中非 web 环境容器(新)
StaticApplicationContext:它能让你编程注册。而不是从配置文件中读取,一般用于测试环境(忽略)
1、ClassPathXmlApplicationContext:可以加载类路径下的配置文件,要求配置文件必须在类路径之下。
2、FileSystemXmlApplicationContext:可以加载磁盘中任意路径下的配置文件,要求具有访问权限。
3、AnnotationConfigApplicationContext:用于读取注解创建容器。