107-Spring的底层原理(上篇)

news/2024/11/8 3:26:10/

Spring的底层原理

之前说明的都是Spring的应用(64章博客开始(包括其后面的相关博客)),现在来说明他为什么可以那样做
在说明之前,这里需要对64章博客中的spring的了解需要再次的说明:
Spring 是分层的 full-stack(英文意思:全栈的,一般意味着各层能够无缝的集成在一起) 轻量级(复杂度比较低)开源框架,以 IoC 和 AOP 为内核,提供了展现层 Spring MVC 和业务层事务管理等众多的企业级应用技术,还能整合开源世界众多著名的第三方框架和类库,已经成为使用最多的 Java EE 企业应用开源框架
Spring 官方网址:http://spring.io/
我们经常说的 Spring 其实指的是Spring Framework(spring 框架)
一般spring可能对jdk有版本的限制,比如jdk8及其以上是操作spring5开始的版本,6(jdk)则是4(spring),5则是3,具体可能随着时间的推移会发生改变,最好是观察官网
Spring是一个分层⾮常清晰并且依赖关系、职责定位⾮常明确的轻量级框架,主要包括⼏个大模块:数据处理模块、Web模块、AOP(Aspect Oriented Programming)/Aspects模块、Core Container模块 和 Test 模块,如下图所示,Spring依靠这些基本模块,实现了一个令⼈愉悦的融合了现有解决方案的零 侵⼊的轻量级框架

在这里插入图片描述

1:Spring核心容器(Core Container) :
容器是Spring框架最核心的部分,它管理着Spring应用中bean的创建、配置和管理,在该模块中,包括了Spring bean工⼚,它为Spring提供了DI的功能,基于bean工⼚,我们还会发现有多种Spring应用上下⽂的实现,所有的Spring模块都构建于核心 容器之上
2:面向切面编程(AOP)/Aspects:
Spring对面向切面编程提供了丰富的⽀持,这个模块是Spring应用系统中开发切面的基础,与DI一样,AOP可以帮助应用对象解耦
3:数据访问与集成(Data Access/Integration):
Spring的JDBC和DAO模块封装了大量样板代码,这样可以使得数据库代码变得简洁,也可以更专 注于我们的业务,还可以避免数据库资源释放失败而引起的问题,另外,Spring AOP为数据访问 提供了事务管理服务,同时Spring还对ORM进行了集成,如Hibernate、MyBatis等,该模块由JDBC、Transactions、ORM、OXM 和 JMS 等模块组成
4:Web 该模块提供了SpringMVC框架给Web应用,还提供了多种构建和其它应用交互的远程调用方案,SpringMVC框架在Web层提升了应用的松耦合⽔平
5:Test 为了使得开发者能够很方便的进行测试,Spring提供了测试模块以致⼒于Spring应用的测试, 通过该模块,Spring为使用Servlet、JNDI等编写单元测试提供了一系列的mock对象实现

在这里插入图片描述

这里首先建议先复习复习64章博客的内容,对后面的源码说明或者自定义框架(部分)才会有更好的理解(虽然在对应的博客中也大致说明了我自己认为的实现原理(那个时候并没有看源码,但理解还是正确的),看看即可,现在我们来直接看看源码是如何实现的)
同样的,与上一章博客相同的是,这里还是先操作对应的自定义框架(这里是Spring),来更加的容易理解后面的源码
最后:之前说明的Spring的应用(64章博客开始)中,其实还是有很多细节并没有说明,比如循环依赖(只是说明了理论的原因),在这里都会进行说明的
现在开始操作:
实际上IOC和AOP只是一个思想而已,并非Spring存在他们才存在,而是他们首先就存在,只是Spring更好的利用他们而已
有些框架中,并没有通过配置文件将创建对象进行保存,而Spring就保存了(这样的操作就是IOC思想,Spring利用了这个),使得可以使用,IOC中我们可以利用构造的改变来解释为什么使用IOC,这是一个主要的解释,无论是直接依赖还是硬编码基本都存在这样的问题,因为类是在自身的,而不是从其他地方获取,IOC在解决硬编码(基本自带解决一点耦合,如编译期耦合)问题的同时,在Spring中还会保存
为什么叫做控制反转(IOC):
控制:指的是对象创建(实例化、管理)的权利
反转:控制权交给外部环境了(IOC容器给你类)
当然还包括DI,关系如下:

在这里插入图片描述

DI利用IOC进一步操作
大多数IOC(包括DI)和AOP在64章博客都有大致说明这里我们来说明没有说明过的
其中AOP是OOP的延续,那么我们具体说明一下OOP
OOP三大特征:封装、继承和多态,OOP是一种垂直继承体系

在这里插入图片描述

OOP编程思想可以解决大多数的代码重复问题(封装代码,这样就不用继续写对应很多的代码了),但是有一些情况是处理不了的,比如下面的在顶级⽗类al中的多个方法中相同位置出现了重复代码,OOP就解决不了
public class al {public static void main(String[] args) {al a = new al();a.fa1();a.fa2();}public void fa1() {long l = System.currentTimeMillis();System.out.println("fa1");long l1 = System.currentTimeMillis();System.out.println("执行时长:" + (l1 - l));}public void fa2() {long l = System.currentTimeMillis();System.out.println("fa2");long l1 = System.currentTimeMillis();System.out.println("执行时长:" + (l1 - l));}}
横切逻辑代码:

在这里插入图片描述

横切逻辑代码存在什么问题:
1:横切代码重复问题
2:横切逻辑代码和业务代码混杂在一起,代码臃肿,维护不方便
AOP出场,AOP独辟蹊径提出横向抽取机制,将横切逻辑代码和业务逻辑代码进行一起分析处理

在这里插入图片描述

代码拆分容易,那么如何在不改变原有业务逻辑的情况下,悄⽆声息的把横切逻辑代码应用到原有的业 务逻辑中,达到和原来一样的效果,这个是比较难的,这就是AOP要解决的问题
为什么叫做面向切面编程:
切:指的是横切逻辑,原有业务逻辑代码我们不能动,只能操作横切逻辑代码,所以面向横切逻辑
面:横切逻辑代码往往要影响的是很多个方法,每一个方法都如同一个点,多个点构成面,有一个 面的概念在⾥面
至此IOC和AOP要操作的问题说明完毕
⼿写实现 IoC 和 AOP:
这里参照这个图:

在这里插入图片描述

具体实现想象即可

在这里插入图片描述

问题一:在上述案例实现中,service 层实现类在使用 dao 层对象时,直接在TransferServiceImpl 中通过 AccountDao accountDao = new JdbcAccountDaoImpl() 获得了 dao层对 象,然而一个 new 关键字却将 TransferServiceImpl 和 dao 层具体的一个实现类JdbcAccountDaoImpl 耦合在了一起,如果说技术架构发生一些变动,dao 层的实现要使用其它技术, 比如 Mybatis,思考切换起来的成本,每一个 new 的地方都需要修改源代码,重新编译,面向接⼝开发 的意义将大打折扣
问题二:service 层代码没有竟然还没有进行事务控制,如果转账过程中出现异常,将可能导致 数据库数据错乱,后果可能会很严重,尤其在金融业务
问题解决思路:
针对问题一思考: 实例化对象的方式除了 new 之外,还有什么技术,反射 (需要把类的全限定类名配置在xml中)
考虑使用设计模式中的工⼚模式解耦合,另外项⽬中往往有很多对象需要实例化,那就在工⼚中使 用反 射技术实例化对象,工⼚模式很合适,当然,这里工厂并非一定操作单纯的对象,map也是一个对象
针对问题二思考:
service 层没有添加事务控制,怎么办,既然没有事务就添加上事务控制,⼿动控制 JDBC 的Connection 事务,但要注意将Connection和当前线程绑定(即保证一个线程只有一个Connection,这样操作才针对的是同一个 Connection,进而控制的是同一个事务,而不是不同的事务)
现在我们来操作案例(上面图片想象即可),在64章博客中拿取一个案例即可(或者看后面的跟着来,虽然基本是拿取的)
这里给出具体的流程:
先导入依赖:
    <dependencies><dependency><groupId>dom4j</groupId><artifactId>dom4j</artifactId><version>1.6.1</version></dependency><dependency><!--dom4j是依赖jaxen项目的,所以一般不能省略(若省略的话,那么操作会比较麻烦,具体可以查看42章博客),因为我们比较需要--><groupId>jaxen</groupId><artifactId>jaxen</artifactId><version>1.1.6</version></dependency><dependency><!--测试@Test--><groupId>junit</groupId><artifactId>junit</artifactId><version>4.12</version></dependency></dependencies>
创建类和接口:
package com.lagou.dao; //后面自己看这个地方的包来进行创建/****/
public interface IUserDao {public void save();
}
package com.lagou.dao.Impl;import com.lagou.dao.IUserDao;/****/
public class IUserDaoImpl implements IUserDao {public void save() {System.out.println("dao被调用了,保存成功...");}
}
public interface IUserService { //service包下public void save();}
public class IUserServiceImpl implements IUserService {IUserDao userDao;public IUserServiceImpl(IUserDao userDao) {this.userDao = userDao;}public IUserDao getUserDao() {return userDao;}public void setUserDao(IUserDao userDao) {this.userDao = userDao;}public void save() {System.out.println(1);userDao.save();}
}
创建工厂类BeanFactory:
public class BeanFactory { //utils包下private static Map<String, Object> iocmap = new HashMap<>();static {InputStream resourceAsStream = BeanFactory.class.getClassLoader().getResourceAsStream("beans.xml");SAXReader saxReader = new SAXReader();try {Document read = saxReader.read(resourceAsStream);//使用xpath表达式,定位String xpath = "//bean";//根据上面表达式,获取所有bean标签(在学习spring时,后面会有多个对应标签,也使用类似于这个操作来进行)List<Element> list = read.selectNodes(xpath);//遍历并使用放射创建对应实例,存在map集合(充当IOC容器)中for (Element element : list) {//attributeValue(…) 获得指定属性名的属性值,...代表有多个重载方法String id = element.attributeValue("id");String aClass = element.attributeValue("class");Object o = Class.forName(aClass).newInstance();//接下来我们将上面的对应数据,存放在map中,充当IOC容器iocmap.put(id, o);}} catch (Exception ex) {ex.printStackTrace();}}public static Object getBean(String beanId) {Object o = iocmap.get(beanId);return o;}}
创建测试类:
public class SpringTest {IUserService userService = (IUserService) BeanFactory.getBean("ServiceImpl");@Testpublic void test1() {// 调用save方法userService.save();}}
创建配置文件beans.xml:
<beans><!--id:标识,class要生成实例的类的全路径--><bean id="userDao" class="com.lagou.dao.Impl.IUserDaoImpl"></bean><bean id="ServiceImpl" class="com.lagou.service.Impl.IUserServiceImpl"><property name="userDao" ref="userDao"></property></bean></beans>
执行测试类的方法,若打印了说明操作成功(对应的userDao还没有进行注入(使用DI方式),只是操作了IOC的方式,DI还没有利用)
现在开始我们将操作DI
首先改变配置文件,修改如下:
<beans><!--id:标识,class要生成实例的类的全路径--><bean id="userDao" class="com.lagou.dao.Impl.IUserDaoImpl"></bean><bean id="ServiceImpl" class="com.lagou.service.Impl.IUserServiceImpl"><!--这里加上--><property name="userDao" ref="userDao"></property></bean></beans>
<!--我们在学习spring时,我们知道也使用了对应的依赖注入方式,并且对应的name首字母忽略大小写,其他必须相同-->
而修改读取配置文件的类之前,我们首先需要先执行一下,然后看看还是否打印,很明显他还是打印的,只是对应的配置没有获取而已,那么现在我们改变对应的读取配置文件的BeanFactory操作:
package com.lagou.utils;import org.dom4j.Document;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;import java.io.InputStream;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.List;
import java.util.Map;public class BeanFactory {private static Map<String, Object> iocmap = new HashMap<>();static {InputStream resourceAsStream = BeanFactory.class.getClassLoader().getResourceAsStream("beans.xml");SAXReader saxReader = new SAXReader();try {//核心类SaxReader加载xml文档获得DocumentDocument read = saxReader.read(resourceAsStream);//使用xpath表达式,定位String xpath = "//bean";//根据上面表达式,获取所有bean标签(在学习spring时,后面会有多个对应标签,也使用类似于这个操作来进行)List<Element> list = read.selectNodes(xpath);//遍历并使用放射创建对应实例,存在map集合(充当IOC容器)中for (Element element : list) {//attributeValue(…) 获得指定属性名的属性值,...代表有多个重载方法String id = element.attributeValue("id");String aClass = element.attributeValue("class");//操作反射获取对象,由于并不知道对应的类型是什么,所以直接使用ObjectObject o = Class.forName(aClass).newInstance();//接下来我们将上面的对应数据,存放在map中,充当IOC容器iocmap.put(id, o);}//获取所有的property元素(//代表只要是就进行获取),并确定起父id,然后进行修改List<Element> property = read.selectNodes("//property");//解析他获取他的父元素,用来确定id的for (int i = 0; i < property.size(); i++) {//得到对应的元素,这里自然就代表property标签的元素Element element = property.get(i);String name = element.attributeValue("name");String ref = element.attributeValue("ref");//得到父元素Element parent = element.getParent();//得到idString id = parent.attributeValue("id");//通过对应的id得到对象Object o = iocmap.get(id);//在学习Spring中,我们知道他是通过对应的值来找方法的,那么既然是找,肯定不是我们这样的直接执行一个固定方法//所以这里需要反射通过对应的值信息来进行查找,为了与Spring相同的类似原理,所以这里使用set+name来进行确定//得到对应的类的Class方法的信息//Method[] getMethods(),用于获取该Class对象表示类中所有公共成员方法Method[] methods = o.getClass().getMethods();//我们进行遍历for (int j = 0; j < methods.length; j++) {Method method = methods[j];//得到了Class的方法信息,我们可以选择执行他,前提给出他要操作的对象(反过来的哦)//但是这里我们需要得到方法名称String name1 = method.getName();//这里我们直接的考虑忽略大小写(并包括set,一般来说set和get是不能操作的,大多数框架都是这样的,即set和get不会变,当然,随着时间的推移并不保证一定会改变的)if (name1.equalsIgnoreCase("set" + name)) {//如果进入了,说明找到了对应的方法,现在我们直接的调用对应对象的方法//iocmap.get(ref)得到对应的对象method.invoke(o, iocmap.get(ref)); //其他的参数是参数列表的值//至此我们操作完毕,要注意:我们只是拿取对象,那么改变对象的内容,对应的map自然也会改变,所以到这里就操作完毕了//当然了,由于实例是先放入map的,所以我们一般在启动时,如果没有执行完,那么对应读取配置文件的代码不会操作完毕,所以我们是基本不能在没有设置之前进行获取的,代码都是有顺序的,怎么可以操作到呢//并且他也不会将map设为静态遍历,就算设置了也是私有的,且他是在类加载过程中就进行操作的,综上所述,基本不能在中间进行获取}}}} catch (Exception ex) {ex.printStackTrace();}}public static Object getBean(String beanId) {Object o = iocmap.get(beanId);return o;}}//从上面操作可以知道,大多数Spring的配置文件为什么可以操作的原因就是因为这里进行的操作,这里只是给出一个例子而已,注解的只是另外一种读取形式,是类似的,就不做说明了
//所以实际上大多数的其他功能都是建立在对应工厂中进行补充的,那么其他的操作这里就不给出说明了(具体可以百度,或者自己思考)
然后再将对应IUserServiceImpl的构造方法去掉,然后我们直接的执行测试类的方法,若发现调用了说明注入成功,DI操作完毕(利用容器,那么从容器层面说明的),IOC则直接保存对象到容器(对象的层面)
至此通过IOC和DI解决了对应的问题一(包含了硬编码的问题(只需要改变配置文件,就可以所有对应地方都操作,且可以随意写包名,而直接的反射和new(导入的)只能操作一个地址,不随意,所以认为是硬编码),前置是new,然后字符串,最后解决硬编码),IOC解决,DI来赋值(问题一主要是解决,而赋值可以进行省略直接的编写,即进一步的省略)
接下来我们来解决问题二(AOP的利用):
要解决问题二,首先我们需要一个场景,首先导入依赖:
<dependency><!--mysql驱动--><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><version>5.1.47</version></dependency>
创建数据库的sql:
CREATE DATABASE springtext CHARACTER SET utf8;
USE springtext;
CREATE TABLE spring(
id INT PRIMARY KEY AUTO_INCREMENT,
NAME VARCHAR(20),
money INT
);INSERT INTO spring VALUES(1,'张三',1000);
INSERT INTO spring VALUES(2,'李四',1000);
现在我们来编写或者修改IUserDao和其实现类的对应的代码,代码如下:
package com.lagou.dao;/****/
public interface IUserDao {public void dao();public void in(int id, int money);public void ou(int id, int money);
}
package com.lagou.dao.Impl;import com.lagou.dao.IUserDao;import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.SQLException;/****/
public class IUserDaoImpl implements IUserDao {@Overridepublic void dao() {System.out.println("dao调用了");}public void in(int id, int money) {Connection con = null;PreparedStatement ps = null;try {Class.forName("com.mysql.jdbc.Driver");String url = "jdbc:mysql://localhost:3306/springtext?characterEncoding=UTF-8&useSSL=false";con = DriverManager.getConnection(url, "root", "123456");String sql = "UPDATE spring SET money = money + ? WHERE id = ?";ps = con.prepareStatement(sql);ps.setInt(1, money);ps.setInt(2, id);ps.executeUpdate();ps.close();con.close();} catch (Exception e) {e.printStackTrace();}}public void ou(int id, int money) {Connection con = null;PreparedStatement ps = null;try {Class.forName("com.mysql.jdbc.Driver");String url = "jdbc:mysql://localhost:3306/springtext?characterEncoding=UTF-8&useSSL=false";con = DriverManager.getConnection(url, "root", "123456");String sql = "UPDATE spring SET money = money - ? WHERE id = ?";ps = con.prepareStatement(sql);ps.setInt(1, money);ps.setInt(2, id);ps.executeUpdate();ps.close();con.close();} catch (Exception e) {e.printStackTrace();}}}
编写或者修改IUserService及其实现类:
package com.lagou.service;/****/
public interface IUserService {public void dao();public void inou(int inid, int ouid, int money);}
package com.lagou.service.Impl;import com.lagou.dao.IUserDao;
import com.lagou.service.IUserService;/****/
public class IUserServiceImpl implements IUserService {IUserDao userDao;public IUserDao getUserDao() {return userDao;}public void setUserDao(IUserDao userDao) {this.userDao = userDao;}@Overridepublic void dao() {System.out.println(1);userDao.dao();}@Overridepublic void inou(int inid, int ouid, int money) {userDao.in(inid, money);System.out.println("中间");userDao.ou(ouid, money);}}
测试类SpringTest也改一改:
package com.lagou.test;import com.lagou.service.IUserService;
import com.lagou.utils.BeanFactory;
import org.junit.Test;/****/
public class SpringTest {IUserService userService = (IUserService) BeanFactory.getBean("ServiceImpl");@Testpublic void test1() {System.out.println(1);// 调用dao方法userService.dao();}}
为了进行测试,我们再次的创建方法:
package com.lagou.test;import com.lagou.service.IUserService;
import com.lagou.utils.BeanFactory;
import org.junit.Test;/****/
public class SpringTest {IUserService userService = (IUserService) BeanFactory.getBean("ServiceImpl");@Testpublic void test1() {System.out.println(1);// 调用dao方法userService.dao();}@Testpublic void test2() {System.out.println(2);userService.inou(1, 2, 100);}}
执行test2方法,查看数据库是否修改,若修改了,说明操作成功
现在我们来操作问题二的场景,修改对应的代码:
 @Overridepublic void inou(int inid, int ouid, int money) {userDao.in(inid, money);System.out.println("中间");int i= 1/0;userDao.ou(ouid, money);}
继续test2方法,若数据出现错误,那么场景创建成功,数据错误,现在我们开始来解决这样的问题
首先我们要知道,事务是由一个连接来操作的,两个连接自然不会是同一个事务,连接就相当于一个会话了
所以我们需要将他们的两次操作由一个连接来完成,那么很明显,根据层的分开,我们的事务一般需要在service里加上而不是在dao中进行操作(他必然是与其他dao分开的)
首先我们在utils包下创建ConnectionUtils类:
package com.lagou.utils;import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;/****/
public class ConnectionUtils {private static ThreadLocal<Connection> threadLocal = new ThreadLocal<Connection>();//进行绑定public static Connection getThreadConnection() {//先从ThreadLocal上获取连接Connection conn = threadLocal.get();//判断是否为空,即当前线程中是否有连接if (conn == null) {//从数据源中获取一个连接,并且放到ThreadLocal中try {//不为null//这里就不操作连接池了Class.forName("com.mysql.jdbc.Driver");String url = "jdbc:mysql://localhost:3306/springtext?characterEncoding=UTF-8&useSSL=false";conn = DriverManager.getConnection(url, "root", "123456");threadLocal.set(conn);} catch (Exception e) {e.printStackTrace();}}return conn;}//解除绑定public void removeThreadConnection() {threadLocal.remove();}
}
至此,一个线程必然是同一个连接,也就使得会操作同一个事务,继续进行修改如下代码:
//将前面的两个代码中,in方法和ou方法,修改成如下即可:try {con = ConnectionUtils.getThreadConnection();
然后去掉对应的con.close();(再次的得到就不能进行操作了,如con.prepareStatement(sql);),然后继续操作,首先去掉int i= 1/0;,若数据库数据发生改变,那么说明初始配置操作成功,接下来我们来操作事务(修改对应的代码):
package com.lagou.service.Impl;import com.lagou.dao.IUserDao;
import com.lagou.service.IUserService;
import com.lagou.utils.ConnectionUtils;import java.sql.SQLException;/****/
public class IUserServiceImpl implements IUserService {IUserDao userDao;public IUserDao getUserDao() {return userDao;}public void setUserDao(IUserDao userDao) {this.userDao = userDao;}@Overridepublic void dao() {System.out.println(1);userDao.dao();}@Overridepublic void inou(int inid, int ouid, int money) {try {//开启事务,也就是关闭自动提交,就相当于开启事务了ConnectionUtils.getThreadConnection().setAutoCommit(false);userDao.in(inid, money);System.out.println("中间");int i = 1 / 0;userDao.ou(ouid, money);//提交事务ConnectionUtils.getThreadConnection().commit();} catch (Exception e) {e.printStackTrace();//回滚事务try {ConnectionUtils.getThreadConnection().rollback();throw e; //抛出使得会出现错误,而不会出现后续的打印(比如后面我们会定义一些成功的操作,虽然这里我们并没有)/*若没有这个,那么若对应的代码是这样的:userService.inou(1, 2, 100);System.out.println(4);那么这个4会进行打印,加上这个就不会了,因为有没有进行处理的异常,那么后面不操作*/} catch (SQLException ex) { //try没有数据时,这里就只能是Exception规定的(防止其他情况)ex.printStackTrace();}}}}
注意:上面的事务可以利用一个类来完成,这里就不进行操作了,自己可以进行修改
现在开始执行,若数据没有变化,那么事务回滚成功,注意:大多数回滚是需要自己进行操作的,否则单纯的报错一般不会进行回滚,但是并非不回滚数据就会变,由于报错后面不操作,那么他就不会进行提交,这也使得有些地方,没有回滚,数据却也没有变化的原因,其中大多数框架中,我们也会说成报错就回滚,实际上底层是操作手动回滚的而不是真的报错就回滚,所以我们也会认为报错就会操作回滚(可以这样的认为,因为他们(一般包括框架)基本操作事务时,都这样操作报错就回滚的)
问题二大致解决,为什么说大致呢,因为现在有个问题,如果有很多这样的方法,那么是否都需要写上对应的关闭事务,提交事务和回滚事务呢,很明显,都要的,既然都要那么非常麻烦,所以这里就是AOP要核心解决的问题(在问题二中的部分核心问题,第一个核心是相同连接,第二个核心是AOP要解决的,所以说使用AOP解决问题二也不过分)
现在开始操作:
我们知道一个类一般都会有对应的实现接口,而在实现接口,且每个接口方法中进行同样的操作的解决方式我们一般都会使用jdk动态代理,同样的,这里我们也进行使用,在utils包下创建一个类Proxy:
package com.lagou.utils;import com.lagou.service.IUserService;
import com.lagou.service.Impl.IUserServiceImpl;import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;/****/
public class ProxyIn {public static IUserService JdkProxy(Object o) {IUserService o1 = (IUserService) Proxy.newProxyInstance(o.getClass().getClassLoader(), o.getClass().getInterfaces(), new InvocationHandler() {@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {try {System.out.println(3);ConnectionUtils.getThreadConnection().setAutoCommit(false);method.invoke(o, args);ConnectionUtils.getThreadConnection().commit();} catch (Exception e) {e.printStackTrace();ConnectionUtils.getThreadConnection().rollback();}return null;}});return o1;}}
然后改变IUserServiceImpl的方法:
    @Overridepublic void inou(int inid, int ouid, int money) {userDao.in(inid, money);System.out.println("中间");int i = 1 / 0;userDao.ou(ouid, money);}
改变测试类方法:
 @Testpublic void test2() {IUserService iUserService = ProxyIn.JdkProxy(userService);iUserService.inou(1,2,100);}
执行后,若数据还是没有变化,说明操作成功
实际上上面你可以选择使用单例模式来操作,使得我们只能操作一个类里面的,而不是占用静态名称,或者说防止其他人直接的进行调用来使用(实际上静态大多都会限制不是静态的使用,所以除非他不会利用其他变量,否则一般我们不会使用静态,因为一个类里面都是静态的,是不好扩展的,不能继承静态的(在于必须也对子操作静态,那么自然没有继承的说法了),即操作不了super,所以我们一般一个类里面使用静态时,不会使用很多,也通常需要在不会利用其他变量的前提下,当然,只是一种说明而已,你可以选择在里面创建对象来使用,虽然并不好),但是这里只要求说明原理,并不要求什么模式,所以就不操作了
实际上还有一个代理模式:CGLIB动态代理
实际上代理模式大多数都是操作反射来完成的,我们也可以利用反射来自己实现代理模式,具体实现方式可以百度(需要很多反射的知识,甚至可能有特殊的操作,使得可以改变原来的类的结构,如补充实现接口,继承类等等)
现在我们来利用CGLIB动态代理:
继续回到utils包下,创建CglibIn类:
前提是需要依赖:
 <dependency><!--IOC容器需要--><groupId>org.springframework</groupId><artifactId>spring-context</artifactId><version>5.1.5.RELEASE</version></dependency>
也就是说他并不是JDK自带的,是其他依赖包里面的,我们看看这个类:
package com.lagou.utils;import com.lagou.service.IUserService;
import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;import java.lang.reflect.Method;/****/
public class CglibIn {public static IUserService CglibProxy(Object o) {IUserService o1 = (IUserService) Enhancer.create(o.getClass(), new MethodInterceptor() {@Overridepublic Object intercept(Object oo, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {try {System.out.println(3);ConnectionUtils.getThreadConnection().setAutoCommit(false);method.invoke(o, objects);ConnectionUtils.getThreadConnection().commit();} catch (Exception e) {e.printStackTrace();ConnectionUtils.getThreadConnection().rollback();}return null;}});return o1;}
}
在测试类中加上如下:
   @Testpublic void test3() {IUserService iUserService = CglibIn.CglibProxy(userService);iUserService.inou(1,2,100);}
进行测试,若数据没有变化,说明操作成功
这里要注意,由于自身能打印(toString也会进行触发,所以对应的invoke里面的参数不要写当前代理类(他也是存在对应方法的),否则是会无限循环的)
上面我们测试完成,那么AOP就说明完成,总结:IOC和DI(是i的大写)通过配置来完成,AOP通过代理来完成,当然,AOP也可以通过配置来得到类,然后使用,或者自动的改变对应的map集合相关方法,变成代理类等等操作,一般来说,我们不会将代理类进行打印返回,而是将要代理的原来的对象类进行返回(代理类打印自身一般也会触发拦截的,代理是特殊的,具体是保证不会给其他人直接使用)
当然了Spring主要是为了将实例自己创建,所以我们也可以将APO相关的类放在配置文件中,具体实现参照前面的userDao的注入,这里就不多说了
当然,由于这里只是操作实现,对应的代理并没有什么流程可言,如专门操作事务的类和代理类等等,但是基本为了方便就使用了静态,如同样是操作事务的操作和代理操作等等,注意即可,当然64章博客开始的内容基本都说明了,且有顺序
如果在框架中,需要知道他要什么值,可以ctrl+左键即可,以scope为例子,是:<xsd:attribute name=“scope” type=“xsd:string”>
当然,如果是xsd:integer,那么你也只能写数字了(默认是String的,只是转换成String(一般直接赋值)或者Integer,String一般直接赋值不转换)
至此IOC和AOP的相关代码都手写完成
这里最好复习64章开始的内容,是为后面说明做准备的
现在我们来补充对应博客的内容
Spring IOC⾼级特性:
lazy-Init 延迟加载:
我们知道mybatis有延迟加载,代表对实际上Spring也是有的,只是他代表对实例的延迟创建
Bean的延迟加载(延迟创建):
ApplicationContext 容器的默认行为是在启动服务器时将所有 singleton bean (单例bean)提前进行实例化放入map集合中(多例不放入,且与map无关,所以我们一般不会考虑多例延迟加载的情况,虽然他也是使用就会创建),提前 实例化意味着作为初始化过程的一部分,在前面就是在static代码块中进行操作,ApplicationContext 实例会创建并配置所有的singleton bean
这里我首先给出测试环境,首先创建项目,给出依赖:
<dependencies><dependency><groupId>org.springframework</groupId><artifactId>spring-context</artifactId><version>5.1.5.RELEASE</version></dependency><dependency><groupId>junit</groupId><artifactId>junit</artifactId><version>4.12</version></dependency></dependencies>
项目图如下:

在这里插入图片描述

package com.domain;/****/
public class Account {private Integer id;private String name;private Double money;public Integer getId() {return id;}public void setId(Integer id) {this.id = id;}public String getName() {return name;}public void setName(String name) {this.name = name;}public Double getMoney() {return money;}public void setMoney(Double money) {this.money = money;}@Overridepublic String toString() {return "Account{" +"id=" + id +", name='" + name + '\'' +", money=" + money +'}';}
}
package com.test;
import com.domain.Account;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;/****/
public class Test1 {@Testpublic void text1(){ApplicationContext classPathXmlApplicationContext = newClassPathXmlApplicationContext("applicationContext.xml");Account account = (Account) classPathXmlApplicationContext.getBean("ac");System.out.println(account);}
}
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans.xsd"><bean id="ac" class="com.domain.Account"></bean>
</beans>
执行一下,若有数据,代表环境操作成功
对于上面的延迟加载,一般这样设置,比如:
<bean id="testBean" class="cn.lagou.LazyBean" /><!--该bean默认的设置为:--><bean id="testBean" calss="cn.lagou.LazyBean" lazy-init="false" />
lazy-init=“false”,立即加载,表示在spring启动时,立刻进行实例化,这是默认的值
如果不想让一个singleton bean 在 ApplicationContext实现初始化时被提前实例化,那么可以将bean设置为延迟实例化
<bean id="testBean" calss="cn.lagou.LazyBean" lazy-init="true" /> 
设置 lazy-init 为 true 的 bean 将不会在 ApplicationContext 启动时提前被实例化,而是第一次向容器 通过 getBean 索取 bean 时实例化的,看起来就是多例了,但他是多例,即多人操作会不同的,而不是相同,相当于我们在博客中学习的:
 BeanFactory xmlBeanFactory = new XmlBeanFactory(newClassPathResource("applicationContext.xml")); //即不使用传统的了
如果一个设置了立即加载的 bean1,引用了一个延迟加载的 bean2 ,那么 bean1 在容器启动时被实例 化,而 bean2 由于被 bean1 引用,所以也被实例化,这种情况也符合延时加载的 bean 在第一次调用 时才被实例化的规则(根据前面的代码可以知道操作了getBean)
但是在前面我们手写的对应代码中知道,他首先是从对应的map中获取,可是没有对应的实例,那么我们哪里是需要进行修改的,那么很明显,设置延迟时,一般在Spring中有存在对应的编号,首先他并不会放入map,而对应的getBean调用时,内部也会判断这个编号,从而创建实例放入map,再返回,这就是延迟加载的机制
也可以在容器层次中通过在 元素上使用 “default-lazy-init” 属性来控制延时初始化,如下面配置:
<beans default-lazy-init="true"> <!--位置可以随便放,只要空格(回车相当于空格)隔开即可,不会影响其他属性--><!-- 
no beans will be eagerly pre-instantiated... :
没有bean会被急切地预实例化
--></beans>
他就相当于给所有的bean加上lazy-init=“true”,当然,如果bean自己设置了lazy-init=“false”,那么按照他的lazy-init="false"为主,当然,随着版本的变化,他可能是以default-lazy-init="true"为主
如果一个 bean 的 scope 属性为 scope=“pototype” 时,即使设置了 lazy-init=“false”,容器启动时也不 会实例化bean,而是调用 getBean 方法实例化的,因为多例不考虑是否延迟,即无作用
应用场景:
1:开启延迟加载一定程度提⾼容器启动和运转性能(相对于原来的资源多了,自然相对来说也会或多或少提高性能,这也是为什么提供了beans标签上可以加default-lazy-init="true"属性的原因,大多数针对硬盘只有不足时可能会明显的影响性能,通常足够不会,针对JVM内存也是如此,只有快要不足时,才会考虑性能的出现,但是资源被占用,需要的判断或者移动放入也会变多,所以或多或少也会减低一点性能,且对与存储的地方而言,因为需要找寻他给你,所以总体来说,存放东西就一定会影响性能,只是非常小而已)
2:对于不常使用的 Bean 设置延迟加载,这样偶尔使用的时候再加载,不必要从一开始该 Bean 就占 用资源
实际上在源码上,可以看看:
/*
进行调试:
进入:classPathXmlApplicationContext(是到对应的对象,这个一般小写的c代表引用)
找到beanFactory,然后继续找到singletonObjects,里面可以看到是否存在对应的id,以及value值了,可以自己测试去掉延迟或者加上延迟看看在操作注解时,可以在对应操作注解的类上(前提他需要变成实例,即需要对应的四个注解将他变成实例),加上@Lazy,代表他操作延迟,由于@Lazy里面默认是true,所以操作延迟,可以设置里面的值来操作回来立即加载
*/
BeanFactory与ApplicationContext区别:
BeanFactory是Spring框架中IoC容器的顶层接⼝,它只是用来定义一些基础功能,定义一些基础规范,而ApplicationContext是它的一个⼦接⼝,所以ApplicationContext是具备BeanFactory提供的全部功能的,通常,我们称BeanFactory为SpringIOC的基础容器,ApplicationContext是容器的⾼级接⼝,比BeanFactory要拥有更多的功能,比如说国际化⽀持和资源访问(xml,java配置类)等等
实际上在博客学习中也具体说明过,这里可以给出图片:

在这里插入图片描述

当然,这个图片是网上找的,并且比较旧,但是也大致的给出的具体的架构关联
FactoryBean 和 BeanFactory:
BeanFactory接⼝是容器的顶级接⼝,定义了容器的一些基础行为,负责生产和管理Bean的一个工⼚, 具体使用它下面的⼦接⼝类型,比如ApplicationContext
此处我们重点分析FactoryBean,Spring中Bean有两种,一种是普通Bean,一种是工⼚Bean(FactoryBean,工厂有两种(静态和普通)),FactoryBean可以生成 某一个类型的Bean实例(返回给我们),也就是说我们可以借助于它⾃定义Bean的创建过程
Bean创建的三种方式中的工厂静态方法和工厂(普通)实例化方法和FactoryBean作用类似,FactoryBean使用较多,尤 其在Spring框架一些组件中会使用,还有其他框架和Spring框架整合时使用
我们可以自定义Bean的创建过程(也可以叫做工厂),看如下这个接口:
我们再前面测试的环境中的com包下,创建factory.FactoryBean接口:
package com.factory;import org.springframework.lang.Nullable;/****/
// 可以让我们⾃定义Bean的创建过程(完成复杂Bean的定义)
public interface FactoryBean<T> extends org.springframework.beans.factory.FactoryBean {@Nullable //注解提醒你使用非空判断(方法和成员变量都可以,局部和类一般不行),但也只是提醒,除了特别的地方,并无特殊作用,你也可以通过注释来代替他// 返回FactoryBean创建的Bean实例,如果isSingleton返回true,则该实例会放到Spring容器的单例对象缓存池中(Map)T getObject() throws Exception;@Nullable// 返回FactoryBean创建的Bean类型Class<?> getObjectType();// 返回作用域是否单例default boolean isSingleton() {return true;}
}//实际上在spring就存在这个接口:
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//package org.springframework.beans.factory;import org.springframework.lang.Nullable;public interface FactoryBean<T> {@NullableT getObject() throws Exception;@NullableClass<?> getObjectType();default boolean isSingleton() {return true;}
}
然后我们再domain包下创建Company:
package com.domain;/****/
public class Company {private String name;private String address;private int scale;public String getName() {return name;}public void setName(String name) {this.name = name;}public String getAddress() {return address;}public void setAddress(String address) {this.address = address;}public int getScale() {return scale;}public void setScale(int scale) {this.scale = scale;}@Overridepublic String toString() {return "Company{" +"name='" + name + '\'' +", address='" + address + '\'' +", scale=" + scale +'}';}
}
然后在factory包下创建CompanyFactoryBean类:
package com.factory;import com.domain.Company;/****/
public class CompanyFactoryBean implements FactoryBean<Company> {private String companyInfo; // 公司名称,地址,规模public void setCompanyInfo(String companyInfo) {this.companyInfo = companyInfo;}@Overridepublic Company getObject() throws Exception {// 模拟创建复杂对象CompanyCompany company = new Company();String[] strings = companyInfo.split(",");company.setName(strings[0]);company.setAddress(strings[1]);company.setScale(Integer.parseInt(strings[2]));return company;}@Overridepublic Class<?> getObjectType() {return Company.class;}@Overridepublic boolean isSingleton() {/*//操作接口的default可以直接操作isSingleton或者下面的方式,不能直接的super,因为接口没有实例return FactoryBean.super.isSingleton();*/return true;}
}
创建好后,我们在xml中加上如下:
 <bean id="fac" class="com.factory.CompanyFactoryBean"><property name="companyInfo" value="哈哈,heihie,11"></property></bean>
这里大多数你可能会认为是实现类,实际上由于对应的类实现了FactoryBean(package org.springframework.beans.factory;里面的),且在map中会判断是否实现这个,从而不将这个类放入map,而是将对应getObject()方法的值放入map
为了进行验证,我们测试:
package com.test;import com.domain.Account;
import com.domain.Company;
import org.junit.Test;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.xml.XmlBeanFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.core.io.ClassPathResource;/****/public class Test1 {@Testpublic void text1() {ApplicationContext classPathXmlApplicationContext = newClassPathXmlApplicationContext("applicationContext.xml");Company company = (Company) classPathXmlApplicationContext.getBean("fac");System.out.println(company); //Company{name='哈哈', address='heihie', scale=11}}
}
可以知道对方打印了对应的信息,且是getObject()方法的值,也经过测试,如果没有实现对应的接口,那么就是对应的类对象(记得改一改),而不是执行getObject()方法的值
若我们不需要他操作对应的getObject()方法的值,可以操作如下:
 CompanyFactoryBean company = (CompanyFactoryBean) classPathXmlApplicationContext.getBean("&fac");System.out.println(company);
这样就是对应的实例了,而不是getObject()方法的值,实际上在前面的singletonObjects中并没有,因为这里是另外的地方来获取的
后置处理器:
Spring提供了两种后处理bean的扩展接⼝,分别为 BeanPostProcessor 和BeanFactoryPostProcessor,两者在使用上是有所区别的
由于我们可以通过工⼚初始化(BeanFactory,这里代表操作xml或者配置类的工厂)—> Bean对象
在BeanFactory初始化之后可以使用BeanFactoryPostProcessor进行后置处理做一些事情
在Bean对象实例化(并不是Bean的整个生命周期完成)之后可以使用BeanPostProcessor进行后置处 理做一些事情
注意:对象不一定是springbean(比如没有放入map里面的对象),而springbean一定是个对象
springbean更加详细的生命周期是:
/*
实例化
设置值(如注入)
调用BeanNameAware接口的setBeanName方法
调用BeanFactoryAware接口的setBeanFactory方法
调用ApplicationContextAware接口的setApplicationContext方法
调用BeanPostProcessor接口的对应方法----------------->BeanPostProcessor(他的后方法会在初始化后面,并且所有的方法都操作其他没有实现对应接口的初始化的前后面)
调用InitializingBean接口的afterPropertiesSet方法
调用我们定制(init-method)的初始化方法------------------->init-method
调用BeanFactoryPostProcessor接口的后处理方法(在BeanFactory初始化之后,即的确是init-method初始化后)---------------->BeanFactoryPostProcessor(并且是所有的初始化之后才会操作,但这个所有是实现对应接口的,而不是真的所有)
然后判断是操作单例还是多例,如果是多例,那么最后直接将对应的对象给你
如果是单例,放在map中,在要销毁时操作destroy-method至此,上面就是一个bean的更加详细的生命周期当然,如果你不明白,没有关系,最后会给出所有的流程
*/
也可以看图(虽然没有给出BeanFactoryPostProcessor接口的处理方法):

在这里插入图片描述

那么为了进行验证,我们改变前面的Account类,操作如下:
package com.domain;import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.beans.factory.BeanNameAware;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;/****/
public class Account implements BeanNameAware, BeanFactoryAware, ApplicationContextAware {@Overridepublic void setBeanName(String s) { //重写的变量名称是可以随便改变的System.out.println("注册我成为bean时定义的id:" + s); //ac,当前类实例的idSystem.out.println("BeanNameAware");}@Overridepublic void setBeanFactory(BeanFactory beanFactory) throws BeansException {System.out.println("可以管理我的BeanFactory:" + beanFactory); //前面测试源码时的BeanFactorySystem.out.println("BeanFactoryAware");}@Overridepublic void setApplicationContext(ApplicationContext applicationContext) throws BeansException {System.out.println("高级容器接口:" + applicationContext); //也就是前面的"进入:classPathXmlApplicationContext"System.out.println("ApplicationContextAware");}
}
测试类我们修改成如下:
package com.test;import com.domain.Account;
import com.domain.Company;
import com.factory.CompanyFactoryBean;
import org.junit.Test;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.xml.XmlBeanFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.core.io.ClassPathResource;/****/public class Test1 {@Testpublic void text1() {ApplicationContext classPathXmlApplicationContext = newClassPathXmlApplicationContext("applicationContext.xml");System.out.println(1);}
}
执行看看结果:
/*
注册我成为bean时定义的id:ac
BeanNameAware
可以管理我的BeanFactory:org.springframework.beans.factory.support.DefaultListableBeanFactory@c03cf28: defining beans [ac,fac]; root of factory hierarchy
BeanFactoryAware
高级容器接口:org.springframework.context.support.ClassPathXmlApplicationContext@6b1274d2, started on Thu May 04 17:43:58 CST 2023
ApplicationContextAware
1*/
现在我们来看看BeanPostProcessor接口,BeanPostProcessor是针对Bean级别的处理,可以针对某个具体的Bean
public interface BeanPostProcessor {@Nullabledefault Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {return bean;}@Nullabledefault Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {return bean;}
}
该接⼝提供了两个方法,分别在Bean的初始化方法前和初始化方法后执行,具体这个初始化方法指的是 什么方法,类似我们在定义bean时,定义了init-method所指定的方法,但是若实现了他,就都在初始化方法后操作了,且是在所有的初始化操作后才操作,那么实现了他的初始化必然比没有实现他的初始化先操作,然后其他的初始化都在这个实现接口的初始化的前方法(先给出所有)和后方法中间,这里你可能并不明白为什么,到后面自己测试时就知道了(看后面的"细节流程如下",或者"现在给出所有的流程")
现在我们操作xml:
<bean id="ac" class="com.domain.Account" init-method="init"></bean>
在Account类中加上如下:
public void init(){System.out.println("init");}
定义一个类实现了BeanPostProcessor,默认是会对整个Spring容器中所有的bean进行处理,如果要对 具体的某个bean处理,可以通过方法参数判断,两个类型参数分别为Object和String,第一个参数是每 个bean的实例,第二个参数是每个bean的id属性的值(或者name,前提是id不存在),所以我们可以通过第二个参数,来判 断我们将要处理的具体的bean
现在来测试,创建一个类User(在domain包下):
package com.domain;import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.beans.factory.BeanNameAware;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;/****/
public class User implements BeanNameAware, BeanFactoryAware, ApplicationContextAware, BeanPostProcessor {@Overridepublic void setBeanName(String s) { //重写的变量名称是可以随便改变的System.out.println("User-注册我成为bean时定义的id:" + s); //ac,当前类实例的idSystem.out.println("User-BeanNameAware");}@Overridepublic void setBeanFactory(BeanFactory beanFactory) throws BeansException {System.out.println("User-可以管理我的BeanFactory:" + beanFactory); //前面测试源码时的BeanFactorySystem.out.println("User-BeanFactoryAware");}@Overridepublic void setApplicationContext(ApplicationContext applicationContext) throws BeansException {System.out.println("User-高级容器接口:" + applicationContext); //也就是前面的"进入:classPathXmlApplicationContext"System.out.println("User-ApplicationContextAware");}public void init() {System.out.println("User-init");}@Overridepublic Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {System.out.println("User-前");return BeanPostProcessor.super.postProcessBeforeInitialization(bean, beanName);}@Overridepublic Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {System.out.println("User-后");return BeanPostProcessor.super.postProcessAfterInitialization(bean, beanName);}
}
在xml中加上如下:
 <bean id="acc" class="com.domain.User" init-method="init"></bean>
执行看看结果:
/*
注册我成为bean时定义的id:ac
BeanNameAware
可以管理我的BeanFactory:org.springframework.beans.factory.support.DefaultListableBeanFactory@c03cf28: defining beans [ac,acc,fac]; root of factory hierarchy
BeanFactoryAware
高级容器接口:org.springframework.context.support.ClassPathXmlApplicationContext@6b1274d2, started on Thu May 04 17:54:37 CST 2023
ApplicationContextAware
init
User-注册我成为bean时定义的id:acc
User-BeanNameAware
User-可以管理我的BeanFactory:org.springframework.beans.factory.support.DefaultListableBeanFactory@c03cf28: defining beans [ac,acc,fac]; root of factory hierarchy
User-BeanFactoryAware
User-高级容器接口:org.springframework.context.support.ClassPathXmlApplicationContext@6b1274d2, started on Thu May 04 17:54:37 CST 2023
User-ApplicationContextAware
User-init
-------------------可以发现,这里的确是最后的
前
User-前
后
User-后
1Process finished with exit code 0*//*
注意:若将xml这样写:<bean id="acc" class="com.domain.User" init-method="init"></bean><bean id="ac" class="com.domain.Account" init-method="init"></bean>即会先操作acc,也就是说,上面的打印是这样写的:<bean id="ac" class="com.domain.Account" init-method="init"></bean><bean id="acc" class="com.domain.User" init-method="init"></bean>即根据顺序来的
*/
然而如果对方的类实现了BeanPostProcessor接口,那么就不会出现在对应的处理中,所以若上面的该接口方法里面写上如下:
System.out.println(bean); //对应的实例System.out.println(beanName); //对应的id或者name
后面的名称只有fac了(上面的也就是fac),若没有实现对应的接口,那么考虑前面的"分别在Bean的初始化方法前和初始化方法后执行",来操作初始化方法了
至此,改BeanPostProcessor接口说明完毕
细节流程如下:
/*
如果对应的类实现了BeanPostProcessor接口,那么在执行BeanPostProcessor接口方法之前,对应都需要进行初始化,并且需要所有的对应bean都初始化完毕才会操作,然后操作对应的接口方法,没有实现该接口的类的初始化在所有对应实现该类的初始化接口方法的(前方法)后面,后方法前面如user,acc类实现了BeanPostProcessor接口,hh类,jj类没有实现,并且他们都有初始化方法,那么会出现如下:
user的init
acc的inituser的前
acc的前
hh的init  ---------------  (可以认为他调用会操作其他实现了接口的方法),也是按照顺序的,在xml中顺序先后会先操作hh
user的后
acc的后user的前
acc的前
jj的init
user的后
acc的后getBean执行
user的后
acc的后 
*/
然后继续给Account类进行实现接口,调用InitializingBean接口的afterPropertiesSet方法,可以在初始化之前执行,若没有实现BeanPostProcessor接口,那么在对应的前方法打印后,我来执行,然后初始化执行
/*
4 对应的afterPropertiesSet方法打印
init
User-init 上面两个是实现的
前
User-前
44 对应的afterPropertiesSet方法打印
User-initssssss  没有实现的
后
User-后
*/
我们继续实现BeanFactoryPostProcessor接口,他的实现导致与实现BeanPostProcessor接口一样的,但是他的方法只是操作当前类,不会被其他人调用
现在给出所有的流程:
对应的Account类:
package com.domain;import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.beans.factory.BeanNameAware;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;/****/
public class Account implements BeanNameAware, BeanFactoryAware, ApplicationContextAware, BeanPostProcessor, InitializingBean, BeanFactoryPostProcessor {@Overridepublic void setBeanName(String s) { //重写的变量名称是可以随便改变的System.out.println("BeanNameAware");}@Overridepublic void setBeanFactory(BeanFactory beanFactory) throws BeansException {System.out.println("BeanFactoryAware");}@Overridepublic void setApplicationContext(ApplicationContext applicationContext) throws BeansException {System.out.println("ApplicationContextAware");}public void init() {System.out.println("init");}public void tt() {System.out.println("tt");}@Overridepublic Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {System.out.println("前");return BeanPostProcessor.super.postProcessBeforeInitialization(bean, beanName);}@Overridepublic Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {System.out.println("后");return BeanPostProcessor.super.postProcessAfterInitialization(bean, beanName);}@Overridepublic void afterPropertiesSet() throws Exception {System.out.println("InitializingBean");}@Overridepublic void postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException {//对应的参数与前面的BeanFactory结果基本一样System.out.println("后后");}
}
对应的类User:
package com.domain;import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.beans.factory.BeanNameAware;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;/****/
public class User implements BeanNameAware, BeanFactoryAware, ApplicationContextAware, BeanPostProcessor, InitializingBean, BeanFactoryPostProcessor {@Overridepublic void setBeanName(String s) { //重写的变量名称是可以随便改变的System.out.println("User-BeanNameAware");}@Overridepublic void setBeanFactory(BeanFactory beanFactory) throws BeansException {System.out.println("User-BeanFactoryAware");}@Overridepublic void setApplicationContext(ApplicationContext applicationContext) throws BeansException {System.out.println("User-ApplicationContextAware");}public void init() {System.out.println("User-init");}public void tt() {System.out.println("User-tt");}@Overridepublic Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {System.out.println("User-前");return BeanPostProcessor.super.postProcessBeforeInitialization(bean, beanName);}@Overridepublic Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {System.out.println("User-后");return BeanPostProcessor.super.postProcessAfterInitialization(bean, beanName);}@Overridepublic void afterPropertiesSet() throws Exception {System.out.println("User-InitializingBean");}@Overridepublic void postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException {//对应的参数与前面的BeanFactory结果基本一样System.out.println("User-后后");}
}
对应的Test类:
package com.domain;import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.beans.factory.BeanNameAware;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;/****/
public class Test implements BeanNameAware, BeanFactoryAware, ApplicationContextAware, InitializingBean {@Overridepublic void setBeanName(String s) { //重写的变量名称是可以随便改变的System.out.println("Test-BeanNameAware");}@Overridepublic void setBeanFactory(BeanFactory beanFactory) throws BeansException {System.out.println("Test-BeanFactoryAware");}@Overridepublic void setApplicationContext(ApplicationContext applicationContext) throws BeansException {System.out.println("Test-ApplicationContextAware");}public void init() {System.out.println("Test-init");}public void tt() {System.out.println("Test-init");}@Overridepublic void afterPropertiesSet() throws Exception {System.out.println("Test-InitializingBean");}}
对应的测试类:
package com.test;import com.domain.Account;
import com.domain.Company;
import com.factory.CompanyFactoryBean;
import org.junit.Test;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.xml.XmlBeanFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.core.io.ClassPathResource;/****/public class Test1 {@Testpublic void text1() {ApplicationContext classPathXmlApplicationContext = newClassPathXmlApplicationContext("applicationContext.xml");System.out.println(1);Object fac = classPathXmlApplicationContext.getBean("fac");System.out.println(5);}
}
对应的配置文件:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans.xsd"><bean id="ac" class="com.domain.Account" init-method="init" destroy-method="tt"></bean><bean id="acc" class="com.domain.User" init-method="init" destroy-method="tt"></bean><bean id="facc" class="com.domain.Test" init-method="init" destroy-method="tt"></bean><bean id="fac" class="com.domain.Test" init-method="init" destroy-method="tt"></bean></beans>
现在我们来看看结果:
/*
BeanNameAware  //如果对应有构造方法,那么在这个前面
BeanFactoryAware
ApplicationContextAware
InitializingBean
init     //实际上对应的两个接口也有优先级,如果你的结束实现是BeanFactoryPostProcessor,另外一个是BeanPostProcessor,那么实现了BeanFactoryPostProcessor的先操作(对应的都是,即包括"后后"),若都有,那么看配置文件先后了,并且后后在对应的所有初始化之后User-BeanNameAware  //如果对应有构造方法(打印信息,一般这里都是说明打印的信息),那么在这个前面
User-BeanFactoryAware
User-ApplicationContextAware
User-InitializingBean
User-init
--------------------------------- 上面初始化都完毕(实现了对应接口的先执行,无论没有实现的在配置文件内容的相应实现接口的前面还是在后面,都是实现接口的先执行)
后后
User-后后
--------------------------------- 上面操作后后方法
Test-BeanNameAware  //如果对应有构造方法,那么在这个前面
Test-BeanFactoryAware
Test-ApplicationContextAware
--------------------------------- 上面一般操作
前
User-前
--------------------------------- 上面实现了对应的BeanPostProcessor和BeanFactoryPostProcessor接口要操作的方法,但要注意,如果对应的类都是实现接口的,自然没有他这个操作了,因为他只是针对没有实现对应接口的类,前面也说明了"其他的初始化都在这个实现接口的初始化的前方法(先给出所有)和后方法中间",其中说明的是"其他",好像拦截一样哦
Test-InitializingBean
Test-init
--------------------------------- 操作对应的初始化
后
User-后
--------------------------------- 上面实现了对应的BeanPostProcessor和BeanFactoryPostProcessor接口要操作的方法
Test-BeanNameAware
Test-BeanFactoryAware
Test-ApplicationContextAware
前
User-前
Test-InitializingBean
Test-init
后
User-后
--------------------------------- 另外一个类
1
5
--------------------------------- 打印,如果对应类实现了FactoryBean,那么在getBean后面还会打印一个如下(前提是获取实现了该接口的类,否则不打印):
后
User-后
将对应测试类的ApplicationContext修改成ClassPathXmlApplicationContext,并操作cloas方法(前提对方是单例),那么5后面会打印:
Test-tt
Test-tt
User-tt
tt
*///实际上构造方法执行后,他一般并不存放在map中,这里与我们自定义的是不同的,只有在初始化后,并且经过对应的postProcessBeanFactory方法,才会最终到map中的,可以在postProcessBeanFactory方法中看看ConfigurableListableBeanFactory的singletonObjects值,可以发现有对应的实例,最后将该ConfigurableListableBeanFactory的singletonObjects值交给对应的类的singletonObjects(值)(finishBeanFactoryInitialization(beanFactory);的操作),如
/*ClassPathXmlApplicationContext classPathXmlApplicationContext = newClassPathXmlApplicationContext("applicationContext.xml");
最后该classPathXmlApplicationContext就有对应的singletonObjects值了
*/
至此,大致流程给出完毕
这里还要说明一下BeanFactoryPostProcessor:针对整个Bean的工⼚进行处理,其对应接口只有一个,且方法参数为ConfigurableListableBeanFactory,其中有个方法名为getBeanDefinition的方法,我们可以根据此方法,找到我们定义bean 的BeanDefinition对象,然后我们可以对定义的属性进行修改
BeanDefinition对象(他一般是接口)中的方法名字类似我们bean标签的属性,setBeanClassName对应bean标签中的class属性,所以当我们拿到BeanDefinition对象时,我们可以⼿动修改bean标签中所定义的属性值
BeanDefinition对象(实现该接口的对象,具体是谁可以百度):我们在 XML 中定义的 bean标签,Spring 解析 bean 标签成为一个 JavaBean, 这个JavaBean 就是 BeanDefinition,在后面会说明的他的加载过程
注意:调用 BeanFactoryPostProcessor 方法时,这时候bean还没有实例化,此时 bean 刚被解析成BeanDefinition对象,但也只是针对没有实现对应接口的类,看对应的参数信息就知道了(初始化之后才是(class的值变的)实例的产生,虽然前面可能是实例了,但是并不完全,这里只考虑完全的,即放入map中的,即初始化后才会放入map中)
Spring IOC源码深度剖析:
一般来说我们ctrl+左键是看class文件(反编译,idea看的基本都是),可能会与真正的源码有点问题(反编译并不一定都对,可以认为是失帧)
所以我们需要直接的源码,那么我们来进行下载:首先是github上搜索spring-framework,找到点击第一个spring-projects/spring-framework,这里直接给出地址:https://github.com/spring-projects/spring-framework,找到5.1.x版本,下载ZIP文件即可
然后我们解压,现在我们还要再下载一个工具:gradle(类似于maven,也拥有maven的一些规定),为什么要他,这是因为现在大多数的源码基本都是由他来管理了,而不是maven,所以这里需要下载,可能你并没有学习过,这里给出一个视频地址可以来学习学习:
https://www.bilibili.com/video/BV1yT41137Y7/?spm_id_from=333.337.search-card.all.click&vd_source=5ea24d17d411aaa69d753a5f20d6e0a2
还有几个博客可以学习构建:
https://blog.csdn.net/weixin_44167408/article/details/121769949
https://blog.51cto.com/u_15773967/5638868
https://blog.csdn.net/tuomazhao/article/details/105400234(建议这个)
在视频(这里并没有说明博客)中虽然说明必须对应版本,但是其实通常是可以高于对方版本的,比如gradle 的版本可以高于等于idea 版本(但一般建议等于,虽然大多数都是可以,只是对方在升级以后的版本时,可能随时舍弃兼容低版本的功能),jdk也可以高于等于对应的版本(一般是高于等于8),实际上将gradle看成maven就行了,只是某些不同而已,但是最终的结果还是相同的
相关文件的下载地址:
链接:https://pan.baidu.com/s/1I1S_vut_-VRhsNj_hH7e1A
提取码:alsk
当然,对应的源码只是源代码,通常是需要编译的,并且一般需要顺序(其中一个编译可能需要其他的jar,且一般只是当前项目的(但是需要编译,这里与maven是不同的一点),因为spring官方给出的源码中,他既然是下载的,所以通常并不提供远程jar包)
一般的顺序是spring-core,spring-oxm,sprinh-context,spring-beans,spring-aspects,spring-aop等等顺序,当然,可能顺序改变了,但是一般core通常都需要先编译,其他的顺序一般不固定,编译方式(gradle的编译与maven是不同的,必须编译后才可提供给其他项目,否则其他项目一般获取不了,当然随着时代的发展,可能也会借鉴maven的,所以具体如何,还是需要自己测试):

在这里插入图片描述

双击上面的compileTestJava即可编译,每一个项目基本都是双击他,好了现在我们来直接的使用该源码来操作spring,在该整个项目下再创建子模块,项目如下:

在这里插入图片描述

对应的build.gradle:
plugins {id 'java'
}group 'org.springframework'
version '5.1.21.BUILD-SNAPSHOT'repositories {mavenCentral()
}dependencies {implementation 'org.testng:testng:7.1.0'testImplementation 'org.junit.jupiter:junit-jupiter-api:5.8.1'testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.8.1'compile(project(":spring-context"))compile group: 'org.aspectj', name: 'aspectjweaver', version: '1.8.6'testCompile group: 'junit', name: 'junit', version: '4.12'
}
对应的ItBean类:
package com.lagou.edu;/****/
public class ItBean {private LagouBean lagouBean;public void setLagouBean(LagouBean lagouBean) {this.lagouBean = lagouBean;}/*** 构造函数*/public ItBean(){System.out.println("ItBean 构造器...");}
}
对应的LagouBean类:
package com.lagou.edu;import org.springframework.beans.BeansException;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;/****/
public class LagouBean implements InitializingBean, ApplicationContextAware {private ItBean itBean;public void setItBean(ItBean itBean) {this.itBean = itBean;}/*** 构造函数*/public LagouBean(){System.out.println("LagouBean 构造器...");}/*** InitializingBean 接口实现*/public void afterPropertiesSet() throws Exception {System.out.println("LagouBean afterPropertiesSet...");}public void print() {System.out.println("print方法业务逻辑执行");}@Overridepublic void setApplicationContext(ApplicationContext applicationContext) throws BeansException {System.out.println("setApplicationContext....");}
}
对应的application.xml:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"xmlns:aop="http://www.springframework.org/schema/aop"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://www.springframework.org/schema/beanshttps://www.springframework.org/schema/beans/spring-beans.xsd"><!--循环依赖问题--><bean id="lagouBean" class="com.lagou.edu.LagouBean">
<property name="itBean" ref="itBean"></property></bean><bean id="itBean" class="com.lagou.edu.ItBean">
<property name="lagouBean" ref="lagouBean"></property></bean><!--实际上如果根据前面我们自己操作的注入方式,一般没有循环依赖,这是因为他们只是操作同一个值,只是赋值关系但是如果是创建对象,即多例的情况下,就容易出现了(单例一般没有),你认为下面的代码会出现问题吗:
public class a {public b b = new b();}public class b {public a a = new a(); //这里是创建对象,如果是赋值的话,那么就不会出现问题}
public class c {public static void main(String[] args) {a a  =new a();b b = new b();System.out.println(a);System.out.println(b);}
}
答:实际上会出现问题,一直操作中途的,而多例由于也是中途(getBean中继续getBean,一直这样(Spring内部造成的循环,因为需要考虑其自身方法,导致方法的多次调用,且基本无限,最终导致容易导致栈帧的变多,所以一般情况下,方法本身可能会有变量使得在调用一定的程度时,他的值也在一定的值,最终出现报错来直接的结束方法,这就是为什么使用多例,是出现错误而没有出现栈溢出的原因),是否与这里的创建对象中继续创建对象是类似的呢),所以就会出现循环依赖,但是上面的都是单例,所以不会出现--></beans>
对应的IocTest类:
import com.lagou.edu.LagouBean;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;/****/public class IocTest {/*** Ioc 容器源码分析基础案例*/@Testpublic void testIoC() {// ApplicationContext是容器的高级接口,BeanFacotry是顶级容器/根容器,规范了/定义了容器的基础行为// Spring应用上下文,官方称之为 IoC容器(错误的认识:容器就是map而已;准确来说,map是ioc容器的一个成员,// 叫做单例池,singletonObjects(这个有印象吧,前面有过说明),容器是一组组件和过程的集合,包括BeanFactory、单例池、BeanPostProcessor等以及之间的协作流程),所以如果说容器是"进入:classPathXmlApplicationContext"的对象也是可以的,虽然他是其中一种,当然,可能由于关联也算,所以才说容器是组件和过程的集合ApplicationContext applicationContext = new ClassPathXmlApplicationContext("classpath:application.xml");LagouBean lagouBean = applicationContext.getBean(LagouBean.class);System.out.println(lagouBean);}
}
进行执行,若出现类似的如下,代表操作成功(即也代表spring源码也是正确的):
/*
LagouBean 构造器...
setApplicationContext....
LagouBean afterPropertiesSet...
ItBean 构造器...
com.lagou.edu.LagouBean@187877cc //多次的执行,自然不同,因为都是class来创建的,自然不是同一个,且给堆的空间自然也会不同,只是天选的人可能会相同,但是一般对应的信息也会与时间戳相关,所以相同是非常难的(之所以没有说不可能,是因为你也可以修改时间戳,或者改变源码)
*/
我们来操作多例,看看是否存在循环依赖:
<bean id="lagouBean" class="com.lagou.edu.LagouBean" scope="prototype"><property name="itBean" ref="itBean"></property></bean><bean id="itBean" class="com.lagou.edu.ItBean" scope="prototype">
<property name="lagouBean" ref="lagouBean"></property></bean>
执行后,可以明显的发现,他发生了报错,也就是说,的确存在循环依赖,但是他的报错是监测的,所以是直接的报错,而不是因为堆空间形成的错误
现在我们来ctrl+左键进入,可以明显的发现,与class反射不同的是,他出现了对应的注释,在jdk自带的类中也是这样的(在某些情况下,可能不会,具体可以百度),这就是class和java的显示区别,因为对方的jar包基本只提供了class(虽然jar基本都是提供class,可能会提供java,具体可以百度),而没有java,当然,class反编译的显示问题是很难看见的,所以我们了解即可
Spring IoC容器初始化主体流程:
Spring IoC的容器体系:
IoC容器是Spring的核心模块,是抽象了对象管理、依赖关系管理的框架解决方案,Spring 提供了很多 的容器,其中 BeanFactory 是顶层容器(根容器),不能被实例化,它定义了所有 IoC 容器 必须遵从 的一套原则,具体的容器实现可以增加额外的功能,比如我们常用到的ApplicationContext,其下更具 体的实现如 ClassPathXmlApplicationContext 包含了解析 xml 等一系列的内容,AnnotationConfigApplicationContext 则是包含了注解解析等一系列的内容,Spring IoC 容器继承体系 ⾮常聪明,需要使用哪个层次用哪个层次即可,不必使用功能大而全的
BeanFactory 顶级接⼝方法栈如下:

在这里插入图片描述

最后一个是&,前面操作的".getBean(“&fac”);“是否还记得呢,就是对应的作用,而不是操作工厂(我们不需要他操作对应的getObject()方法的值),而由于在前面,所以一般我们称为"工厂bean前缀”(前提是操作了对应的工厂,否则是会报错的,即单纯的不识别该符号,内部错误,一般在bean初始化放入map时就会报错了)
BeanFactory 容器继承体系(当然,版本不同,这个继承可能也不同):

在这里插入图片描述

通过其接⼝设计,我们可以看到我们一贯使用的 ApplicationContext 除了继承BeanFactory的⼦接⼝, 还继承了ResourceLoader、MessageSource等接⼝,因此其提供的功能也就更丰富了,下面我们以 ClasspathXmlApplicationContext(ApplicationContext的子类,上面只是说明主要的接口)为例,深⼊源码说明 IoC 容器的初始化流程
Bean生命周期关键时机点:
思路:创建一个类 LagouBean ,让其实现⼏个特殊的接⼝,并分别在接⼝实现的构造器、接⼝方法中 断点,观察线程调用栈,分析出 Bean 对象创建和管理关键点的触发时机
修改LagouBean类:
package com.lagou.edu;import org.springframework.beans.BeansException;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;/****/
public class LagouBean implements InitializingBean {private ItBean itBean;public void setItBean(ItBean itBean) {this.itBean = itBean;}/*** 构造函数*/public LagouBean() {System.out.println("LagouBean 构造器...");}/*** InitializingBean 接口实现*/public void afterPropertiesSet() throws Exception {System.out.println("LagouBean afterPropertiesSet...");}}
在对应的edu包下创建MyBeanPostProcessor类:
package com.lagou.edu;import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;/****/
public class MyBeanPostProcessor implements BeanPostProcessor {public MyBeanPostProcessor() {System.out.println("BeanPostProcessor 实现类构造函数...");}@Overridepublic Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {if ("lagouBean".equals(beanName)) {System.out.println("BeanPostProcessor 实现类 postProcessBeforeInitialization 方法被调用中......");}return bean;}@Overridepublic Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {if ("lagouBean".equals(beanName)) {System.out.println("BeanPostProcessor 实现类 postProcessAfterInitialization 方法被调用中......");}return bean;}
}
同样的还在该包下创建MyBeanFactoryPostProcessor类:
package com.lagou.edu;import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;/****/
public class MyBeanFactoryPostProcessor implements BeanFactoryPostProcessor {public MyBeanFactoryPostProcessor() {System.out.println("BeanFactoryPostProcessor的实现类构造函数...");}@Overridepublic void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {System.out.println("BeanFactoryPostProcessor的实现方法调用中......");}
}
application.xml修改成如下:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"xmlns:aop="http://www.springframework.org/schema/aop"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://www.springframework.org/schema/beanshttps://www.springframework.org/schema/beans/spring-beans.xsd"><bean id="lagouBean" class="com.lagou.edu.LagouBean"/><bean id="myBeanFactoryPostProcessor" class="com.lagou.edu.MyBeanFactoryPostProcessor"/><bean id="myBeanPostProcessor" class="com.lagou.edu.MyBeanPostProcessor"/></beans>
IocTest类如下(虽然基本没有改变):
import com.lagou.edu.LagouBean;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;/****/public class IocTest {/*** Ioc 容器源码分析基础案例*/@Testpublic void testIoC() {ApplicationContext applicationContext = new ClassPathXmlApplicationContext("classpath:application.xml");LagouBean lagouBean = applicationContext.getBean(LagouBean.class);System.out.println(lagouBean);}
}
现在开始进行分析,分析 Bean 的创建是在容器初始化时还是在 getBean 时
/*
在这里打上断点:
LagouBean lagouBean = applicationContext.getBean(LagouBean.class);查看对应的singletonObjects是否有对应的实例了,很明显,是有的,因为我们并没有操作什么配置(如延迟加载,多例等等)*/
既然实例是创建好的,那么我们往前走,分析分析构造函数调用情况:
/*
在这里打上断点:
public LagouBean() {System.out.println("LagouBean 构造器..."); //这里打上}进入AbstractApplicationContext类中:
找到refresh方法,在这里打上断点
synchronized (this.startupShutdownMonitor) {// Prepare this context for refreshing.prepareRefresh(); //这里打上// Tell the subclass to refresh the internal bean factory.ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();打上后,一直进行下一步,可以找到这个地方:
finishBeanFactoryInitialization(beanFactory);
然后继续下一步,可以到上面的构造函数,也就是说构造函数的调用时机:
在AbstractApplicationContext类refresh方法的finishBeanFactoryInitialization(beanFactory)处;
*/
为什么我知道是在AbstractApplicationContext类中呢,我们看这里:

在这里插入图片描述

我们点击左边的地方,会到对应的代码那里去,我们在后面可以找到这个:

在这里插入图片描述

后面的基本是操作测试类了,所以这个类是入口,他最终会操作到构造方法(一般在下面的说明先执行的)
分析 InitializingBean 之 afterPropertiesSet 初始化方法调用情况:
/*
给LagouBean类的:
public void afterPropertiesSet() throws Exception {System.out.println("LagouBean afterPropertiesSet..."); //这里打上断点}还是在前面的finishBeanFactoryInitialization(beanFactory);方法里进入即 InitializingBean中afterPropertiesSet 方法的调用时机也是在AbstractApplicationContext类refresh方法的finishBeanFactoryInitialization(beanFactory);处
*/
分析BeanFactoryPostProcessor 初始化和调用情况:
给MyBeanFactoryPostProcessor对应的postProcessBeanFactory方法打上断点,然后给构造方法打上断点,前面的断点不变,然后进行操作:
可以发现发生了改变
MyBeanFactoryPostProcessor构造方法,postProcessBeanFactory方法都在invokeBeanFactoryPostProcessors(beanFactory);里执行(AbstractApplicationContext对应的refresh方法里面),并且顺序是构造方法,postProcessBeanFactory方法
也符合前面的操作,只是他的方法是invokeBeanFactoryPostProcessors(beanFactory);里执行,另外的可以发现在finishBeanFactoryInitialization(beanFactory);方法里执行,并且发现在finishBeanFactoryInitialization(beanFactory);方法的确在invokeBeanFactoryPostProcessors(beanFactory);里后面,也符合前面的打印信息(前面的"现在我们来看看结果"的打印结果)
分析 BeanPostProcessor 初始化和调用情况:
同理,给MyBeanPostProcessor的对应两个方法打上断点查看结果:
/*
可以发现他在registerBeanPostProcessors(beanFactory);执行对应的构造方法,并且在BeanFactoryPostProcessor对应的方法invokeBeanFactoryPostProcessors(beanFactory);后面执行,且在finishBeanFactoryInitialization(beanFactory);对应构造方法之后执行,在执行对应的afterPropertiesSet方法,在操作后方法,然后结束
也就是说,有如下顺序
invokeBeanFactoryPostProcessors(beanFactory); //执行BeanFactoryPostProcessor构造(中间自然有很多,忽略)和postProcessBeanFactory方法
registerBeanPostProcessors(beanFactory); //执行BeanPostProcessor的构造(中间自然有很多,忽略),这里就是前面说明的"实际上对应的两个接口也有优先级"finishBeanFactoryInitialization(beanFactory); //执行LagouBean的构造(中间自然有很多,忽略),执行前方法,执行afterPropertiesSet方法(包括对应的初始化的),执行后方法,至此完毕
可以发现,与前面的打印结果"现在我们来看看结果"是一样的*/
总结:
根据上面的调试分析,我们发现 Bean对象创建的⼏个关键时机点代码层级的调用都在AbstractApplicationContext 类 的 refresh 方法中,可⻅这个方法对于Spring IoC 容器初始化来说相当 关键,汇总如下:

在这里插入图片描述

Spring IoC容器初始化主流程:
由上分析可知,Spring IoC 容器初始化的关键环节就在 AbstractApplicationContext#refresh()方法中(#代表对应方法是该类里面的方法) ,我们查看 refresh 方法来俯瞰容器创建的主体流程,主体流程下的具体⼦流程我们后面再来讨论
//public class ClassPathXmlApplicationContext extends AbstractXmlApplicationContext {
//public abstract class AbstractXmlApplicationContext extends AbstractRefreshableConfigApplicationContext {
//public abstract class AbstractRefreshableConfigApplicationContext extends AbstractRefreshableApplicationContext
//public abstract class AbstractRefreshableApplicationContext extends AbstractApplicationContext {
//所以ClassPathXmlApplicationContext可以执行这里的refresh方法
//	ApplicationContext applicationContext = new ClassPathXmlApplicationContext("classpath:application.xml");public abstract class AbstractApplicationContext extends DefaultResourceLoaderimplements ConfigurableApplicationContext {//..@Overridepublic void refresh() throws BeansException, IllegalStateException {synchronized (this.startupShutdownMonitor) {// Prepare this context for refreshing.// 第一步:刷新前的预处理prepareRefresh();// Tell the subclass to refresh the internal bean factory.//第二步:获取BeanFactory,默认实现是DefaultListableBeanFactory//加载BeanDefinition 并注册到 BeanDefinitionRegistryConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();// Prepare the bean factory for use in this context.// 第三步:BeanFactory的预准备工作(BeanFactory进行一些设置,比如context的类加载器等)prepareBeanFactory(beanFactory);try {// Allows post-processing of the bean factory in context subclasses.// 第四步:BeanFactory准备工作完成后进行的后置处理工作postProcessBeanFactory(beanFactory);// Invoke factory processors registered as beans in the context.// 第五步:实例化并调用实现了BeanFactoryPostProcessor接⼝的BeaninvokeBeanFactoryPostProcessors(beanFactory);// Register bean processors that intercept bean creation.// 第六步:注册BeanPostProcessor(Bean的后置处理器),在创建bean的前后等执行registerBeanPostProcessors(beanFactory);//要额外注意,上面的两个操作也是存在循环依赖的,最终他们两个方法是有联系的,只是一般我们不会操作注入,基本上操作方式也是三级缓存,或者说是一样的(最终到一级的),由于一般不操作注入,所以通常我们对循环依赖的说明只是针对单纯的bean来说的,且由于也是三级缓存,所以我们只要分析单纯的bean即可// Initialize message source for this context.// 第七步:初始化MessageSource组件(做国际化功能;消息绑定,消息解析)initMessageSource();// Initialize event multicaster for this context.// 第⼋步:初始化事件派发器initApplicationEventMulticaster();// Initialize other special beans in specific context subclasses.// 第九步:⼦类重写这个方法,在容器刷新的时候可以⾃定义逻辑onRefresh();// Check for listener beans and register them.// 第⼗步:注册应用的监听器,就是注册实现了ApplicationListener接⼝的监听器beanregisterListeners();// Instantiate all remaining (non-lazy-init) singletons./*第⼗一步:初始化所有剩下的⾮懒加载的单例bean初始化创建⾮懒加载方式的单例Bean实例(未设置属性)填充属性初始化方法调用(比如调用afterPropertiesSet方法、init-method方法)调用BeanPostProcessor(后置处理器)对实例bean进行后置处*/finishBeanFactoryInitialization(beanFactory);// Last step: publish corresponding event./*
第⼗二步:完成context的刷新。主要是调用LifecycleProcessor的onRefresh()方法,并且发布事件 (ContextRefreshedEvent)*/finishRefresh();}catch (BeansException ex) {if (logger.isWarnEnabled()) {logger.warn("Exception encountered during context initialization - " +"cancelling refresh attempt: " + ex);}// Destroy already created singletons to avoid dangling resources.destroyBeans();// Reset 'active' flag.cancelRefresh(ex);// Propagate exception to caller.throw ex;}finally {// Reset common introspection caches in Spring's core, since we// might not ever need metadata for singleton beans anymore...resetCommonCaches();}}}//..}
BeanFactory创建流程:
获取BeanFactory⼦流程:
时序图如下:

在这里插入图片描述

上面从ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();开始,在之前的refresh方法中哦,具体过程如下:
/*
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory(); //这个obtainFreshBeanFactoryprotected ConfigurableListableBeanFactory obtainFreshBeanFactory() {refreshBeanFactory(); //这个refreshBeanFactoryreturn getBeanFactory();}AbstractRefreshableApplicationContext类里面的refreshBeanFactory@Overrideprotected final void refreshBeanFactory() throws BeansException {//判断是否存在beanfactoryif (hasBeanFactory()) {destroyBeans();closeBeanFactory();}try {DefaultListableBeanFactory beanFactory = createBeanFactory(); //这个beanFactory.setSerializationId(getId());//定义一些工厂属性,比如是否覆盖(多个配置文件相同id,那么是否被覆盖呢,没有覆盖的情况下,一般多个相同id会报错,发生冲突,发生冲突的错误出现在于我们主动去读取配置文件,否则是不会出现错误的,而name的多个一般存在于getBean的读取,虽然可以避免),是否允许循环依赖(一般只是代表存在,具体看源码,后面会有例子的,即isPrototypeCurrentlyInCreation,即代表操作赋值,虽然必然会的,所以这里注意即可,但并不需要了解,主要在说明循环依赖那里)customizeBeanFactory(beanFactory);//加载应用的BeanDefinitionloadBeanDefinitions(beanFactory);this.beanFactory = beanFactory;}catch (IOException ex) {throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex);}}前面的return getBeanFactory();@Overridepublic final ConfigurableListableBeanFactory getBeanFactory() {//@Nullable//private volatile DefaultListableBeanFactory beanFactory;DefaultListableBeanFactory beanFactory = this.beanFactory; //这个if (beanFactory == null) {throw new IllegalStateException("BeanFactory not initialized or already closed - " +"call 'refresh' before accessing beans via the ApplicationContext");}return beanFactory;}//public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFactoryimplements ConfigurableListableBeanFactory, BeanDefinitionRegistry, Serializable {正好对应于时序图,最终得到ConfigurableListableBeanFactory,是DefaultListableBeanFactory的父类(父接口)
BeanDefinition加载解析及注册⼦流程:
该⼦流程涉及到如下⼏个关键步骤:
1:Resource定位:指对BeanDefinition的资源定位过程,通俗讲就是找到定义Javabean信息的XML⽂件,并将其封装成Resource对象(你看看对应的postProcessBeanFactory方法的参数是否就是最终得到ConfigurableListableBeanFactory,并且前面也说明过他的方法,即getBeanDefinition的方法)
2:BeanDefinition载⼊ :把用户定义好的Javabean表示为IoC容器内部的数据结构,这个容器内部的数 据结构就是BeanDefinition
3:注册BeanDefinition到 Ioc 容器
过程分析:
⼦流程⼊⼝在 AbstractRefreshableApplicationContext#refreshBeanFactory 方法中,也就是前面的:
@Overrideprotected final void refreshBeanFactory() throws BeansException {if (hasBeanFactory()) {destroyBeans();closeBeanFactory();}try {DefaultListableBeanFactory beanFactory = createBeanFactory();beanFactory.setSerializationId(getId());customizeBeanFactory(beanFactory);//就是这里loadBeanDefinitions(beanFactory);this.beanFactory = beanFactory;}catch (IOException ex) {throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex);}}
我们在上面进行调试:
public abstract class AbstractXmlApplicationContext extends AbstractRefreshableConfigApplicationContext {//..//到这里来@Overrideprotected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {// Create a new XmlBeanDefinitionReader for the given BeanFactory.//给指定的beanFactory创建这个对象,用来解析xml对象的,实际上对应的方法refresh对应的类,早就给出一个指定xml配置文件的地方了,只是我们并没有说明而已XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);// Configure the bean definition reader with this context's// resource loading environment.//给这个对象设置一些context的上下文环境属性beanDefinitionReader.setEnvironment(this.getEnvironment());beanDefinitionReader.setResourceLoader(this);beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));// Allow a subclass to provide custom initialization of the reader,// then proceed with actually loading the bean definitions.//提供给子类实现一些自定义的初始化策略initBeanDefinitionReader(beanDefinitionReader);//真正的进行加载BeanDefinition(或者说BeanDefinitions)loadBeanDefinitions(beanDefinitionReader);}//..//到这里protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws BeansException, IOException {//从对应的资源对象中加载BeanDefinitionResource[] configResources = getConfigResources();if (configResources != null) {reader.loadBeanDefinitions(configResources);}//从xml配置文件中加载BeanDefinitionString[] configLocations = getConfigLocations();if (configLocations != null) {//XmlBeanDefinitionReader readerreader.loadBeanDefinitions(configLocations); //到这里}}//..}//XmlBeanDefinitionReader reader
//public class XmlBeanDefinitionReader extends AbstractBeanDefinitionReader {public abstract class AbstractBeanDefinitionReader implements BeanDefinitionReader, EnvironmentCapable {//..//第四到@Overridepublic int loadBeanDefinitions(Resource... resources) throws BeanDefinitionStoreException {Assert.notNull(resources, "Resource array must not be null");int count = 0;for (Resource resource : resources) {count += loadBeanDefinitions(resource); //这里进入,直接操作资源对象了,而不是xml(String,String可以保存非常多信息哦,甚至是html网页信息也行的,所以xml被String读取保存也是可行的)}return count;}//第二到@Overridepublic int loadBeanDefinitions(String location) throws BeanDefinitionStoreException {return loadBeanDefinitions(location, null);}//第三到public int loadBeanDefinitions(String location, @Nullable Set<Resource> actualResources) throws BeanDefinitionStoreException {//获取上下文的资源加载器ResourceLoader resourceLoader = getResourceLoader();if (resourceLoader == null) {throw new BeanDefinitionStoreException("Cannot load bean definitions from location [" + location + "]: no ResourceLoader available");}
//判断资源加载器是否是对应的ResourcePatternResolver类型(是否是其或者其的子类),一般读取文件路径操作基本都是操作这个接口if (resourceLoader instanceof ResourcePatternResolver) {// Resource pattern matching available.try {//统一加载转换为Resource资源对象Resource[] resources = ((ResourcePatternResolver) resourceLoader).getResources(location);//加载资源中的BeanDefinition的对象,并返回其数量,这里是主要的,我们进入,发现回到第二到int count = loadBeanDefinitions(resources);if (actualResources != null) {Collections.addAll(actualResources, resources);}if (logger.isTraceEnabled()) {logger.trace("Loaded " + count + " bean definitions from location pattern [" + location + "]");}return count;}catch (IOException ex) {throw new BeanDefinitionStoreException("Could not resolve bean definition resource pattern [" + location + "]", ex);}}else {// Can only load single resources by absolute URL.Resource resource = resourceLoader.getResource(location);int count = loadBeanDefinitions(resource);if (actualResources != null) {actualResources.add(resource);}if (logger.isTraceEnabled()) {logger.trace("Loaded " + count + " bean definitions from location [" + location + "]");}return count;}}//先到这里@Overridepublic int loadBeanDefinitions(String... locations) throws BeanDefinitionStoreException {Assert.notNull(locations, "Location array must not be null");int count = 0;//如果有多个配置文件,循环读取加载,并统计总共加载了多少个BeanDefinition(一般标签就是一个BeanDefinition,Spring 解析 bean 标签成为一个 JavaBean, 这个JavaBean 就是 BeanDefinition(显示问题,一般是复制粘贴的),在后面会说明的他的加载过程)for (String location : locations) {count += loadBeanDefinitions(location);}return count;}}public class XmlBeanDefinitionReader extends AbstractBeanDefinitionReader {//..
@Overridepublic int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException {return loadBeanDefinitions(new EncodedResource(resource));}public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException {Assert.notNull(encodedResource, "EncodedResource must not be null");if (logger.isTraceEnabled()) {logger.trace("Loading XML bean definitions from " + encodedResource);}Set<EncodedResource> currentResources = this.resourcesCurrentlyBeingLoaded.get();if (currentResources == null) {currentResources = new HashSet<>(4);this.resourcesCurrentlyBeingLoaded.set(currentResources);}if (!currentResources.add(encodedResource)) {throw new BeanDefinitionStoreException("Detected cyclic loading of " + encodedResource + " - check your import definitions!");}try {InputStream inputStream = encodedResource.getResource().getInputStream();try {//把xml文件流封装为InputSource对象InputSource inputSource = new InputSource(inputStream);if (encodedResource.getEncoding() != null) {inputSource.setEncoding(encodedResource.getEncoding());}//执行加载逻辑(大多数do开头的才会真正的进行数据的处理),而不是将数据移动或者较少的处理return doLoadBeanDefinitions(inputSource, encodedResource.getResource());}finally {inputStream.close();}}catch (IOException ex) {throw new BeanDefinitionStoreException("IOException parsing XML document from " + encodedResource.getResource(), ex);}finally {currentResources.remove(encodedResource);if (currentResources.isEmpty()) {this.resourcesCurrentlyBeingLoaded.remove();}}}//..//到这里protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)throws BeanDefinitionStoreException {try {//读取xml信息,将xml中信息保存到Document对象中//我们大多数可能知道import org.dom4j.Document;,但是这里是package org.w3c.dom.Document;,最终的作用其实还是一样的,只是实现不同而已Document doc = doLoadDocument(inputSource, resource);//解析Document对象,真正的封装BeanDefinitions对象并进行注册int count = registerBeanDefinitions(doc, resource);if (logger.isDebugEnabled()) {logger.debug("Loaded " + count + " bean definitions from " + resource);}return count;}catch (BeanDefinitionStoreException ex) {throw ex;}catch (SAXParseException ex) {throw new XmlBeanDefinitionStoreException(resource.getDescription(),"Line " + ex.getLineNumber() + " in XML document from " + resource + " is invalid", ex);}catch (SAXException ex) {throw new XmlBeanDefinitionStoreException(resource.getDescription(),"XML document from " + resource + " is invalid", ex);}catch (ParserConfigurationException ex) {throw new BeanDefinitionStoreException(resource.getDescription(),"Parser configuration exception parsing XML from " + resource, ex);}catch (IOException ex) {throw new BeanDefinitionStoreException(resource.getDescription(),"IOException parsing XML document from " + resource, ex);}catch (Throwable ex) {throw new BeanDefinitionStoreException(resource.getDescription(),"Unexpected exception parsing XML document from " + resource, ex);}}//..//到这里public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();//获取已有BeanDefinition的数量int countBefore = getRegistry().getBeanDefinitionCount();//注册BeanDefinition,这里我们进入registerBeanDefinitions以及createReaderContext(某些初始化,比如命令空间的判断)documentReader.registerBeanDefinitions(doc, createReaderContext(resource));//返回新注册的BeanDefinition的数量return getRegistry().getBeanDefinitionCount() - countBefore;}//..public XmlReaderContext createReaderContext(Resource resource) {return new XmlReaderContext(resource, this.problemReporter, this.eventListener,this.sourceExtractor, this, getNamespaceHandlerResolver());}//..}//BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
public class DefaultBeanDefinitionDocumentReader implements BeanDefinitionDocumentReader {
//..//到这里@Overridepublic void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) {this.readerContext = readerContext;//最终注册的地方doRegisterBeanDefinitions(doc.getDocumentElement());}//..protected void doRegisterBeanDefinitions(Element root) {// Any nested <beans> elements will cause recursion in this method. In// order to propagate and preserve <beans> default-* attributes correctly,// keep track of the current (parent) delegate, which may be null. Create// the new (child) delegate with a reference to the parent for fallback purposes,// then ultimately reset this.delegate back to its original (parent) reference.// this behavior emulates a stack of delegates without actually necessitating one.BeanDefinitionParserDelegate parent = this.delegate;this.delegate = createDelegate(getReaderContext(), root, parent);if (this.delegate.isDefaultNamespace(root)) {String profileSpec = root.getAttribute(PROFILE_ATTRIBUTE);if (StringUtils.hasText(profileSpec)) {String[] specifiedProfiles = StringUtils.tokenizeToStringArray(profileSpec, BeanDefinitionParserDelegate.MULTI_VALUE_ATTRIBUTE_DELIMITERS);// We cannot use Profiles.of(...) since profile expressions are not supported// in XML config. See SPR-12458 for details.if (!getReaderContext().getEnvironment().acceptsProfiles(specifiedProfiles)) {if (logger.isDebugEnabled()) {logger.debug("Skipped XML bean definition file due to specified profiles [" + profileSpec +"] not matching: " + getReaderContext().getResource());}return;}}}preProcessXml(root);//到这里parseBeanDefinitions(root, this.delegate);postProcessXml(root);this.delegate = parent;}//..protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {if (delegate.isDefaultNamespace(root)) {NodeList nl = root.getChildNodes();for (int i = 0; i < nl.getLength(); i++) {Node node = nl.item(i);if (node instanceof Element) {Element ele = (Element) node;if (delegate.isDefaultNamespace(ele)) {//解析默认的标签元素,我们进入这里parseDefaultElement(ele, delegate);}else {//解析自定义的标签元素delegate.parseCustomElement(ele);}}}}else {delegate.parseCustomElement(root);}}//到这里private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) {//import元素处理if (delegate.nodeNameEquals(ele, IMPORT_ELEMENT)) {importBeanDefinitionResource(ele);}//alias元素处理else if (delegate.nodeNameEquals(ele, ALIAS_ELEMENT)) {processAliasRegistration(ele);}//bean元素处理else if (delegate.nodeNameEquals(ele, BEAN_ELEMENT)) {//我们进入这个processBeanDefinition(ele, delegate);}//嵌套beans的处理else if (delegate.nodeNameEquals(ele, NESTED_BEANS_ELEMENT)) {// recursedoRegisterBeanDefinitions(ele);}}//..//到这里protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {//解析对应bean的BeanDefinition(bean信息封装给他的),BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);if (bdHolder != null) {//如果有自定义标签,则处理自定义标签bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);try {// Register the final decorated instance.//这里会完成BeanDefinition的注册,我们进入BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());}catch (BeanDefinitionStoreException ex) {getReaderContext().error("Failed to register bean definition with name '" +bdHolder.getBeanName() + "'", ele, ex);}// Send registration event.getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder));}}//..}
//实际上大多数的框架的类在调用使用时,都是从上到下的调用,当然,有些类并非如此,所以只是说明大多数而已
//最终到这里
public abstract class BeanDefinitionReaderUtils {//..public static void registerBeanDefinition(BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry)throws BeanDefinitionStoreException {// Register bean definition under primary name.String beanName = definitionHolder.getBeanName(); //这个一般就是对应的id名称了,当然,也会与name相关的,前提是id不存在//最终的注册,我们进入registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition());// Register aliases for bean name, if any.String[] aliases = definitionHolder.getAliases();if (aliases != null) {for (String alias : aliases) {//别名的保存registry.registerAlias(beanName, alias);}}}//..}@SuppressWarnings("serial")
public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFactoryimplements ConfigurableListableBeanFactory, BeanDefinitionRegistry, Serializable {//..//到这里@Overridepublic void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)throws BeanDefinitionStoreException {Assert.hasText(beanName, "Bean name must not be empty");Assert.notNull(beanDefinition, "BeanDefinition must not be null");if (beanDefinition instanceof AbstractBeanDefinition) {try {((AbstractBeanDefinition) beanDefinition).validate();}catch (BeanDefinitionValidationException ex) {throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName,"Validation of bean definition failed", ex);}}BeanDefinition existingDefinition = this.beanDefinitionMap.get(beanName);if (existingDefinition != null) {if (!isAllowBeanDefinitionOverriding()) {throw new BeanDefinitionOverrideException(beanName, beanDefinition, existingDefinition);}else if (existingDefinition.getRole() < beanDefinition.getRole()) {// e.g. was ROLE_APPLICATION, now overriding with ROLE_SUPPORT or ROLE_INFRASTRUCTUREif (logger.isInfoEnabled()) {logger.info("Overriding user-defined bean definition for bean '" + beanName +"' with a framework-generated bean definition: replacing [" +existingDefinition + "] with [" + beanDefinition + "]");}}else if (!beanDefinition.equals(existingDefinition)) {if (logger.isDebugEnabled()) {logger.debug("Overriding bean definition for bean '" + beanName +"' with a different definition: replacing [" + existingDefinition +"] with [" + beanDefinition + "]");}}else {if (logger.isTraceEnabled()) {logger.trace("Overriding bean definition for bean '" + beanName +"' with an equivalent definition: replacing [" + existingDefinition +"] with [" + beanDefinition + "]");}}this.beanDefinitionMap.put(beanName, beanDefinition);}else {if (hasBeanCreationStarted()) {// Cannot modify startup-time collection elements anymore (for stable iteration)synchronized (this.beanDefinitionMap) {this.beanDefinitionMap.put(beanName, beanDefinition);List<String> updatedDefinitions = new ArrayList<>(this.beanDefinitionNames.size() + 1);updatedDefinitions.addAll(this.beanDefinitionNames);updatedDefinitions.add(beanName);this.beanDefinitionNames = updatedDefinitions;removeManualSingletonName(beanName);}}else {// Still in startup registration phase//真正的注册,可以看到是put方法,即放入map中了,一个名称,一个beanDefinitionthis.beanDefinitionMap.put(beanName, beanDefinition);this.beanDefinitionNames.add(beanName);removeManualSingletonName(beanName);}this.frozenBeanDefinitionNames = null;}if (existingDefinition != null || containsSingleton(beanName)) {resetBeanDefinition(beanName);}else if (isConfigurationFrozen()) {clearByTypeCache();}}//..}//至此,我们最终到达this.beanDefinitionMap.put(beanName, beanDefinition);,即对应的bean注册或者添加完毕,最终返回存放bean的工厂,即ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();的确是获取工厂的
测试String是否可以写上很多信息:
package com.test;import org.junit.Test;/****/public class Test1 {@Testpublic void text1() {String a = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" +"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" +"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" +"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" +"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" +"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" +"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" +"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" +"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" +"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" +"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" +"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" +"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" +"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" +"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" +"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" +"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" +"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" +"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" +"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" +"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" +"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" +"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" +"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" +"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" +"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" +"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" +"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" +"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" +"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" +"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" +"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa";System.out.println(a);}
}
执行一下吧,在一些常见的编程语言中,如Java和C#,String类型的上限是根据底层内存限制来确定的,一般来说,Java中的String对象最大长度是Integer.MAX_VALUE - 2,即2^31 - 2个字符(因为Java中的String对象还需要2个额外的字符用于存储长度信息),在C#中,String对象的最大长度是2GB(即只看具体的内存大小)
前面我们知道了bean的封装以及存放他的一个bean工厂,即ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
他进入的类是如下:
/*
从上往下进入
AbstractApplicationContext
AbstractRefreshableApplicationContext
AbstractXmlApplicationContext
AbstractBeanDefinitionReader
XmlBeanDefinitionReader
DefaultBeanDefinitionDocumentReader
BeanDefinitionReaderUtils
DefaultListableBeanFactory
*/
现在我们来看看Bean创建流程(即通过封装好的信息,来创建对象(class)):
我们知道普通的bean(没有实现对应接口的)是在前面的finishBeanFactoryInitialization(beanFactory);方法里面的(一般他的执行完,才是真的创建实例,即这个时候对应的真正map中(从其他移动到这里(前面有说明singletonObjects的变化"交给对应的类的singletonObjects(值)"),一般并没有重新创建,创建一次就行了,在一定程度上也可以说是一级缓存移动到这里)才会存在id和value了),由于主要说明普通的,所以这里我们就看这个方法:
/*finishBeanFactoryInitialization(beanFactory); 我们进入*/public abstract class AbstractApplicationContext extends DefaultResourceLoaderimplements ConfigurableApplicationContext {//..protected void finishBeanFactoryInitialization(ConfigurableListableBeanFactory beanFactory) {// Initialize conversion service for this context.if (beanFactory.containsBean(CONVERSION_SERVICE_BEAN_NAME) &&beanFactory.isTypeMatch(CONVERSION_SERVICE_BEAN_NAME, ConversionService.class)) {beanFactory.setConversionService(beanFactory.getBean(CONVERSION_SERVICE_BEAN_NAME, ConversionService.class));}// Register a default embedded value resolver if no bean post-processor// (such as a PropertyPlaceholderConfigurer bean) registered any before:// at this point, primarily for resolution in annotation attribute values.if (!beanFactory.hasEmbeddedValueResolver()) {beanFactory.addEmbeddedValueResolver(strVal -> getEnvironment().resolvePlaceholders(strVal));}// Initialize LoadTimeWeaverAware beans early to allow for registering their transformers early.String[] weaverAwareNames = beanFactory.getBeanNamesForType(LoadTimeWeaverAware.class, false, false);for (String weaverAwareName : weaverAwareNames) {getBean(weaverAwareName);}// Stop using the temporary ClassLoader for type matching.beanFactory.setTempClassLoader(null);// Allow for caching all bean definition metadata, not expecting further changes.beanFactory.freezeConfiguration();// Instantiate all remaining (non-lazy-init) singletons.//实例化所有立即加载的单例bean,我们进入这里beanFactory.preInstantiateSingletons();}//..   }//到这里
@SuppressWarnings("serial")
public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFactoryimplements ConfigurableListableBeanFactory, BeanDefinitionRegistry, Serializable {
//..@Overridepublic void preInstantiateSingletons() throws BeansException {if (logger.isTraceEnabled()) {logger.trace("Pre-instantiating singletons in " + this);}// Iterate over a copy to allow for init methods which in turn register new bean definitions.// While this may not be part of the regular factory bootstrap, it does otherwise work fine.//所有bean的名字,后面就操作循环,来为每个bean进行创建List<String> beanNames = new ArrayList<>(this.beanDefinitionNames);// Trigger initialization of all non-lazy singleton beans...//触发所有非延迟加载单例bean的初始化,主要步骤为getBeanfor (String beanName : beanNames) {//合并父BeanDefinition信息,通常需要父的某些信息RootBeanDefinition bd = getMergedLocalBeanDefinition(beanName);if (!bd.isAbstract() && bd.isSingleton() && !bd.isLazyInit()) {if (isFactoryBean(beanName)) {//主要步骤,String FACTORY_BEAN_PREFIX = "&";这里就知道了吧(前面的工厂,或者说自定义创建过程)Object bean = getBean(FACTORY_BEAN_PREFIX + beanName);if (bean instanceof FactoryBean) {FactoryBean<?> factory = (FactoryBean<?>) bean;boolean isEagerInit;if (System.getSecurityManager() != null && factory instanceof SmartFactoryBean) {isEagerInit = AccessController.doPrivileged((PrivilegedAction<Boolean>) ((SmartFactoryBean<?>) factory)::isEagerInit,getAccessControlContext());}else {isEagerInit = (factory instanceof SmartFactoryBean &&((SmartFactoryBean<?>) factory).isEagerInit());}if (isEagerInit) {getBean(beanName);}}}else {//直接到这里,因为不是工厂bean,自然也没有对应的&存放,我们进入这里getBean(beanName); //他内部进行了保存,所以并不需要进行得到返回值//这里需要额外注意,如果不是立即加载可能并不操作保存}}}// Trigger post-initialization callback for all applicable beans...for (String beanName : beanNames) {Object singletonInstance = getSingleton(beanName);if (singletonInstance instanceof SmartInitializingSingleton) {SmartInitializingSingleton smartSingleton = (SmartInitializingSingleton) singletonInstance;if (System.getSecurityManager() != null) {AccessController.doPrivileged((PrivilegedAction<Object>) () -> {smartSingleton.afterSingletonsInstantiated();return null;}, getAccessControlContext());}else {smartSingleton.afterSingletonsInstantiated();}}}}//..    }public abstract class AbstractBeanFactory extends FactoryBeanRegistrySupport implements ConfigurableBeanFactory {//..   @Overridepublic Object getBean(String name) throws BeansException {//继续进入return doGetBean(name, null, null, false);}//..protected <T> T doGetBean(String name, @Nullable Class<T> requiredType, @Nullable Object[] args, boolean typeCheckOnly)throws BeansException {//解析beanName(也就是id或者name),得到对应的名称来表示class的,也会考虑工厂的&来得到对应表示的beanName,在后面通常会使用该名称来进一步处理或者说得到&,一般与单纯的得到是不同的String beanName = transformedBeanName(name);Object bean;// Eagerly check singleton cache for manually registered singletons.//单纯尝试从缓存中获取bean(一般spring也有缓存,代表bean不用去容器操作步骤找了,而是直接的返回)Object sharedInstance = getSingleton(beanName);//如果已经存在就返回if (sharedInstance != null && args == null) {if (logger.isTraceEnabled()) {if (isSingletonCurrentlyInCreation(beanName)) {logger.trace("Returning eagerly cached instance of singleton bean '" + beanName +"' that is not fully initialized yet - a consequence of a circular reference");}else {logger.trace("Returning cached instance of singleton bean '" + beanName + "'");}}bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);}else {// Fail if we're already creating this bean instance:// We're assumably within a circular reference.//如果是Prototype(即多例模式),且开启允许循环依赖,那么报错,因为多例模式是容易出现循环依赖的,所以就不能直接的允许循环依赖if (isPrototypeCurrentlyInCreation(beanName)) {throw new BeanCurrentlyInCreationException(beanName);}// Check if bean definition exists in this factory.//检查父工厂中是否存在该对象(一般代表配置文件中的配置文件,那么里面的配置文件就是子工厂,而外面的就是父工厂),若存在的话一般拿出来进行处理,并考虑覆盖关系(读取时,是只能操作一个id的)BeanFactory parentBeanFactory = getParentBeanFactory();//没有父工厂,自然不会考虑这里if (parentBeanFactory != null && !containsBeanDefinition(beanName)) {// Not found -> check parent.String nameToLookup = originalBeanName(name);if (parentBeanFactory instanceof AbstractBeanFactory) {return ((AbstractBeanFactory) parentBeanFactory).doGetBean(nameToLookup, requiredType, args, typeCheckOnly);}else if (args != null) {// Delegation to parent with explicit args.return (T) parentBeanFactory.getBean(nameToLookup, args);}else if (requiredType != null) {// No args -> delegate to standard getBean method.return parentBeanFactory.getBean(nameToLookup, requiredType);}else {return (T) parentBeanFactory.getBean(nameToLookup);}}//然后到这里if (!typeCheckOnly) {markBeanAsCreated(beanName);}try {//合并父子bean的属性RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);checkMergedBeanDefinition(mbd, beanName, args);// Guarantee initialization of beans that the current bean depends on.//处理dependsOn配置(这个我们并不熟悉,虽然他也是一个校验)String[] dependsOn = mbd.getDependsOn();if (dependsOn != null) {for (String dep : dependsOn) {if (isDependent(beanName, dep)) {throw new BeanCreationException(mbd.getResourceDescription(), beanName,"Circular depends-on relationship between '" + beanName + "' and '" + dep + "'");}registerDependentBean(dep, beanName);try {getBean(dep);}catch (NoSuchBeanDefinitionException ex) {throw new BeanCreationException(mbd.getResourceDescription(), beanName,"'" + beanName + "' depends on missing bean '" + dep + "'", ex);}}}// Create bean instance.//开始创建单例beanif (mbd.isSingleton()) {//我们进入这里sharedInstance = getSingleton(beanName, () -> {try {//创建bean,这个表达式作为参数传递return createBean(beanName, mbd, args);}catch (BeansException ex) {// Explicitly remove instance from singleton cache: It might have been put there// eagerly by the creation process, to allow for circular reference resolution.// Also remove any beans that received a temporary reference to the bean.destroySingleton(beanName);throw ex;}});bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);}else if (mbd.isPrototype()) {// It's a prototype -> create a new instance.Object prototypeInstance = null;try {beforePrototypeCreation(beanName);prototypeInstance = createBean(beanName, mbd, args);}finally {afterPrototypeCreation(beanName);}bean = getObjectForBeanInstance(prototypeInstance, name, beanName, mbd);}else {String scopeName = mbd.getScope();if (!StringUtils.hasLength(scopeName)) {throw new IllegalStateException("No scope name defined for bean ´" + beanName + "'");}Scope scope = this.scopes.get(scopeName);if (scope == null) {throw new IllegalStateException("No Scope registered for scope name '" + scopeName + "'");}try {Object scopedInstance = scope.get(beanName, () -> {beforePrototypeCreation(beanName);try {return createBean(beanName, mbd, args);}finally {afterPrototypeCreation(beanName);}});bean = getObjectForBeanInstance(scopedInstance, name, beanName, mbd);}catch (IllegalStateException ex) {throw new BeanCreationException(beanName,"Scope '" + scopeName + "' is not active for the current thread; consider " +"defining a scoped proxy for this bean if you intend to refer to it from a singleton",ex);}}}catch (BeansException ex) {cleanupAfterBeanCreationFailure(beanName);throw ex;}}// Check if required type matches the type of the actual bean instance.if (requiredType != null && !requiredType.isInstance(bean)) {try {T convertedBean = getTypeConverter().convertIfNecessary(bean, requiredType);if (convertedBean == null) {throw new BeanNotOfRequiredTypeException(name, requiredType, bean.getClass());}return convertedBean;}catch (TypeMismatchException ex) {if (logger.isTraceEnabled()) {logger.trace("Failed to convert bean '" + name + "' to required type '" +ClassUtils.getQualifiedName(requiredType) + "'", ex);}throw new BeanNotOfRequiredTypeException(name, requiredType, bean.getClass());}}return (T) bean;}//..
}public class DefaultSingletonBeanRegistry extends SimpleAliasRegistry implements SingletonBeanRegistry {
//..public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {Assert.notNull(beanName, "Bean name must not be null");synchronized (this.singletonObjects) {Object singletonObject = this.singletonObjects.get(beanName);if (singletonObject == null) {//是否正在销毁,是的话,就会抛出异常的if (this.singletonsCurrentlyInDestruction) {throw new BeanCreationNotAllowedException(beanName,"Singleton bean creation not allowed while singletons of this factory are in destruction " +"(Do not request a bean from a BeanFactory in a destroy method implementation!)");}if (logger.isDebugEnabled()) {logger.debug("Creating shared instance of singleton bean '" + beanName + "'");}//验证完要真正开始创建的对象,先标识该bean正在被创建,因为springbean创建的过程复杂,步骤很多,需要标识防止万一(比如一个bean还会存在很多子标签,以及属性的操作,并且是否需要其他的bean来创建他,然后考虑是否保留最终注入,还是帮助创建,然后注入的说法,这里有很多种方式,具体的spring通常是先注入,后放map)beforeSingletonCreation(beanName);boolean newSingleton = false;boolean recordSuppressedExceptions = (this.suppressedExceptions == null);if (recordSuppressedExceptions) {this.suppressedExceptions = new LinkedHashSet<>();}try {//ObjectFactory<?> singletonFactory//创建bean,这个表达式作为参数传递/*所以我们进入之前的://创建bean,这个表达式作为参数传递return createBean(beanName, mbd, args); 进入这里*/singletonObject = singletonFactory.getObject();newSingleton = true;}catch (IllegalStateException ex) {// Has the singleton object implicitly appeared in the meantime ->// if yes, proceed with it since the exception indicates that state.singletonObject = this.singletonObjects.get(beanName);if (singletonObject == null) {throw ex;}}catch (BeanCreationException ex) {if (recordSuppressedExceptions) {for (Exception suppressedException : this.suppressedExceptions) {ex.addRelatedCause(suppressedException);}}throw ex;}finally {if (recordSuppressedExceptions) {this.suppressedExceptions = null;}afterSingletonCreation(beanName);}if (newSingleton) {addSingleton(beanName, singletonObject);}}return singletonObject;}}//..    
}public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFactoryimplements AutowireCapableBeanFactory {//..//后置最终到这里(按照顺序来的,所以记得仔细观察,这个顺序一般是类里面的方法顺序,以后就不提醒了)@Overridepublic Object applyBeanPostProcessorsAfterInitialization(Object existingBean, String beanName)throws BeansException {Object result = existingBean;//操作对应的所有的后置 for (BeanPostProcessor processor : getBeanPostProcessors()) {Object current = processor.postProcessAfterInitialization(result, beanName); //对应的后置方法,所以这里就操作了,最后返回beanif (current == null) {return result;}result = current;}return result;}//..@Overrideprotected Object createBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)throws BeanCreationException {if (logger.isTraceEnabled()) {logger.trace("Creating instance of bean '" + beanName + "'");}RootBeanDefinition mbdToUse = mbd;// Make sure bean class is actually resolved at this point, and// clone the bean definition in case of a dynamically resolved Class// which cannot be stored in the shared merged bean definition.//获取类信息/*//合并父子bean的属性RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName); 得到的信息中也包括mbd的*/Class<?> resolvedClass = resolveBeanClass(mbd, beanName);if (resolvedClass != null && !mbd.hasBeanClass() && mbd.getBeanClassName() != null) {mbdToUse = new RootBeanDefinition(mbd);mbdToUse.setBeanClass(resolvedClass);}// Prepare method overrides.try {mbdToUse.prepareMethodOverrides();}catch (BeanDefinitionValidationException ex) {throw new BeanDefinitionStoreException(mbdToUse.getResourceDescription(),beanName, "Validation of method overrides failed", ex);}try {// Give BeanPostProcessors a chance to return a proxy instead of the target bean instance.Object bean = resolveBeforeInstantiation(beanName, mbdToUse);if (bean != null) {return bean;}}catch (Throwable ex) {throw new BeanCreationException(mbdToUse.getResourceDescription(), beanName,"BeanPostProcessor before instantiation of bean failed", ex);}try {//这里我们进入,真正的创建bean(do来做事情)Object beanInstance = doCreateBean(beanName, mbdToUse, args);if (logger.isTraceEnabled()) {logger.trace("Finished creating instance of bean '" + beanName + "'");}return beanInstance;}catch (BeanCreationException | ImplicitlyAppearedSingletonException ex) {// A previously detected exception with proper bean creation context already,// or illegal singleton state to be communicated up to DefaultSingletonBeanRegistry.throw ex;}catch (Throwable ex) {throw new BeanCreationException(mbdToUse.getResourceDescription(), beanName, "Unexpected exception during bean creation", ex);}}//到这里protected Object doCreateBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)throws BeanCreationException {// Instantiate the bean.BeanWrapper instanceWrapper = null;if (mbd.isSingleton()) {instanceWrapper = this.factoryBeanInstanceCache.remove(beanName);}if (instanceWrapper == null) {//创建bean实例,仅仅调用构造方法,但是并没有设置属性instanceWrapper = createBeanInstance(beanName, mbd, args);}Object bean = instanceWrapper.getWrappedInstance();Class<?> beanType = instanceWrapper.getWrappedClass();if (beanType != NullBean.class) {mbd.resolvedTargetType = beanType;}// Allow post-processors to modify the merged bean definition.synchronized (mbd.postProcessingLock) {if (!mbd.postProcessed) {try {applyMergedBeanDefinitionPostProcessors(mbd, beanType, beanName);}catch (Throwable ex) {throw new BeanCreationException(mbd.getResourceDescription(), beanName,"Post-processing of merged bean definition failed", ex);}mbd.postProcessed = true;}}// Eagerly cache singletons to be able to resolve circular references// even when triggered by lifecycle interfaces like BeanFactoryAware.boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&isSingletonCurrentlyInCreation(beanName));if (earlySingletonExposure) {if (logger.isTraceEnabled()) {logger.trace("Eagerly caching bean '" + beanName +"' to allow for resolving potential circular references");}//这个是扩展的,其中一般我们会将后面的表达式放入对应的value中,在得到三级缓存时,他会进行对二级缓存的扩展,最终放入二级缓存中addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));}// Initialize the bean instance.//初始化bean实例Object exposedObject = bean;try {//前面是创建bean,这里是填充属性populateBean(beanName, mbd, instanceWrapper);//调用初始化方法,应用BeanPostProcessor后置处理器,我们进入这里exposedObject = initializeBean(beanName, exposedObject, mbd);}catch (Throwable ex) {if (ex instanceof BeanCreationException && beanName.equals(((BeanCreationException) ex).getBeanName())) {throw (BeanCreationException) ex;}else {throw new BeanCreationException(mbd.getResourceDescription(), beanName, "Initialization of bean failed", ex);}}if (earlySingletonExposure) {Object earlySingletonReference = getSingleton(beanName, false);if (earlySingletonReference != null) {if (exposedObject == bean) {exposedObject = earlySingletonReference;}else if (!this.allowRawInjectionDespiteWrapping && hasDependentBean(beanName)) {String[] dependentBeans = getDependentBeans(beanName);Set<String> actualDependentBeans = new LinkedHashSet<>(dependentBeans.length);for (String dependentBean : dependentBeans) {if (!removeSingletonIfCreatedForTypeCheckOnly(dependentBean)) {actualDependentBeans.add(dependentBean);}}if (!actualDependentBeans.isEmpty()) {throw new BeanCurrentlyInCreationException(beanName,"Bean with name '" + beanName + "' has been injected into other beans [" +StringUtils.collectionToCommaDelimitedString(actualDependentBeans) +"] in its raw version as part of a circular reference, but has eventually been " +"wrapped. This means that said other beans do not use the final version of the " +"bean. This is often the result of over-eager type matching - consider using " +"'getBeanNamesForType' with the 'allowEagerInit' flag turned off, for example.");}}}}// Register bean as disposable.try {registerDisposableBeanIfNecessary(beanName, bean, mbd);}catch (BeanDefinitionValidationException ex) {throw new BeanCreationException(mbd.getResourceDescription(), beanName, "Invalid destruction signature", ex);}return exposedObject; //最后返回}//..//到这里protected Object initializeBean(String beanName, Object bean, @Nullable RootBeanDefinition mbd) {if (System.getSecurityManager() != null) {AccessController.doPrivileged((PrivilegedAction<Object>) () -> {invokeAwareMethods(beanName, bean);return null;}, getAccessControlContext());}else {invokeAwareMethods(beanName, bean);}Object wrappedBean = bean;if (mbd == null || !mbd.isSynthetic()) {wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);}try {invokeInitMethods(beanName, wrappedBean, mbd);}catch (Throwable ex) {throw new BeanCreationException((mbd != null ? mbd.getResourceDescription() : null),beanName, "Invocation of init method failed", ex);}if (mbd == null || !mbd.isSynthetic()) {//进入这里wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);}return wrappedBean;}//..}
他进入的类是(在后面就不给出这样的说明了,因为只需要了解,并且过程中也给出了步骤,就不需要总结一起了,且我们记住这个东西也没有必要,因为对知识毫无用处的):
/*
AbstractApplicationContext
DefaultListableBeanFactory
AbstractBeanFactory
DefaultSingletonBeanRegistry
AbstractAutowireCapableBeanFactory
*/
至此,我们的大致流程操作完毕
lazy-init 延迟加载机制原理:
lazy-init 延迟加载机制分析:
普通 Bean 的初始化是在容器启动初始化阶段执行的,而被lazy-init=true修饰的 bean 则是在从容器⾥ 第一次进行getBean() 时进行触发,Spring 启动的时候会把所有bean信息(包括XML和注解)解析转化成Spring能够识别的BeanDefinition并存到Hashmap⾥供下面的初始化时用,然后对每个BeanDefinition 进行处理,如果是懒加载的则在容器初始化阶段不处理,而是在getBean中处理(可以认为也是前面的填充),其他的则在容器初始化阶段进行初始化并依赖注⼊(前面的填充,基本解决了循环依赖,不用考虑错误)
找到前面的如下:
//到这里
@SuppressWarnings("serial")
public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFactoryimplements ConfigurableListableBeanFactory, BeanDefinitionRegistry, Serializable {
//..@Overridepublic void preInstantiateSingletons() throws BeansException {if (logger.isTraceEnabled()) {logger.trace("Pre-instantiating singletons in " + this);}// Iterate over a copy to allow for init methods which in turn register new bean definitions.// While this may not be part of the regular factory bootstrap, it does otherwise work fine.//所有bean的名字List<String> beanNames = new ArrayList<>(this.beanDefinitionNames);// Trigger initialization of all non-lazy singleton beans...//触发所有非延迟加载单例bean的初始化,主要步骤为getBeanfor (String beanName : beanNames) {//合并父BeanDefinition信息,通常需要父的某些信息RootBeanDefinition bd = getMergedLocalBeanDefinition(beanName);//这里判断的if (!bd.isAbstract() && bd.isSingleton() && !bd.isLazyInit()) {//这里判断的if (isFactoryBean(beanName)) {//主要步骤,String FACTORY_BEAN_PREFIX = "&";这里就知道了吧(前面的工厂,或者说自定义创建过程)Object bean = getBean(FACTORY_BEAN_PREFIX + beanName);if (bean instanceof FactoryBean) {FactoryBean<?> factory = (FactoryBean<?>) bean;boolean isEagerInit;if (System.getSecurityManager() != null && factory instanceof SmartFactoryBean) {isEagerInit = AccessController.doPrivileged((PrivilegedAction<Boolean>) ((SmartFactoryBean<?>) factory)::isEagerInit,getAccessControlContext());}else {isEagerInit = (factory instanceof SmartFactoryBean &&((SmartFactoryBean<?>) factory).isEagerInit());}if (isEagerInit) {getBean(beanName);}}}else {//直接到这里,因为不是工厂bean,自然也没有对应的&存放,我们进入这里getBean(beanName);}}}// Trigger post-initialization callback for all applicable beans...for (String beanName : beanNames) {Object singletonInstance = getSingleton(beanName);if (singletonInstance instanceof SmartInitializingSingleton) {SmartInitializingSingleton smartSingleton = (SmartInitializingSingleton) singletonInstance;if (System.getSecurityManager() != null) {AccessController.doPrivileged((PrivilegedAction<Object>) () -> {smartSingleton.afterSingletonsInstantiated();return null;}, getAccessControlContext());}else {smartSingleton.afterSingletonsInstantiated();}}}}//..    }
上面并不是主要的,我们在如下打上断点:
import org.junit.Test;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;/****/public class IocTest {/*** Ioc 容器源码分析基础案例*/@Testpublic void testIoC() {System.out.println(BeanFactory.FACTORY_BEAN_PREFIX); //factory bean prefixApplicationContext applicationContext = new ClassPathXmlApplicationContext("classpath:application.xml");//这里打上,并且对应的<bean id="lagouBean" class="com.lagou.edu.LagouBean" lazy-init="true"/>//因为前面有存在是否延迟的判断,所以没有加载存在可能Object lagouBean = (Object) applicationContext.getBean(LagouBean.class);System.out.println(lagouBean);}
}
我们进入后到如下:
public abstract class AbstractApplicationContext extends DefaultResourceLoaderimplements ConfigurableApplicationContext {//..@Overridepublic Object getBean(String name) throws BeansException {assertBeanFactoryActive();return getBeanFactory().getBean(name); //我们进入}//..}public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFactoryimplements ConfigurableListableBeanFactory, BeanDefinitionRegistry, Serializable {//..@Overridepublic <T> T getBean(Class<T> requiredType) throws BeansException {return getBean(requiredType, (Object[]) null);}@Overridepublic <T> T getBean(Class<T> requiredType, @Nullable Object... args) throws BeansException {Assert.notNull(requiredType, "Required type must not be null");//我们继续进入Object resolved = resolveBean(ResolvableType.forRawClass(requiredType), args, false);if (resolved == null) {throw new NoSuchBeanDefinitionException(requiredType);}return (T) resolved;}//..//我们到这里@Nullableprivate <T> T resolveBean(ResolvableType requiredType, @Nullable Object[] args, boolean nonUniqueAsNull) {//我们继续进入NamedBeanHolder<T> namedBean = resolveNamedBean(requiredType, args, nonUniqueAsNull);if (namedBean != null) {return namedBean.getBeanInstance();}BeanFactory parent = getParentBeanFactory();if (parent instanceof DefaultListableBeanFactory) {return ((DefaultListableBeanFactory) parent).resolveBean(requiredType, args, nonUniqueAsNull);}else if (parent != null) {ObjectProvider<T> parentProvider = parent.getBeanProvider(requiredType);if (args != null) {return parentProvider.getObject(args);}else {return (nonUniqueAsNull ? parentProvider.getIfUnique() : parentProvider.getIfAvailable());}}return null;}//..//到这里了@Nullableprivate <T> NamedBeanHolder<T> resolveNamedBean(ResolvableType requiredType, @Nullable Object[] args, boolean nonUniqueAsNull) throws BeansException {Assert.notNull(requiredType, "Required type must not be null");String[] candidateNames = getBeanNamesForType(requiredType);if (candidateNames.length > 1) {List<String> autowireCandidates = new ArrayList<>(candidateNames.length);for (String beanName : candidateNames) {if (!containsBeanDefinition(beanName) || getBeanDefinition(beanName).isAutowireCandidate()) {autowireCandidates.add(beanName);}}if (!autowireCandidates.isEmpty()) {candidateNames = StringUtils.toStringArray(autowireCandidates);}}if (candidateNames.length == 1) {String beanName = candidateNames[0];//找到这里,进入这里的getBean方法里面return new NamedBeanHolder<>(beanName, (T) getBean(beanName, requiredType.toClass(), args));}else if (candidateNames.length > 1) {Map<String, Object> candidates = new LinkedHashMap<>(candidateNames.length);for (String beanName : candidateNames) {if (containsSingleton(beanName) && args == null) {Object beanInstance = getBean(beanName);candidates.put(beanName, (beanInstance instanceof NullBean ? null : beanInstance));}else {candidates.put(beanName, getType(beanName));}}String candidateName = determinePrimaryCandidate(candidates, requiredType.toClass());if (candidateName == null) {candidateName = determineHighestPriorityCandidate(candidates, requiredType.toClass());}if (candidateName != null) {Object beanInstance = candidates.get(candidateName);if (beanInstance == null || beanInstance instanceof Class) {beanInstance = getBean(candidateName, requiredType.toClass(), args);}return new NamedBeanHolder<>(candidateName, (T) beanInstance);}if (!nonUniqueAsNull) {throw new NoUniqueBeanDefinitionException(requiredType, candidates.keySet());}}return null;}//..
}public abstract class AbstractBeanFactory extends FactoryBeanRegistrySupport implements ConfigurableBeanFactory {//..public <T> T getBean(String name, @Nullable Class<T> requiredType, @Nullable Object... args)throws BeansException {
//真正做的事情(do)return doGetBean(name, requiredType, args, false);}//到这里,你是否有眼熟呢,可以看到,就是之前的/*//直接到这里,因为不是工厂bean,自然也没有对应的&存放,我们进入这里getBean(beanName);获取bean的方法,即他也的确操作了获取bean*///@SuppressWarnings("unchecked"),注解什么的可能会漏写,但是并不影响阅读protected <T> T doGetBean(String name, @Nullable Class<T> requiredType, @Nullable Object[] args, boolean typeCheckOnly)throws BeansException {String beanName = transformedBeanName(name);Object bean;// Eagerly check singleton cache for manually registered singletons.Object sharedInstance = getSingleton(beanName); //但是若你这里是一个立即加载,那么他这里有值了,而不是null(因为缓存的原因,在对应的后面有相同的该方法名称的操作,其中一般就在创建bean时保存了缓存,但也只是自己的),这就是延迟加载和立即加载的唯一的区别,也就是说延迟加载,是没有加载bean的,但是由于缓存的存在,所以spring的延迟加载在打印两次时,他与多例不同的是,得到的结果实例是相同的,他只是一开始没有放入map而已//..}
这里再次的给出前面的是否延迟的方法:
//到这里
@SuppressWarnings("serial")
public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFactoryimplements ConfigurableListableBeanFactory, BeanDefinitionRegistry, Serializable {
//..@Overridepublic void preInstantiateSingletons() throws BeansException {if (logger.isTraceEnabled()) {logger.trace("Pre-instantiating singletons in " + this);}// Iterate over a copy to allow for init methods which in turn register new bean definitions.// While this may not be part of the regular factory bootstrap, it does otherwise work fine.//所有bean的名字List<String> beanNames = new ArrayList<>(this.beanDefinitionNames);// Trigger initialization of all non-lazy singleton beans...//触发所有非延迟加载单例bean的初始化,主要步骤为getBeanfor (String beanName : beanNames) {//合并父BeanDefinition信息,通常需要父的某些信息RootBeanDefinition bd = getMergedLocalBeanDefinition(beanName);if (!bd.isAbstract() && bd.isSingleton() && !bd.isLazyInit()) { //只能是立即的进入,否则不会操作到getBean方法,这就是延迟加载的唯一判断的地方,即延迟操作不创建bean的原因(或者初始化不创建)if (isFactoryBean(beanName)) {//主要步骤,String FACTORY_BEAN_PREFIX = "&";这里就知道了吧(前面的工厂,或者说自定义创建过程)Object bean = getBean(FACTORY_BEAN_PREFIX + beanName);if (bean instanceof FactoryBean) {FactoryBean<?> factory = (FactoryBean<?>) bean;boolean isEagerInit;if (System.getSecurityManager() != null && factory instanceof SmartFactoryBean) {isEagerInit = AccessController.doPrivileged((PrivilegedAction<Boolean>) ((SmartFactoryBean<?>) factory)::isEagerInit,getAccessControlContext());}else {isEagerInit = (factory instanceof SmartFactoryBean &&((SmartFactoryBean<?>) factory).isEagerInit());}if (isEagerInit) {getBean(beanName);}}}else {//直接到这里,因为不是工厂bean,自然也没有对应的&存放,我们进入这里getBean(beanName); //他内部进行了保存,所以并不需要进行得到返回值//这里需要额外注意,如果不是立即加载可能并不操作保存}}}// Trigger post-initialization callback for all applicable beans...for (String beanName : beanNames) {Object singletonInstance = getSingleton(beanName);if (singletonInstance instanceof SmartInitializingSingleton) {SmartInitializingSingleton smartSingleton = (SmartInitializingSingleton) singletonInstance;if (System.getSecurityManager() != null) {AccessController.doPrivileged((PrivilegedAction<Object>) () -> {smartSingleton.afterSingletonsInstantiated();return null;}, getAccessControlContext());}else {smartSingleton.afterSingletonsInstantiated();}}}}//..    }
Spring IoC循环依赖问题:
前面也有过一个小小的例子,这里我们来完整的进行说明
什么是循环依赖:
循环依赖其实就是循环引用,也就是两个或者两个以上的 Bean 互相持有对方,最终形成闭环,比如A依赖于B,B依赖于C,C⼜依赖于A

在这里插入图片描述

注意,这⾥不是函数的循环调用,是对象的相互依赖关系,循环调用其实就是一个死循环,除⾮有终结 条件
Spring中循环依赖场景有:
构造器的循环依赖(构造器注⼊),Field 属性的循环依赖(set注⼊),由于无论是先放入map取出注入还是先注入再放入map的依赖循环问题都是相同的,即对应的注入问题需要对方操作(创建)完毕,而不是单纯的赋值(前面说明的单例就是,所以单例一般没有循环依赖,前提是没有操作构造)
其中,构造器的循环依赖问题⽆法解决,只能拋出 BeanCurrentlyInCreationException 异常(这是因为在构造时,我必然是没有对应的class的,所以如果循环到自己,必然只能报错,因为赋值不了),在解决 属性循环依赖时(spring通常是有顺序的),spring采用的是提前暴露对象的方法
我们可以回到之前的源码中:
//从这里可以看出,属性的设置和初始化(这里的前面的代码)是一起的,只是可能由于在设置时,帮助其进行操作使得对应的循环跳过,而这个帮助可能在让他初始化时,继续到这里形成循环依赖,而spring采用提前暴露,也就是直接的赋值(你已经初始化了的,所以可以赋值(能获得,自然也是提前暴露出去的,所以称为提前暴露对象的方法),然后我得到了,自然你也可以来得到我了)//前面是创建bean,这里是填充属性populateBean(beanName, mbd, instanceWrapper);//一般的,我们将这样的操作称为三级缓存的解决操作(可以说是专门操作循环依赖的,或者说是类),一般其他暴露并初始化的放在三级缓存,而进行了处理即成型的放入一级缓存操作,中间的二级缓存也存放成型的bean,只是他只进行某些扩展处理,而不会像一级缓存暴露使用,和三级缓存暴露使用,所以在一定程度上,只有二个缓存,虽然实际存在三个(一级和三级一般是map,而二级一般是三级对应的value)//上面的:然后我得到了,自然你也可以来得到我了,在一定程度上可以使用代码来表示:
/*
package com.lagou.edu;
public class a {class b {public b() {}d d;void setb(d d) {this.d = d;}}class d {b b;public d() {}void setd(b b) {this.b = b;}}public void main(String[] args) {d d = new d(); //首先初始化的b b = new b();b.setb(d); //然后我得到了d.setd(b); //自然你也可以来得到我了,虽然上面的也是暴露的b b = new b();,但要明白的是,b认为是先操作的,所以你必然得到完整,而不是一开始的残缺,但是b先操作并不代表在spring中是b先完毕,这是由于spring的创建和set是一起的,所以在spring中一般是b先完毕,我们看后面的源码就知道了}
}*/
循环依赖处理机制:
单例 bean 构造器参数循环依赖(⽆法解决,我自身都没有,那么你只能创建,又没有,又只能创建,无限循环了),其属性赋值解决了,所以我们不用考虑
prototype 原型 bean循环依赖(⽆法解决),多例的必然不能,因为每次都是新的,是不会考虑赋值的
对于多例原型bean的初始化过程中不论是通过构造器参数循环依赖还是通过setXxx方法产生循环依 赖,Spring都 会直接报错处理(一般也是BeanCurrentlyInCreationException),而单例的只是构造报错,当然BeanCurrentlyInCreationException的错误信息可能存在,但是也可能随着版本的变化而发生改变
我们可以进行测试,对应的配置文件(application.xml):
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"xmlns:aop="http://www.springframework.org/schema/aop"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://www.springframework.org/schema/beanshttps://www.springframework.org/schema/beans/spring-beans.xsd"><bean id="lagouBean" class="com.lagou.edu.LagouBean" scope="prototype"><property name="itBean" ref="itBean"></property></bean><bean id="itBean" class="com.lagou.edu.ItBean" scope="prototype"><property name="lagouBean" ref="lagouBean"></property></bean></beans>
测试类:
import com.lagou.edu.LagouBean;
import org.junit.Test;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;/****/public class IocTest {/*** Ioc 容器源码分析基础案例*/@Testpublic void testIoC() {//这个ApplicationContext是上下文,所有的信息都是给他的(虽然是通过操作ClassPathXmlApplicationContext后的返回),所以在一定程度上,spring说成就是ApplicationContext上下文也是可以的ApplicationContext applicationContext = new ClassPathXmlApplicationContext("classpath:application.xml");Object lagouBean = (Object) applicationContext.getBean(LagouBean.class);System.out.println(lagouBean);}}
在之前的这个地方打上断点(当然,你可以先执行看看错误,看看是否存在BeanCurrentlyInCreationException的错误):
//实例化所有立即加载的单例bean,我们进入这里beanFactory.preInstantiateSingletons();
分析如下:
//我们直接到之前的如下:public abstract class AbstractBeanFactory extends FactoryBeanRegistrySupport implements ConfigurableBeanFactory {//..@SuppressWarnings("unchecked")protected <T> T doGetBean(String name, @Nullable Class<T> requiredType, @Nullable Object[] args, boolean typeCheckOnly)throws BeansException {String beanName = transformedBeanName(name);Object bean;// Eagerly check singleton cache for manually registered singletons.Object sharedInstance = getSingleton(beanName);if (sharedInstance != null && args == null) {if (logger.isTraceEnabled()) {if (isSingletonCurrentlyInCreation(beanName)) {logger.trace("Returning eagerly cached instance of singleton bean '" + beanName +"' that is not fully initialized yet - a consequence of a circular reference");}else {logger.trace("Returning cached instance of singleton bean '" + beanName + "'");}}bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);}else {// Fail if we're already creating this bean instance:// We're assumably within a circular reference.//如果是Prototype(即多例模式),且开启允许循环依赖(一般是只是否存在,主要看源码就知道了),那么报错,因为多例模式是容易出现循环依赖的,所以就不能直接的允许循环依赖//这里就是对应的一个错误(BeanCurrentlyInCreationException)的地方,我们进入if (isPrototypeCurrentlyInCreation(beanName)) {throw new BeanCurrentlyInCreationException(beanName);}// Check if bean definition exists in this factory.BeanFactory parentBeanFactory = getParentBeanFactory();if (parentBeanFactory != null && !containsBeanDefinition(beanName)) {// Not found -> check parent.String nameToLookup = originalBeanName(name);if (parentBeanFactory instanceof AbstractBeanFactory) {return ((AbstractBeanFactory) parentBeanFactory).doGetBean(nameToLookup, requiredType, args, typeCheckOnly);}else if (args != null) {// Delegation to parent with explicit args.return (T) parentBeanFactory.getBean(nameToLookup, args);}else if (requiredType != null) {// No args -> delegate to standard getBean method.return parentBeanFactory.getBean(nameToLookup, requiredType);}else {return (T) parentBeanFactory.getBean(nameToLookup);}}if (!typeCheckOnly) {markBeanAsCreated(beanName);}try {RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);checkMergedBeanDefinition(mbd, beanName, args);// Guarantee initialization of beans that the current bean depends on.String[] dependsOn = mbd.getDependsOn();if (dependsOn != null) {for (String dep : dependsOn) {if (isDependent(beanName, dep)) {throw new BeanCreationException(mbd.getResourceDescription(), beanName,"Circular depends-on relationship between '" + beanName + "' and '" + dep + "'");}registerDependentBean(dep, beanName);try {getBean(dep);}catch (NoSuchBeanDefinitionException ex) {throw new BeanCreationException(mbd.getResourceDescription(), beanName,"'" + beanName + "' depends on missing bean '" + dep + "'", ex);}}}// Create bean instance.if (mbd.isSingleton()) { //单例进入sharedInstance = getSingleton(beanName, () -> {try {return createBean(beanName, mbd, args);}catch (BeansException ex) {// Explicitly remove instance from singleton cache: It might have been put there// eagerly by the creation process, to allow for circular reference resolution.// Also remove any beans that received a temporary reference to the bean.destroySingleton(beanName);throw ex;}});bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);}else if (mbd.isPrototype()) {// It's a prototype -> create a new instance.Object prototypeInstance = null;try {//到这里代表没有开启允许循环依赖,那么到这里//创建原型bean之前添加标记beforePrototypeCreation(beanName);//创建原型bean,由于在初始化填充时,可能会继续操作(多例是实时创建的,所以存在继续操作的说法,而单例由于三级缓存的缘故,所以基本解决的),最终导致由于上面标记设置了值,使得isPrototypeCurrentlyInCreation方法为true,那么对应的错误就出现了prototypeInstance = createBean(beanName, mbd, args);}finally {//都创建好了,他自然也不用考虑什么循环了,所以删除标记,而避免其他因为他的存在导致的循环依赖,因为是一步一步过去的,即有顺序的//创建原型bean之后删除标记afterPrototypeCreation(beanName);}bean = getObjectForBeanInstance(prototypeInstance, name, beanName, mbd);}else {String scopeName = mbd.getScope();if (!StringUtils.hasLength(scopeName)) {throw new IllegalStateException("No scope name defined for bean ´" + beanName + "'");}Scope scope = this.scopes.get(scopeName);if (scope == null) {throw new IllegalStateException("No Scope registered for scope name '" + scopeName + "'");}try {Object scopedInstance = scope.get(beanName, () -> {beforePrototypeCreation(beanName);try {return createBean(beanName, mbd, args);}finally {afterPrototypeCreation(beanName);}});bean = getObjectForBeanInstance(scopedInstance, name, beanName, mbd);}catch (IllegalStateException ex) {throw new BeanCreationException(beanName,"Scope '" + scopeName + "' is not active for the current thread; consider " +"defining a scoped proxy for this bean if you intend to refer to it from a singleton",ex);}}}catch (BeansException ex) {cleanupAfterBeanCreationFailure(beanName);throw ex;}}// Check if required type matches the type of the actual bean instance.if (requiredType != null && !requiredType.isInstance(bean)) {try {T convertedBean = getTypeConverter().convertIfNecessary(bean, requiredType);if (convertedBean == null) {throw new BeanNotOfRequiredTypeException(name, requiredType, bean.getClass());}return convertedBean;}catch (TypeMismatchException ex) {if (logger.isTraceEnabled()) {logger.trace("Failed to convert bean '" + name + "' to required type '" +ClassUtils.getQualifiedName(requiredType) + "'", ex);}throw new BeanNotOfRequiredTypeException(name, requiredType, bean.getClass());}}return (T) bean;}//..protected boolean isPrototypeCurrentlyInCreation(String beanName) {Object curVal = this.prototypesCurrentlyInCreation.get();return (curVal != null &&(curVal.equals(beanName) || (curVal instanceof Set && ((Set<?>) curVal).contains(beanName))));}//..
}
所以多例的确就是必然操作循环依赖,即不能解决,所以尽量避免多例的循环依赖出现,只要不操作循环不触发即可
单例bean通过setXxx或者@Autowired进行循环依赖(构造报错,但是他的set是可以解决的,虽然他存在,但是已经解决了)
Spring 的循环依赖的理论依据基于 Java 的引用传递,当获得对象的引用时,对象的属性是可以延 后设置的,但是构造器必须是在获取引用之前,所以前面测试循环依赖错误时,基本都是使用引用来完成的,所以实际上循环依赖问题就是单例的问题,因为多例必然报错,所以我们来看看他是如果解决单例的循环依赖的:
Spring(单例)通过setXxx(名称一般我们会认为是setxxx来说明)或者@Autowired方法(考虑到延迟加载,因为其底层是getBean,最终也会考虑setxxx的形式的,因为不是构造,必然是setxxx的形式来解决,或者一些同样功能的来解决,只是名称一般我们会认为是setxxx来说明而已)解决循环依赖其实是通过提前暴露一个ObjectFactory对 象来完成的,简单来说ClassA在调用构造器完成对象初始化之后,在调用ClassA的setClassB方法 之前就把ClassA实例化的对象通过ObjectFactory提前暴露到Spring容器中,这里我们可以观察源码:
//我们回到之前的prototypeInstance = createBean(beanName, mbd, args);,实际上单例也存在这个方法
//我们进入:
//最终可以找到这个(AbstractAutowireCapableBeanFactory类里面的):
Object beanInstance = doCreateBean(beanName, mbdToUse, args); //前面说明了//我们进入://到这里protected Object doCreateBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)throws BeanCreationException {// Instantiate the bean.BeanWrapper instanceWrapper = null;if (mbd.isSingleton()) {instanceWrapper = this.factoryBeanInstanceCache.remove(beanName);}if (instanceWrapper == null) {//创建bean实例,仅仅调用构造方法,但是并没有设置属性instanceWrapper = createBeanInstance(beanName, mbd, args);}//开始考虑三级缓存Object bean = instanceWrapper.getWrappedInstance();Class<?> beanType = instanceWrapper.getWrappedClass();if (beanType != NullBean.class) {mbd.resolvedTargetType = beanType;}// Allow post-processors to modify the merged bean definition.synchronized (mbd.postProcessingLock) {if (!mbd.postProcessed) {try {applyMergedBeanDefinitionPostProcessors(mbd, beanType, beanName);}catch (Throwable ex) {throw new BeanCreationException(mbd.getResourceDescription(), beanName,"Post-processing of merged bean definition failed", ex);}mbd.postProcessed = true;}}// Eagerly cache singletons to be able to resolve circular references// even when triggered by lifecycle interfaces like BeanFactoryAware.//到这里boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&isSingletonCurrentlyInCreation(beanName));if (earlySingletonExposure) {if (logger.isTraceEnabled()) {logger.trace("Eagerly caching bean '" + beanName +"' to allow for resolving potential circular references");}//这里就是创建(一般一开始就有的)或者给三级缓存添加信息的地方,我们进入addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));}// Initialize the bean instance.//初始化bean实例Object exposedObject = bean;try {//前面是创建bean,这里是填充属性(bean),现在我们进入了(之前没有进入)populateBean(beanName, mbd, instanceWrapper);//调用初始化方法,应用BeanPostProcessor后置处理器,我们进入这里exposedObject = initializeBean(beanName, exposedObject, mbd);}catch (Throwable ex) {//后面的省略了
/*
我们进入上面的addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));里面:
*/
public class DefaultSingletonBeanRegistry extends SimpleAliasRegistry implements SingletonBeanRegistry {
//..protected void addSingletonFactory(String beanName, ObjectFactory<?> singletonFactory) {Assert.notNull(singletonFactory, "Singleton factory must not be null");synchronized (this.singletonObjects) {if (!this.singletonObjects.containsKey(beanName)) {//加入三级缓存(beanName是当前的bean名称),并且给出singletonFactory来给二级缓存进行扩展操作this.singletonFactories.put(beanName, singletonFactory);//二级缓存,对应加上三级缓存,自然不能存在低级缓存this.earlySingletonObjects.remove(beanName);this.registeredSingletons.add(beanName);}}}//..}
public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFactoryimplements AutowireCapableBeanFactory {//..@SuppressWarnings("deprecation")  // for postProcessPropertyValuesprotected void populateBean(String beanName, RootBeanDefinition mbd, @Nullable BeanWrapper bw) {if (bw == null) {if (mbd.hasPropertyValues()) {throw new BeanCreationException(mbd.getResourceDescription(), beanName, "Cannot apply property values to null instance");}else {// Skip property population phase for null instance.return;}}// Give any InstantiationAwareBeanPostProcessors the opportunity to modify the// state of the bean before properties are set. This can be used, for example,// to support styles of field injection.if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {for (BeanPostProcessor bp : getBeanPostProcessors()) {if (bp instanceof InstantiationAwareBeanPostProcessor) {InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp;if (!ibp.postProcessAfterInstantiation(bw.getWrappedInstance(), beanName)) {return;}}}}PropertyValues pvs = (mbd.hasPropertyValues() ? mbd.getPropertyValues() : null);int resolvedAutowireMode = mbd.getResolvedAutowireMode();if (resolvedAutowireMode == AUTOWIRE_BY_NAME || resolvedAutowireMode == AUTOWIRE_BY_TYPE) {MutablePropertyValues newPvs = new MutablePropertyValues(pvs);// Add property values based on autowire by name if applicable.if (resolvedAutowireMode == AUTOWIRE_BY_NAME) {autowireByName(beanName, mbd, bw, newPvs);}// Add property values based on autowire by type if applicable.if (resolvedAutowireMode == AUTOWIRE_BY_TYPE) {autowireByType(beanName, mbd, bw, newPvs);}pvs = newPvs;}boolean hasInstAwareBpps = hasInstantiationAwareBeanPostProcessors();boolean needsDepCheck = (mbd.getDependencyCheck() != AbstractBeanDefinition.DEPENDENCY_CHECK_NONE);PropertyDescriptor[] filteredPds = null;if (hasInstAwareBpps) {if (pvs == null) {pvs = mbd.getPropertyValues();}for (BeanPostProcessor bp : getBeanPostProcessors()) {if (bp instanceof InstantiationAwareBeanPostProcessor) {InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp;PropertyValues pvsToUse = ibp.postProcessProperties(pvs, bw.getWrappedInstance(), beanName);if (pvsToUse == null) {if (filteredPds == null) {filteredPds = filterPropertyDescriptorsForDependencyCheck(bw, mbd.allowCaching);}pvsToUse = ibp.postProcessPropertyValues(pvs, filteredPds, bw.getWrappedInstance(), beanName);if (pvsToUse == null) {return;}}pvs = pvsToUse;}}}if (needsDepCheck) {if (filteredPds == null) {filteredPds = filterPropertyDescriptorsForDependencyCheck(bw, mbd.allowCaching);}checkDependencies(beanName, mbd, filteredPds, pvs);}if (pvs != null) {//直接到这里applyPropertyValues(beanName, mbd, bw, pvs);}}//..protected void applyPropertyValues(String beanName, BeanDefinition mbd, BeanWrapper bw, PropertyValues pvs) {if (pvs.isEmpty()) {return;}if (System.getSecurityManager() != null && bw instanceof BeanWrapperImpl) {((BeanWrapperImpl) bw).setSecurityContext(getAccessControlContext());}MutablePropertyValues mpvs = null;List<PropertyValue> original;if (pvs instanceof MutablePropertyValues) {mpvs = (MutablePropertyValues) pvs;if (mpvs.isConverted()) {// Shortcut: use the pre-converted values as-is.try {bw.setPropertyValues(mpvs);return;}catch (BeansException ex) {throw new BeanCreationException(mbd.getResourceDescription(), beanName, "Error setting property values", ex);}}original = mpvs.getPropertyValueList();}else {original = Arrays.asList(pvs.getPropertyValues());}TypeConverter converter = getCustomTypeConverter();if (converter == null) {converter = bw;}BeanDefinitionValueResolver valueResolver = new BeanDefinitionValueResolver(this, beanName, mbd, converter);// Create a deep copy, resolving any references for values.List<PropertyValue> deepCopy = new ArrayList<>(original.size());boolean resolveNecessary = false;for (PropertyValue pv : original) {if (pv.isConverted()) {deepCopy.add(pv);}else {//到这里,这里是对应操作set的依赖名称,也就是解决循环依赖的地方,你进行调试可以发现对应的beanName是我们的bean名称,而propertyName则是property标签对应的名称String propertyName = pv.getName();Object originalValue = pv.getValue();//拿取对应的bean,准备进行设置操作的(里面最终操作了三级缓存的),我们进入Object resolvedValue = valueResolver.resolveValueIfNecessary(pv, originalValue);//后面就是进行了设置操作,如set,了解即可Object convertedValue = resolvedValue;boolean convertible = bw.isWritableProperty(propertyName) &&!PropertyAccessorUtils.isNestedOrIndexedProperty(propertyName);if (convertible) {convertedValue = convertForProperty(resolvedValue, propertyName, bw, converter);}// Possibly store converted value in merged bean definition,// in order to avoid re-conversion for every created bean instance.if (resolvedValue == originalValue) {if (convertible) {pv.setConvertedValue(convertedValue);}deepCopy.add(pv);}else if (convertible && originalValue instanceof TypedStringValue &&!((TypedStringValue) originalValue).isDynamic() &&!(convertedValue instanceof Collection || ObjectUtils.isArray(convertedValue))) {pv.setConvertedValue(convertedValue);deepCopy.add(pv);}else {resolveNecessary = true;deepCopy.add(new PropertyValue(pv, convertedValue));}}}if (mpvs != null && !resolveNecessary) {mpvs.setConverted();}// Set our (possibly massaged) deep copy.try {bw.setPropertyValues(new MutablePropertyValues(deepCopy));}catch (BeansException ex) {throw new BeanCreationException(mbd.getResourceDescription(), beanName, "Error setting property values", ex);}}//..}class BeanDefinitionValueResolver {//..@Nullablepublic Object resolveValueIfNecessary(Object argName, @Nullable Object value) {// We must check each value to see whether it requires a runtime reference// to another bean to be resolved.if (value instanceof RuntimeBeanReference) {RuntimeBeanReference ref = (RuntimeBeanReference) value;//我们进入return resolveReference(argName, ref);}else if (value instanceof RuntimeBeanNameReference) {String refName = ((RuntimeBeanNameReference) value).getBeanName();refName = String.valueOf(doEvaluate(refName));if (!this.beanFactory.containsBean(refName)) {throw new BeanDefinitionStoreException("Invalid bean name '" + refName + "' in bean reference for " + argName);}return refName;}else if (value instanceof BeanDefinitionHolder) {// Resolve BeanDefinitionHolder: contains BeanDefinition with name and aliases.BeanDefinitionHolder bdHolder = (BeanDefinitionHolder) value;return resolveInnerBean(argName, bdHolder.getBeanName(), bdHolder.getBeanDefinition());}else if (value instanceof BeanDefinition) {// Resolve plain BeanDefinition, without contained name: use dummy name.BeanDefinition bd = (BeanDefinition) value;String innerBeanName = "(inner bean)" + BeanFactoryUtils.GENERATED_BEAN_NAME_SEPARATOR +ObjectUtils.getIdentityHexString(bd);return resolveInnerBean(argName, innerBeanName, bd);}else if (value instanceof ManagedArray) {// May need to resolve contained runtime references.ManagedArray array = (ManagedArray) value;Class<?> elementType = array.resolvedElementType;if (elementType == null) {String elementTypeName = array.getElementTypeName();if (StringUtils.hasText(elementTypeName)) {try {elementType = ClassUtils.forName(elementTypeName, this.beanFactory.getBeanClassLoader());array.resolvedElementType = elementType;}catch (Throwable ex) {// Improve the message by showing the context.throw new BeanCreationException(this.beanDefinition.getResourceDescription(), this.beanName,"Error resolving array type for " + argName, ex);}}else {elementType = Object.class;}}return resolveManagedArray(argName, (List<?>) value, elementType);}else if (value instanceof ManagedList) {// May need to resolve contained runtime references.return resolveManagedList(argName, (List<?>) value);}else if (value instanceof ManagedSet) {// May need to resolve contained runtime references.return resolveManagedSet(argName, (Set<?>) value);}else if (value instanceof ManagedMap) {// May need to resolve contained runtime references.return resolveManagedMap(argName, (Map<?, ?>) value);}else if (value instanceof ManagedProperties) {Properties original = (Properties) value;Properties copy = new Properties();original.forEach((propKey, propValue) -> {if (propKey instanceof TypedStringValue) {propKey = evaluate((TypedStringValue) propKey);}if (propValue instanceof TypedStringValue) {propValue = evaluate((TypedStringValue) propValue);}if (propKey == null || propValue == null) {throw new BeanCreationException(this.beanDefinition.getResourceDescription(), this.beanName,"Error converting Properties key/value pair for " + argName + ": resolved to null");}copy.put(propKey, propValue);});return copy;}else if (value instanceof TypedStringValue) {// Convert value to target type here.TypedStringValue typedStringValue = (TypedStringValue) value;Object valueObject = evaluate(typedStringValue);try {Class<?> resolvedTargetType = resolveTargetType(typedStringValue);if (resolvedTargetType != null) {return this.typeConverter.convertIfNecessary(valueObject, resolvedTargetType);}else {return valueObject;}}catch (Throwable ex) {// Improve the message by showing the context.throw new BeanCreationException(this.beanDefinition.getResourceDescription(), this.beanName,"Error converting typed String value for " + argName, ex);}}else if (value instanceof NullBean) {return null;}else {return evaluate(value);}}//..//到这里@Nullableprivate Object resolveReference(Object argName, RuntimeBeanReference ref) {try {Object bean;String refName = ref.getBeanName();refName = String.valueOf(doEvaluate(refName));if (ref.isToParent()) {if (this.beanFactory.getParentBeanFactory() == null) {throw new BeanCreationException(this.beanDefinition.getResourceDescription(), this.beanName,"Can't resolve reference to bean '" + refName +"' in parent factory: no parent factory available");}bean = this.beanFactory.getParentBeanFactory().getBean(refName);}else {//到这里,我们进入可以发现,是操作了对应的:/*@Overridepublic Object getBean(String name) throws BeansException {return doGetBean(name, null, null, false);}是否发现非常眼熟呢,也就是说,你没有那么我帮你进行创建,然后根据流程,你在操作时,由于三级缓存的存在,必然可以拿取引用,从而解决循环问题,这里可以发现,是第一次对方的来先完毕的(在spring中一般是b先完毕,我们看后面的源码就知道了)*/bean = this.beanFactory.getBean(refName); //refName就是对应依赖的bean的名称this.beanFactory.registerDependentBean(refName, this.beanName);}if (bean instanceof NullBean) {bean = null;}return bean;}catch (BeansException ex) {throw new BeanCreationException(this.beanDefinition.getResourceDescription(), this.beanName,"Cannot resolve reference to bean '" + ref.getBeanName() + "' while setting " + argName, ex);}}//..}
所以可以发现,在填充时,的确考虑了对应的bean的三级缓存,所以单例是解决循环依赖的,即我们只需要不主动操作多例的循环,那么循环依赖的问题我们基本不会出现的
我们可以继续调试,当第二个bean操作了对应的bean = this.beanFactory.getBean(refName);时,可以在这里发现原因:
public abstract class AbstractBeanFactory extends FactoryBeanRegistrySupport implements ConfigurableBeanFactory {//..@SuppressWarnings("unchecked")protected <T> T doGetBean(String name, @Nullable Class<T> requiredType, @Nullable Object[] args, boolean typeCheckOnly)throws BeansException {String beanName = transformedBeanName(name);Object bean;// Eagerly check singleton cache for manually registered singletons.//在这里发现原因,前面我们说明了,是从缓存中拿取,那么自然就是三级缓存了,我们进入Object sharedInstance = getSingleton(beanName);if (sharedInstance != null && args == null) {if (logger.isTraceEnabled()) {//..}public class DefaultSingletonBeanRegistry extends SimpleAliasRegistry implements SingletonBeanRegistry {
//..@Override@Nullablepublic Object getSingleton(String beanName) {return getSingleton(beanName, true);}@Nullableprotected Object getSingleton(String beanName, boolean allowEarlyReference) {// Quick check for existing instance without full singleton lock//可以看到的确是拿取三级缓存的数据,因为前面就是this.singletonFactories.put(beanName, singletonFactory);,并且进行了二级缓存处理(当然这个处理可能并不在这里,而在对应三级缓存添加后的后续代码中,这里了解即可,如何是一般是后面的getObject里面操作的,而他的value一般是直接给一级,而不是继续的其他处理,如getObject,所以对应前面说明的addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));也并没有问题)Object singletonObject = this.singletonObjects.get(beanName); //这里是一级缓存,一般来说,完整的bean会存在一级缓存中,具体通常在设置(最终填充)后进行的添加(具体如何添加了解即可,spring代码这么多是基本说不完的),在对应的bean完成创建后,就会保存在一级缓存,然后清除当前自身所放在的三级缓存,具体在如下:addSingleton(beanName, singletonObject);,也就是之前的singletonObject = singletonFactory.getObject();后面,即sharedInstance = getSingleton(beanName, () -> {里面if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {singletonObject = this.earlySingletonObjects.get(beanName); //二级缓存if (singletonObject == null && allowEarlyReference) {synchronized (this.singletonObjects) {// Consistent creation of early reference within full singleton locksingletonObject = this.singletonObjects.get(beanName);if (singletonObject == null) {singletonObject = this.earlySingletonObjects.get(beanName);if (singletonObject == null) {//三级缓存的地方ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);if (singletonFactory != null) {singletonObject = singletonFactory.getObject(); //对应值的扩展的地方(前面的表达式),所以可以知道对应的value一般是保留bean信息,因为最终会通过他得到bean的,只是最终会进行统一处理的(因为进入到这里的方法并不是最终操作的)this.earlySingletonObjects.put(beanName, singletonObject);this.singletonFactories.remove(beanName);}}}}}}return singletonObject;}//..}
由于拿取了三级缓存的数据,所以对应的doGetBean方法直接返回该数据,完成了当前bean的设置,放入一级缓存,清空其他缓存,然后返回,也使得对应调用的进行了数据的设置,也完成了对应的bean设置,然后也放入一级缓存,至此循环依赖流程说明完毕,即单例也的确解决了循环依赖,很明显,在设置的过程中三级缓存解决循环依赖,并且也顺序放入一级缓存,使得我们通过名称得到map时节省时间,所以真正解决循环依赖的是三级缓存,删除的三级缓存并不影响循环依赖的解决,因为是方法里面调用方法,只要一个完成,后续都会完成的,所以只要三级缓存完成了他的一次作用就行了(三级缓存到一级缓存中间处理了二级缓存了,这里了解即可)
一般来说,Spring是默认允许循环依赖的,所以就会默认操作三级缓存,否则是不会的,那么就会出现循环的问题(具体设置可以百度,可能随着时间的推移,是不允许的,或者这个不允许通常只是建立在错误的情况下是不允许的,即错误导致的不会出现循环依赖,称为不允许)
Spring AOP 应用:
在前面我们基本说明了IOC相关的源码,现在来说Spring两个重要概念中IOC和AOP的AOP
AOP本质:在不改变原有业务逻辑的情况下增强横切逻辑,横切逻辑代码往往是权限校验代码、⽇志代 码、事务控制代码、性能监控代码
AOP 相关术语:
业务主线:
在讲解AOP术语之前,我们先来看一下下面这两张图:

在这里插入图片描述

上图描述的就是未采用AOP思想设计的程序,当我们红⾊框中圈定的方法时,会带来大量的重复劳动,程序中充斥着大量的重复代码,使我们程序的独立性很差,而下图中是采用了AOP思想设计的程序,它 把红框部分的代码抽取出来的同时,运用动态代理技术,在运行期对需要使用的业务逻辑方法进行增 强
在看下面这个图时,建议回顾第65章博客:

在这里插入图片描述

当然,学习对应的第65章博客的内容就知道了,学习后,我们继续看后面:
首先给出一个环境:

在这里插入图片描述

对应的pom.xml文件:
 <dependencies><!--导入spring的context坐标,由于对应aop的命名空间和约束空间起作用,aop没有对应依赖
当然,程序上context也是依赖aop,因为aop基本是需要context的--><dependency><groupId>org.springframework</groupId><artifactId>spring-context</artifactId><version>5.1.5.RELEASE</version></dependency><!-- aspectj的织入(切点表达式需要用到该jar包) --><dependency><groupId>org.aspectj</groupId><artifactId>aspectjweaver</artifactId><version>1.8.13</version></dependency><!--spring整合junit--><dependency><groupId>org.springframework</groupId><artifactId>spring-test</artifactId><version>5.1.5.RELEASE</version></dependency><dependency><groupId>junit</groupId><artifactId>junit</artifactId><version>4.12</version></dependency></dependencies>
对应的MyAdvice类:
package com.lagou.advice;/****/
public class MyAdvice {public void before() {System.out.println("前置通知执行了");}public void after() {System.out.println("后置通知执行了");}public void aftert() {System.out.println("最终通知执行了");}}
对应的AccountService接口:
package com.lagou.service;/****/
public interface AccountService {/*目标方法:(切入点:要进行拦截增强的方法)*/public void transfer();
}
对应的AccountServiceImpl实现类:
package com.lagou.service.impl;import com.lagou.service.AccountService;/****/
public class AccountServiceImpl implements AccountService {/*目标方法:(切入点:要进行拦截增强的方法)*/@Overridepublic void transfer() {System.out.println("转账方法执行了");}
}
对应的AccountServiceTest类:
package com.lagou.test;import com.lagou.service.AccountService;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;/****/
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:ap.xml")public class AccountServiceTest {@Autowiredprivate AccountService accountService;@Testpublic void testTransfer() {accountService.transfer();}
}
对应的配置文件ap.xml:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns:aop="http://www.springframework.org/schema/aop"xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/aophttp://www.springframework.org/schema/aop/spring-aop.xsd"><bean id="accountService" class="com.lagou.service.impl.AccountServiceImpl"></bean><!--通知类放入IOC容器--><bean id="myAdvice" class="com.lagou.advice.MyAdvice"></bean><!--AOP配置--><aop:config><!--配置切面:切入点+通知--><aop:aspect ref="myAdvice" id="myAdvice"> <!--ref指定通知的类名位置,即上面的id,这里的id属性可以与bean的id相同,但是确不能与其他切面的id相同(当然,现在可能可以相同,即spring会进行解决,即结合一起,使得操作两次,可以认为存在数组),如果id不写,那么对应的id值是"切面的类名"
一般是操作实例类的名称,即不写id,那么这里默认是MyAdvice(因为com.lagou.advice.MyAdvice)
--><aop:before method="before"pointcut="execution(voidcom.lagou.service.impl.AccountServiceImpl.transfer())"/><aop:after-returning method="after"pointcut="execution(voidcom.lagou.service.impl.AccountServiceImpl.transfer())"/><aop:after method="aftert"pointcut="execution(voidcom.lagou.service.impl.AccountServiceImpl.transfer())"/></aop:aspect><!--当然,上面是可以提取的,这里就不操作了,比如:<aop:pointcut id="myPointcut"expression="execution(voidcom.lagou.service.impl.AccountServiceImpl.transfer())"/><aop:aspect ref="myAdvice"><aop:before method="before" pointcut-ref="myPointcut"/></aop:aspect>
或者<aop:aspect ref="myAdvice"><aop:pointcut id="myPointcut"expression="execution(voidcom.lagou.service.impl.AccountServiceImpl.transfer())"/><aop:before method="before" pointcut-ref="myPointcut"/></aop:aspect>
如果外面和里面同时存在,且对应的id相同,那么操作内部的,当然,对应的方法不存在,自然不会增强,他并不会报错,只是判断是否存在而决定是否增强而已同样的,如果是结合一起,那么:<aop:aspect ref="myAdvice" id="a"><aop:before method="before" pointcut-ref="myPointcut"/><aop:before method="before" pointcut-ref="myPointcut"/></aop:aspect>和<aop:aspect ref="myAdvice" id="a"><aop:before method="before" pointcut-ref="myPointcut"/></aop:aspect><aop:aspect ref="myAdvice" id="a"><aop:before method="before" pointcut-ref="myPointcut"/></aop:aspect>
是同样的结果所以综上所述,对应的id基本可以随便写(可能随着时间的推移会发生改变,注意即可),所以大多数情况下,id这个属性我们是不会写的--></aop:config></beans>
执行一下,出现结果如下:
/*
前置通知执行了
转账方法执行了
后置通知执行了
最终通知执行了
*/
我们可以这样:
修改配置文件:
    <!--AOP配置--><aop:config><!--配置切面:切入点+通知--><aop:aspect ref="myAdvice" id="myAdvice"> <!--ref指定通知的类名位置,即上面的id--><aop:before method="before"pointcut="execution(voidcom.lagou.service.impl.AccountServiceImpl.transfer(java.lang.Integer))"/> <!--加上参数,并且只操作Integer,而不是int,他是直接判断的,而没有什么装箱或者拆箱的操作,同样的在重写时也是如此,他也是判断,所以你重写时,若对方是Integer,那么你也只能是Integer,而不能是int,因为都是直接的判断,而不是在赋值时的装箱或者拆箱的操作,即本质上它们在类型上是不同的--></aop:aspect></aop:config>
对应修改如下:
public interface AccountService {/*目标方法:(切入点:要进行拦截增强的方法)*/public void transfer(Integer i);
}public class AccountServiceImpl implements AccountService {/*目标方法:(切入点:要进行拦截增强的方法)*/@Overridepublic void transfer(Integer i) {System.out.println("转账方法执行了");}
}@Testpublic void testTransfer() {accountService.transfer(1);}
最终的修改:
   public void before(JoinPoint joinPoint) {System.out.println(joinPoint);System.out.println(joinPoint.getArgs()); //Object[] getArgs();for(Object o : joinPoint.getArgs()){System.out.println(o); //打印参数1}System.out.println("前置通知执行了");}
即对应的joinPoint.getArgs()保存了被切入点的参数列表的值,若是之前,那么什么都没有的(对与环绕通知,对应的ProceedingJoinPoint也有该方法,并且也可以作为参数传递,但是对应的proceed = pjp.proceed()可以不写,默认是对应的参数,写上,那么参数变成你写的,所以如果你操作ProceedingJoinPoint的getArgs()方法,那么不变,若你操作new Object[]{3},那么对应的参数变成了3,这里注意即可,可以选中试一下,前面你复习过的话,应该知道怎么操作,并且对应的还可以不执行业务逻辑,即ProceedingJoinPoint的proceed方法可以不执行)
也要记得,对应是使用代理的,所以如果你不执行对应的方法,那么就拦截不了,自然没有什么通知(增强,包括环绕通知)
我们还存在这样的操作:
   <!--配置切面:切入点+通知--><aop:aspect ref="myAdvice"> <!--ref指定通知的类名位置,即上面的id--><aop:after-returning method="after" returning="aa"pointcut="execution(intcom.lagou.service.impl.AccountServiceImpl.transfer(int))"/><!--一般只有该标签存在returning属性(aa指定的是参数名),其他的没有
并且对应的参数是int,而不是java.lang.Integer,他们是不同的类型,没有操作什么装箱和拆箱,只是单纯的判断类型
--></aop:aspect>
public int transfer(int i);@Overridepublic int transfer(int i) {System.out.println(i); //结果为8System.out.println("转账方法执行了");return 9; //给对应的返回值}@Testpublic void testTransfer() {accountService.transfer(8);}public void after(Object aa) { //aa参数名,要指定参数名称是唯一的,否则编译不过去的System.out.println(aa); //这里得到返回值9,如果对方没有返回值,那么他就是nullSystem.out.println("后置通知执行了");}
我们也可以通过注解来解决(大改变):
对应的MyAdvice类:
package com.lagou.advice;import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;/****/
@Component //变成bean
@Aspect //相当于<aop:aspect
public class MyAdvice {/*<aop:before method="before"pointcut="execution(voidcom.lagou.service.impl.AccountServiceImpl.transfer())"/>*/@Before("execution(* com.lagou.service.impl.AccountServiceImpl.transfer())")public void before() {System.out.println("前置通知执行了");}}
AccountService接口及其实现类:
package com.lagou.service;/****/
public interface AccountService {/*目标方法:(切入点:要进行拦截增强的方法)*/public void transfer();
}
package com.lagou.service.impl;import com.lagou.service.AccountService;
import org.springframework.stereotype.Component;/****/
@Component
public class AccountServiceImpl implements AccountService {/*目标方法:(切入点:要进行拦截增强的方法)*/@Overridepublic void transfer() {System.out.println("转账方法执行了");}
}
测试类AccountServiceTest:
package com.lagou.test;import com.lagou.service.AccountService;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;/****/
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:ap.xml")public class AccountServiceTest {@Autowiredprivate AccountService accountService;@Testpublic void testTransfer() {accountService.transfer();}
}
对与的配置文件:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns:aop="http://www.springframework.org/schema/aop"xmlns:context="http://www.springframework.org/schema/context"xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/aophttp://www.springframework.org/schema/aop/spring-aop.xsdhttp://www.springframework.org/schema/contexthttp://www.springframework.org/schema/context/spring-context.xsd"><!--开启注解扫描,但只能操作对应(所以需要下面的注解)的注解,比如变成bean实例,对于aop可能操作不了,所以需要下面的注解--><context:component-scan base-package="com.lagou"></context:component-scan><!--AOP自动代理(一般默认是jdk代理),使得对应的注解也扫描生效,且spring会采用动态代理完成织入增强,并且生成代理--><aop:aspectj-autoproxy proxy-target-class="true"></aop:aspectj-autoproxy><!--proxy-target-class="true"强制使用cglib动态代理--><aop:aspectj-autoproxy proxy-target-class="false"></aop:aspectj-autoproxy>
<!--这里运行并不报错,那么就存在两种情况,要么是true,要么是false,一般情况下,spring的配置是覆盖的配置所以这里是false,而不是true,当然,随着时间的推移(时间的推移也包括往前面移动,所以旧版本也会考虑,而不是只考虑以后的版本)可能不会这样,所以最好写上一个,而不是这里的多个,可以写3,4个哦,或者更多--><!--当然你可以操作默认,即直接的<aop:aspectj-autoproxy/>或者<aop:aspectj-autoproxy></aop:aspectj-autoproxy>,也行的,覆盖变成false,默认是false的,即JDK代理-->
</beans>
执行看看是否有结果,如果有,那么操作成功,现在,我们继续修改MyAdvice:
package com.lagou.advice;import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;/****/
@Component //变成bean
@Aspect //相当于<aop:aspect
public class MyAdvice {@Pointcut("execution(* com.lagou.service.impl.AccountServiceImpl.transfer())")public void myPoint() {}//    @Before("myPoint()") //这个可以,默认是MyAdvice.myPoint()@Before("MyAdvice1.myPoint()") //这个也可以,大多数对与类来说,或者在参数中类来说,没有写上全限定名,那么默认是当前类所在的包下的,这里要注意哦,大多数框架都是这样public void before() {System.out.println("前置通知执行了");}}
给出MyAdvice1类:
package com.lagou.advice;import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;/****/
//aop是aop,bean是bean,他们的扫描是不同的,所以这里可以只给出对应aop的注解即可,并不影响具体操作
//所以这里就不将他变成实例了,因为在aop扫描时,会自动(扫描到,并得到信息)给出其具体信息的,所以是否实例与aop并无影响
public class MyAdvice1 {@Pointcut("execution(* com.lagou.service.impl.AccountServiceImpl.transfer())")public void myPoint() {}
}
继续测试,如果有结果,那么操作成功,如果说之前的参数,我们可以通过改变execution来添加参数,那么返回值怎么进行设置呢,根据参数和返回值都只是逻辑上的操作,参数自然是目标方法的改变,而返回值,自然是目标方法之后的改变,自然就是后置通知,所以当操作如下时(MyAdvice的改变):
package com.lagou.advice;import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;/****/
@Component //变成bean
@Aspect //相当于<aop:aspect
public class MyAdvice {//可以在内部@Pointcut("execution(* com.lagou.service.impl.AccountServiceImpl.transfer())")public void myPoint() {}@AfterReturning(value = "myPoint()",returning = "aa")public void afre(Object aa) {System.out.println(aa);System.out.println("后置通知执行了");}}
我们同样的需要到这里进行修改:
public interface AccountService {/*目标方法:(切入点:要进行拦截增强的方法)*/public int transfer();
}@Component
public class AccountServiceImpl implements AccountService {/*目标方法:(切入点:要进行拦截增强的方法)*/@Overridepublic int transfer() {System.out.println("转账方法执行了");return 10;}
}
执行,看看结果,若有数据说明操作完毕,那么若存在环绕通知的情况下,对应的返回值一般什么时候进行返回呢,答:在环绕通知自身的最终通知之后,以及被环绕通知或者注解改变顺序使得原来后置通知在最终通知之后的之前,比如:
/*
前置通知执行了11
转账方法执行了
后置通知执行了11
最终通知执行了11   
最终通知执行了
10
后置通知执行了加上11的代表是环绕通知的,当然,这些操作了解即可,因为实际上我们基本都会使用环绕通知,而不会结合其他增强的通知的
*/
我们可以使用完全的注解,在lagou包下创建config包,并创建SpringConfig类:
package com.lagou.config;import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;/****/
@Configuration
@ComponentScan("com.lagou")
@EnableAspectJAutoProxy //开启AOP自动代理 替代了aop:aspectj-autoproxy
public class SpringConfig {
}
然后改变测试类AccountServiceTest:
package com.lagou.test;import com.lagou.config.SpringConfig;
import com.lagou.service.AccountService;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = SpringConfig.class)public class AccountServiceTest {@Autowiredprivate AccountService accountService;@Testpublic void testTransfer() {accountService.transfer();}
}
这样就是纯注解了,这里是必须需要先操作配置类的,只是将配置类的读取变成读取xml,所以在SpringBoot中同样也是如此(操作配置类)
由于博客字数限制,其他内容,请到下一篇博客(108章博客)去看

http://www.ppmy.cn/news/350560.html

相关文章

Python模板模式

概念 模板方法模式是一种设计模式&#xff0c;它是一种抽象类&#xff0c;其中定义了一个操作中的算法框架&#xff0c;具体步骤实现由子类延迟到运行时决定。模板方法模式的目的是提供一种重用代码的机制&#xff0c;它通过定义一个抽象类&#xff0c;其中包含一个基本算法的…

React生命周期

生命周期是一个抽象的概念&#xff0c;在生命周期的整个过程&#xff0c;分成了很多个阶段&#xff1a; 比如挂载阶段&#xff08;Mount&#xff09;&#xff0c;组件第一次在DOM树中被渲染的过程&#xff1b; 比如更新过程&#xff08;Update&#xff09;&#xff0c;组件状…

Spark大数据处理学习笔记1.4 掌握Scala运算符

文章目录 一、学习目标二、运算符等价于方法&#xff08;一&#xff09;运算符即方法&#xff08;二&#xff09;方法即运算符1、单参方法2、多参方法3、无参方法 三、Scala运算符&#xff08;一&#xff09;运算符分类表&#xff08;二&#xff09;Scala与Java运算符比较 四、…

android 局域网对讲机

参考了一些代码&#xff0c;实现了局域网的实时语音对讲功能&#xff0c;只要同网段局域网即可通话&#xff0c;文字聊天&#xff0c;传输文件等&#xff0c;包含了飞鸽传输的功能。 主要是录音发送和接收播放录音比较重要。录音线程&#xff1a; Java代码 public class Audi…

【对讲机的那点事】玩公网对讲机,你知道公网对讲机的模块吗?

公网对讲机模块的大量使用&#xff0c;让终端厂家的开发、生产变得相当容易&#xff0c;有人说&#xff0c;模块是实现复杂到简单的桥梁&#xff0c;是托起行业发展的翅膀&#xff0c;但是有多少公网集群对讲机经销商对公网对讲机模块有过了解吗&#xff1a; 如果说模拟集群向数…

【对讲机的那点事】免费的公网对讲机平台你敢用吗?

公网对讲由前几年的概念阶段到现在迅速爆发阶段,凭借其音质清晰、没有距离限制、GPS定位、单呼选呼等集群功能强大等优势迅速占据了对讲市场的半壁江山,但是公网对讲也有他的短板,即公网对讲的使用是需要收费的,这也是部分用户不选择公网对讲的原因之一,下面我们就把公网对…

this 关键字的用法,super 关键字的用法,this 与 super 的区别

this 是自身的一个对象&#xff0c;代表对象本身&#xff0c;是指向对象本身的一个指针。 用法分为3种&#xff1a; 1.普通的直接引用&#xff0c;this 相当于是指向当前对象本身 2.形参与成员名字重名&#xff0c;用this 来区分 public Student(String name, int age) {this.…

Vue第八篇Vue3

一 Vue3的变化 1.性能的提升 打包大小减少41% 初次渲染快55%, 更新渲染快133% 内存减少54% 2.源码的升级 使用Proxy代替defineProperty实现响应式 重写虚拟DOM的实现和Tree-Shaking 3.拥抱TypeScript Vue3可以更好的支持TypeScript 4.新的特性 Composition API&#…