Spring作用域 (Scope:Request,Session,Thread,Refresh) 的代理机制源码解析
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格式如下:
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就能在配置更新后动态的更新这些对象,保证配置信息能够实时的起作用。