概述
Spring为生命周期长的bean调用生命周期短的bean提供了三种解决方案。第一种是使用默认命名空间(beans)的<look-up>标签;第二种是使用context命名空间的<context:component-scan>解析@Scope注解;第三种是使用AOP命名空间的<aop:scoped-proxy>标签装饰生命周期短的bean。<aop:scoped-proxy>的使用如下
<bean id="user" class="com.chyohn.User" scope="session">
<aop:scoped-proxy/>
</bean>
<bean id="userManager" class="com.chyohn.UserManager" scope="singleton">
<property name="targetUser" ref="user"/>
</bean>
<aop:scoped-proxy>是AOP命名空间的三大标签之一,它的作用是对生命周期短的bean提供装饰,使其能被生命周期长的bean正确调用,下面我们来探讨Spring是如何解析<aop:scoped-proxy>标签的。
解析<aop:scoped-proxy>标签
<aop:scoped-proxy>标签属于spring<bean>标签的装饰标签,它的装饰器是ScopedProxyBeanDefinitionDecorator,它直接继承了BeanDefinitionDecorator接口的decorate方法,这个方法的源码如下。
@Override
public BeanDefinitionHolder decorate(Node node, BeanDefinitionHolder definition, ParserContext parserContext) {
boolean proxyTargetClass = true;
if (node instanceof Element) {
Element ele = (Element) node;
if (ele.hasAttribute("proxy-target-class")) {
// 设置是用CGLIB还是JDK动态代理,true使用前者,false使用后者。默认为true,即使用CGLIB
proxyTargetClass = Boolean.valueOf(ele.getAttribute("proxy-target-class"));
}
}
// 调用作用域代理工具类ScopedProxyUtils创建作用域代理
// 注册被装饰的BeanDefinition,并返回代理BeanDefintion
BeanDefinitionHolder holder =
ScopedProxyUtils.createScopedProxy(definition, parserContext.getRegistry(), proxyTargetClass);
String targetBeanName = ScopedProxyUtils.getTargetBeanName(definition.getBeanName());
parserContext.getReaderContext().fireComponentRegistered(
new BeanComponentDefinition(definition.getBeanDefinition(), targetBeanName));
return holder;
}
继续看作用域代理工具类ScopedProxyUtils的createScopedProxy方法源码如下。
public static BeanDefinitionHolder createScopedProxy(BeanDefinitionHolder definition,
BeanDefinitionRegistry registry, boolean proxyTargetClass) {
String originalBeanName = definition.getBeanName();
BeanDefinition targetDefinition = definition.getBeanDefinition();
// targetBeanName格式为scopedTarget. + originalBeanName
String targetBeanName = getTargetBeanName(originalBeanName);
// Create a scoped proxy definition for the original bean name,
// "hiding" the target bean in an internal target definition.
// 创建一个ScopedProxyFactoryBean类对应BeanDefinition对象
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);
}
// 根据代理目标BeanDefinition设置是否可以为自动注入的候选bean
proxyDefinition.setAutowireCandidate(targetDefinition.isAutowireCandidate());
proxyDefinition.setPrimary(targetDefinition.isPrimary());
if (targetDefinition instanceof AbstractBeanDefinition) {
proxyDefinition.copyQualifiersFrom((AbstractBeanDefinition) targetDefinition);
}
// 隐藏被代理的bean
targetDefinition.setAutowireCandidate(false);
targetDefinition.setPrimary(false);
// 注册被代理的bean的BeanDefinition对象
registry.registerBeanDefinition(targetBeanName, targetDefinition);
// 返回代理bean的BeanDefinitionHolder对象
return new BeanDefinitionHolder(proxyDefinition, originalBeanName, definition.getAliases());
}
createScopedProxy方法向容器中创建了ScopedProxyFactoryBean对象用于代理bean。
总结
<aop:scoped-proxy>的作用域代理方式和@Scope注解的代理方式一样,都是通过ScopedProxyFactoryBean对象来代理的。两者的不同在于一个是基于XML配置,一个是基于注解配置的。
关于@Scope注解的解析见解析context命名空间之component-scan标签中关于解析component-scan标签一节。