实现机制:
bean属性的自动刷新原理:
在spring2的时候,新增了自定义作用域,也就是除了单例和原型,新增了scope注解和接口,以便提高bean的储存生命周期,与它相关的接口和类为
ConfigurableBeanFactory.registerScope,
CustomScopeConfigurer,
org.springframework.aop.scope.ScopedProxyFactoryBean, org.springframework.web.context.request.RequestScope,
org.springframework.web.context.request.SessionScope
实现的基本原理为
- 包扫描的时候,识别到@scope接口后将beandefination修改为ScopedProxyFactoryBean,具体在
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) {
ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(candidate);
candidate.setScope(scopeMetadata.getScopeName());
String beanName = this.beanNameGenerator.generateBeanName(candidate, this.registry);
if (candidate instanceof AbstractBeanDefinition abstractBeanDefinition) {
postProcessBeanDefinition(abstractBeanDefinition, beanName);
}
if (candidate instanceof AnnotatedBeanDefinition annotatedBeanDefinition) {
AnnotationConfigUtils.processCommonDefinitionAnnotations(annotatedBeanDefinition);
}
// 也就是在这里,找到所有scope注解的类,将定义更换为ScopedProxyFactoryBean
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;
}
跟进去代码发现
public static BeanDefinitionHolder createScopedProxy(BeanDefinitionHolder definition, BeanDefinitionRegistry registry, boolean proxyTargetClass) {
String originalBeanName = definition.getBeanName();
BeanDefinition targetDefinition = definition.getBeanDefinition();
String targetBeanName = getTargetBeanName(originalBeanName);
// 可以发现,在这里创建了ScopedProxyFactoryBean的bean定义
RootBeanDefinition proxyDefinition = new RootBeanDefinition(ScopedProxyFactoryBean.class);
// 将原始类设置进去,也就是被scope注解的类
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);
} else {
proxyDefinition.getPropertyValues().add("proxyTargetClass", Boolean.FALSE);
}
proxyDefinition.setAutowireCandidate(targetDefinition.isAutowireCandidate());
proxyDefinition.setPrimary(targetDefinition.isPrimary());
if (targetDefinition instanceof AbstractBeanDefinition abd) {
proxyDefinition.copyQualifiersFrom(abd);
}
targetDefinition.setAutowireCandidate(false);
targetDefinition.setPrimary(false);
registry.registerBeanDefinition(targetBeanName, targetDefinition);
return new BeanDefinitionHolder(proxyDefinition, originalBeanName, definition.getAliases());
}
那么,ScopedProxyFactoryBean做了什么呢?为什么要修改为这个类,我们看看这个类的结构
既然实现了FactoryBean,我们看看这里获取的bean做了什么处理,很简单,首先,在设置BeanFactory的时候生成代理
然后获取bean的时候返回代理
生成的代理有什么用呢?进一步看看代理类的逻辑,其实就是在每次调用方法的时候,会先进入aop回调方法,去获取原始对象
那么问题回到开始,哪里起到了动态刷新呢?其实如果在配置中心的配置改变时候,将bean销毁掉,那么下次调用的时候去获取不是就可以获取最新的bean吗,确实,springcoud就是这个做的,springcloud使用refreshscope注解配合RefreshScope类,refreshscope注解将包装为ScopedProxyFactoryBean,RefreshScope类负责处理bean的生命周期,也就是说,获取的bean不在是去原始的beanfacroty中获取,而是到RefreshScope中获取,源码如下
scope是在RefreshScope中被注册的,因为实现了BeanFactoryPostProcessor, BeanDefinitionRegistryPostProcessor
okok,终于到这里了,休息一下,我们回顾一下上面做了什么?
- 将标注了@refreshscope的bean包装为LockedScopedProxyFactoryBean,产生每次调用方法都去RefreshScope中获取bean对象
- RefreshScope是springcloud注册进去的,在springcoud-context中的自动配置类中,注册到beanfactory中 那么问题来了,为什么每次获取的时候都是最新对象呢,我们自然而然想到的是,每次刷新配置的时候,将RefreshScope保存的bean销毁,然后下次调用方法的时候就会获取到最新的bean了,这里也是这么做的,每次刷新的时候都会通知RefreshScope进行销毁,源码如下,
那么什么时候调用的呢?在ConfigDataContextRefresher中可以看到调用
ConfigDataContextRefresher又是谁调用的呢?在RefreshEventListener中可以看到
最后RefreshEvent事件是谁发布的呢?在我们引入nacos-config依赖之后,会注入一个bean,看起来有关,我们进去看看
ook,终于到头了,也就是每次nacos更新配置的时候,都会发布RefreshEvent事件,然后RefreshEventListener接收事件调用ConfigDataContextRefresher中的refresh,进一步调用RefreshScope中的refresh,然后就将缓存清空了,下次获取就是最新的了
还有一件事,刷新过程我们看看
结尾
上面就是refreshscope自动刷新的流程了,其实还有一点,nacos是如何监听配置刷新和发布事件的呢,这里面就涉及到netty了,具体来说,nacoa会有一个定时任务去查看是否由配置的更改
netty还没学,下次再进一步看看,当然还有bootstrap中如何将远程配置拉去,以及EnvironmentPostProcessorApplicationListener中获取配置也要写写,和配置拉去也有关,因为springboot2.4?之后将bootstrap取消,提出了EnvironmentPostProcessorApplicationListener,更方便的配置导入