spring之我见 - 从 RefreshScope 理解IOC的世界不仅仅只有单例

从 getBean 讲起

getBean 方法是 Spring IOC 的老朋友,在里面有一个细节我们可能会忽略。

Spring IOC 针对不同的 bean 管理模式有不同的执行策略,而我们最常见熟知的就是 单例原型 ,下面在 getBean 的过程中就会首先根据不同的管理策略执行不同的逻辑方法,而我们今天要讲的是比较少走的 else 逻辑部分。

if (mbd.isSingleton()) {
				...
}
else if (mbd.isPrototype()) {
                .....
}
else {
				......
}

单例 的特点是整个生命周期只创建一次对象,而 原型 是每次getBean 都会创建新的对象。除此之外,Spring 也支持自定义的管理策略。比如这次说的 RefreshScopeRefreshScope 是一个能够在程序运行时可控的触发刷新(原理是销毁重新创建一个对象)的一种 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 ,存储了 对象名keyBeanLifecycleWrapper 对象为 valueBeanLifecycleWrapper 对象是一个包装类,包装了 对象实例 ,如果对象实例为空,则通过 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 会被销毁重建,但是怎么服务动态配置,可能还是一头雾水,这里还需要介绍一个新类 ContextRefresherContextRefresherRefreshScopeSpring 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 初始化说起了。对象都是从 BeanDefinitionbean实例 转变,@ComponentScan 扫描所有对象的时候,会判断 BeanDefinitionScopeproxyMode 属性,这个属性很重要,@RefreshScope 注解 proxyMode 默认是 TARGET_CLASS ,如果是 TARGET_CLASSioc 逻辑会创建一个新的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名称 作为 被代理的beanbeanName 。该方法最后返回的是代理类的 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 beanioc容器 里已经不是原始的类,而是一个代理对象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做业务逻辑)机制实现每次对象调用都拿取最新的对象。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值