本文主要从两个层次来分析@RefreshScope;
1.加了@RefreshScope注解的bean是如何注入到IOC容器中的;
2.触发@RefreshScope后IOC容器是如何工作的。
注:本文不讨论@RefreshScope是如何触发的,springCloud只是提供了一个规范,每种框架的触发原理机制不同,说实话我也不是很明白,等弄懂了再来写
一、@RefreshScope是如何完成bean的实列化的
首先我们需要了解,我们在不声明bean的作用域时,bean默认是单列的,原因是因为在bean的merge也就是合并时,会自己声明其为单列,当我们加上注解@RefreshScope是,其scope就是refresh,不是singleton
// Set default singleton scope, if not configured before.
if (!StringUtils.hasLength(mbd.getScope())) {
mbd.setScope(SCOPE_SINGLETON);
}
一般的bean都是通过ClassPathBeanDefinitionScanner#doScan(String... basePackages)先扫描出来加载成beanDefinition,如图所示当加载到加了@RefreshScope的bean时
这里有行致关重要的代码
AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);
static BeanDefinitionHolder applyScopedProxyMode(
ScopeMetadata metadata, BeanDefinitionHolder definition, BeanDefinitionRegistry registry) {
ScopedProxyMode scopedProxyMode = metadata.getScopedProxyMode();
if (scopedProxyMode.equals(ScopedProxyMode.NO)) {
return definition;
}
boolean proxyTargetClass = scopedProxyMode.equals(ScopedProxyMode.TARGET_CLASS);
return ScopedProxyCreator.createScopedProxy(definition, registry, proxyTargetClass);
}
可以看到当普通bean没有加上@RefreshScope注解时,代码会直接进入if然后return不会执行后面的代码,那么我们需要看一下 ScopedProxyCreator.createScopedProxy(definition, registry, proxyTargetClass);的代码逻辑,这时代码会掉到org.springframework.aop.scope.ScopedProxyUtils#createScopedProxy,这里的代码至关重要
String originalBeanName = definition.getBeanName();
BeanDefinition targetDefinition = definition.getBeanDefinition();
String targetBeanName = getTargetBeanName(originalBeanName);
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);
} else {
proxyDefinition.getPropertyValues().add("proxyTargetClass", Boolean.FALSE);
}
proxyDefinition.setAutowireCandidate(targetDefinition.isAutowireCandidate());
proxyDefinition.setPrimary(targetDefinition.isPrimary());
if (targetDefinition instanceof AbstractBeanDefinition) {
proxyDefinition.copyQualifiersFrom((AbstractBeanDefinition)targetDefinition);
}
targetDefinition.setAutowireCandidate(false);
targetDefinition.setPrimary(false);
registry.registerBeanDefinition(targetBeanName, targetDefinition);
return new BeanDefinitionHolder(proxyDefinition, originalBeanName, definition.getAliases());
String targetBeanName = getTargetBeanName(originalBeanName);
//这行代码会生成一个新的beanNamme为scopedTarget.***,
此时 创建了一个新的代理bd,
RootBeanDefinition proxyDefinition = new RootBeanDefinition(ScopedProxyFactoryBean.class);但是此时bd的类型为ScopedProxyFactoryBean。注意这个proxyDefinition从头到尾没有设置它的作用域scope,也就是说这个bd是一个单列的。最后看这个
registry.registerBeanDefinition(targetBeanName, targetDefinition);这里将targetDefinition也就是原来的bd,但是名字是新生成的scopedTarget.***不是原来的名字,最后将新生成的bd--proxyDefinition(单列) 返回。返回后又会调用
registerBeanDefinition(definitionHolder, this.registry);此时注入容器的bd用的是原来的beanName,但是bd对象换成了proxyDefinition。这里有点绕需要仔细体会。
将beanDefinition注入到容器后,接着就是bean的实列化过程,但在实列化前,@Refrescope还会干一些事。
由于整合了springcloud,springboot会自动注入一个类RefreshScope,注意这个是类不是注解,这个类干了什么事呢,首先看这个类的关系图
这个类实现了BeanDefinitionRegistryPostProcessor,那么会在bean实列化前执行一个方法
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
for (String name : registry.getBeanDefinitionNames()) {
BeanDefinition definition = registry.getBeanDefinition(name);
if (definition instanceof RootBeanDefinition) {
RootBeanDefinition root = (RootBeanDefinition) definition;
if (root.getDecoratedDefinition() != null && root.hasBeanClass()
&& root.getBeanClass() == ScopedProxyFactoryBean.class) {
if (getName().equals(root.getDecoratedDefinition().getBeanDefinition().getScope())) {
root.setBeanClass(LockedScopedProxyFactoryBean.class);
root.getConstructorArgumentValues().addGenericArgumentValue(this);
// surprising that a scoped proxy bean definition is not already
// marked as synthetic?
root.setSynthetic(true);
}
}
}
}
}
root.setBeanClass(LockedScopedProxyFactoryBean.class);可以看到这里又替换了bd的beanClass,这个被替换的bd就是我们前面生成的proxyDefinition。这个类后面再将看名字proxy肯定是和代理相关得。
在我们实列化proxyDefinition这个bd时实际上是实列化LockedScopedProxyFactoryBean这个类,这个类实现了两个接口ScopedProxyFactoryBean,MethodInterceptor。MethodInterceptor是和切面相关的。又因为ScopedProxyFactoryBean实现了BeanFactoryAware接口,所有bean的初始化过程中回调会setBeanFactory()方法,这里很简单就是创建了一个代理对象,子类通过如下代码添加切面,也就是会执行this的invoke方法
Advised advised = (Advised) proxy;
advised.addAdvice(0, this);
还有就是ScopedProxyFactoryBean实现了FactoryBean接口,意思当我们通过byType获取bean时,实际上返回的就是我们的代理对象,这个代理对象会继承我们的原始类型
public Object getObject() {
if (this.proxy == null) {
throw new FactoryBeanNotInitializedException();
} else {
return this.proxy;
}
}
//这个就是原始类型
public Class<?> getObjectType() {
return this.proxy != null ? this.proxy.getClass() : this.scopedTargetSource.getTargetClass();
}
那么我们看一下invoke
@Override
public Object invoke(MethodInvocation invocation) throws Throwable {
Method method = invocation.getMethod();
if (AopUtils.isEqualsMethod(method) || AopUtils.isToStringMethod(method)
|| AopUtils.isHashCodeMethod(method) || isScopedObjectGetTargetObject(method)) {
return invocation.proceed();
}
Object proxy = getObject();
ReadWriteLock readWriteLock = this.scope.getLock(this.targetBeanName);
if (readWriteLock == null) {
if (logger.isDebugEnabled()) {
logger.debug("For bean with name [" + this.targetBeanName
+ "] there is no read write lock. Will create a new one to avoid NPE");
}
readWriteLock = new ReentrantReadWriteLock();
}
Lock lock = readWriteLock.readLock();
lock.lock();
try {
if (proxy instanceof Advised) {
Advised advised = (Advised) proxy;
ReflectionUtils.makeAccessible(method);
return ReflectionUtils.invokeMethod(method, advised.getTargetSource().getTarget(),
invocation.getArguments());
}
return invocation.proceed();
}
// see gh-349. Throw the original exception rather than the
// UndeclaredThrowableException
catch (UndeclaredThrowableException e) {
throw e.getUndeclaredThrowable();
}
finally {
lock.unlock();
}
}
的真实逻辑。
ReflectionUtils.invokeMethod(method, advised.getTargetSource().getTarget(),
invocation.getArguments());
这行代码就是反射调用目标对象的方法,那么目标对象是如何获取的
这个targetSource就是父类的
private final SimpleBeanTargetSource scopedTargetSource = new SimpleBeanTargetSource();这个getTarget就是从
public Object getTarget() throws Exception {
return this.getBeanFactory().getBean(this.getTargetBeanName());
}
原来是从bean容器获取的这个getTargetBeanName就是加了scopedTarget.***的名字是原始的beanDefinition,但这个的scope是refresh。当作用域是refresh时spring是如何实列化的呢?主要是在这段代码中org.springframework.beans.factory.support.AbstractBeanFactory#doGetBean
else {
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);
}
这里看到首先是通过scopes.get(),其实这里也能猜到 这个获取到的scope类型一定是RefreshScope,那么他是在那里注册的呢,其实这种代码 只需要找到在那里put的就可以顺藤摸瓜找到下面去,
因为GenericScope是一个beanFactoryPostprocess,在bean实列化前就会执行postProcessBeanFactory将自己注册放scopes中去
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
this.beanFactory = beanFactory;
beanFactory.registerScope(this.name, this);
setSerializationId(beanFactory);
}
public void registerScope(String scopeName, Scope scope) {
Assert.notNull(scopeName, "Scope identifier must not be null");
Assert.notNull(scope, "Scope must not be null");
if (SCOPE_SINGLETON.equals(scopeName) || SCOPE_PROTOTYPE.equals(scopeName)) {
throw new IllegalArgumentException("Cannot replace existing scopes 'singleton' and 'prototype'");
}
Scope previous = this.scopes.put(scopeName, scope);
if (previous != null && previous != scope) {
if (logger.isDebugEnabled()) {
logger.debug("Replacing scope '" + scopeName + "' from [" + previous + "] to [" + scope + "]");
}
}
else {
if (logger.isTraceEnabled()) {
logger.trace("Registering scope '" + scopeName + "' with implementation [" + scope + "]");
}
}
}
接着看获取到scope后的处理流程
Object scopedInstance = scope.get(beanName, () -> {
beforePrototypeCreation(beanName);
try {
return createBean(beanName, mbd, args);
}
finally {
afterPrototypeCreation(beanName);
}
});
###get方法
public Object get(String name, ObjectFactory<?> objectFactory) {
BeanLifecycleWrapper value = this.cache.put(name, new BeanLifecycleWrapper(name, objectFactory));
this.locks.putIfAbsent(name, new ReentrantReadWriteLock());
try {
return value.getBean();
}
catch (RuntimeException e) {
this.errors.put(name, e);
throw e;
}
}
##value.getBean()
public Object getBean() {
if (this.bean == null) {
synchronized (this.name) {
if (this.bean == null) {
this.bean = this.objectFactory.getObject();
}
}
}
return this.bean;
}
可以看到这里很明显是先从缓存中获取,若获取不到则调用objectFactory.getObject()实际上就是调用createBean(),创建一个新的bean。这就是scope的流程。
我们总结一下上面的流程,
1.如果用@RefreshScope修饰的单列bean,会往beanFactory中注入2个beanDefinition;第一个是普通的bd,class对象就是改bean本来的class,但这个bd的scope是refresh,beanName=scopedTarget.***;第二个bd是的class是LockedScopedProxyFactoryBean.class这是一个factoryBean,最终通过getObject返回的是一个代理对象,scope为单列singleton,beanName=***,当我们通过依赖注入或者getBean时返回的是一个单列的代理对象。
2.通过代理对象调用目标方法时执行的是
ReflectionUtils.invokeMethod(method, advised.getTargetSource().getTarget(),
invocation.getArguments());
执行流程:
当我们想修改配置文件后,我们只需要清除缓存中的bean,这样就会重新调用createBean()生成一个新bean,@RefreshSocpe触发后也是这种工作原理,请看调用流程
最终将cache全部清除,清除后会触发bean的destory逻辑。最终发布事件RefreshScopeRefreshedEvent。
这里需要注意一个点当我们加了@Scheduled注解bean被销毁了,并不会立马重建,只有当真正调用目标方法时,此时从缓存中获取不到时,才会从容器中去获取getBean(),此时bean才会真正创建。所以解决这个问题时我们可以通过监听RefreshScopeRefreshedEvent事件,当事件触发时,在调用getBean(name),需要注意这个name必须是加了scopedTarget.前缀的name,或者调用一下目标方法也能触发
。加了scopedTarget.前缀的bean是不允许进行属性注入的
targetDefinition.setAutowireCandidate(false);
targetDefinition.setPrimary(false);
所以当通过getBean(Class)是获取不到目标类型的bean的