Spring-Framework 源码阅读之@Autowired和AutowiredAnnotationBeanPostProcessor
今天接下去讲我们的内容,上次的解析了AnnotationBeanUtils这个类的运用和源码。今天主要关注的是Autowired和 AutowiredAnnotationBeanPostProcessor这2个类。首先我们来看一下Autowired标签的定义。
@Target({ElementType.CONSTRUCTOR, ElementType.METHOD, ElementType.PARAMETER, ElementType.FIELD, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Autowired { /**
*
* <p>Defaults to {@code true}.
*/
boolean required() default true;
}
从标签的定义和上面的注释可以知道,该标签可以用于构造函数、方法、参数、标签上。为了使这个标签生效,我们需要一个解析这个标签的类,Spring 为我们提供解析的类就是AutowiredAnnotationBeanPostProcessor,这是个BeanPostProcessor类。关于BeanPostProcessor大家可以查阅相关资料,之后等我读到这个接口的时候,会具体的和大家一起探讨。这里我们以参数注入为例,来分析了一下,这个类到底做了哪些事情。这个参数注入主要是它的一个内部类AutowiredFieldElement来处理。
而这个类的inject()方法被调用是在AutowiredAnnotationBeanPostProcessor的postProcessPropertyValues方法上。
@Override
public PropertyValues postProcessPropertyValues(
PropertyValues pvs, PropertyDescriptor[] pds, Object bean, String beanName) throws BeanCreationException { InjectionMetadata metadata = findAutowiringMetadata(beanName, bean.getClass(), pvs);
try {
metadata.inject(bean, beanName, pvs);
}
catch (BeanCreationException ex) {
throw ex;
}
catch (Throwable ex) {
throw new BeanCreationException(beanName, "Injection of autowired dependencies failed", ex);
}
return pvs;
}
从这里我们知道 findAutowiringMetadata()方法这里获取注入元数据信息,然后调用InjectionMetadata.inject()的方法。在以参数注入就是调用AutowiredFieldElement.inject()方法。
这些类型的关系,将在之后解释。让我们具体来看一下 AutowiredFieldElement.inject()方法。
@Override
protected void inject(Object bean, String beanName, PropertyValues pvs) throws Throwable {
Field field = (Field) this.member;
Object value;
if (this.cached) {
value = resolvedCachedArgument(beanName, this.cachedFieldValue);
}
else {
DependencyDescriptor desc = new DependencyDescriptor(field, this.required);
desc.setContainingClass(bean.getClass());
Set<String> autowiredBeanNames = new LinkedHashSet<String>(1);
TypeConverter typeConverter = beanFactory.getTypeConverter();
try {
//通过BeanFactory的resolveDependency()方法解决依赖的值。也就是这个参数需要注入的值
value = beanFactory.resolveDependency(desc, beanName, autowiredBeanNames, typeConverter);
}
catch (BeansException ex) {
throw new UnsatisfiedDependencyException(null, beanName, new InjectionPoint(field), ex);
}
synchronized (this) {
if (!this.cached) {
if (value != null || this.required) {
this.cachedFieldValue = desc;
registerDependentBeans(beanName, autowiredBeanNames);
if (autowiredBeanNames.size() == 1) {
String autowiredBeanName = autowiredBeanNames.iterator().next();
if (beanFactory.containsBean(autowiredBeanName)) {
if (beanFactory.isTypeMatch(autowiredBeanName, field.getType())) {
this.cachedFieldValue = new ShortcutDependencyDescriptor(
desc, autowiredBeanName, field.getType());
}
}
}
}
else {
this.cachedFieldValue = null;
}
this.cached = true;
}
}
}
if (value != null) {//这里就是通过反射设置参数可见性,然后把值设置到该参数上。
ReflectionUtils.makeAccessible(field);
field.set(bean, value);
}
}
接下来,我们需要知道,AutowiredAnnotationBeanPostProcessor的postProcessPropertyValues()方法什么时候被调用。在这个函数上,我们关注2个参数,一个是PropertyDescriptor
类型的数组,一个beanName。beanName就是我们实例化的对象,其中PropertyDescriptor就是描述这个名为beanName的Bean的参数内容。在AbstractAutowireCapableBeanFactory的
doCreateBean()方法里,有一个populateBean()里面调用了postProcessPropertyValues()。从方法名称上,我们知道创建Bean(doCreateBean)>填充Bean(populateBean)。在这个populateBean()里面有如一下这么一段代码:
if (hasInstAwareBpps || needsDepCheck) {
PropertyDescriptor[] filteredPds = filterPropertyDescriptorsForDependencyCheck(bw, mbd.allowCaching);
if (hasInstAwareBpps) {
for (BeanPostProcessor bp : getBeanPostProcessors()) {
if (bp instanceof InstantiationAwareBeanPostProcessor) {
InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp;
pvs = ibp.postProcessPropertyValues(pvs, filteredPds, bw.getWrappedInstance(), beanName);
if (pvs == null) {
return;
}
}
}
}
if (needsDepCheck) {
checkDependencies(beanName, mbd, filteredPds, pvs);
}
}
在for循环里面,判断每一个BeanPostProcessor ,看这个BeanPostProcessor 是否实现InstantiationAwareBeanPostProcessor这个接口,刚好,我们知道AutowiredAnnotationBeanPostProcessors实现了这个接口,重载了InstantiationAwareBeanPostProcessor的postProcessPropertyValues()方法。到此为止,我们知道Bean的Autowired的注入实现。那么我们在回到postProcessPropertyValues的findAutowiringMetadata(),从上面已经点出这个方法是找出注入元数据信息。那么它是如何查找的,看如下代码:
private InjectionMetadata findAutowiringMetadata(String beanName, Class<?> clazz, PropertyValues pvs) {
// Fall back to class name as cache key, for backwards compatibility with custom callers.
String cacheKey = (StringUtils.hasLength(beanName) ? beanName : clazz.getName());
// Quick check on the concurrent map first, with minimal locking.
InjectionMetadata metadata = this.injectionMetadataCache.get(cacheKey);
if (InjectionMetadata.needsRefresh(metadata, clazz)) {
synchronized (this.injectionMetadataCache) {
metadata = this.injectionMetadataCache.get(cacheKey);
if (InjectionMetadata.needsRefresh(metadata, clazz)) {
if (metadata != null) {
metadata.clear(pvs);
}
try {
metadata = buildAutowiringMetadata(clazz);
this.injectionMetadataCache.put(cacheKey, metadata);
}
catch (NoClassDefFoundError err) {
throw new IllegalStateException("Failed to introspect bean class [" + clazz.getName() +
"] for autowiring metadata: could not find class that it depends on", err);
}
}
}
}
return metadata;
}
其中的关键代码就是InjectionMetadata metadata = this.injectionMetadataCache.get(cacheKey);这句话。从这里我们知道他是从this.injectionMetadataCache,这是一个Map,key为beanName,value为注入元数据InjectionMetadata。即直接从这个Map中获取,那么接下来我们就要知道这个注入信息是什么时候放入到这个缓存Map上的。从上面代码上,我们看到
this.injectionMetadataCache.put(cacheKey, metadata);这段代码,这代码就是把注入信息放到this.injectionMetadataCache上。那么,从这里我们可以猜测,findAutowiringMetadata()这个方法肯定被调用了多次,在Bean实例化过程中。从查看代码,印证了我的想法。
再回到AutowiredAnnotationBeanPostProcessor的BeanPostProcessor,之前我们知道他实现InstantiationAwareBeanPostProcessor这个接口。在这里,我要说AutowiredAnnotationBeanPostProcessor还实现了MergedBeanDefinitionPostProcessor这个接口,这个MergedBeanDefinitionPostProcessor接口只有一个函数postProcessMergedBeanDefinition(),该方法就是用来整合BeanDefinition。让我们自己毛估估也知道,这个对Bean内部的参数描述PropertyDescriptor也应该在专门用来整合Bean定义的这种BeanPostProcessors。从 AutowiredAnnotationBeanPostProcessor的如下代码,它果然实现postProcessMergedBeanDefinition函数。在其中调用了findAutowiringMetadata()。
@Override
public void postProcessMergedBeanDefinition(RootBeanDefinition beanDefinition, Class<?> beanType, String beanName) {
if (beanType != null) {
InjectionMetadata metadata = findAutowiringMetadata(beanName, beanType, null);
metadata.checkConfigMembers(beanDefinition);
}
}
第一次调用findAutowiringMetadata()的时候,我们this.injectionMetadataCache.get()得到metadata为null,这样就会进入if{}段代码,接着 调用 buildAutowiringMetadata()从该Bean的字节码中得到注入元信息。接着我们把得到的注入源信息InjectionMetadata放到this.injectionMetadataCache上。那么我们看一下这个方法。
private InjectionMetadata buildAutowiringMetadata(final Class<?> clazz) {
LinkedList<InjectionMetadata.InjectedElement> elements = new LinkedList<InjectionMetadata.InjectedElement>();
Class<?> targetClass = clazz; do {
final LinkedList<InjectionMetadata.InjectedElement> currElements =
new LinkedList<InjectionMetadata.InjectedElement>(); ReflectionUtils.doWithLocalFields(targetClass, new ReflectionUtils.FieldCallback() {
@Override
public void doWith(Field field) throws IllegalArgumentException, IllegalAccessException {
AnnotationAttributes ann = findAutowiredAnnotation(field);
if (ann != null) {
if (Modifier.isStatic(field.getModifiers())) {
if (logger.isWarnEnabled()) {
logger.warn("Autowired annotation is not supported on static fields: " + field);
}
return;
}
boolean required = determineRequiredStatus(ann);
currElements.add(new AutowiredFieldElement(field, required));
}
}
}); ReflectionUtils.doWithLocalMethods(targetClass, new ReflectionUtils.MethodCallback() {
@Override
public void doWith(Method method) throws IllegalArgumentException, IllegalAccessException {
Method bridgedMethod = BridgeMethodResolver.findBridgedMethod(method);
if (!BridgeMethodResolver.isVisibilityBridgeMethodPair(method, bridgedMethod)) {
return;
}
AnnotationAttributes ann = findAutowiredAnnotation(bridgedMethod);
if (ann != null && method.equals(ClassUtils.getMostSpecificMethod(method, clazz))) {
if (Modifier.isStatic(method.getModifiers())) {
if (logger.isWarnEnabled()) {
logger.warn("Autowired annotation is not supported on static methods: " + method);
}
return;
}
if (method.getParameterTypes().length == 0) {
if (logger.isWarnEnabled()) {
logger.warn("Autowired annotation should only be used on methods with parameters: " +
method);
}
}
boolean required = determineRequiredStatus(ann);
PropertyDescriptor pd = BeanUtils.findPropertyForMethod(bridgedMethod, clazz);
currElements.add(new AutowiredMethodElement(method, required, pd));
}
}
}); elements.addAll(0, currElements);
targetClass = targetClass.getSuperclass();
}
while (targetClass != null && targetClass != Object.class); return new InjectionMetadata(clazz, elements);
}
从上面,我们知道这个方法主要通过ReflectionUtils.doWithLocalFields()和ReflectionUtils.doWithLocalMethods()来得到源注入信息。到这里,我们可以大致知道依赖注入了。瞎 main提供一下test,大家可以通过debug知道Bean的创建流程。
@Test
public void testResourceInjection2() {
DefaultListableBeanFactory bf = new DefaultListableBeanFactory();
AutowiredAnnotationBeanPostProcessor bpp = new AutowiredAnnotationBeanPostProcessor();
bpp.setBeanFactory(bf);
bf.addBeanPostProcessor(bpp);
RootBeanDefinition bd1 = new RootBeanDefinition(Uss.class); bd1.setScope(RootBeanDefinition.SCOPE_SINGLETON);
bf.registerBeanDefinition("uss", bd1); RootBeanDefinition bd2 = new RootBeanDefinition(Tss.class); bd2.setScope(RootBeanDefinition.SCOPE_SINGLETON);
bf.registerBeanDefinition("tss", bd2); Uss uss = (Uss) bf.getBean("uss");
Tss tss = (Tss) bf.getBean("tss"); System.out.println(uss.getTss() == tss);
System.out.println(tss.getUss() == uss);
}
public class Uss {
private String id; @Autowired
private Tss tss; public String getId() {
return id;
} public void setId(String id) {
this.id = id;
} public Tss getTss() {
return tss;
} public void setTss(Tss tss) {
this.tss = tss;
}
}
public class Tss {
private String id; @Autowired
private Uss uss; public String getId() {
return id;
} public void setId(String id) {
this.id = id;
} public Uss getUss() {
return uss;
} public void setUss(Uss uss) {
this.uss = uss;
}
}
在这里,还有一些东西没有讲的非常的清楚,第一,是我自己有些代码还不太清楚,整体把握不住,还有些要等到我看到了其他内容在和大家一起分享。
Spring-Framework 源码阅读之@Autowired和AutowiredAnnotationBeanPostProcessor的更多相关文章
- Spring Ioc源码分析系列--@Autowired注解的实现原理
Spring Ioc源码分析系列--@Autowired注解的实现原理 前言 前面系列文章分析了一把Spring Ioc的源码,是不是云里雾里,感觉并没有跟实际开发搭上半毛钱关系?看了一遍下来,对我的 ...
- Spring事务源码阅读笔记
1. 背景 本文主要介绍Spring声明式事务的实现原理及源码.对一些工作中的案例与事务源码中的参数进行总结. 2. 基本概念 2.1 基本名词解释 名词 概念 PlatformTransaction ...
- spring framework 源码
spring framework 各版本源码下载地址 现在spring的源码下载地址真是不好找,这次终于找到了.记录一下,以帮助需要的朋友. https://github.com/spring-pro ...
- Idea搭建spring framework源码环境
spring的源码目前放在github上,https://github.com/spring-projects/spring-framework 一.安装Git 二.安装Gradle gradle为解 ...
- Spring框架源码阅读之Springs-beans(一)容器的基本实现概述(待续)
去年通过实际框架代码的阅读,以及结合<Spring源码深度解析>和<Spring技术内幕>的阅读,对Spring框架内Bean模块有了一个整体性的认识.对此进行的总结性整理和回 ...
- Spring JdbcTemplate源码阅读报告
写在前面 spring一直以删繁就简为主旨,所以设计出非常流行的bean管理模式,简化了开发中的Bean的管理,少写了很多重复代码.而JdbcTemplate的设计更令人赞叹,轻量级,可做ORM也可如 ...
- 【面试】足够应付面试的Spring事务源码阅读梳理(建议珍藏)
Starting from a joke 问:把大象放冰箱里,分几步? 答:三步啊,第一.把冰箱门打开,第二.把大象放进去,第三.把冰箱门带上. 问:实现Spring事务,分几步? 答:三步啊,第一. ...
- Spring,tk-mapper源码阅读
Mybatis的源码学习(一): 前言: 结合spring本次学习会先从spring-mybatis开始分析 在学习mybatis之前,应该要对spring的bean有所了解,本文略过 先贴一下myb ...
- Spring Framework 源码编译导入
预先准备环境 Window 10 JDK环境 List item Gradle 以及其环境变量配置 spring-framework源码(https://gitee.com/mirrors/Sprin ...
随机推荐
- [转] 面向对象原则之GOF是招式,九大原则才是精髓
只有到了一定层次后才会真正的深入体会到面向对象的一些知识点啊! 不谈具体程序,谈的是你对软件的理解 模式: 每一个模式描述了一个在我们周围不断重复发生的问题,以及该问题的解决方案的核心. “模式”这个 ...
- C#开发移动应用系列(3.使用照相机扫描二维码+各种基础知识)
前言 上篇文章地址: C#开发移动应用系列(1.环境搭建) C#开发移动应用系列(2.使用WebView搭建WebApp应用) 今天我们来讲一下如何使用Camera来调用照相机扫描二维码. (Tips ...
- Linux下nginx+多个Tomcat负载均衡的实现
博主原创,转载请注明. 由于项目需要,共创建了10个Tomcat端,由nginx负责转发.9个Tomcat端口分别是8080,11000,12000,13000,14000,15000,16000,1 ...
- MySQL如何有效的存储IP地址及字符串IP和数值之间如何转换
mysql> select inet_aton('192.168.0.1'); +--------------------------+ | inet_aton('192.168.0.1') | ...
- mysql left join
MySQL左连接不同于简单连接.MySQL LEFT JOIN提供该表额外字段在左侧. 如果使用LEFT JOIN,得到的所有记录的匹配方式相同, 在左边表中得到的每个记录不匹配也会有一个额外的记录. ...
- 基于 HTML5 WebGL 的 3D 网络拓扑图
在数据量很大的2D 场景下,要找到具体的模型比较困难,并且只能显示出模型的的某一部分,显示也不够直观,这种时候能快速搭建出 3D 场景就有很大需求了.但是搭建 3D 应用场景又依赖于通过 3ds Ma ...
- iis部署wcf服务过程
一.在iis网站中添加wcf服务,一直添加到web.config目录即可 二.点击基本设置-->连接为-->特定用户.填写登入电脑的用户名和密码. 三.点击身份验证 四.控制面板,设置防火 ...
- 第一章:windows下 python 的安装和使用
1. 主流的python版本和大部分人使用的版本都是 2.7 和3.6 2.安装 python2.7 和 python3.6的步骤 1. 下载 python对应的版本:选择使用的 系统, 64位和32 ...
- JavaSE中Collection集合框架学习笔记(2)——拒绝重复内容的Set和支持队列操作的Queue
前言:俗话说“金三银四铜五”,不知道我要在这段时间找工作会不会很艰难.不管了,工作三年之后就当给自己放个暑假. 面试当中Collection(集合)是基础重点.我在网上看了几篇讲Collection的 ...
- jpg、jpeg、png... 的区别
对于做设计这一行的人来说,这几个图片格式是最常用的,也是最常见的,几乎每一天都要与他们打交道. 刚刚入门的新人通常不知道在什么地方如何使用他们或者说如何更有效的使用他们. 那他们到底是有什么区别?(一 ...