您的位置:首页 > 编程语言 > Java开发

Spring依赖注入(DI)核心接口AutowireCandidateResolver深度分析,解析@Lazy、@Qualifier注解的原理

2020-03-05 07:16 645 查看

AutowireCandidateResolver

  • 用于确定特定的Bean定义是否符合特定的依赖项的候选者的策略接口。
public interface AutowireCandidateResolver {

/**
* 判断给定的bean定义是否允许被依赖注入(bean定义的默认值都是true)
* @param bdHolder bean名称和别名的持有者
* @param descriptor 目标方法和其参数或字段的描述符
*/
default boolean isAutowireCandidate(BeanDefinitionHolder bdHolder, DependencyDescriptor descriptor) {
return bdHolder.getBeanDefinition().isAutowireCandidate();
}

/**
* 给定的descriptor是否是必须的
* @param descriptor 目标方法参数或字段的描述符
*/
default boolean isRequired(DependencyDescriptor descriptor) {
return descriptor.isRequired();
}

/**
* 确定给定的描述符是否标注了@Qualifier
* @param descriptor 目标方法参数或字段的描述符
*/
default boolean hasQualifier(DependencyDescriptor descriptor) {
return false;
}

/**
* 返回@value标注的文本
* @param descriptor 目标方法参数或字段的描述符
*/
@Nullable
default Object getSuggestedValue(DependencyDescriptor descriptor) {
return null;
}

/**
* 如果注入点injection point需要的话,就创建一个proxy来作为最终的解决方案ContextAnnotationAutowireCandidateResolver
* @param descriptor 目标方法参数或字段的描述符
* @param beanName bean名字
*/
@Nullable
default Object getLazyResolutionProxyIfNecessary(DependencyDescriptor descriptor, @Nullable String beanName) {
return null;
}

}


SimpleAutowireCandidateResolver
是最简单的实现,适配器形式的存在,不可直接使用~

GenericTypeAwareAutowireCandidateResolver
从名字可以看出和泛型有关。Spring4.0后的泛型依赖注入主要是它来实现的,所以这个类也是Spring4.0后出现的

QualifierAnnotationAutowireCandidateResolver
这个实现类非常非常的重要,它不仅仅能处理@Qualifier、@Value,还能够处理泛型依赖注入,因此功能已经很完善了~~~ 在Spring2.5之后都使用它来处理依赖关系~

QualifierAnnotationAutowireCandidateResolver

public class QualifierAnnotationAutowireCandidateResolver extends GenericTypeAwareAutowireCandidateResolver {

//默认支持@Qualifier注解,这个可以通过addQualifierType增加我们自定义的注解
private final Set<Class<? extends Annotation>> qualifierTypes = new LinkedHashSet<>(2);
//默认支持@value注解
private Class<? extends Annotation> valueAnnotationType = Value.class;

public QualifierAnnotationAutowireCandidateResolver() {
this.qualifierTypes.add(Qualifier.class);
try {
this.qualifierTypes.add((Class<? extends Annotation>) ClassUtils.forName("javax.inject.Qualifier",
QualifierAnnotationAutowireCandidateResolver.class.getClassLoader()));
}
catch (ClassNotFoundException ex) {
// JSR-330 API not available - simply skip.
}
}
public QualifierAnnotationAutowireCandidateResolver(Class<? extends Annotation> qualifierType) {
Assert.notNull(qualifierType, "'qualifierType' must not be null");
this.qualifierTypes.add(qualifierType);
}
public QualifierAnnotationAutowireCandidateResolver(Set<Class<? extends Annotation>> qualifierTypes) {
Assert.notNull(qualifierTypes, "'qualifierTypes' must not be null");
this.qualifierTypes.addAll(qualifierTypes);
}
// 我们可以调用这个方法来自定义注解
public void addQualifierType(Class<? extends Annotation> qualifierType) {
this.qualifierTypes.add(qualifierType);
}
//@Value注解类型Spring也是允许我们改成自己的类型的
public void setValueAnnotationType(Class<? extends Annotation> valueAnnotationType) {
this.valueAnnotationType = valueAnnotationType;
}

//判断bdHolder所提供的bean定义是否为注入的候选。
@Override
public boolean isAutowireCandidate(BeanDefinitionHolder bdHolder, DependencyDescriptor descriptor) {
boolean match = super.isAutowireCandidate(bdHolder, descriptor);
// 这里发现,及时父类都匹配上了,我本来还得再次校验一把~~~
if (match) {
//qualifierTypes里的注解在此处生效(默认qualifierTypes里只有@Qualifier注解),最终可能匹配出一个或者0个出来
match = checkQualifiers(bdHolder, descriptor.getAnnotations());
if (match) {
// 这里处理的是方法入参们~~~~  只有方法有入参才需要继续解析
MethodParameter methodParam = descriptor.getMethodParameter();
if (methodParam != null) {
//获取这个入参它所属于的方法
Method method = methodParam.getMethod();
// 如果它不是任何方法或者属于方法的返回值是void  才去看它头上标注的@Qualifier注解
if (method == null || void.class == method.getReturnType()) {
match = checkQualifiers(bdHolder, methodParam.getMethodAnnotations());
}
}
}
}
return match;
}
...

protected boolean isQualifier(Class<? extends Annotation> annotationType) {
for (Class<? extends Annotation> qualifierType : this.qualifierTypes) {
if (annotationType.equals(qualifierType) || annotationType.isAnnotationPresent(qualifierType)) {
return true;
}
}
return false;
}
...

// 标注的所有注解里是否在qualifierTypes集合里,当然了默认我们可以理解是否有@Qualifier这个注解~
public boolean hasQualifier(DependencyDescriptor descriptor) {
for (Annotation ann : descriptor.getAnnotations()) {
if (isQualifier(ann.annotationType())) {
return true;
}
}
return false;
}
/**
* Determine whether the given dependency declares an autowired annotation,
* checking its required flag.
* @see Autowired#required()
*/
@Override
public boolean isRequired(DependencyDescriptor descriptor) {
if (!super.isRequired(descriptor)) {
return false;
}
Autowired autowired = descriptor.getAnnotation(Autowired.class);
return (autowired == null || autowired.required());
}

// @since 3.0   这是本类的另外一个核心 解析@Value注解
// 需要注意的是此类它不负责解析占位符啥的  只复杂把字符串返回
// 最终是交给value = evaluateBeanDefinitionString(strVal, bd);它处理~~~
@Override
@Nullable
public Object getSuggestedValue(DependencyDescriptor descriptor) {
Object value = findValue(descriptor.getAnnotations());
if (value == null) {
MethodParameter methodParam = descriptor.getMethodParameter();
if (methodParam != null) {
value = findValue(methodParam.getMethodAnnotations());
}
}
return value;
}
}

这个注解的功能已经非常强大了,Spring4.0之前都是使用的它去解决候选、依赖问题,但也不建议直接使用,因为下面这个,也就是它的子类更为强大~

ContextAnnotationAutowireCandidateResolver

官方把这个类描述为:策略接口的完整实现。它不仅仅支持上面所有描述的功能,还支持@Lazy懒处理~~~(注意此处懒处理(延迟处理),不是懒加载~)

@Lazy一般含义是懒加载,它只会作用于BeanDefinition.setLazyInit()。而此处给它增加了一个能力:延迟处理(代理处理)

public class ContextAnnotationAutowireCandidateResolver extends QualifierAnnotationAutowireCandidateResolver {

public Object getLazyResolutionProxyIfNecessary(DependencyDescriptor descriptor, @Nullable String beanName) {
// 如果isLazy=true  那就返回一个代理,否则返回null
// 相当于若标注了@Lazy注解,就会返回一个代理(当然@Lazy注解的value值不能是false)
return (isLazy(descriptor) ? buildLazyResolutionProxy(descriptor, beanName) : null);
}

// 这个比较简单,@Lazy注解标注了就行(value属性默认值是true)
// @Lazy支持标注在属性上和方法入参上~~~  这里都会解析
protected boolean isLazy(DependencyDescriptor descriptor) {
for (Annotation ann : descriptor.getAnnotations()) {
Lazy lazy = AnnotationUtils.getAnnotation(ann, Lazy.class);
if (lazy != null && lazy.value()) {
return true;
}
}
MethodParameter methodParam = descriptor.getMethodParameter();
if (methodParam != null) {
Method method = methodParam.getMethod();
if (method == null || void.class == method.getReturnType()) {
Lazy lazy = AnnotationUtils.getAnnotation(methodParam.getAnnotatedElement(), Lazy.class);
if (lazy != null && lazy.value()) {
return true;
}
}
}
return false;
}
// 核心内容,是本类的灵魂~~~ 创建代理类
protected Object buildLazyResolutionProxy(final DependencyDescriptor descriptor, final @Nullable String beanName) {
final DefaultListableBeanFactory beanFactory = (DefaultListableBeanFactory) getBeanFactory();
//TargetSource是代理对象的目标类
TargetSource ts = new TargetSource() {
@Override
public Class<?> getTargetClass() {
return descriptor.getDependencyType();
}
@Override
public boolean isStatic() {
return false;
}
@Override
public Object getTarget() {
Object target = beanFactory.doResolveDependency(descriptor, beanName, null, null);
if (target == null) {
Class<?> type = getTargetClass();
if (Map.class == type) {
return Collections.emptyMap();
}
else if (List.class == type) {
return Collections.emptyList();
}
else if (Set.class == type || Collection.class == type) {
return Collections.emptySet();
}
throw new NoSuchBeanDefinitionException(descriptor.getResolvableType(),
"Optional dependency not present for lazy injection point");
}
return target;
}
@Override
public void releaseTarget(Object target) {
}
};
ProxyFactory pf = new ProxyFactory();
pf.setTargetSource(ts);
Class<?> dependencyType = descriptor.getDependencyType();
if (dependencyType.isInterface()) {
pf.addInterface(dependencyType);
}
return pf.getProxy(beanFactory.getBeanClassLoader());
}

}

它很好的用到了TargetSource这个接口,结合动态代理来支持到了@Lazy注解。
标注有@Lazy注解完成注入的时候,最终注入只是一个此处临时生成的代理对象,只有在真正执行目标方法的时候才会去容器内拿到真是的bean实例来执行目标方法。

通过@Lazy注解能够解决很多情况下的循环依赖问题,它的基本思想是先’随便’给你创建一个代理对象先放着,等你真正执行方法的时候再实际去容器内找出目标实例执行~

我们要明白这种解决问题的思路带来的好处是能够解决很多场景下的循环依赖问题,但是要知道它每次执行目标方法的时候都会去执行TargetSource.getTarget()方法,所以需要做好缓存,避免对执行效率的影响(实测执行效率上的影响可以忽略不计)

  • 点赞
  • 收藏
  • 分享
  • 文章举报
闻道勤 发布了18 篇原创文章 · 获赞 0 · 访问量 347 私信 关注
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: