首先查看下@Scope注解的定义信息,其共有三个方法,分别为value、scopeName、proxyMode,其中value和scopeName利用了Spring的显性覆盖,这两个方法的作用是一样的,只不过scopeName要比value更具有语义性。重点是proxyMode方法,其默认值为ScopedProxyMode.DEFAULT。
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Scope {
@AliasFor("scopeName")
String value() default "";
@AliasFor("value")
String scopeName() default "";
ScopedProxyMode proxyMode() default ScopedProxyMode.DEFAULT;
}
ScopedProxyMode是一个枚举类,该类共定义了四个枚举值,分别为NO、DEFAULT、INTERFACE、TARGET_CLASS,其中DEFAULT和NO的作用是一样的。INTERFACES代表要使用JDK的动态代理来创建代理对象,TARGET_CLASS代表要使用CGLIB来创建代理对象。
public enum ScopedProxyMode {
DEFAULT,
NO,
INTERFACES,
TARGET_CLASS
}
解析@Scope注解的入口有很多,但它们的行为都是一致的,这里以扫描组件信息入口为例来进行分析。
在ClassPathBeanDefinitionScanner的doScan方法中,对于findCandidateComponents方法返回值进行遍历时,会首先调用ScopeMetadataResolver的resolveScopeMetadata方法,传入BeanDefinition对象。该方法会返回一个ScopeMetadata对象,然后将该对象设置到BeanDefinition中去,通过BeanDefinition的setScope方法。
接下来便是通过BeanNameGenerator的generatedBeanName方法来生成BeanName,判断BeanDefinition对象(以下简称为candidate)是否是AbstractBeanDefinition,如果判断成立,则调用postProcessBeanDefinition方法(该方法主要用来设置BeanDefinition的一些默认值),判断candidate是否是AnnotatedBeanDefinition,如果判断成立则调用AnnotationConfigUtils的processCommonDefinitionAn-notations方法(通过方法名也可以看出,该方法主要用来解析一些通用的注解)。
调用checkCandidate方法,如果该方法返回值为true(该方法用来判断当前(注意,不是层级查找)IoC容器中是否指定BeanName的BeanDefinition信息,如果包含,则进行兼容性比对)。
创建BeanDefinitionHolder实例,然后调用AnnotationConfigUtils的applyScopedProxyMode方法来根据前面解析好的ScopeMetadata对象来处理BeanDefinitionHolder,注意这里传了BeanDefinitionRegistry实例,最后调用registerBeanDefinition方法将AnnotationConfigUtils的applyScopedProxyMode方法返回值注册到BeanDefinition到IoC容器中。
private ScopeMetadataResolver scopeMetadataResolver = new AnnotationScopeMetadataResolver();
// ClassPathBeanDefinitionScanner#doScan
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) {
// 根据指定包路径扫描Bean资源并加载
Set<BeanDefinition> candidates = findCandidateComponents(basePackage);
for (BeanDefinition candidate : candidates) {
// 使用AnnotationScopeMetadataResolver的resolveScopeMeatdata方法来根据Bean中@Scope(如果有)注解创建ScopeMeatdata对象
ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(candidate);
candidate.setScope(scopeMetadata.getScopeName());
// 调用AnnotationBeanNameGenerator的generatorBeanName方法生成beanName
String beanName = this.beanNameGenerator.generateBeanName(candidate, this.registry);
// 如果BeanDefinition是AbstractBeanDefinition类型的,设置BeanDefinition的默认值
if (candidate instanceof AbstractBeanDefinition) {
postProcessBeanDefinition((AbstractBeanDefinition) candidate, beanName);
}
// 如果BeanDefinition是AnnotatedBeanDefinition类型,解析通用注解
if (candidate instanceof AnnotatedBeanDefinition) {
AnnotationConfigUtils.processCommonDefinitionAnnotations((AnnotatedBeanDefinition) candidate);
}
// 如果BeanDefinition可以兼容
if (checkCandidate(beanName, candidate)) {
BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(candidate, beanName);
// 解析Bean中的@Scope注解
definitionHolder =
AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);
beanDefinitions.add(definitionHolder);
registerBeanDefinition(definitionHolder, this.registry);
}
}
}
return beanDefinitions;
}
首先分析下ScopeMetadataResolver这个接口。该接口存在两个实现类,分别为AnnotationScopeM-etadataResolver和Jsr330ScopeMetadataResolver。
AnnotationScopeMetadataResolver用来处理Spring的@Scope注解,而Jsr330ScopeMetadataResolver则用来处理Jsr-330规范中提出的@Scope注解。ClassPathBeanDefinitionScanner默认使用的是AnnotationScopeMetadataResolver。
在AnnotationScopeMetadataResolver的resolveScopeMetadata方法中,首先创建ScopeMetadata实例,然后判断传入的BeanDefinition是否是AnnotatedBeanDefinition类型的。这里需要说明下通过Cl-assPathBeanDefinitionScanner扫描的类信息并创建的BeanDefinition都是ScannedGenericBeanDefin-ition类型的,该类型实现了AnnotatedBeanDefinition接口,因此这里的判断成立。
判断成立后首先将BeanDefinition强制转型为AnnotatedBeanDefinition,调用AnnotationConfigUtils的attributesFor方法,传入从注解元数据(AnnotationMetadata)以及@Scope注解的类型,返回AnnotationAttributes对象(以下简称attributes),如果返回的对象不为空,则设置ScopeMetadata的scopeName(通过调用atributes的getString方法),调用attributes的getEnum方法来获取@Scope注解中proxyMode方法的返回值,如果返回的proxyMode等等于ScopeProxyMode的DEFAULT,则将proxyMode重置为ScopedProxyMode.NO(这也是前面讲到的DEFAULT和NO的作用是一样的),将proxyMode设置到metadata中。
最后返回设置好的metadata。
public AnnotationScopeMetadataResolver() {
this.defaultProxyMode = ScopedProxyMode.NO;
}
// AnnotationScopeMetadataResolver#resolveScopeMetadata
public ScopeMetadata resolveScopeMetadata(BeanDefinition definition) {
ScopeMetadata metadata = new ScopeMetadata();
if (definition instanceof AnnotatedBeanDefinition) {
AnnotatedBeanDefinition annDef = (AnnotatedBeanDefinition) definition;
AnnotationAttributes attributes = AnnotationConfigUtils.attributesFor(
annDef.getMetadata(), this.scopeAnnotationType);
if (attributes != null) {
metadata.setScopeName(attributes.getString("value"));
ScopedProxyMode proxyMode = attributes.getEnum("proxyMode");
if (proxyMode == ScopedProxyMode.DEFAULT) {
proxyMode = this.defaultProxyMode;
}
metadata.setScopedProxyMode(proxyMode);
}
}
return metadata;
}
在AnnotationConfigUtils的applyScopedProxyMode方法中,通过传入的ScopeMetadata实例的getScopedProxyMode方法来获取ScopedProxyMode,如果获取到的ScopedProxyMode等于ScopedProxyMode.NO,则直接原样返回。
接下来则是判断获取到的scopedProxyMode是否等于ScopedProxyMode.TARGET_CLASS,并将比较结果赋值给proxyTargetClass,调用ScopedProxyCreator的createScopeProxy方法。
// AnnotationConfigUtils#applyScopedProxyMode
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);
}
在ScopedProxyCreator的createScopedProxy方法中直接委派给ScopedProxyUtils的createdScopedProxy方法实现。
// ScopedProxyCreator#createScopedProxy
public static BeanDefinitionHolder createScopedProxy(
BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry, boolean proxyTargetClass) {
return ScopedProxyUtils.createScopedProxy(definitionHolder, registry, proxyTargetClass);
}
在ScopedProxyUtils的createScopedProxy方法中,调用传入的BeanDefinitionHolder的getBeanName方法获取beanName并赋值给originalBeanName,调用传入的BeanDefinitionHolder的getBeanDefinition方法来获取BeanDefinition并赋值给targetBeanDefinition变量,调用getTargetBeanName方法来处理originalBeanName并赋值给targetBeanName变量(该方法的处理逻辑就是在传入的beanName前拼接上“scopedTarget.”)。
重点是接下来创建的RootBeanDefinition-proxyDefinition,传入的beanClass为ScopedProxyFactoryBean的Class,根据targetBeanDefinition以及targetBeanName来创建BeanDefinitionHolder并设置到proxyDefinition的decoratedDefinition属性中,设置targetDefinition到proxyDefinition的originatingBeanDefinition属性中,获取proxyDefinition的属性元数据(getPropertyValues方法),将其targetBea-nName的属性值设置为targetBeanName。设置…。将targetBeanDefinition的autowireCandidate以及primary设置为false(设置这两个属性的意义在后面会分析到)。
通过调用传入的BeanDefinitionRegistry的registerBeanDefinition方法,来注册targetDefinition,需重点关注的是,在注册targetDefinition时,传递的beanName为targetBeanName(即拼接上“scopedTarget.”前缀的beanName)。
最后创建BeanDefinitionHolder,指定的beanName却为originalBeanName(即未拼接上“scopedTarget.”前缀的beanName)。返回该实例。
// ScopedProxyUtils#createScopedProxy
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());
}
private static final String TARGET_NAME_PREFIX = "scopedTarget.";
public static String getTargetBeanName(String originalBeanName) {
return TARGET_NAME_PREFIX + originalBeanName;
}
把目光回到调用入口处-ClassPathBeanDefinitionScanner的doScan方法中,在该方法的最后将Annot-ationConfigUtils的applyScopedProxyMode方法返回的BeanDefinitionHolder注册到BeanDefinitionR-egistry中。
这意味着如果某个Bean添加了@Scope注解,并且将proxyMode设置为非DEFAULT、NO时,IoC容器中将会存在该Bean的两个实例,一个名为“scopedTarget.beanName”其对应的是真正的Bean实例,另一个为“beanName”其对应的是ScopedProxyFactoryBean创建出来的目标Bean的代理对象。
BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(candidate, beanName);
definitionHolder =
AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);
beanDefinitions.add(definitionHolder);
registerBeanDefinition(definitionHolder, this.registry);
代码验证
使用启动类来进行@Scope的proxyMode属性测试。
@Scope(proxyMode = ScopedProxyMode.TARGET_CLASS)
public class ScopedBeanDemo {
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
context.register(ScopedBeanDemo.class);
context.refresh();
String[] beanDefinitionNames = context.getBeanDefinitionNames();
Stream.of(beanDefinitionNames)
.forEach(
beanName -> {
Class<?> beanType = context.getType(beanName);
System.out.printf("beanName : %s -----> beanType:%s \n",beanName,beanType.getName());
});
}
}
运行结果如下:
可以发现,IoC容器中的确存在ScopedBeanDemo的两个BeanDefinition数据,一个beanName为“scopedTarget.scopedBeanName”,另一个为“scopeBeanDemo”。
相同类型的Bean,谁生效?
如上面的分析结果,IoC容器中存在两个相同类型的Bean,那么当我们通过BeanFactory的getBean(Class)方法来查找时,是会抛出异常呢?还是正常返回呢?如果正常返回,那么该返回那个呢?
下面我们先来编写代码测试下:
import org.springframework.aop.scope.ScopedProxyUtils;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Scope;
import org.springframework.context.annotation.ScopedProxyMode;
import java.beans.Introspector;
import java.lang.reflect.Field;
@Scope(proxyMode = ScopedProxyMode.TARGET_CLASS)
public class ScopedBeanDemo {
public static void main(String[] args) throws Exception {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
context.register(ScopedBeanDemo.class);
context.refresh();
// 根据 ScopedBeanDemo 类型来查找
ScopedBeanDemo byType = context.getBean(ScopedBeanDemo.class);
// 根据 ScopedBeanDemo 在IoC容器中的BeanName来进行查找 -> 其底层也是通过 Java Beans 中的
// Introspector#decapitalize方法来生成BeanName
ScopedBeanDemo byName =
(ScopedBeanDemo) context.getBean(Introspector.decapitalize("ScopedBeanDemo"));
// 在 ScopedBeanDemo 在IoC容器中的BeanName 前面拼接上 ScopedProxyUtils#TARGET_NAME_PREFIX 字段的值
Field field = ScopedProxyUtils.class.getDeclaredField("TARGET_NAME_PREFIX");
field.setAccessible(true);
Object value = field.get(null);
ScopedBeanDemo byScopedName =
(ScopedBeanDemo)
context.getBean(value + Introspector.decapitalize("ScopedBeanDemo"));
System.out.println("根据ScopedBeanDemo类型查找到的:" + byType.getClass());
System.out.println("根据ScopedBeanDemo名称查找到的:" + byName.getClass());
System.out.println("根据scopedTarget.ScopedBeanDemo名称查找到的:" + byScopedName.getClass());
// 关闭Spring 应用上下文
context.close();
}
}
运行结果:
可以发现无论是根据类型还是根据beanName来进行IoC容器返回的始终是是代理后的对象。只有按其拼接的规则来拼接beanName后(在beanName前拼接上“scopedTarget.”前缀),再使用BeanFactory的getBean(String)方法来查找才会返回原始对象。
按照beanName来进行查找,IoC容器会返回代理对象,这点可以理解,因为在ScopedProxyUtils的createScopedProxy方法偷梁换柱,将原始的beanName对应的BeanDefinition替换为代理BeanDefinition,所以查找根据原始beanName查找出来的bean为代理Bean就不奇怪了,那么为什么根据类型来查找返回的依然是代理Bean呢?
这里先说下结论:是因为前面在ScopedProxyUtils的createScopedProxy方法中将原始的BeanDefinit-ion(targetDefinition)的autowireCandidate设置为false导致的。
// The target bean should be ignored in favor of the scoped proxy.
targetDefinition.setAutowireCandidate(false);
targetDefinition.setPrimary(false);
下面我们来分析下BeanFactory的getBean(Class)方法。AsbtractApplicationContext实现了该方法,在该方法中首先来对BeanFactory实现类实例的存活状态进行校验。之后就是调用BeanFactory实现类实例的getBean方法,传入要获取的Class。
// AbstractApplicationContext#getBean(java.lang.Class<T>)
public <T> T getBean(Class<T> requiredType) throws BeansException {
assertBeanFactoryActive();
return getBeanFactory().getBean(requiredType);
}
在DefaultListableBeanFactory实现的getBean方法中,调用resolveBean方法来根据类型获取,如果该方法的返回值为null,抛出NoSuchBeanDefinitionException异常。可以看出的是resolveBean方法并不会主动抛出异常,而是getBean方法抛出的异常,这一点很重要,因为包括BeanFactory提供的安全查找Bean的getBeanProvider方法底层也是基于该方法进行实现,这里就不再展开分析了。
// DefaultListableBeanFactory#getBean(java.lang.Class<T>, java.lang.Object...)
public <T> T getBean(Class<T> requiredType, @Nullable Object... args) throws BeansException {
Assert.notNull(requiredType, "Required type must not be null");
Object resolved = resolveBean(ResolvableType.forRawClass(requiredType), args, false);
if (resolved == null) {
throw new NoSuchBeanDefinitionException(requiredType);
}
return (T) resolved;
}
在resolveBean方法中,调用resolveNamedBean方法来进行查找,如果该方法返回值不为null,则直接返回,否则获取当前IoC容器的父容器(如果有),层层查找。
// DefaultListableBeanFactory#resolveBean
private <T> T resolveBean(ResolvableType requiredType, @Nullable Object[] args, boolean nonUniqueAsNull) {
NamedBeanHolder<T> namedBean = resolveNamedBean(requiredType, args, nonUniqueAsNull);
if (namedBean != null) {
return namedBean.getBeanInstance();
}
BeanFactory parent = getParentBeanFactory();
if (parent instanceof DefaultListableBeanFactory) {
return ((DefaultListableBeanFactory) parent).resolveBean(requiredType, args, nonUniqueAsNull);
}
else if (parent != null) {
ObjectProvider<T> parentProvider = parent.getBeanProvider(requiredType);
if (args != null) {
return parentProvider.getObject(args);
}
else {
return (nonUniqueAsNull ? parentProvider.getIfUnique() : parentProvider.getIfAvailable());
}
}
return null;
}
在resolveNamedBean方法中,首先根据getNamesForType方法来获取指定类型的所有beanName,该方法的返回值是一个数组。结合前面的代码可以得出这里获取到的candidateNames有两个,分别为:scopedTarget.scopedBeanDemo和scopedBeanDemo。
因此会进入第一个判断即candidateNames的长度大于1,遍历candidateNames集合,对于遍历到的每一个beanName,通过containsBeanDefinition方法来判断当前IoC容器中是否包含指定beanName的BeanDefinition数据(注意这里是对结果进行取反,因此判断失败),第二个判断是根据beanName获取到对应的BeanDefinition实例后,然后调用其isAutowireCandidate方法,注意前面我们已经分析过在ScopedProxyUtils的createScopedProxy方法将targetDefinition的autowireCandidate属性设置为false,因此真正的BeanDefinition是不会被作为候选的BeanDefinition,反而是代理BeanDefinition会作为候选的BeanDefinition。
next,判断candidateNames数组的长度是否等等于1,如果判断成立,则调用getBean方法来根据beanName获取,并将方法返回结果构建为NamedBeanHolder返回。
// DefaultListableBeanFactory#resolveNamedBean
private <T> NamedBeanHolder<T> resolveNamedBean(
ResolvableType requiredType, @Nullable Object[] args, boolean nonUniqueAsNull) throws BeansException {
Assert.notNull(requiredType, "Required type must not be null");
// getBean(ScopedBeanDemo.class) -> candidateNames 中存在两个beanName,分别为
// “scopedTarget.scopedBeanDemo”以及“scopedBeanDemo”。
String[] candidateNames = getBeanNamesForType(requiredType);
if (candidateNames.length > 1) {
List<String> autowireCandidates = new ArrayList<>(candidateNames.length);
for (String beanName : candidateNames) {
// 由于真正的ScopedBeanDemo的BeanDefinition的autowireCandidate属性被设置为false,
// 因此这里被保存到autowireCandidates集合中的是代理Bean的BeanDefinition
if (!containsBeanDefinition(beanName) || getBeanDefinition(beanName).isAutowireCandidate()) {
autowireCandidates.add(beanName);
}
}
if (!autowireCandidates.isEmpty()) {
candidateNames = StringUtils.toStringArray(autowireCandidates);
}
}
// 如果candidateNames的长度为1,通过getBean方法来触发初始化或者从缓存中获取并构建为
// NamedBeanHolder 对象返回。
if (candidateNames.length == 1) {
String beanName = candidateNames[0];
return new NamedBeanHolder<>(beanName, (T) getBean(beanName, requiredType.toClass(), args));
} else if (candidateNames.length > 1) {
Map<String, Object> candidates = new LinkedHashMap<>(candidateNames.length);
for (String beanName : candidateNames) {
if (containsSingleton(beanName) && args == null) {
Object beanInstance = getBean(beanName);
candidates.put(beanName, (beanInstance instanceof NullBean ? null : beanInstance));
} else {
candidates.put(beanName, getType(beanName));
}
}
String candidateName = determinePrimaryCandidate(candidates, requiredType.toClass());
if (candidateName == null) {
candidateName = determineHighestPriorityCandidate(candidates, requiredType.toClass());
}
if (candidateName != null) {
Object beanInstance = candidates.get(candidateName);
if (beanInstance == null || beanInstance instanceof Class) {
beanInstance = getBean(candidateName, requiredType.toClass(), args);
}
return new NamedBeanHolder<>(candidateName, (T) beanInstance);
}
if (!nonUniqueAsNull) {
throw new NoUniqueBeanDefinitionException(requiredType, candidates.keySet());
}
}
return null;
}
总结
@Scope注解中的proxyMode方法值指示了IoC容器要不要为Bean创建代理,如何创建代理,是使用JDK的动态代理还是使用CGLIB?
我们通过源码也了解到ScopedProxyMode的DEFAULT和NO作用是一样的,如果配置为INTERFACES或TARGET_CLASS,在ScopedProxyUtils的createScopedProxy方法中将会为目标Bean创建一个ScopedProxyFactoryBean的BeanDefinition,并使用目标Bean的beanName来注册这个BeanDefinition,将目标Bean的beanName拼接上“SscopedTarget.”前缀来注册目标Bean的BeanDefinition。
同时将目标BeanDefinition的autowireCandidate属性设置为false,以此来确保IoC容器在查找该类型的单个Bean时(getBean方法)不会返回原始Bean实例,而是返回经过代理后的Bean实例。