本文并不打算讲述@RefreshScope注解如何使用。相反,我们将关注使用@RefreshScope注解的实现原理
前置知识
@RefreshScope
注解标记的class,最终会向容器中注入两个BeanDefinition
,一个是原始class对应的BeanDefinition
,另一个是原始class对应的代理的BeanDefinition
BeanDefinition
会被设置为不可注入,假设class名为Person
,当Person
被依赖注入到别的对象时,实际上注入的是代理对象beanFactory
去获取Person
未被代理的bean ( 也就是那个不可被注入的BeanDefinition
对应的bean ),通过bean去执行方法。1.
spring容器获取到bean 2.
通过bean去执行方法@Target({ ElementType.TYPE, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@Scope("refresh")
@Documented
public @interface RefreshScope {
ScopedProxyMode proxyMode() default ScopedProxyMode.TARGET_CLASS;
}
可以看到,@RefreshScope
注解上使用了@Scope
注解, 对应的value为"refresh"
如此,一旦某个类被@RefreshScope
所标注,spring扫描到这个类时,就可以扫描到@Scope
注解,并且得到value为"refresh"
=================================================================
跟踪扫描注解的代码前,先有一个认知
Spring在扫描到被@Scope
标注的类时, 会生成一个额外的 BeanDefinition
这个BeanDefinition
的beanClass为ScopedProxyFactoryBean
=================================================================
doScan
方法位于ClassPathBeanDefinitionScanner
类中,Spring基于backPackages
扫描组件就是通过这个方法实现的
protected Set<BeanDefinitionHolder> doScan(String... basePackages) {
Set<BeanDefinitionHolder> beanDefinitions = new LinkedHashSet<>();
for (String basePackage : basePackages) {
// 1. 扫描后得到 { BeanDefinition } 集合
// 具体的扫描逻辑在 { findCandidateComponents } 方法中, 这里就不深究了
Set<BeanDefinition> candidates = findCandidateComponents(basePackage);
for (BeanDefinition candidate : candidates) {
// 2. 扫描 { BeanDefinition } 上的 { @Scope } 注解
ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(candidate);
// { scopeMetadata.getScopeName() } 就是 { @Scope.value() }
candidate.setScope(scopeMetadata.getScopeName());
String beanName = this.beanNameGenerator.generateBeanName(candidate, this.registry);
BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(candidate, beanName);
// 3. 核心方法, 这个方法里, spring创建了另一个 { BeanDefinition } 并且注入到容器中
definitionHolder =
AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);
beanDefinitions.add(definitionHolder);
// 4. 将 { AnnotationConfigUtils.applyScopedProxyMode } 方法
// 返回的 { BeanDefinition } 注入到容器中
registerBeanDefinition(definitionHolder, this.registry);
}
}
return beanDefinitions;
}
下面继续跟踪
AnnotationConfigUtils.applyScopedProxyMode
static BeanDefinitionHolder applyScopedProxyMode(
ScopeMetadata metadata, BeanDefinitionHolder definition, BeanDefinitionRegistry registry) {
// 1. 获取 { proxyMode }
// 无论是 { @RefreshScope } 还是 { @Scope } 注解都有一个 { proxyMode() } 属性
// 对应的类型就是 { ScopedProxyMode } , 默认值为 { TARGET_CLASS }
ScopedProxyMode scopedProxyMode = metadata.getScopedProxyMode();
// 2. 如果手动设置为NO了, 那么不启用代理, 这种case很少见
if (scopedProxyMode.equals(ScopedProxyMode.NO)) {
return definition;
}
boolean proxyTargetClass = scopedProxyMode.equals(ScopedProxyMode.TARGET_CLASS);
// 3. 转而调用 { ScopedProxyCreator.createScopedProxy }
return ScopedProxyCreator.createScopedProxy(definition, registry, proxyTargetClass);
}
下面继续跟踪
ScopedProxyCreator.createScopedProxy
public static BeanDefinitionHolder createScopedProxy(
BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry, boolean proxyTargetClass) {
return ScopedProxyUtils.createScopedProxy(definitionHolder, registry, proxyTargetClass);
}
没做啥事,直接调用了ScopedProxyUtils.createScopedProxy
ScopedProxyUtils.createScopedProxy是一个非常核心的方法,这个方法主要做了三件事
BeanDefinition
BeanDefinition
设置为不可注入BeanDefinition
到Spring容器中。在Spring的下一个生命周期,这些BeanDefinition
将被创建为Bean
public static BeanDefinitionHolder createScopedProxy(BeanDefinitionHolder definition,
BeanDefinitionRegistry registry, boolean proxyTargetClass) {
String originalBeanName = definition.getBeanName();
BeanDefinition targetDefinition = definition.getBeanDefinition();
String targetBeanName = getTargetBeanName(originalBeanName);
// 1. 创建代理的 { BeanDefinition }, 对应的beanClass为 { ScopedProxyFactoryBean.class }
RootBeanDefinition proxyDefinition = new RootBeanDefinition(ScopedProxyFactoryBean.class);
proxyDefinition.setDecoratedDefinition(new BeanDefinitionHolder(targetDefinition, targetBeanName));
proxyDefinition.setOriginatingBeanDefinition(targetDefinition);
proxyDefinition.setSource(definition.getSource());
proxyDefinition.setRole(targetDefinition.getRole());
// 2. 设置 { targetBeanName }, 为原始 { BeanDefinition } 的 beanName
// !!!很重要,因为到时候需要基于 { targetBeanName } 从spring容器中获取原始的bean
proxyDefinition.getPropertyValues().add("targetBeanName", targetBeanName);
if (proxyTargetClass) {
targetDefinition.setAttribute(AutoProxyUtils.PRESERVE_TARGET_CLASS_ATTRIBUTE, Boolean.TRUE);
}
else {
proxyDefinition.getPropertyValues().add("proxyTargetClass", Boolean.FALSE);
}
proxyDefinition.setAutowireCandidate(targetDefinition.isAutowireCandidate());
proxyDefinition.setPrimary(targetDefinition.isPrimary());
if (targetDefinition instanceof AbstractBeanDefinition) {
proxyDefinition.copyQualifiersFrom((AbstractBeanDefinition) targetDefinition);
}
// 3. 为原始 { BeanDefinition } 设置为不可注入
// 如此, 这个 { BeanDefinition } 创建出来的bean就无法被依赖注入了
targetDefinition.setAutowireCandidate(false);
targetDefinition.setPrimary(false);
// 4. 注册原始的 { BeanDefinition } 到IOC容器中
registry.registerBeanDefinition(targetBeanName, targetDefinition);
// 5. 将 { proxyBeanDefinition } 包装成 { BeanDefinitionHolder }
// 在第二步的 { doScan } 方法中, 会将这里 return的 { proxyBeanDefinition } 注册到IOC容器中
return new BeanDefinitionHolder(proxyDefinition, originalBeanName, definition.getAliases());
}
经历以上步骤后,每个被@RefrehScope
注解标注的class,会生成两个BeanDefinition
注册到容器中
假设有一个Person
的class
@RefreshScope
@Component
class Person {
// ...
}
当这个class被扫描后,就会出现如下图的情况
Spring容器的下一个生命周期,就会将容器中的BeanDefinition
初始化为Bean
接下来看看
ScopedProxyFactoryBean
的逻辑
ScopedProxyFactoryBean
是一个FactoryBean
, Spring通过调用FactoryBean
的getObject()
方法获取beanScopedProxyFactoryBean
还实现了BeanFactoryAware
接口,那么Spring将会回调setBeanFactory
方法,并且传递BeanFactory
作为参数#getObject
@Override
public Object getObject() {
if (this.proxy == null) {
throw new FactoryBeanNotInitializedException();
}
return this.proxy;
}
getObject()
方法直接将proxy
属性返回,而proxy
属性在setBeanFactory
方法中初始化
#setBeanFactory
@Override
public void setBeanFactory(BeanFactory beanFactory) {
ConfigurableBeanFactory cbf = (ConfigurableBeanFactory) beanFactory;
// 1. 给 { scopedTargetSource } 赋值 { beanFactory }
this.scopedTargetSource.setBeanFactory(beanFactory);
ProxyFactory pf = new ProxyFactory();
pf.copyFrom(this);
// 2. 给 { proxyFactory } 赋值 { targetSource }
pf.setTargetSource(this.scopedTargetSource);
Assert.notNull(this.targetBeanName, "Property 'targetBeanName' is required");
ScopedObject scopedObject = new DefaultScopedObject(cbf, this.scopedTargetSource.getTargetBeanName());
pf.addAdvice(new DelegatingIntroductionInterceptor(scopedObject));
pf.addInterface(AopInfrastructureBean.class);
// 3. 创建代理
this.proxy = pf.getProxy(cbf.getBeanClassLoader());
}
这里又使用到了
ProxyFactory
来创建代理
- 关于
ProxyFactory
的使用,可以查看这篇文章ProxyFactory使用- 关于
ProxyFactory
的源码,可以查看这篇文章ProxyFactory源码
注释1和注释2,一直在提及scopedTargetSource
scopedTargetSource
是ScopedProxyFactoryBean
的成员属性
对应的class为SimpleBeanTargetSource
public class SimpleBeanTargetSource extends AbstractBeanFactoryBasedTargetSource {
@Override
public Object getTarget() throws Exception {
return getBeanFactory().getBean(getTargetBeanName());
}
}
只需要关注getTarget
方法,其逻辑为:每次都从beanFactory中根据beanName获取bean
那么这个beanName是什么呢?
还记得在ScopedProxyUtils.createScopedProxy
方法中给ScopedProxyFactoryBean
的BeanDefinition
中设置了beanName
跳转回顾ScopedProxyUtils.createScopedProxy方法
scopedTargetSource
被赋值给了proxyFactory
ProxyFactory
创建出来的代理对象实现了Adviced
接口Adviced
接口就具有获取targetSource
的能力LockedScopedProxyFactoryBean
就会将代理对象转换为Adviced
, 然后获取targetSource
,实际上就是scopedTargetSource
targetSource
的getTarget()
方法,根据上面的代码可知,getTarget()
方法将从Spring容器中获取bean,这一点非常重要,暂且有个印象GenericScope
是RefreshScope
的父类
GenericScope
同时还是一个BeanDefinitionRegistryPostProcessor
Spring会回调GenericScope
的postProcessBeanDefinitionRegistry
方法
GenericScope
在postProcessBeanDefinitionRegistry
将ScopedProxyFactoryBean
的BeanDefinition
替换为了LockedScopedProxyFactoryBean
LockedScopedProxyFactoryBean
是ScopedProxyFactoryBean
的子类
这块内容本应该在下面提及,但是LockedScopedProxyFactoryBean
重写了ScopedProxyFactoryBean
的setBeanFactory
方法
@Override
public void setBeanFactory(BeanFactory beanFactory) {
// 1. 先调用父类的 { setBeanFactory }
super.setBeanFactory(beanFactory);
// 2. 获取proxy对象
Object proxy = getObject();
// 3. 强制转换为 { Advised } , 并且添加一个 "advice" { this }
if (proxy instanceof Advised) {
Advised advised = (Advised) proxy;
// 插入在索引为0的位置
advised.addAdvice(0, this);
}
}
LockedScopedProxyFactoryBean
实现了MethodInterceptor
方法,是一个Advice
那么,执行代理对象的任何方法时,都会首先执行LockedScopedProxyFactoryBean
的invoke
方法
@Override
public Object invoke(MethodInvocation invocation) throws Throwable {
Method method = invocation.getMethod();
// equals / hashCode / toString ... 直接执行方法
if (AopUtils.isEqualsMethod(method) || AopUtils.isToStringMethod(method)
|| AopUtils.isHashCodeMethod(method)
|| isScopedObjectGetTargetObject(method)) {
return invocation.proceed();
}
// 1. 获取代理对象
Object proxy = getObject();
// 2. 获取读写锁, 读写锁的创建逻辑会在分析 { RefreshScope } 的源码时提及
ReadWriteLock readWriteLock = this.scope.getLock(this.targetBeanName);
Lock lock = readWriteLock.readLock();
// 3. 上读锁
lock.lock();
try {
// 4. 强制转换为 { adviced }
Advised advised = (Advised) proxy;
ReflectionUtils.makeAccessible(method);
// 5. 反射执行方法
// 第一个参数:要执行的方法
// 第二个参数:{ dvised.getTargetSource().getTarget() } 这里很关键
// 还记得 5.2 分析 { setBeanFactory } 方法创建代理时提及的吗
// 这里的 { getTarget } 方法会从spring容器中获取bean
// 第三个参数:方法参数
return ReflectionUtils.invokeMethod(method,
advised.getTargetSource().getTarget(),
invocation.getArguments());
}
catch (UndeclaredThrowableException e) {
throw e.getUndeclaredThrowable();
}
finally {
lock.unlock();
}
}
跳转回顾getTarget()方法
1~5步,主要分析了以下逻辑
@Refresh
注解标注的class被Spring扫描后,会向容器中注入两个BeanDefinition
,一个是原始的Bean的BeanDefinition
,另一个是ScopedProxyFactoryBean
的BeanDefinition
ScopedProxyFactoryBean
被赋值targetBeanName
属性,对应原始的Bean的BeanName ,使ScopedProxyFactoryBean
具有从Spring容器中根据BeanName获取原始Bean的能力RefreshScope
会在Spring容器创建Bean之前,将容器中BeanDefinition
为ScopedProxyFactoryBean
替换为LockedScopedProxyFactoryBean
LockedScopedProxyFactoryBean
是一个FactoryBean
,其getObject
方法会返回一个代理。假设有一个Person
类使用了@RefreshScope
注解,另外一个类依赖Spring注入Person
类的实例,实际上注入的是getObject
方法返回的代理对象关键点就在于,每次执行方法都是从Spring容器中获取的bean来执行方法
我们已经知道了实际上注入的是代理对象,而代理对象会通过IOC容器获取原始bean去执行方法
根据第一部分扫描Bean时可知,原始Bean是一个Scope类型的Bean
Spring容器中只有单例的Bean会被缓存!!!
Spring容器中只有单例的Bean会被缓存!!!
Spring容器中只有单例的Bean会被缓存!!!
也就是说,代理对象每次获取原始的Bean,都会执行下面的逻辑
if (mbd.isSingleton()) {
// 获取单例Bean
// ...
}
else if (mbd.isPrototype()) {
// 获取多例Bean
// ...
}
else {
// 1. 获取 { beanDefinition } 的 { scope } 属性
String scopeName = mbd.getScope();
// 2. 根据 { scope } 属性找到对应的 { Scope } 对象
// 例如 scope = "refresh"对应的就是 { RefreshScope }
final Scope scope = this.scopes.get(scopeName);
// 3. 调用 { scope } 对象的 { get } 方法创建bean
// 第一个参数是 { beanName } : bean的名称,
// !!!第二个参数是 { ObjectFactory } : bean创建工厂, 拥有创建bean的能力
Object scopedInstance = scope.get(beanName, () -> {
beforePrototypeCreation(beanName);
try {
return createBean(beanName, mbd, args);
}
finally {
afterPrototypeCreation(beanName);
}
});
bean = getObjectForBeanInstance(scopedInstance, name, beanName, mbd);
}
Spring根据BeanDefinition
创建对象时
如果BeanDenifition
既不是单例也不是多态的,那么根据BeanDenifition
的scope
属性来创建对象
而@Scope(value = "refresh")
, 对应的 Scope
就是 RefreshScope
当前
RefreshScope
指定是类,而不是注解
也就是说,Spring将创建bean委托给了RefreshScope
根据上一步的分析,Spring调用了Scope
对象的get(beanName, obejctFactory)
方法
这个方法位于RefreshScope
的父类GenericScope
RefreshScope并没有重写这个方法,所以跟踪父类的get方法
public Object get(String name, ObjectFactory<?> objectFactory) {
// 1. 创建BeanLifecycleWrapper
BeanLifecycleWrapper value = new BeanLifecycleWrapper(name, objectFactory);
// 2. 放入cache中
this.cache.put(name, value);
// 3. 每个 { beanName } 都有一把读写锁
// 在Locked
this.locks.putIfAbsent(name, new ReentrantReadWriteLock());
try {
// 4. 调用 { BeanLifecycleWrapper#getBean } 方法, 创建一个对象
return value.getBean();
}
catch (RuntimeException e) {
this.errors.put(name, e);
throw e;
}
}
@Override
public Object get(String name, ObjectFactory<?> objectFactory) {
// 1. 创建BeanLifecycleWrapper, 并且放入缓存中
// 这个put方法被封装了一层, 其实里面调用的是 { putIfAbsent }
// 也就是说, 只有cache不存在的时候才会put进去
// Spring其实这里写的不太好, 每次都new了一个新的 { BeanLifecycleWrapper }, 却不一定能用上
BeanLifecycleWrapper value = this.cache.put(name,
new BeanLifecycleWrapper(name, objectFactory));
// 2. 每个 { beanName } 都有一把读写锁
// 在 { LockedScopedProxyFactoryBean } 的 { invoke } 方法中就使用了读锁
this.locks.putIfAbsent(name, new ReentrantReadWriteLock());
try {
// 3. 调用 { BeanLifecycleWrapper#getBean } 方法, 创建一个对象
return value.getBean();
}
catch (RuntimeException e) {
this.errors.put(name, e);
throw e;
}
}
值得注意的是:
RefreshScope
对象是单例的,也就是说,所有scope
为refresh
的对象
都是由RefreshScope
对象创建的,那么也就全都保存在了RefreshScope
对象的cache
属性中
可以看到,RefreshScope做了这些事情
BeanLifecycleWrapper
cache
beanName
创建ReentrantReadWriteLock
BeanLifecycleWrapper
的getBean
方法继续跟踪BeanLifecycleWrapper
class BeanLifecycleWrapper {
// 1. beanName
private final String name;
// 2. bean创建工厂, 就是步骤二调用 { get } 方法时的第二个参数
private final ObjectFactory<?> objectFactory;
// 3. bean的实例, 通过 { objectFactory } 创建得到, 一开始为 { null }
private Object bean;
// 4. bean销毁前的回调, 如果为 { null } , 那么bean无需被销毁
private Runnable callback;
}
public Object getBean() {
if (this.bean == null) {
// 双重检测锁
synchronized (this.name) {
if (this.bean == null) {
// 调用objectFactory的getObject()方法创建bean
this.bean = this.objectFactory.getObject();
}
}
}
return this.bean;
}
就是调用了objectFactory
的getBean
方法
objectFactory
就是IOC容器创建bean时的第二个参数,跳转回顾第二个参数
将对象赋值给this.bean
属性,下次再调用getBean()
方法时直接返回this.bean
那么如果,将某个BeanName在Cache中的BeanLifecycleWrapper移除掉
下次再调用getBean方法时,这个对象就会通过objectFactory#getObject
重新创建一次
所以,GenericScope有一个destroy
方法,就可以清除缓存
@Override
public void destroy() {
List<Throwable> errors = new ArrayList<Throwable>();
// 看这里:清空缓存
Collection<BeanLifecycleWrapper> wrappers = this.cache.clear();
for (BeanLifecycleWrapper wrapper : wrappers) {
try {
Lock lock = this.locks.get(wrapper.getName()).writeLock();
lock.lock();
try {
wrapper.destroy();
}
finally {
lock.unlock();
}
}
catch (RuntimeException e) {
errors.add(e);
}
}
if (!errors.isEmpty()) {
throw wrapIfNecessary(errors.get(0));
}
this.errors.clear();
}
那我们接下来应该去寻找哪里调用了
destory
方法,应该就是动态刷新的入口点了
get
方法获取RefreshScope
有一个cache
属性,Refresh
使用cache
通过beanName获取BeanLifecycleWrapper,再调用BeanLifecycleWrapper的getBean
方法获取对象RefreshScope
的cache
被清除,再次获取bean的时候,无法命中缓存,就会重新创建BeanLifecycleWrapper,也就会再次调用objectFactory#getObject
创建BeanobjectFactory#getObject
创建Bean的时候会使用Spring的Environment
中的属性为Bean的属性赋值RefreshEventListener
是一个ApplicationListener
一旦接收到RefreshEvent
,就会调用ContextRefresher
的refresh
方法
ContextRefresher
先重新刷新上下文,再调用RefreshScope
的refreshAll
方法
在refreshAll
方法中调用了destory
方法:清空缓存
那么就还剩下了最后两个疑问
ContextRefresher
是如何刷新environment
的RefreshEvent
事件public synchronized Set<String> refresh() {
// 刷新environment
Set<String> keys = refreshEnvironment();
// this.scope 就是 refreshScope
this.scope.refreshAll();
return keys;
}
public synchronized Set<String> refreshEnvironment() {
// 忽略:为EnvironmentChangeEvent做准备的
Map<String, Object> before = extract(
this.context.getEnvironment().getPropertySources());
// 核心方法:就是在这里更新了ApplicationContext的environment属性
addConfigFilesToEnvironment();
Set<String> keys = changes(before,
extract(this.context.getEnvironment().getPropertySources())).keySet();
this.context.publishEvent(new EnvironmentChangeEvent(this.context, keys));
return keys;
}
/* For testing. */ ConfigurableApplicationContext addConfigFilesToEnvironment() {
ConfigurableApplicationContext capture = null;
// 1. 保存当前的environment
StandardEnvironment environment = copyEnvironment(
this.context.getEnvironment());
// 2. 创建一个SpringApplicationBuilder, 用于创建SpringApplication
SpringApplicationBuilder builder = new SpringApplicationBuilder(Empty.class)
.bannerMode(Mode.OFF).web(WebApplicationType.NONE)
// !!! 设置environment
.environment(environment);
// 3.
// - 添加BootstrapApplicationListener读取bootstrap.yml
// - 添加ConfigFileApplicationListener读取application.yml
builder.application()
.setListeners(Arrays.asList(new BootstrapApplicationListener(),
new ConfigFileApplicationListener()));
// 4. 启动SpringBootApplication
// 启动完成后, 这个environment对象就有最新的值了
capture = builder.run();
// 5. target是当前contenxt的environment
MutablePropertySources target = this.context.getEnvironment()
.getPropertySources();
String targetName = null;
// 6. 进行比较
for (PropertySource<?> source : environment.getPropertySources()) {
String name = source.getName();
if (target.contains(name)) {
targetName = name;
}
// standardSources包含了一些不需要更新的属性集合
if (!this.standardSources.contains(name)) {
// 当前context有这个key对应的值, 那么替换为最新的
if (target.contains(name)) {
target.replace(name, source);
}
else {
// 没有则新增
if (targetName != null) {
target.addAfter(targetName, source);
}
else {
target.addFirst(source);
targetName = name;
}
}
}
}
return capture;
}
为什么重新启动一个SpringBootApplication,Environment里就有最新的值了呢?
以Nacos为例
PropertySourceBootstrapConfiguration
位于spring.factories文件中,会被SpringBootApplication加载到
通过SpringBoot回调initialize
,initialize
通过PropertySourceLocator加载属性,放入到environment中
而Nacos的AutoConfiguration则注入了NacosPropertySourceLocator
到容器中
NacosPropertySourceLocator
通过网络链接到nacos,读取配置信息,最终被放入到了environment中
这一块属于nacos-config的实现细节,本篇就不深究了
=====================================================================
只需要知道一个结论
重新启动的SpringBootApplication的environment具有最新的属性,ContextRefresher
进行比较后,给当前context的environment赋最新的值
=====================================================================
在NacosContextRefresher#registerNacosListener方法中
private void registerNacosListener(final String groupKey, final String dataKey) {
String key = NacosPropertySourceRepository.getMapKey(dataKey, groupKey);
// 监听器, configServer长轮询nacos-server
// 一旦监听到配置变更, 就会回调listener的 { innerReceive } 方法
Listener listener = listenerMap.computeIfAbsent(key,
lst -> new AbstractSharedListener() {
@Override
public void innerReceive(String dataId, String group,
String configInfo) {
refreshCountIncrement();
nacosRefreshHistory.addRefreshRecord(dataId, group, configInfo);
// !!! 发布RefreshEvent事件
applicationContext.publishEvent(
new RefreshEvent(this, null, "Refresh Nacos config"));
}
});
try {
// 注册listener, 启用长轮询监听
configService.addListener(dataKey, groupKey, listener);
}
catch (NacosException e) {
log.warn(String.format(
"register fail for nacos listener ,dataId=[%s],group=[%s]", dataKey,
groupKey), e);
}
}
这块属于Nacos-Config的源码,就不深究了
@RefreshScope
注解标注的class,会生成两个BeanDefinition
applicationContext
获取原始Bean来执行的applicationContext
通过RefreshScope
对象来获取原型BeanRefreshScope
具有缓存机制RefreshEvent
事件
ContextRefresh
就会通过创建一个新的SpringApplication
来更新environment,赋最新值ContextRefresh
会调用RefreshScope
清空缓存