Spring作用域 (Scope:Request,Session,Thread,Refresh) 的代理机制源码解析

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/qq_27529917/article/details/82781067

Spring有很多Scope,比如Singleton,Prototype,Request,Session,SpringCloud又新增了Thread,Refresh。默认的Scope是Singleton,Spring容器内最多的就是Singleton类型的Bean了,但其他不同类型的Scope在特定的场合也有特殊的作用。
Spring对非Singleton的Scode都使用了代理机制,这篇博客主要是讲解针对Scope的代理机制和不同Scope的Bean是如何具备不同的特性的。

Spring在解析标识@Scope注解的Bean时,会执行如下代码:

public abstract class ScopedProxyUtils {

	/** 
	 * 先定义一个ScopedProxyFactoryBean类型的BeanDefinition,其是一个FactoryBean,getObject() 方法返回的是一个Cglib生成的代理对象
	 * 通过Cglib为Scope的Bean生成代理对象,这个ProxyBean是Singleton类型的Bean,容器内其他Bean依赖得到的就是这个代理对象
	 * 这样Spring容器内的Bean就能在初始化时就依赖非Singleton类型的Bean了,而不需要每次都用BeanFactory去获取
	 */
	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.
		// 使用ScopedProxyFactoryBean包装原始的BeanClass
		RootBeanDefinition proxyDefinition = new RootBeanDefinition(ScopedProxyFactoryBean.class);
		......
		// 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());
	}

}

下面看看ScopedProxyFactoryBean内部关键代码:

public class ScopedProxyFactoryBean extends ProxyConfig implements FactoryBean<Object>, BeanFactoryAware {

	/** The TargetSource that manages scoping,关键代码,AOP动态代理时被代理对象的来源类 */
	private final SimpleBeanTargetSource scopedTargetSource = new SimpleBeanTargetSource();

	/** The cached singleton proxy */
	private Object proxy;
	
	@Override
	public void setBeanFactory(BeanFactory beanFactory) {
		if (!(beanFactory instanceof ConfigurableBeanFactory)) {
			throw new IllegalStateException("Not running in a ConfigurableBeanFactory: " + beanFactory);
		}
		ConfigurableBeanFactory cbf = (ConfigurableBeanFactory) beanFactory;
		
		// 将BeanFactory设置入TargetSource,也就是动态代理时被代理对象的来源
		this.scopedTargetSource.setBeanFactory(beanFactory);

		ProxyFactory pf = new ProxyFactory();
		pf.copyFrom(this);
		pf.setTargetSource(this.scopedTargetSource);
		......
		this.proxy = pf.getProxy(cbf.getBeanClassLoader());
	}


	@Override
	public Object getObject() {
		if (this.proxy == null) {
			throw new FactoryBeanNotInitializedException();
		}
		return this.proxy;  // 其他Bean依赖Scope Bean时得到的是被代理对象
	}

}

通过对一个@RequestScope标识的Class,实例化后生成的Bean格式如下:

RequestScope类型的Bean

targetSource代表这个对象代理的Bean;
advisorArray标识当前代理对象织入了多少个切面,当前Bean只有一个,DefaultIntroductionAdvisor;
这个切面的作用主要是将BeanName设置入Joinpoint中:

public abstract class ExposeBeanNameAdvisors {

	 /**
	 * Create a new advisor that will expose the given bean name, introducing
	 * the NamedBean interface to make the bean name accessible without forcing
	 * the target object to be aware of this Spring IoC concept.
	 * @param beanName the bean name to expose
	 */
	public static Advisor createAdvisorIntroducingNamedBean(String beanName) {
		// 对外暴露BeanName的Advisor
		return new DefaultIntroductionAdvisor(new ExposeBeanNameIntroduction(beanName));
	}

}
 

之后就直接执行被代理对象的方法。

当然我们也可以对非Singleton类型的Bean做AOP代理,这样advisorArray里面就会生成额外的切面对象,感兴趣的可以自行尝试。

AOP代理里还有一个重要组件,TargetSource,也就是被代理对象的来源:

// 动态的切面拦截器,AOP代理对象都会走进其 #intercept 方法,织入Advisor
private static class DynamicAdvisedInterceptor implements MethodInterceptor, Serializable {
	private final AdvisedSupport advised;
	
	//  获取被代理对象
	protected Object getTarget() throws Exception {
		return this.advised.getTargetSource().getTarget();
	}

}

之前在看ScopedProxyFactoryBean源码时,其内部的TargetSource实例是:SimpleBeanTargetSource,看其源码:

public class SimpleBeanTargetSource extends AbstractBeanFactoryBasedTargetSource {

	@Override
	public Object getTarget() throws Exception {
		// 每次执行代理对象的方法时,都从BeanFactory处获取指定名称的Scope Bean
		return getBeanFactory().getBean(getTargetBeanName());
	}

}

每次通过代理对象执行原始Bean的方法时,都会从BeanFactory处获取Scope Bean(注意不是Scope Bean 内部调用),接下来看看BeanFactory对非Singleton类型的Bean的获取方式代码:

public abstract class AbstractBeanFactory extends FactoryBeanRegistrySupport implements ConfigurableBeanFactory {

	protected <T> T doGetBean(final String name, final Class<T> requiredType, final Object[] args, boolean typeCheckOnly)
			throws BeansException {
				......
				// Create bean instance.
				if (mbd.isSingleton()) {
					......
				}

				else if (mbd.isPrototype()) {     // 如果是Prototype类型的Bean,每次getBean( ) 都new一个
					// It's a prototype -> create a new instance.
					Object prototypeInstance = null;
					try {
						beforePrototypeCreation(beanName);
						prototypeInstance = createBean(beanName, mbd, args);
					}
					finally {
						afterPrototypeCreation(beanName);
					}
					bean = getObjectForBeanInstance(prototypeInstance, name, beanName, mbd);
				}

				else {       // 非Singleton,Prototype类型的Bean
					String scopeName = mbd.getScope();
					final Scope scope = this.scopes.get(scopeName);    // 获取对应Scope的处理器
					if (scope == null) {
						throw new IllegalStateException("No Scope registered for scope name '" + scopeName + "'");
					}
					try {
						// 通过Scope处理器的get( ) 方法获取Bean实例,同时将new Bean的操作封装成一个回调函数,
						// 由Scope处理器来决定是否创建一个新的Bean
						Object scopedInstance = scope.get(beanName, new ObjectFactory<Object>() {
							@Override
							public Object getObject() throws BeansException {
								beforePrototypeCreation(beanName);
								try {
									return createBean(beanName, mbd, args);
								}
								finally {
									afterPrototypeCreation(beanName);
								}
							}
						});
						bean = getObjectForBeanInstance(scopedInstance, name, beanName, mbd);
					}
				}
		......
}

到了这里,结合之前的代码和讲解,应该能很容易的看明白不同非Singleton类型的Bean的代理机制和内部实现了,Scope类型有不少,我就不一一去详细的解释源码了, 接下来我针对每个Scope做个总结:

  • Request:从RequestContextHolder获取ThreadLocal类型的RequestAttributes,正常情况下返回ServletRequestAttributes类型的对象,里面封装了HttpServletRequest,HttpServletResponse等Http请求相关的对象。当Spring接收到一个Http请求时,会将请求相关对象封装成一个ServletRequestAttributes,设置到RequestContextHolder里。如果获取ServletRequestAttributes时返回null,也就是Spring还没有接收到Http请求,比如Spring容器初始化时。那么就会抛出异常。如果获取ServletRequestAttributes,但是没有获取到此Bean,那么通过ObjectFactory的回调函数new一个Bean,然后设置到ServletRequestAttributes里,实际就是设置到HttpServletRequest里。也就是说Request Scope 类型的对象不能在容器初始化时调用其方法,但是能引用到Scope的代理对象;不要异步的获取Request Scope类型的Bean;在一次Http请求中,多次获取到的相同名称的Request Scope Bean是同一个对象
  • Session:作用机制与Request类似,也是从ServletRequestAttributes里获取的,当不存在时new一个,设置到Session中,注意事项与Request也一致。
  • Thread:通过ThreadLocalScopeCache 来管理Thread类型的Bean,每个线程都持有一个ConcurrentMap<String, Object>用于存储Thread Bean,key就是BeanName,value就是Thread Bean,当不存在时通过ObjectFactory new 一个。对于线程复用情况下,记得手动清理之前的ThreadLocalCache
  • Refresh:也提供了Cache机制,当第一次get时,通过ObjectFactory创建一个,然后缓存起来,之后每次获取都先从缓存中获取,如果不存在,再通过ObjectFactory创建。同时可以通过ContextRefresher 清空所有缓存,这样下次获取的Refresh Bean就是重新生成的。当一个Bean里的持有Spring Environment的一些信息,且这些数据是可配置,动态刷新的,那么使用@RefreshScope标识Bean,同时搭配ContextRefresher就能在配置更新后动态的更新这些对象,保证配置信息能够实时的起作用。

猜你喜欢

转载自blog.csdn.net/qq_27529917/article/details/82781067
今日推荐