苏州市城市建设局网站,网页加速器,seo推广招聘,东莞环保公司前言这是Spring依赖注入系列的第三篇#xff0c;前两篇主要分析了Spring bean依赖属性注入的两种方式#xff0c;是字段注入和setter方法注入#xff0c;单独比较这两种方式#xff0c;会发现其过程和工作原理非常类似#xff0c;那么构造方法注入会不会也和前两种比较类似…前言这是Spring依赖注入系列的第三篇前两篇主要分析了Spring bean依赖属性注入的两种方式是字段注入和setter方法注入单独比较这两种方式会发现其过程和工作原理非常类似那么构造方法注入会不会也和前两种比较类似呢本篇文章将会揭晓答案。构造方法注入方法以Autowired注解为例即把Autowired注解标记在目标bean的构造方法上而构造方法的入参数是引用bean类型构造方法注入示例示例主要内容1、定义Teachert类; 2、定义Student类3、在Student类中依赖Teacher4、使用Autowired注解标记在Student(Teacher teacher)上即在Student对象中以构造方法注入的方式注入Teacher对象Slf4j
Component
public class Student {private String name小明;private Teacher teacher;public Student() {log.info(----student的无参数构造方法被执行);}Autowiredpublic Student(Teacher teacher) {this.teacher teacher;log.info(----student的有参数构造方法teacher被执行);}public Student(String name, Teacher teacher) {this.name name;this.teacher teacher;log.info(----student的有参数构造方法nameteacher被执行);}public String getName() {return name;}public void setName(String name) {this.name name;}public Teacher getTeacher() {return teacher;}public void setTeacher(Teacher teacher) {this.teacher teacher;log.info(----student中的setTeacher方法被调用);}}Slf4j
Component
public class Teacher {private String name李老师;private Student student;public Teacher() {log.info(----teacher的无参数构造方法被执行);}public Teacher(Student student) {this.student student;log.info(----teacher的有参数构造方法被执行);}public String getName() {return name;}public void setName(String name) {this.name name;}public Student getStudent() {return student;}public void setStudent(Student student) {log.info(----teacher中的setStudent方法被调用);this.student student;}
}Component
Slf4j
public class MyBeanPostProcessor implements BeanPostProcessor {Overridepublic Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {if (beanName.equals(student)) {Student student (Student) bean;log.info(----student属性注入完成,student.name: student.getName());}return bean;}
}Component
Slf4j
public class MyInstantiationAwareBeanPostProcessor implements InstantiationAwareBeanPostProcessor {Overridepublic boolean postProcessAfterInstantiation(Object bean, String beanName) throws BeansException {if (beanName.equals(student)||beanName.equals(teacher)) {log.info(----bean实例化完成beanName beanName);}return true;}
} Testpublic void test4() {log.info(----单元测试执行开始);AnnotationConfigApplicationContext context new AnnotationConfigApplicationContext(com.fanfu);Student bean context.getBean(Student.class);log.info(----单元测试执行完毕);}单元测试执行日志从单元调试执行日志结果来看teacher先被实例化然后再调用student的有参数构造方法进行student对象的实例化同时把teacher对象注入到了student对象里这和字段注入、setter方法注入的过程明显不同字段注入、setter方法注入都是student先实例化然后实例化teacher对象再把teacher对象注入到student对象里。这是一个非常明显的区别要加深印象。构造方法注入的工作原理这里从工作原理上深入分析一下构造方法注入和字段注入、setter方法注入的过程的区别在哪里1、通过之前的分析已经知道bean实例化时机是在AbstractAutowireCapableBeanFactory#doCreateBean---createBeanInstance()那么先执行createBeanInstance()并观察其返回值发现student对象实例化好的同时teacher对象也注入到student对象内这和单元测试的执行日志结果是一致的2、那就直接着进入到AbstractAutowireCapableBeanFactory#createBeanInstance()方法内第一行resolveBeanClass(mbd, beanName)从名字看意思是解析出bean的Class实例对象接着往下走调用determineConstructorsFromBeanPostProcessors(beanClass, beanName)又引起了我的注意看过我的这篇文章的小伙伴Springboot扩展点之SmartInstantiationAwareBeanPostProcessor一定对determineCandidateConstructors()不陌生这个方法可以决定使用哪个构造器构造 bean执行结果就是student的有参数构造方法Student(Teacher teacher)再来看下面的autowireConstructor(beanName, mbd, ctors, args)调用从名字就能看出来是根据构造器自动装配bean3、那还等什么进入AbstractAutowireCapableBeanFactory#autowireConstructor()里面太简单了new一个构造方法解析器对象ConstructorResolver调用ConstructorResolver#autowireConstructor()4、ConstructorResolver#autowireConstructor()内的解析过程比较复杂各种的判断让人眼花缭乱但没关系谁让我是一机灵鬼呢我发现第一句就是BeanWrapperImpl bw new BeanWrapperImpl()这太熟悉了Spring容器刚实例化好的bean都包装成一个BeanWrapper类型对象我只需要牢牢看好这个对象肯定能找到在什么时候注入student对象。后面果然让我抓到了getUserDeclaredConstructor(candidate)拿到了有参数的构造方法对象然后调用createArgumentArray()入参数有bean的Class对象实例、bean构造方法形参名称这个方法十有八九有我想要的结果5、进入到ConstructorResolver#createArgumentArray()里面又是很多的判断判断完之后又调用了ConstructorResolver#resolveAutowiredArgument(); 这一块判断比较多一定要耐心; 这个方法要加印象一会记得看一下返回值在第10步6、进入到ConstructorResolver#resolveAutowiredArgument()眼前一亮发现了 this.beanFactory.resolveDependency(...)而这里的this.beanFactory是DefaultListableBeanFactory有看过Spring依赖注入一字段注入的方式是如何工作的Sprng依赖注入二setter注入是如何工作的的一对这个方法也不会陌生从名字也能看出来是bean的依赖解析7、进入到DefaultListableBeanFactory#resolveDependency()又调用了doResolveDependency();8、doResolveDependency()中又调用了findAutowireCandidates(beanName, type, descriptor)查找Student类依赖属性Teacher类的Class对象9、doResolveDependency()中继续向下会调用descriptor.resolveCandidate(...)而这个方法就是根据上一步得到Teacher类的Class对象实例化Teacher进入到descriptor.resolveCandidate(...)就一句beanFactory.getBean(beanName)是不是熟悉的配方熟悉的味道10、跳过teacher对象的创建过程继续往下直到ConstructorResolver#createArgumentArray()执行完返回值是teacher对象还记得开始是准备实例化student但是student还没实例化其依赖的引用属性teacher已经完成了实例化了, java.lang.Object[])实例化Student拿到student对象再包装进BeanWrapperImpl对象这里着重看一下instantiate()的入参数是构造方法对象、teacher对象12、至此teacher、student对象都已经实例化了并且student对象的依赖属性teacher也注入了继续往下走直到AbstractAutowireCapableBeanFactory#doCreateBean---createBeanInstance()执行完还记得在Spring依赖注入一字段注入的方式是如何工作的是如何分析的吗如下Spring中bean创建核心逻辑都在AbstractAutowireCapableBeanFactory#doCreateBean()中这个方法主要干了这几件事第一bean的实例化第二bean的后置处理方法执行第三把实例化完成、未完成属性注入的bean提前暴露到Spring的第三级缓存中去第四bean依赖属性注入第五bean的初始化student对象在完成这几步后就可以作为Spring容器中正式的bean开始被使用了。但是事实上createBeanInstance()执行完的时候依赖属性注入已经完成了第三步注入到Spring第三级缓存里的bean已经是一个完成属性注入的bean了第四步也不会再执行注入操作了构造方法注入总结Spring bean使用构造方法注入依赖属性的注入时机要早于字段注入和、setter方法注入而且注入到Spring第三级缓存里的bean已经是一个完成依赖属性注入的bean。得到这个结论后面再来分析Spring的bean的循环依赖为什么有的是可以解决的有的是解决不了的其实答案已经明显了下面一篇文章将重点分析。