【Spring】ApplicationContext 三 AnnotationConfigApplicationContext
前言
基于之前对 ApplicationContext
相关接口和 AbstractApplicationContext
的相关解读,本章节了解对比下最常用的两个实现:
AnnotationConfigApplicationContext
,非web
环境使用AnnotationConfigWebApplicationContext
,web
环境使用
版本
Spring 5.3.x
GenericApplicationContext
public class GenericApplicationContext extends AbstractApplicationContext implements BeanDefinitionRegistry
Generic:通用的
,容器的通用实现,基于 AbstractApplicationContext
,本类基本实现了所有容器相关功能
1
可以看到,它是一个 BeanDefinitionRegistry
,因此不难推测: BeanFactory
注册 BeanDefinition
的能力是从这个类暴露出去的,对应方法的实现如下:
@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);
}
// ...
相关方法全权委托给 beanFactory
2
同时,伴随此类的创建,其内部的 BeanFactory
也完成了初始化:
public GenericApplicationContext() {
this.beanFactory = new DefaultListableBeanFactory();
}
可以看到,类型为 BeanFactory
的最终实现 DefaultListableBeanFactory
3
上一章节了解到,refresh
方法最后阶段会基于容器中注册的 BeanDefinition
来创建对应的 bean实例
,我们可以借助其他 BeanDefinitionReader
来收集、注册对应的 BeanDefinition
,如下示例扫描指定 xml
文件的 BeanDefinition
:
@Test
public void test() {
GenericApplicationContext ctx = new GenericApplicationContext();
// 扫描指定 xml
XmlBeanDefinitionReader xmlReader = new XmlBeanDefinitionReader(ctx);
xmlReader.loadBeanDefinitions(new ClassPathResource("demo.xml"));
// 容器启动
ctx.refresh();
}
AnnotationConfigApplicationContext
public class AnnotationConfigApplicationContext extends GenericApplicationContext implements AnnotationConfigRegistry
GenericApplicationContext
的子类,基于注解配置的容器- 抛开远古时期
xml
配置的时代,基于注解的容器配置已成主流,其中我们最常使用的就是此实现类
1
private final AnnotatedBeanDefinitionReader reader;
private final ClassPathBeanDefinitionScanner scanner;
核心成员属性,实现基于配置类、路径扫描来解析 BeanDefinition
:
AnnotatedBeanDefinitionReader
:基于注解驱动配置类的扫描,对应的组件被注册成AnnotatedGenericBeanDefinition
ClassPathBeanDefinitionScanner
:基于路径配置类的扫描,此类用途更为广泛一点,对应的组件被注册为ScannedGenericBeanDefinition
- 相关链接
【源码】Spring —— AnnotatedBeanDefinitionReader 解读
【源码】Spring —— ClassPathBeanDefinitionScanner 解读
2
// 成员初始化
public AnnotationConfigApplicationContext() {
StartupStep createAnnotatedBeanDefReader = this.getApplicationStartup().start("spring.context.annotated-bean-reader.create");
this.reader = new AnnotatedBeanDefinitionReader(this);
createAnnotatedBeanDefReader.end();
this.scanner = new ClassPathBeanDefinitionScanner(this);
}
// 扫描配置类
public AnnotationConfigApplicationContext(Class<?>... componentClasses) {
this();
register(componentClasses);
refresh();
}
// 路径扫描
public AnnotationConfigApplicationContext(String... basePackages) {
this();
scan(basePackages);
refresh();
}
常用构造方法:
- 无参构造:初始化
AnnotatedBeanDefinitionReader
和ClassPathBeanDefinitionScanner
- 指定配置类扫描,基于
register
方法,最终委托AnnotatedBeanDefinitionReader
- 指定路径扫描,基于
scan
方法,最终委托ClassPathBeanDefinitionScanner
demo
public class AnnotationConfigApplicationContextDemo {
public static class A {}
@Configuration
static class Config {
@Bean
public A a() {
return new A();
}
}
@Test
public void test() {
// 指定配置类就不用手动 refresh
AnnotationConfigApplicationContext context
= new AnnotationConfigApplicationContext(Config.class);
context.getBean(A.class);
}
}
最眼熟的一段代码了
AnnotationConfigWebApplicationContext
public class AnnotationConfigWebApplicationContext extends AbstractRefreshableWebApplicationContext
implements AnnotationConfigRegistry
- 它是一个
AbstractRefreshableWebApplicationContext
,因此具有Refreshable(可刷新)
的能力 - 它是一个
WebApplicationContext
,因此主要用在web
环境 - 不同于
AnnotationConfigApplicationContext
,它不是一个BeanDefinitionRegistry
,因此不暴露注册BeanDefinition
的方法
1 AbstractRefreshableApplicationContext#refreshBeanFactory
@Override
protected final void refreshBeanFactory() throws BeansException {
// 销毁之前的 BeanFactory
if (hasBeanFactory()) {
destroyBeans();
closeBeanFactory();
}
// 创建新的 BeanFactory 并重新加载 BeanDefinition
// 及支持容器的热刷新
try {
DefaultListableBeanFactory beanFactory = createBeanFactory();
beanFactory.setSerializationId(getId());
customizeBeanFactory(beanFactory);
loadBeanDefinitions(beanFactory);
this.beanFactory = beanFactory;
}
catch (IOException ex) {
// ...
}
}
- 作为一个
AbstractRefreshableWebApplicationContext
,其父类AbstractRefreshableApplicationContext
复写了refreshBeanFactory
- 该方法会销毁并创建新的
BeanFactory
实例,主要是用于重新读取BeanDefinition
- 具体读取
BeanDefinition
的方法loadBeanDefinitions
由子类实现
2 AbstractRefreshableWebApplicationContext#postProcessBeanFactory
@Override
protected void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {
// 处理并忽略依赖注入 ServletContextAware ServletConfigAware
beanFactory.addBeanPostProcessor(new ServletContextAwareProcessor(this.servletContext, this.servletConfig));
beanFactory.ignoreDependencyInterface(ServletContextAware.class);
beanFactory.ignoreDependencyInterface(ServletConfigAware.class);
// 1.注册 request session application 三个 web 作用域
// 2.注册 ServletRequest ServletResponse HttpSession WebRequest 的 bean 实例
WebApplicationContextUtils.registerWebApplicationScopes(beanFactory, this.servletContext);
// 注册 web 环境的一些单例
WebApplicationContextUtils.registerEnvironmentBeans(beanFactory, this.servletContext, this.servletConfig);
}
- 作为一个
WebApplicationContext
,其父类AbstractRefreshableWebApplicationContext
实现了方法postProcessBeanFactory
- 主要是一些
web
组件的处理,比如注册一些Scope
Bean组件
等
3 AnnotationConfigWebApplicationContext#loadBeanDefinitions
@Override
protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) {
/**
* 还是委托 AnnotatedBeanDefinitionReader 和 ClassPathBeanDefinitionScanner
* 来加载 BeanDefinition 的
*/
AnnotatedBeanDefinitionReader reader = getAnnotatedBeanDefinitionReader(beanFactory);
ClassPathBeanDefinitionScanner scanner = getClassPathBeanDefinitionScanner(beanFactory);
// 创建并注册 BeanNameGenerator:beanName 生成器
BeanNameGenerator beanNameGenerator = getBeanNameGenerator();
if (beanNameGenerator != null) {
reader.setBeanNameGenerator(beanNameGenerator);
scanner.setBeanNameGenerator(beanNameGenerator);
beanFactory.registerSingleton(AnnotationConfigUtils.CONFIGURATION_BEAN_NAME_GENERATOR, beanNameGenerator);
}
// Scope 元数据解析器,主要用来处理 BD 的 Scope 相关属性
ScopeMetadataResolver scopeMetadataResolver = getScopeMetadataResolver();
if (scopeMetadataResolver != null) {
reader.setScopeMetadataResolver(scopeMetadataResolver);
scanner.setScopeMetadataResolver(scopeMetadataResolver);
}
// 处理 componentClasses,即配置类
if (!this.componentClasses.isEmpty()) {
reader.register(ClassUtils.toClassArray(this.componentClasses));
}
// 处理 basePackages,即 @ComponentScan 路径扫描
if (!this.basePackages.isEmpty()) {
scanner.scan(StringUtils.toStringArray(this.basePackages));
}
// 加载通过 setConfigLocations 设置的配置文件
String[] configLocations = getConfigLocations();
if (configLocations != null) {
for (String configLocation : configLocations) {
try {
Class<?> clazz = ClassUtils.forName(configLocation, getClassLoader());
reader.register(clazz);
}
catch (ClassNotFoundException ex) {
// ...
}
}
}
}
- 作为
AbstractRefreshableWebApplicationContext
的子类,实现了loadBeanDefinitions
方法 loadBeanDefinitions
方法会重新读取BeanDefinition
,从而实现配置热更新
,这也意味着我们可以执行多次refresh
方法
demo
public class AnnotationConfigWebApplicationContextDemo {
@AllArgsConstructor
public static class A {
public String name;
}
@Configuration
static class Config1 {
@Bean
public A a() {
return new A("1");
}
}
@Configuration
static class Config2 {
@Bean
public A a() {
return new A("2");
}
}
@Test
public void test() {
AnnotationConfigWebApplicationContext context
= new AnnotationConfigWebApplicationContext();
context.register(Config1.class);
context.refresh();
System.out.println(context.getBean(A.class).name);
// 配置覆盖
context.register(Config2.class);
// 多次执行 refresh
context.refresh();
System.out.println(context.getBean(A.class).name);
}
}
这里只是体现了 refreshable
的能力,而对于 WebApplicationContext
的能力,可以结合 springmvc
了解
总结
最后简单的对比下 AnnotationConfigApplicationContext
和 AnnotationConfigWebApplicationContext
相同点
- 它们都是对应分支的最终实现类,也就是能力最强、最常使用的实现
- 它们加载
BeanDefinition
的行为都是委托AnnotatedBeanDefinitionReader
和ClassPathBeanDefinitionScanner
完成的
不同点
AnnotationConfigApplicationContext
用于非web
环境,而AnnotationConfigWebApplicationContext
用于web
环境AnnotationConfigApplicationContext
是GenericApplicationContext
的子类,因此暴露了BeanDefinitionRegistry
的相关方法,尽管最终都是委托到DefaultListableBeanFactory
上,而AnnotationConfigApplicationContext
没有相关方法AnnotationConfigWebApplicationContext
是一个refreshable(可刷新)
的容器实现,因此可以多次调用refresh
方法,它会重新加载BeanDefinition
来实现热更新
,而AnnotationConfigApplicationContext
只能调用一次refresh
方法