从 getBean 讲起
getBean
方法是 Spring IOC
的老朋友,在里面有一个细节我们可能会忽略。
Spring IOC
针对不同的 bean
管理模式有不同的执行策略,而我们最常见熟知的就是 单例
和 原型
,下面在 getBean
的过程中就会首先根据不同的管理策略执行不同的逻辑方法,而我们今天要讲的是比较少走的 else
逻辑部分。
if (mbd.isSingleton()) {
...
}
else if (mbd.isPrototype()) {
.....
}
else {
......
}
单例
的特点是整个生命周期只创建一次对象,而 原型
是每次getBean
都会创建新的对象。除此之外,Spring
也支持自定义的管理策略。比如这次说的 RefreshScope
, RefreshScope
是一个能够在程序运行时可控的触发刷新(原理是销毁重新创建一个对象)的一种 Spring bean
管理策略。而使用场景最多的是项目配置项的及时更新(动态配置),其原理就是销毁老对象,然后重新创建对象的时候对配置进行重新获取,这样就可以实现配置的及时获取。
如何做到销毁创建
大概说了 RefreshScope
是做啥用的,就从注解开始说起吧,@RefreshScope
注解作用于类上,里面有一个关键的属性 proxyMode
暂且不提,然后 @RefreshScope
还套娃着一个 @Scope("refresh")
注解,这里的作用就是生成这个对象的 BeanDefinition
时,其scope属性为 'refresh'
。所以在上一节判断 mbd.isSingleton()
时,其实判断的是 BeanDefinition
属性是否为 singleton
或者 prototype
,很明显不是这两个,所以走了 else
逻辑。
@Scope("refresh")
@Documented
public @interface RefreshScope {
/**
* @see Scope#proxyMode()
* @return proxy mode
*/
ScopedProxyMode proxyMode() default ScopedProxyMode.TARGET_CLASS;
}
而 else
逻辑一开始也是根据 scopeName
找到对应的处理类,也就是 RefreshScope
(跟注解 @RefreshScope
不是一个东西),RefreshScope
本身继承了 GenericScope
,执行 get
方法的时候也是执行 GenericScope
的实现方法。
String scopeName = mbd.getScope();
if (!StringUtils.hasLength(scopeName)) {
throw new IllegalStateException("No scope name defined for bean ´" + beanName + "'");
}
Scope scope = this.scopes.get(scopeName);
if (scope == null) {
throw new IllegalStateException("No Scope registered for scope name '" + scopeName + "'");
}
try {
Object scopedInstance = scope.get(beanName, () -> {
beforePrototypeCreation(beanName);
try {
return createBean(beanName, mbd, args);
}
finally {
afterPrototypeCreation(beanName);
}
});
beanInstance = getObjectForBeanInstance(scopedInstance, name, beanName, mbd);
}
catch (IllegalStateException ex) {
throw new ScopeNotActiveException(beanName, scopeName, ex);
}
get
方法内部是有一个 cache map
,存储了 对象名
为 key
,BeanLifecycleWrapper
对象为 value
。BeanLifecycleWrapper
对象是一个包装类,包装了 对象实例
,如果对象实例为空,则通过 ObjectFactory
生成新的对象(本质还是调用 createBean
创建对象); 如果不为空,也就相当于已经创建并缓存了对象,直接返回对象即可。从这里可以看出,非单例对象(包括refresh bean
)和单例对象保存的地方已经不在一起了,管理方式也不同,spring内部确实”博大精深“呀~
String scopeName = mbd.getScope();
if (!StringUtils.hasLength(scopeName)) {
throw new IllegalStateException("No scope name defined for bean ´" + beanName + "'");
}
Scope scope = this.scopes.get(scopeName);
if (scope == null) {
throw new IllegalStateException("No Scope registered for scope name '" + scopeName + "'");
}
try {
Object scopedInstance = scope.get(beanName, () -> {
beforePrototypeCreation(beanName);
try {
return createBean(beanName, mbd, args);
}
finally {
afterPrototypeCreation(beanName);
}
});
beanInstance = getObjectForBeanInstance(scopedInstance, name, beanName, mbd);
}
catch (IllegalStateException ex) {
throw new ScopeNotActiveException(beanName, scopeName, ex);
}
我们刚刚讲了创建
,再讲讲销毁
,RefreshScope
提供了指定 name
销毁,或者全部 refresh bean
销毁,逻辑也不复杂,就是调用 GenericScope
父类的逻辑,将 cache map
内容全部清空,这样下次再 getBean
拿对象的时候,拿到的就是重新创建的对象了。
public boolean refresh(String name) {
if (!ScopedProxyUtils.isScopedTarget(name)) {
// User wants to refresh the bean with this name but that isn't the one in the
// cache...
name = ScopedProxyUtils.getTargetBeanName(name);
}
// Ensure lifecycle is finished if bean was disposable
if (super.destroy(name)) {
this.context.publishEvent(new RefreshScopeRefreshedEvent(name));
return true;
}
return false;
}
@ManagedOperation(description = "Dispose of the current instance of all beans "
+ "in this scope and force a refresh on next method execution.")
public void refreshAll() {
super.destroy();
this.context.publishEvent(new RefreshScopeRefreshedEvent());
}
讲到这里,貌似 只提到了 refresh bean
会被销毁重建,但是怎么服务动态配置,可能还是一头雾水,这里还需要介绍一个新类 ContextRefresher
,ContextRefresher
和 RefreshScope
是 Spring Cloud
实现动态配置的两个关键类。
ContextRefresher
基本原理就是拿到最新的 Environment
,也就是最新的配置信息,然后跟老的 Environment
配置做比对,最后返回被更新的 key
集合。
//spring-cloud-context 3.0.3
public synchronized Set<String> refresh() {
//拿到修改的配置项
Set<String> keys = refreshEnvironment();
//调用RefreshScope refreshAll方法,销毁refresh bean
this.scope.refreshAll();
return keys;
}
public synchronized Set<String> refreshEnvironment() {
// 拿到当前上下文的配置项
Map<String, Object> before = extract(this.context.getEnvironment().getPropertySources());
// 更新环境,LegacyContextRefresher的实现是重新创建一个SpringBoot环境(详看源码)。此时会拿到最新的environment。也就相当于拿到最新的配置。
updateEnvironment();
//对比新老environment的配置项信息
Set<String> keys = changes(before, extract(this.context.getEnvironment().getPropertySources())).keySet();
//发送事件,监听的对象可以拿到更改的信息项
this.context.publishEvent(new EnvironmentChangeEvent(this.context, keys));
//返回修改的配置项
return keys;
}
使用 Spring Cloud Config
的同学知道可以调用 /refresh
接口刷新配置项,而 /refresh
接口层层拨开其实就是调用 contextRefresher.refresh()
。而事件广播机制可以让其它组件拿到最新的配置信息加以利用。
如何做到依赖注入
到这里,我们知道对象都是 @Autowired
或者 @Resource
注入进去的,那就会出现一个问题,refresh bean
被销毁重建后,其它类依赖的 refresh bean
怎么更新?答案是代理对象。
这时我们就要开始从 ioc
初始化说起了。对象都是从 BeanDefinition
到 bean实例
转变,@ComponentScan
扫描所有对象的时候,会判断 BeanDefinition
的 Scope
的proxyMode
属性,这个属性很重要,@RefreshScope
注解 proxyMode
默认是 TARGET_CLASS
,如果是 TARGET_CLASS
,ioc
逻辑会创建一个新的BeanDefinition
。
protected Set<BeanDefinitionHolder> doScan(String... basePackages) {
Assert.notEmpty(basePackages, "At least one base package must be specified");
Set<BeanDefinitionHolder> beanDefinitions = new LinkedHashSet<>();
for (String basePackage : basePackages) {
Set<BeanDefinition> candidates = findCandidateComponents(basePackage);
for (BeanDefinition candidate : candidates) {
// 拿去@RefreshScope proxyMode 属性值
ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(candidate);
candidate.setScope(scopeMetadata.getScopeName());
String beanName = this.beanNameGenerator.generateBeanName(candidate, this.registry);
if (candidate instanceof AbstractBeanDefinition) {
postProcessBeanDefinition((AbstractBeanDefinition) candidate, beanName);
}
if (candidate instanceof AnnotatedBeanDefinition) {
AnnotationConfigUtils.processCommonDefinitionAnnotations((AnnotatedBeanDefinition) candidate);
}
if (checkCandidate(beanName, candidate)) {
BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(candidate, beanName);
//详见下方代码注释
definitionHolder =
AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);
beanDefinitions.add(definitionHolder);
registerBeanDefinition(definitionHolder, this.registry);
}
}
}
return beanDefinitions;
}
static BeanDefinitionHolder applyScopedProxyMode(
ScopeMetadata metadata, BeanDefinitionHolder definition, BeanDefinitionRegistry registry) {
ScopedProxyMode scopedProxyMode = metadata.getScopedProxyMode();
//proxyMode 属性为 NO,不改变beanDefinition
if (scopedProxyMode.equals(ScopedProxyMode.NO)) {
return definition;
}
boolean proxyTargetClass = scopedProxyMode.equals(ScopedProxyMode.TARGET_CLASS);
//详见下方代码注释
return ScopedProxyCreator.createScopedProxy(definition, registry, proxyTargetClass);
}
public static BeanDefinitionHolder createScopedProxy(BeanDefinitionHolder definition,
BeanDefinitionRegistry registry, boolean proxyTargetClass) {
String originalBeanName = definition.getBeanName();
BeanDefinition targetDefinition = definition.getBeanDefinition();
String targetBeanName = getTargetBeanName(originalBeanName);
// Create a scoped proxy definition for the original bean name,
// "hiding" the target bean in an internal target definition.
RootBeanDefinition proxyDefinition = new RootBeanDefinition(ScopedProxyFactoryBean.class);
proxyDefinition.setDecoratedDefinition(new BeanDefinitionHolder(targetDefinition, targetBeanName));
proxyDefinition.setOriginatingBeanDefinition(targetDefinition);
proxyDefinition.setSource(definition.getSource());
proxyDefinition.setRole(targetDefinition.getRole());
proxyDefinition.getPropertyValues().add("targetBeanName", targetBeanName);
if (proxyTargetClass) {
targetDefinition.setAttribute(AutoProxyUtils.PRESERVE_TARGET_CLASS_ATTRIBUTE, Boolean.TRUE);
// ScopedProxyFactoryBean's "proxyTargetClass" default is TRUE, so we don't need to set it explicitly here.
}
else {
proxyDefinition.getPropertyValues().add("proxyTargetClass", Boolean.FALSE);
}
// Copy autowire settings from original bean definition.
proxyDefinition.setAutowireCandidate(targetDefinition.isAutowireCandidate());
proxyDefinition.setPrimary(targetDefinition.isPrimary());
if (targetDefinition instanceof AbstractBeanDefinition) {
proxyDefinition.copyQualifiersFrom((AbstractBeanDefinition) targetDefinition);
}
// The target bean should be ignored in favor of the scoped proxy.
targetDefinition.setAutowireCandidate(false);
targetDefinition.setPrimary(false);
// Register the target bean as separate bean in the factory.
registry.registerBeanDefinition(targetBeanName, targetDefinition);
// Return the scoped proxy definition as primary bean definition
// (potentially an inner bean).
return new BeanDefinitionHolder(proxyDefinition, originalBeanName, definition.getAliases());
}
该方法会创建一个新的 BeanDefinition
,该 BeanDefinition
的类型为ScopedProxyFactoryBean
,并且 "scopedTarget." + bean名称
作为 被代理的bean
的beanName
。该方法最后返回的是代理类的 BeanDefinition
。
public static BeanDefinitionHolder createScopedProxy(BeanDefinitionHolder definition,
BeanDefinitionRegistry registry, boolean proxyTargetClass) {
String originalBeanName = definition.getBeanName();
BeanDefinition targetDefinition = definition.getBeanDefinition();
String targetBeanName = getTargetBeanName(originalBeanName);
// Create a scoped proxy definition for the original bean name,
// "hiding" the target bean in an internal target definition.
RootBeanDefinition proxyDefinition = new RootBeanDefinition(ScopedProxyFactoryBean.class);
proxyDefinition.setDecoratedDefinition(new BeanDefinitionHolder(targetDefinition, targetBeanName));
proxyDefinition.setOriginatingBeanDefinition(targetDefinition);
proxyDefinition.setSource(definition.getSource());
proxyDefinition.setRole(targetDefinition.getRole());
proxyDefinition.getPropertyValues().add("targetBeanName", targetBeanName);
if (proxyTargetClass) {
targetDefinition.setAttribute(AutoProxyUtils.PRESERVE_TARGET_CLASS_ATTRIBUTE, Boolean.TRUE);
// ScopedProxyFactoryBean's "proxyTargetClass" default is TRUE, so we don't need to set it explicitly here.
}
else {
proxyDefinition.getPropertyValues().add("proxyTargetClass", Boolean.FALSE);
}
// Copy autowire settings from original bean definition.
proxyDefinition.setAutowireCandidate(targetDefinition.isAutowireCandidate());
proxyDefinition.setPrimary(targetDefinition.isPrimary());
if (targetDefinition instanceof AbstractBeanDefinition) {
proxyDefinition.copyQualifiersFrom((AbstractBeanDefinition) targetDefinition);
}
// The target bean should be ignored in favor of the scoped proxy.
targetDefinition.setAutowireCandidate(false);
targetDefinition.setPrimary(false);
// Register the target bean as separate bean in the factory.
registry.registerBeanDefinition(targetBeanName, targetDefinition);
// Return the scoped proxy definition as primary bean definition
// (potentially an inner bean).
return new BeanDefinitionHolder(proxyDefinition, originalBeanName, definition.getAliases());
}
也就是说,这个 refresh bean
在 ioc容器
里已经不是原始的类,而是一个代理对象ScopedProxyFactoryBean
。
ScopedProxyFactoryBean
是一个 FactoryBean
,通过 getObject
方法拿到具体对象,而 getObject
方法十分简单,直接返回 proxy对象
,proxy对象
是在 setBeanFactory
方法中创建的。
// ScopedProxyFactoryBean class
@Override
public void setBeanFactory(BeanFactory beanFactory) {
if (!(beanFactory instanceof ConfigurableBeanFactory)) {
throw new IllegalStateException("Not running in a ConfigurableBeanFactory: " + beanFactory);
}
ConfigurableBeanFactory cbf = (ConfigurableBeanFactory) beanFactory;
this.scopedTargetSource.setBeanFactory(beanFactory);
ProxyFactory pf = new ProxyFactory();
pf.copyFrom(this);
//这里的TargetSource设置很重要,是解决refresh bean时常销毁重建,但是不影响依赖注入的关键
pf.setTargetSource(this.scopedTargetSource);
Assert.notNull(this.targetBeanName, "Property 'targetBeanName' is required");
Class<?> beanType = beanFactory.getType(this.targetBeanName);
if (beanType == null) {
throw new IllegalStateException("Cannot create scoped proxy for bean '" + this.targetBeanName +
"': Target type could not be determined at the time of proxy creation.");
}
if (!isProxyTargetClass() || beanType.isInterface() || Modifier.isPrivate(beanType.getModifiers())) {
pf.setInterfaces(ClassUtils.getAllInterfacesForClass(beanType, cbf.getBeanClassLoader()));
}
// Add an introduction that implements only the methods on ScopedObject.
ScopedObject scopedObject = new DefaultScopedObject(cbf, this.scopedTargetSource.getTargetBeanName());
pf.addAdvice(new DelegatingIntroductionInterceptor(scopedObject));
// Add the AopInfrastructureBean marker to indicate that the scoped proxy
// itself is not subject to auto-proxying! Only its target bean is.
pf.addInterface(AopInfrastructureBean.class);
this.proxy = pf.getProxy(cbf.getBeanClassLoader());
}
这里要讲一下 Java Proxy
的一个知识点,Java Proxy
代理的对象并不是 直接需要被代理的原始对象
,而是 TargetSource对象
(对 TargetSource对象
感觉陌生的可以看看 TargetSource目标源 这篇文章)。TargetSource对象
可以通过复写 getTarget方法
动态获取 需要被代理的目标对象
,这样做的原因无非就是提升灵活性,满足一些特定需求。比如在这里就可以通过getBean
来获取对象。是不是一下子闭环就完成了?我们再串联一下流程,前面refresh bean
必须要通过调用 getBean
方法才能确定拿到带有最新配置的对象,而通过 java代理机制 + TargetSource对象的灵活性,可以确保每次调用 refresh bean
的任意方法时,都能依靠代理机制每次通过 getBean
拿到最新的 refresh bean
。所以这就是为什么 refresh bean
时常销毁重建,但是不影响依赖注入的关键。
public class SimpleBeanTargetSource extends AbstractBeanFactoryBasedTargetSource {
@Override
public Object getTarget() throws Exception {
//依赖TargetSource机制,每次真正代理的对象都是从 `getBean` 方法中获取
return getBeanFactory().getBean(getTargetBeanName());
}
}
总结
这篇比较系统的总结了RefreshScope
, 一开始介绍了 RefreshScope
本身的特性 – 在一定时机下会销毁并重新创建 refresh bean
,那这个时机是什么?就是使用场景最多的动态配置功能
,比如 springcloudconfig,nacos,apollo等,然后带出ContextRefresher
可以触发拿取最新 Environment
并比对出更改的配置 key
并销毁refresh bean
。最后解释了一下为什么重复销毁并创建的 refresh bean
能在依赖注入中更新自己的实体 – 依赖于 代理机制
(TargetSource可以动态每次从getbean方法拿取最新对象)+ 事件广播
(其它组件可以根据返回的修改key做业务逻辑)机制实现每次对象调用都拿取最新的对象。