前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Spring依赖注入(DI)核心接口AutowireCandidateResolver深度分析,解析@Lazy、@Qualifier注解的原理【享学Spring】

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

作者头像
YourBatman
发布2019-09-03 16:01:10
1.2K0
发布2019-09-03 16:01:10
举报
文章被收录于专栏:BAT的乌托邦BAT的乌托邦

前言

关于AutowireCandidateResolver接口,可能绝大多数小伙伴都会觉得陌生。但若谈起@Autowired@Primary@Qualifier@Value@Lazy等注解,相信没有小伙伴是不知道的吧。

备注:@Primary这个注解是在解析bean定义时候处理的,解析成为isPrimary()从而在beanFactory里得到使用

在上篇文章:【小家Spring】使用@Async异步注解导致该Bean在循环依赖时启动报BeanCurrentlyInCreationException异常的根本原因分析,以及提供解决方案 有提到过可以使用@Autowired + @Lazy的方式来解决那个循环依赖问题,效果是它最终注入进去并不是容器内的代理对象(备注:是能够正常work的),有小伙伴有私聊我咋回事? 这篇文章中你可以找到答案~

AutowireCandidateResolver

用于确定特定的**Bean定义**是否符合特定的依赖项的候选者的策略接口。

这是这个接口类的Javadoc的描述,非常绕口、晦涩有木有???

此处我也先不要急着下定义了,毕竟我们的重点不是定义本身,而是现实。相信了解了它的工作原理,定义自在我心~

代码语言:javascript
复制
// @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;
	}
}

查看它的继承树:

层次特点非常明显:每一层都只有一个类,所以毫无疑问,最后一个实现类肯定是功能最全的了。

SimpleAutowireCandidateResolver
代码语言:javascript
复制
// @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;
	}
}

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

GenericTypeAwareAutowireCandidateResolver

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

代码语言:javascript
复制
//@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<T>它是能够很好的处理好类型的~~~
		return checkGenericTypeMatch(bdHolder, descriptor);
	}
	...
}

本实现类的主要任务就是解决了泛型依赖,此类虽然为实现类,但也不建议直接使用,因为功能还不完整~

QualifierAnnotationAutowireCandidateResolver

这个实现类非常非常的重要,它继承自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注解有多强大?从原理层面去剖析为何它有如此大的“能耐“

代码语言:javascript
复制
// @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之前都是使用的它去解决候选、依赖问题,但也不建议直接使用,因为下面这个,也就是它的子类更为强大~

ContextAnnotationAutowireCandidateResolver

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

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

代码语言:javascript
复制
// @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工厂的参考代码处:

代码语言:javascript
复制
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();这一步就完成了~

最后,把这个四个哥们 从上至下 简单总结如下:

  1. SimpleAutowireCandidateResolver 相当于一个简单的适配器
  2. GenericTypeAwareAutowireCandidateResolver 判断泛型是否匹配,支持泛型依赖注入(From Spring4.0)
  3. QualifierAnnotationAutowireCandidateResolver 处理 @Qualifier@Value 注解
  4. ContextAnnotationAutowireCandidateResolver 处理 @Lazy 注解,重写了 getLazyResolutionProxyIfNecessary 方法。


使用CustomAutowireConfigurer自定义qualifier注解

这其实属于一个骚操作(不明觉厉但然并卵),炫技用,绝大部分情况下都是木有必要这么做的。但是话说回来,如果这么玩了,说不定能成为你面试的砝码,毕竟面试还是需要造飞机嘛,因此此处我写一个案例Demo供给大家参考~

如果你通过本实例助攻获取到了一个offer,不要忘记请我吃饭哦,哈哈~~~

其实通过上面实例已经知道了QualifierAnnotationAutowireCandidateResolver它是支持添加我们自定义的qualifier注解类型的,原理就是它,我们只是想办法往里添加就成,此处Spring给我们提供了CustomAutowireConfigurer来达到这一点。

由于CustomAutowireConfigurer的源代码非常的简单,因此此处就不再展示了,提示使用时注意如下两点就行:

  1. Set<?> customQualifierTypes这个set里面可以是Class类型,也可以是全类名的String类型
  2. 不管是什么类型,必须是annotation type(注解类型)

示例Demo:

代码语言:javascript
复制
// 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:如果觉得本文对你有帮助,不妨点个赞呗。当然分享到你的朋友圈让更多小伙伴看到也是被作者本人许可的~

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2019年06月26日,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 前言
  • AutowireCandidateResolver
    • SimpleAutowireCandidateResolver
      • GenericTypeAwareAutowireCandidateResolver
        • QualifierAnnotationAutowireCandidateResolver
          • ContextAnnotationAutowireCandidateResolver
            • 使用CustomAutowireConfigurer自定义qualifier注解
              • 总结
              相关产品与服务
              容器服务
              腾讯云容器服务(Tencent Kubernetes Engine, TKE)基于原生 kubernetes 提供以容器为核心的、高度可扩展的高性能容器管理服务,覆盖 Serverless、边缘计算、分布式云等多种业务部署场景,业内首创单个集群兼容多种计算节点的容器资源管理模式。同时产品作为云原生 Finops 领先布道者,主导开源项目Crane,全面助力客户实现资源优化、成本控制。
              领券
              问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档