@RefreshScope工作原理

本文主要从两个层次来分析@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的

  

  • 5
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值