喜好和实际相结合的学习。外功短时间内就得到,但内功都需要长时间来修炼。以外功来挣钱养家,并在过程中修炼内功,因为两者是互相促进的。只要这样坚持,假以时日,必然会达到一个相当高的水平
关于AutowireCandidateResolver
接口,可能绝大多数小伙伴都会觉得陌生。但若谈起@Autowired
、@Primary
、@Qualifier
、@Value
、@Lazy
等注解,相信没有小伙伴是不知道的吧。
备注:
@Primary
这个注解是在解析bean定义时候处理的,解析成为isPrimary()
从而在beanFactory
里得到使用
在上篇文章:【小家Spring】使用@Async异步注解导致该Bean在循环依赖时启动报BeanCurrentlyInCreationException异常的根本原因分析,以及提供解决方案 有提到过可以使用@Autowired
+ @Lazy
的方式来解决那个循环依赖问题,效果是它最终注入进去并不是容器内的代理对象(备注:是能够正常work的),有小伙伴有私聊我咋回事? 这篇文章中你可以找到答案~
用于确定特定的Bean定义
是否符合特定的依赖项的候选者的策略接口。
这是这个接口类的Javadoc
的描述,非常绕口、晦涩有木有???
此处我也先不要急着下定义了,毕竟我们的重点不是定义本身,而是现实。相信了解了它的工作原理,定义自在我心~
// @since 2.5 伴随着@Autowired体系出现
public interface AutowireCandidateResolver {
// 判断给定的bean定义是否允许被依赖注入(bean定义的默认值都是true)
default boolean isAutowireCandidate(BeanDefinitionHolder bdHolder, DependencyDescriptor descriptor) {
return bdHolder.getBeanDefinition().isAutowireCandidate();
}
// 给定的descriptor是否是必须的~~~
// @since 5.0
default boolean isRequired(DependencyDescriptor descriptor) {
return descriptor.isRequired();
}
// QualifierAnnotationAutowireCandidateResolver对它有实现
// @since 5.1 此方法出现得非常的晚
default boolean hasQualifier(DependencyDescriptor descriptor) {
return false;
}
// 是否给一个建议值 注入的时候~~~QualifierAnnotationAutowireCandidateResolvert有实现
// @since 3.0
@Nullable
default Object getSuggestedValue(DependencyDescriptor descriptor) {
return null;
}
// 如果注入点injection point需要的话,就创建一个proxy来作为最终的解决方案ContextAnnotationAutowireCandidateResolver
// @since 4.0
@Nullable
default Object getLazyResolutionProxyIfNecessary(DependencyDescriptor descriptor, @Nullable String beanName) {
return null;
}
}
查看它的继承树:
层次特点非常明显:每一层都只有一个类,所以毫无疑问,最后一个实现类肯定是功能最全的了。
// @since 2.5
public class SimpleAutowireCandidateResolver implements AutowireCandidateResolver {
@Override
public boolean isAutowireCandidate(BeanDefinitionHolder bdHolder, DependencyDescriptor descriptor) {
return bdHolder.getBeanDefinition().isAutowireCandidate();
}
@Override
public boolean isRequired(DependencyDescriptor descriptor) {
return descriptor.isRequired();
}
@Override
@Nullable
public Object getSuggestedValue(DependencyDescriptor descriptor) {
return null;
}
@Override
@Nullable
public Object getLazyResolutionProxyIfNecessary(DependencyDescriptor descriptor, @Nullable String beanName) {
return null;
}
}
最简单的实现,适配器形式的存在,不可直接使用~
从名字可以看出和泛型有关。Spring4.0
后的泛型依赖
注入主要是它来实现的,所以这个类也是Spring4.0
后出现的
//@since 4.0 它能够根据泛型类型进行匹配~~~~ 【泛型依赖注入】
public class GenericTypeAwareAutowireCandidateResolver extends SimpleAutowireCandidateResolver implements BeanFactoryAware {
// 它能处理类型 毕竟@Autowired都是按照类型匹配的
@Nullable
private BeanFactory beanFactory;
// 是否允许被依赖~~~
// 因为bean定义里默认是true,绝大多数情况下我们不会修改它~~~
// 所以继续执行:checkGenericTypeMatch 看看泛型类型是否能够匹配上
// 若能够匹配上 这个就会被当作候选的Bean了~~~
@Override
public boolean isAutowireCandidate(BeanDefinitionHolder bdHolder, DependencyDescriptor descriptor) {
// 如果bean定义里面已经不允许了 那就不往下走了 显然我们不会这么做
if (!super.isAutowireCandidate(bdHolder, descriptor)) {
// If explicitly false, do not proceed with any other checks...
return false;
}
// 处理泛型依赖的核心方法~~~ 也是本实现类的灵魂
// 注意:这里还兼容到了工厂方法模式FactoryMethod
// 所以即使你返回BaseDao它是能够很好的处理好类型的~~~
return checkGenericTypeMatch(bdHolder, descriptor);
}
...
}
本实现类的主要任务就是解决了泛型依赖
,此类虽然为实现类,但也不建议直接使用,因为功能还不完整~
这个实现类非常非常的重要,它继承自GenericTypeAwareAutowireCandidateResolver
,所以它不仅仅能处理org.springframework.beans.factory.annotation.Qualifier
、@Value
,还能够处理泛型依赖注入,因此功能已经很完善了~~~ 在Spring2.5
之后都使用它来处理依赖关系~
Spring4.0之前它继承自
SimpleAutowireCandidateResolver
,Spring4.0之后才继承自GenericTypeAwareAutowireCandidateResolver
它不仅仅能够处理@Qualifier
注解,也能够处理通过@Value
注解解析表达式得到的suggested value
,也就是说它还实现了接口方法getSuggestedValue()
;
getSuggestedValue()
方法是Spring3.0后提供的,因为@Value
注解是Spring3.0后提供的强大注解。
关于@Value
注解的魅力,请参阅:【小家Spring】Spring中@Value注解有多强大?从原理层面去剖析为何它有如此大的“能耐“
// @since 2.5
public class QualifierAnnotationAutowireCandidateResolver extends GenericTypeAwareAutowireCandidateResolver {
// 支持的注解类型,默认支持@Qualifier和JSR-330的javax.inject.Qualifier注解
private final Set<Class<? extends Annotation>> qualifierTypes = new LinkedHashSet<>(2);
private Class<? extends Annotation> valueAnnotationType = Value.class;
// 你可可以通过构造函数,增加你自定义的注解的支持~~~
// 注意都是add 不是set
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);
}
// 后面讲的CustomAutowireConfigurer 它会调用这个方法来自定义注解
public void addQualifierType(Class<? extends Annotation> qualifierType) {
this.qualifierTypes.add(qualifierType);
}
//@Value注解类型Spring也是允许我们改成自己的类型的
public void setValueAnnotationType(Class<? extends Annotation> valueAnnotationType) {
this.valueAnnotationType = valueAnnotationType;
}
// 这个实现,比父类的实现就更加的严格了,区分度也就越高了~~~
// checkQualifiers方法是本类的核心,灵魂
// 它有两个方法getQualifiedElementAnnotation和getFactoryMethodAnnotation表名了它支持filed和方法
@Override
public boolean isAutowireCandidate(BeanDefinitionHolder bdHolder, DependencyDescriptor descriptor) {
boolean match = super.isAutowireCandidate(bdHolder, descriptor);
// 这里发现,及时父类都匹配上了,我本来还得再次校验一把~~~
if (match) {
// @Qualifier注解在此处生效 最终可能匹配出一个或者0个出来
match = checkQualifiers(bdHolder, descriptor.getAnnotations());
// 若字段上匹配上了还不行,还得看方法上的这个注解
if (match) {
// 这里处理的是方法入参们~~~~ 只有方法有入参才需要继续解析
MethodParameter methodParam = descriptor.getMethodParameter();
if (methodParam != null) {
Method method = methodParam.getMethod();
// 这个处理非常有意思: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;
}
// 这里显示的使用了Autowired 注解,我个人感觉这里是不应该的~~~~ 毕竟已经到这一步了 应该脱离@Autowired注解本身
// 当然,这里相当于是做了个fallback~~~还算可以接受吧
@Override
public boolean isRequired(DependencyDescriptor descriptor) {
if (!super.isRequired(descriptor)) {
return false;
}
Autowired autowired = descriptor.getAnnotation(Autowired.class);
return (autowired == null || autowired.required());
}
// 标注的所有注解里 是否有@Qualifier这个注解~
@Override
public boolean hasQualifier(DependencyDescriptor descriptor) {
for (Annotation ann : descriptor.getAnnotations()) {
if (isQualifier(ann.annotationType())) {
return true;
}
}
return false;
}
// @since 3.0 这是本类的另外一个核心 解析@Value注解
// 需要注意的是此类它不负责解析占位符啥的 只复杂把字符串返回
// 最终是交给value = evaluateBeanDefinitionString(strVal, bd);它处理~~~
@Override
@Nullable
public Object getSuggestedValue(DependencyDescriptor descriptor) {
// 拿到value注解(当然不一定是@Value注解 可以自定义嘛) 并且拿到它的注解属性value值~~~ 比如#{person}
Object value = findValue(descriptor.getAnnotations());
if (value == null) {
// 相当于@Value注解标注在方法入参上 也是阔仪的~~~~~
MethodParameter methodParam = descriptor.getMethodParameter();
if (methodParam != null) {
value = findValue(methodParam.getMethodAnnotations());
}
}
return value;
}
...
}
这个注解的功能已经非常强大了,Spring4.0
之前都是使用的它去解决候选、依赖问题,但也不建议直接使用,因为下面这个,也就是它的子类更为强大~
官方把这个类描述为:策略接口的完整实现
。它不仅仅支持上面所有描述的功能,还支持@Lazy
懒处理~~~(注意此处懒处理(延迟处理),不是懒加载~)
@Lazy一般含义是懒加载,它只会作用于
BeanDefinition.setLazyInit()
。而此处给它增加了一个能力:延迟处理(代理处理)
// @since 4.0 出现得挺晚,它支持到了@Lazy 是功能最全的AutowireCandidateResolver
public class ContextAnnotationAutowireCandidateResolver extends QualifierAnnotationAutowireCandidateResolver {
// 这是此类本身唯一做的事,此处精析
// 返回该 lazy proxy 表示延迟初始化,实现过程是查看在 @Autowired 注解处是否使用了 @Lazy = true 注解
@Override
@Nullable
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) {
Assert.state(getBeanFactory() instanceof DefaultListableBeanFactory,
"BeanFactory needs to be a DefaultListableBeanFactory");
// 这里毫不客气的使用了面向实现类编程,使用了DefaultListableBeanFactory.doResolveDependency()方法~~~
final DefaultListableBeanFactory beanFactory = (DefaultListableBeanFactory) getBeanFactory();
//TargetSource 是它实现懒加载的核心原因,在AOP那一章节了重点提到过这个接口,此处不再叙述
// 它有很多的著名实现如HotSwappableTargetSource、SingletonTargetSource、LazyInitTargetSource、
//SimpleBeanTargetSource、ThreadLocalTargetSource、PrototypeTargetSource等等非常多
// 此处因为只需要自己用,所以采用匿名内部类的方式实现~~~ 此处最重要是看getTarget方法,它在被使用的时候(也就是代理对象真正使用的时候执行~~~)
TargetSource ts = new TargetSource() {
@Override
public Class<?> getTargetClass() {
return descriptor.getDependencyType();
}
@Override
public boolean isStatic() {
return false;
}
// getTarget是调用代理方法的时候会调用的,所以执行每个代理方法都会执行此方法,这也是为何doResolveDependency
// 我个人认为它在效率上,是存在一定的问题的~~~所以此处建议尽量少用@Lazy~~~
//不过效率上应该还好,对比http、序列化反序列化处理,简直不值一提 所以还是无所谓 用吧
@Override
public Object getTarget() {
Object target = beanFactory.doResolveDependency(descriptor, beanName, null, null);
if (target == null) {
Class<?> type = getTargetClass();
// 对多值注入的空值的友好处理(不要用null)
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 给ts生成一个代理
// 由此可见最终生成的代理对象的目标对象其实是TargetSource,而TargetSource的目标才是我们业务的对象
ProxyFactory pf = new ProxyFactory();
pf.setTargetSource(ts);
Class<?> dependencyType = descriptor.getDependencyType();
// 如果注入的语句是这么写的private AInterface a; 那这类就是借口 值是true
// 把这个接口类型也得放进去(不然这个代理都不属于这个类型,反射set的时候岂不直接报错了吗????)
if (dependencyType.isInterface()) {
pf.addInterface(dependencyType);
}
return pf.getProxy(beanFactory.getBeanClassLoader());
}
}
它很好的用到了TargetSource
这个接口,结合动态代理来支持到了@Lazy
注解。
标注有@Lazy
注解完成注入的时候,最终注入只是一个此处临时生成的代理对象
,只有在真正执行目标方法的时候才会去容器内拿到真是的bean实例来执行目标方法。
特别注意:此代理对象非彼代理对象,这个
一定一定一定
要区分开来~
通过@Lazy
注解能够解决很多情况下的循环依赖问题,它的基本思想是先'随便'
给你创建一个代理对象先放着,等你真正执行方法的时候再实际去容器内找出目标实例执行~
我们要明白这种
解决问题的思路
带来的好处是能够解决很多场景下的循环依赖问题,但是要知道它每次执行
目标方法的时候都会去执行TargetSource.getTarget()
方法,所以需要做好缓存,避免对执行效率的影响(实测执行效率上的影响可以忽略不计)
ContextAnnotationAutowireCandidateResolver
这个处理器才是被Bean工厂最终最终使用的,因为它的功能是最全的~
回顾一下,注册进Bean工厂的参考代码处:
public abstract class AnnotationConfigUtils {
...
public static Set<BeanDefinitionHolder> registerAnnotationConfigProcessors(BeanDefinitionRegistry registry, @Nullable Object source) {
DefaultListableBeanFactory beanFactory = unwrapDefaultListableBeanFactory(registry);
if (beanFactory != null) {
// 设置默认的排序器 支持@Order等
if (!(beanFactory.getDependencyComparator() instanceof AnnotationAwareOrderComparator)) {
beanFactory.setDependencyComparator(AnnotationAwareOrderComparator.INSTANCE);
}
// 设置依赖注入的候选处理器
// 可以看到只要不是ContextAnnotationAutowireCandidateResolver类型 直接升级为最强类型
if (!(beanFactory.getAutowireCandidateResolver() instanceof ContextAnnotationAutowireCandidateResolver)) {
beanFactory.setAutowireCandidateResolver(new ContextAnnotationAutowireCandidateResolver());
}
}
... // ====下面是大家熟悉的注册默认6大后置处理器:====
// 1.ConfigurationClassPostProcessor
// 2.AutowiredAnnotationBeanPostProcessor
// 3.CommonAnnotationBeanPostProcessor
// 4.Jpa的PersistenceAnnotationProcessor(没导包就不会注册)
// 5.EventListenerMethodProcessor
// 6.DefaultEventListenerFactory
}
...
}
此段代码执行还是非常早的,在容器的刷新时的ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
这一步就完成了~
最后,把这个四个哥们 从上至下 简单总结如下:
SimpleAutowireCandidateResolver
相当于一个简单的适配器GenericTypeAwareAutowireCandidateResolver
判断泛型是否匹配,支持泛型依赖注入(From Spring4.0)QualifierAnnotationAutowireCandidateResolver
处理 @Qualifier
和 @Value
注解ContextAnnotationAutowireCandidateResolver
处理 @Lazy
注解,重写了 getLazyResolutionProxyIfNecessary
方法。CustomAutowireConfigurer
自定义qualifier注解这其实属于一个骚操作(不明觉厉但然并卵)
,炫技用,绝大部分情况下都是木有必要这么做的。但是话说回来,如果这么玩了,说不定能成为你面试的砝码,毕竟面试还是需要造飞机嘛,因此此处我写一个案例Demo供给大家参考~
如果你通过本实例助攻获取到了一个offer,不要忘记请我吃饭哦,哈哈~~~
其实通过上面实例已经知道了QualifierAnnotationAutowireCandidateResolver
它是支持添加我们自定义的qualifier注解类型
的,原理就是它,我们只是想办法往里添加就成,此处Spring给我们提供了CustomAutowireConfigurer
来达到这一点。
由于CustomAutowireConfigurer
的源代码非常的简单,因此此处就不再展示了,提示使用时注意如下两点就行:
Set> customQualifierTypes
这个set里面可以是Class类型,也可以是全类名的String类型annotation type
(注解类型)示例Demo:
// 1、定义一个自定义的注解(可以不要任何属性)
@Target({ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface MyQualifier {
String value() default "";
}
// 2、定义Bean CustomAutowireConfigurer
@Configuration
public class RootConfig {
@Bean
public CustomAutowireConfigurer customAutowireConfigurer() {
CustomAutowireConfigurer configurer = new CustomAutowireConfigurer();
configurer.setOrder(Ordered.LOWEST_PRECEDENCE);
configurer.setCustomQualifierTypes(Collections.singleton(MyQualifier.class));
return configurer;
}
}
只需要简单的两步,我们自定义的注解@MyQualifier
就有和@Qualifier
一样的效果,可以正常使用了~
附:这个小知识可以顺带了解一下:
@Required
最先执行,其次@Autowoired
,最后@Resource
。
@Required
注解需要应用到Bean的属性的setter方法上面。 (不开玩笑的说:其实这个注解很少使用~)
依赖注入作为Spring框架的核心内容之一,弄懂了它你就已经掌握了一半(夸张了)。
我经常说,衡量一个开源框架是否优秀主要是看它解决了多少非功能性问题,显然Spring框架
就这这样一款解决了非常非常多非功能性问题的流行框架~
The last:如果觉得本文对你有帮助,不妨点个赞呗。当然分享到你的朋友圈让更多小伙伴看到也是被作者本人许可的~