Java 手写 Spring框架 IOC 和 AOP---SpringIoC高级应用面试知识点,源码

源码(码云):https://gitee.com/yin_zhipeng/implement-_spring_of_myself.git

文章目录

  • 一、手写Spring
  • 二、Spring IoC高级应用面试常问知识点复习
    • 1. springIoC基本概念和使用
      • 1.1 纯xml模式
        • 1.1.1 JavaEE版
        • 1.1.2 web使用监听器版
        • 1.1.3 Bean标签属性
          • 1.1.3.1 Bean的常用创建方式
          • 1.1.3.2 Bean的作用范围以及生命周期
          • 1.1.3.3 Bean标签属性
      • 1.2 纯xml模式的DI依赖注入
        • 1.2.1 构造函数注入
        • 1.2.2 复杂(集合)类型
      • 1.3 xml+注解模式
        • 1.3.1 使用xml+注解模式改造工程
        • 1.3.2 引入外部文件配置
      • 1.4 纯注解模式
    • 2. spring 高级特性
      • 2.1 lazy-Init延迟加载
      • 2.2 FactoryBean和BeanFactory
    • 3. Spring Bean的生命周期
      • 3.1 spring bean的生命周期测试
      • 3.2 实例化之前干的事
    • 4. 后置处理器
      • 3.1 BeanPostProcessor
      • 3.2 BeanFactoryPostProcessor
  • 三、Spring IoC源码
  • 四、Spring AOP 高级应用和源码

一、手写Spring

篇幅限制,我将其放在这篇文章中:https://blog.csdn.net/grd_java/article/details/122625811

二、Spring IoC高级应用面试常问知识点复习

通过手写Spring,我们已经对spring思想有了基本了解,接下来进行高级应用讲解,在此之前,我们依然对一些基本概念做些回顾,同样是面试官经常问到的一些东西
IoC配置bean实例的3种方式
  1. 纯xml,bean信息全部定义在xml文件中,我们可以通过如下方式,指定读取xml
//JavaSE:
AppliccationContext applicationContext = new ClassPathXmlApplicationContext("beans.xml");
AppliccationContext applicationContext = new FileSystemXmlApplicationContext("beans.xml");
//Java Web 可以使用监听器加载xml
ContextLoaderListener
  1. 纯注解,所有bean都用注解定义,加载方法如下
//JavaSE SpringConfig.class注解配置类的class对象
ApplicationContext applicationContext = new AnnotationConfigApplicationContext(SpringConfig.class);
//Java Web 监听器去加载注解配置类
ContextLoaderListener
  1. xml+注解,部分bean配置在xml中,部分bean使用注解定义(加载方法,是xml和注解混合)
BeanFactory和ApplicationContext的区别
  1. 前面我们手写spring,使用BeanFactory生产bean,那么spring怎么做呢?
  2. 这里有一张spring的BeanFactory继承关系图,发现BeanFactory是IoC容器的顶层接口,而ApplicationContext是子接口,所以ApplicationContext的功能更强(国际化支持,资源访问例如xml,java配置类),我们常用ApplicationContext
    Java 手写 Spring框架 IOC 和 AOP---SpringIoC高级应用面试知识点,源码_第1张图片
  3. 可以发现,spring层次划分的很多,功能分工明确,没必要把所有功能都划分到BeanFactory
  4. BeanFactory就像一个领导,它不可能把所有细节的事都做了,它可以掌控大局,但完成整套工作,需要很多人才相互配合
测试环境说明
  1. 我们将手写spring的例子复制一份,将其改造为使用真正的Spring
    Java 手写 Spring框架 IOC 和 AOP---SpringIoC高级应用面试知识点,源码_第2张图片

1. springIoC基本概念和使用

使用spring就得引入基本模块依赖,context,包含core、beans、aop和expression
  1. 引入相关依赖
    Java 手写 Spring框架 IOC 和 AOP---SpringIoC高级应用面试知识点,源码_第3张图片

1.1 纯xml模式

1.1.1 JavaEE版

xml我们一直自己定义,虽然遵循spring规范,但是是我们人为模仿,我们需要引入spring的约束
  1. 给xml文件改名为applicationContext.xml(想不想改都行,只不过大家都叫这名字),然后添加xml约束
    Java 手写 Spring框架 IOC 和 AOP---SpringIoC高级应用面试知识点,源码_第4张图片
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        https://www.springframework.org/schema/beans/spring-beans.xsd">
  1. 使用spring IoC获取bean实例
    Java 手写 Spring框架 IOC 和 AOP---SpringIoC高级应用面试知识点,源码_第5张图片

1.1.2 web使用监听器版

1. 先将我们工程改造为web工程
  1. 引入tomcat依赖和servlet依赖,让maven打包方式为war
    Java 手写 Spring框架 IOC 和 AOP---SpringIoC高级应用面试知识点,源码_第6张图片
    Java 手写 Spring框架 IOC 和 AOP---SpringIoC高级应用面试知识点,源码_第7张图片
  2. 编写Servlet,启动tomcat,然后访问8080端口Servlet
    Java 手写 Spring框架 IOC 和 AOP---SpringIoC高级应用面试知识点,源码_第8张图片
2. 引入spring-web模块依赖,编写web.xml
  1. 引入spring-web依赖
    Java 手写 Spring框架 IOC 和 AOP---SpringIoC高级应用面试知识点,源码_第9张图片
  2. 编写web.xml之前,我们知道我们通过ContextLoaderListener这个监听器来创建bean,那么它其实有一些参数,比如我们可以指定它加载哪个xml配置文件,如下:
    Java 手写 Spring框架 IOC 和 AOP---SpringIoC高级应用面试知识点,源码_第10张图片
  3. 接下来我们编写web.xml(自己创建相关目录和文件即可),指定监听器,并且指定属性,告诉监听器加载那个配置文件
    Java 手写 Spring框架 IOC 和 AOP---SpringIoC高级应用面试知识点,源码_第11张图片
<!DOCTYPE web-app PUBLIC
        "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
        "http://java.sun.com/dtd/web-app_2_3.dtd" >

<web-app>
    <display-name>Archetype Creadted Web Application</display-name>
    <!--ContextLoaderListener的一个属性,指定容器的配置文件位置-->
    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>classpath:applicationContext.xml</param-value>
    </context-param>

    <!--使用监听器启动SpringIoC容器-->
    <listener>
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>

</web-app>
3. 改造Servlet 通过监听器获取bean,然后测试效果

Java 手写 Spring框架 IOC 和 AOP---SpringIoC高级应用面试知识点,源码_第12张图片
Java 手写 Spring框架 IOC 和 AOP---SpringIoC高级应用面试知识点,源码_第13张图片

1.1.3 Bean标签属性

1.1.3.1 Bean的常用创建方式
创建实例的不同方式:先前都使用无参构造来创建,下面给出不同方式
  1. 通过静态方法获取bean实例,一般配合工厂设计模式
    Java 手写 Spring框架 IOC 和 AOP---SpringIoC高级应用面试知识点,源码_第14张图片
    Java 手写 Spring框架 IOC 和 AOP---SpringIoC高级应用面试知识点,源码_第15张图片
<bean id="connectionUtils" class="spring.factory.CreateBeanFactory" factory-method="getInstanceStaticConnectionUtils"></bean>
  1. 通过非静态方法,一般配合工厂设计模式
    Java 手写 Spring框架 IOC 和 AOP---SpringIoC高级应用面试知识点,源码_第16张图片
    Java 手写 Spring框架 IOC 和 AOP---SpringIoC高级应用面试知识点,源码_第17张图片
1.1.3.2 Bean的作用范围以及生命周期
Bean的作用范围可变,spring框架管理Bean对象的创建,默认使用单例模式创建,它支持配置的方式改变作用范围
Scope(:不常用/:常用) Description
singleton 单例,使用单例模式创建的实例,整个IoC容器中就一个,默认方式
prototype 原型(多例)模式,使用原型设计模式创建的实例,每次想要bean,都会拷贝给你一个新的bean
request bean实例会在客户端每次进行Http请求时创建,在WebApplicationContext环境下使用,范围在一个请求中
session bean会在客户端与服务器的每一次会话中创建一个bean,不同会话创建不同对象,同一会话始终使用同一对象,范围在一个会话中
application 范围在ServletContext的声明周期中,只有在web的Spring application上下文中有效
websocket 范围在WebSocket,只在web-aware Spring ApplicationContext的上下文中有效
我们指定类ConnectionUtils为单例模式,启动tomcat后,查看两次bean是不是一个
  1. 指定bean的范围为单例(不指定scope属性,默认就是单例,所以这一步不配置也可以)
    Java 手写 Spring框架 IOC 和 AOP---SpringIoC高级应用面试知识点,源码_第18张图片
  2. 测试
    Java 手写 Spring框架 IOC 和 AOP---SpringIoC高级应用面试知识点,源码_第19张图片
我们指定类ConnectionUtils为原型模式,启动tomcat后,查看两次bean是不是一个
  1. 指定为原型模式,然后测试
    Java 手写 Spring框架 IOC 和 AOP---SpringIoC高级应用面试知识点,源码_第20张图片
单例模式和原型模式bean的生命周期(其它类型的bean压根就用不到,就不说了)
  1. 单例singleton,生命周期和IoC容器相同
  1. 对象出生:当创建容器时,对象被创建
  2. 对象活动范围:只要容器还在,对象就会一直活这
  3. 对象死亡:容器销毁时,对象被销毁
  1. 多例prototype,使用时,IoC容器创建,多会销毁,取决于java垃圾回收器多会回收它
  1. 对象出生:使用对象时,创建新的对象实例
  2. 对象活动范围:只要对象在使用,就一直活着
  3. 对象死亡:被java垃圾回收器回收(没有引用指向,或者满足垃圾回收算法的条件,比如长时间不使用)
1.1.3.3 Bean标签属性
property(:不常用/:常用) Description
id属性 用于给bean提供一个唯一标识,一个< beans>标签内部,标识必须唯一
class属性 用于创建Bean对象的全限定类名
name属性 用于给Bean提供一个或多个名称,多个名称用空格分隔
factory-bean属性 用于指定创建当前bean对象的工厂bean的唯一标识,指定此属性后,class属性失效
factory-method属性 指定创建当前bean对象的工厂方法,配合factory-bean属性使用时,class属性失效,配合class属性使用时,方法必须是static
scope属性 用于指定bean对象的作用范围,默认singleton单例模式,常用的还有prototype原型模式
init-method属性 用于指定bean对象的初始化方法,此方法会在bean对象装配后调用,必须是一个无参方法
destory-method属性 用于指定bean对象的消耗方法,此方法会在bean对象销毁前执行,只能为scope是singleton时起作用
init-method和destory-method属性,是两个生命周期属性,可以在bean创建前和销毁前执行
  1. 在ConnectionUtils类中定义两个无参方法
    Java 手写 Spring框架 IOC 和 AOP---SpringIoC高级应用面试知识点,源码_第21张图片
  2. 配置xml
    Java 手写 Spring框架 IOC 和 AOP---SpringIoC高级应用面试知识点,源码_第22张图片
  3. 测试,销毁方法,需要调用IoC容器的close方法来关闭,只有部分子类有,也不常用就不测试了
    Java 手写 Spring框架 IOC 和 AOP---SpringIoC高级应用面试知识点,源码_第23张图片

1.2 纯xml模式的DI依赖注入

依赖注入分类
  1. 两种常用注入方式
  1. 构造函数注入:利用带参构造函数实现对类成员的数据赋值
  2. set方法注入:通过类成员的set方法实现数据的注入(使用最多)
  1. 按照注入的数据类型
  1. 基本类型和String:注入的数据类型是基本类型或者是字符串类型的数据
  2. 其他Bean类型:注入的数据类型是对象类型,称为其他Bean的原因是,这个对象是要求出现在IoC容器中的,那么针对当前Bean来说,就是其他Bean了。
  3. 复杂类型(集合类型):注入的数据类型时Array,List,Set,Map,Properties中的一种类型
我们先前使用的都是set注入的方式,注入的数据类型是其他Bean类型,如果是基本类型或String,将ref换成value,里面输入值,例如value = “100.3”,很简单不过多介绍了

Java 手写 Spring框架 IOC 和 AOP---SpringIoC高级应用面试知识点,源码_第24张图片

1.2.1 构造函数注入

  1. 为ConnectionUtils类添加几个参数,提供构造方法
    Java 手写 Spring框架 IOC 和 AOP---SpringIoC高级应用面试知识点,源码_第25张图片
  2. xml注入
    Java 手写 Spring框架 IOC 和 AOP---SpringIoC高级应用面试知识点,源码_第26张图片

1.2.2 复杂(集合)类型

  1. 为ConnectionUtils添加复杂类型变量个set方法
    Java 手写 Spring框架 IOC 和 AOP---SpringIoC高级应用面试知识点,源码_第27张图片
  2. xml注入(同样普通值用value,引用类型用ref,map的类型统一是entry,entry中的key和value如果是引用类型,加ref前缀即可,例如key-ref)
    Java 手写 Spring框架 IOC 和 AOP---SpringIoC高级应用面试知识点,源码_第28张图片

1.3 xml+注解模式

xml+注解,spring IoC容器启动依然从加载xml开始,下面介绍xml中标签对应的注解
  1. 自己写的类,用注解
  2. 第三方jar中的bean配置到xml中
xml(:不常用/:常用) Annontation
< bean id = “connectionUtils”>标签 @Component(“connectionUtils”),加在类上,id属性是value(默认一个参数就是给它赋值),如果不配置,id默认为类名(第一个单词首字母小写,其它首字母大写),另外针对分层开发,还提供了3种别名:@Controller、@Service、@Repository,分别用于控制层,服务层,持久层的bean定义,4个注解功能用法完全一样,只是为了区分清晰
DI 依赖注入 @Autowired,用于属性(变量)上,采用策略为,按照类型注入,但是如果一个类型有多个bean值时,会不知道选哪个
DI 依赖注入,告诉Spring具体装配哪个bean @Qualifier(“bean的id”),配合@Autowired使用,告诉Spring具体装配哪个bean实例
Java原生注解 @Resource,和@Autowired相同,java原生注解,但是在JDK11移除了,可以同时指定id和类型,和@Autowired一样,如果写在字段上,不用提供set方法,也可直接写在setter方法上
< bean >标签的scope属性 Scope(“prototype”),加在类上,默认单例
< bean >标签的init-method属性 PostConstruct(),加在方法上,标识该方法为bean初始化后调用方法
< bean >标签的destory-method属性 PreDestory(),加在方法上,标识该方法为bean销毁前调用方法

1.3.1 使用xml+注解模式改造工程

1. 第三方jar配置在xml,其它都用注解
  1. xml中配置第三方jar
    Java 手写 Spring框架 IOC 和 AOP---SpringIoC高级应用面试知识点,源码_第29张图片
    Java 手写 Spring框架 IOC 和 AOP---SpringIoC高级应用面试知识点,源码_第30张图片
  2. 使用注解配置ConnectionUtils
    Java 手写 Spring框架 IOC 和 AOP---SpringIoC高级应用面试知识点,源码_第31张图片
  3. 使用注解配置TransactionManager
    Java 手写 Spring框架 IOC 和 AOP---SpringIoC高级应用面试知识点,源码_第32张图片
  4. 使用注解配置proxyFactory
    Java 手写 Spring框架 IOC 和 AOP---SpringIoC高级应用面试知识点,源码_第33张图片
  5. 使用注解配置dao层
    Java 手写 Spring框架 IOC 和 AOP---SpringIoC高级应用面试知识点,源码_第34张图片
  6. 使用注解配置service
    Java 手写 Spring框架 IOC 和 AOP---SpringIoC高级应用面试知识点,源码_第35张图片
2. web.xml配置使用新的xml文件

Java 手写 Spring框架 IOC 和 AOP---SpringIoC高级应用面试知识点,源码_第36张图片

3. 开启注解驱动

Java 手写 Spring框架 IOC 和 AOP---SpringIoC高级应用面试知识点,源码_第37张图片

<?xml version="1.0" encoding="UTF-8" ?>
<!--beans 根标签,里面有若干个bean子标签-->
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:comtext="http://www.springframework.org/schema/context"
       xsi:schemaLocation="
        http://www.springframework.org/schema/beans
        https://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context
        https://www.springframework.org/schema/context/spring-context.xsd
">

    <!--开启注解扫描,base-package指定扫描的包路径-->
    <comtext:component-scan base-package="com.yzpnb.advanced_application"></comtext:component-scan>
    <comtext:component-scan base-package="spring"></comtext:component-scan>
    <!--第三方bean配置到xml中-->
    <bean id="datasource" class="com.alibaba.druid.pool.DruidDataSource">
        <property name="driverClassName" value="com.mysql.cj.jdbc.Driver"></property>
        <property name="url" value="jdbc:mysql://localhost:3306/bank"></property>
        <property name="username" value="root"></property>
        <property name="password" value="123456"></property>
    </bean>
</beans>
4. 测试

Java 手写 Spring框架 IOC 和 AOP---SpringIoC高级应用面试知识点,源码_第38张图片

1.3.2 引入外部文件配置

通常,我们将数据库配置,都放在单独一个配置文件中

Java 手写 Spring框架 IOC 和 AOP---SpringIoC高级应用面试知识点,源码_第39张图片

然后xml中引入外部文件使用

Java 手写 Spring框架 IOC 和 AOP---SpringIoC高级应用面试知识点,源码_第40张图片

1.4 纯注解模式

纯注解模式,完全不需要xml了,和使用springboot时基本相同,基本注解如下
Description(:不常用/:常用) Annontation
标识当前类是一个配置类 @Configuration
替代context:compoent-scan,开启注解驱动 @ComponentScan
引入外部配置文件 @PropertySource
重点引入其他配置类,如果配置类有多个,可以用这个注解,统一引入到一个配置类中 @Import
对变量赋值,可以直接赋值,或者使用${}读取资源配置文件中信息 @Vlaue
将方法返回对象加入SpringIoC容器中 @Bean
那么spring毕竟不是springboot,没有启动类,我们将这些注解全部配置到配置类上
  1. 创建一个SpringConfiguration配置类,用@Configuration注解标识
    Java 手写 Spring框架 IOC 和 AOP---SpringIoC高级应用面试知识点,源码_第41张图片
  2. 使用@ComponentScan注解,替代context:compoent-scan,开启注解驱动
    Java 手写 Spring框架 IOC 和 AOP---SpringIoC高级应用面试知识点,源码_第42张图片
  3. 使用@PropertySource注解引入外部资源文件
    Java 手写 Spring框架 IOC 和 AOP---SpringIoC高级应用面试知识点,源码_第43张图片
  4. 使用@Vlaue‘注解’注入配置文件中的值,使用@Bean()注解,配置第三方jar的bean实例
    Java 手写 Spring框架 IOC 和 AOP---SpringIoC高级应用面试知识点,源码_第44张图片
没有启动类,我们怎么加载配置类,然后使用我们的配置呢
  1. javaEE版
    Java 手写 Spring框架 IOC 和 AOP---SpringIoC高级应用面试知识点,源码_第45张图片
  2. web版:纯注解已经不需要xml了,web程序需要监听器,做如下更改,监听器配置保留,并指定监听器使用注解配置类,而且指定xml配置文件的配置需要改变,改为指定配置类
    Java 手写 Spring框架 IOC 和 AOP---SpringIoC高级应用面试知识点,源码_第46张图片
  3. 测试
    Java 手写 Spring框架 IOC 和 AOP---SpringIoC高级应用面试知识点,源码_第47张图片

2. spring 高级特性

2.1 lazy-Init延迟加载

一种可以在一定程度上提高容器启动和运转性能的机制,对于不常用的bean设置懒加载,偶尔使用时再加载,没必要一开始这个bean就占用资源
Bean的延迟加载(延迟创建),就是懒加载,按需加载,第一次向容器索取bean的时候,才会实例化
  1. ApplicationContext容器的默认行为是启动服务器时将所有singleton bean提前进行实例化,意味着实例化作为初始化过程的一部分,ApplicationContext实例会创建并配置所有的singleton bean。
  2. 如果不指定lazy-Init参数,则默认为false,不进行懒加载

<bean id="testBean" class = "com.yzpnb.testBean" />
等价于
<bean id="testBean" class = "com.yzpnb.testBean" lazy-init="false"/>


<bean id="testBean" class = "com.yzpnb.testBean" lazy-init="true"/>
  1. 什么时候是需要一个bean的时候,当我们getBean的时候,或者bean1引用了开启懒加载的bean2,bean1实例化时,bean2也会实例化
统一给所有bean都设置懒加载
  1. < beans>标签上使用default-lazy-init属性,来控制< bean>标签的懒加载
<!--容器层次,控制懒加载初始化-->
<beans default-lazy-init = "true">
	<!--经管没指定lazy-init="true",它也开启了懒加载,因为<beans>标签统一设置了-->
	<bean id="testBean" class = "com.yzpnb.testBean" />
</beans>
通过注解@lazy
@Component("connectionUtils")
@Lazy
public class ConnectionUtils {
}
做个简单测试,其实它已经初始化了,只不过放在了一个map中(缓存),需要的时候,从缓存中搞出来,后面看源码会讲

Java 手写 Spring框架 IOC 和 AOP---SpringIoC高级应用面试知识点,源码_第48张图片
Java 手写 Spring框架 IOC 和 AOP---SpringIoC高级应用面试知识点,源码_第49张图片
Java 手写 Spring框架 IOC 和 AOP---SpringIoC高级应用面试知识点,源码_第50张图片

通过注解呢?
  1. 加注解
    Java 手写 Spring框架 IOC 和 AOP---SpringIoC高级应用面试知识点,源码_第51张图片
  2. 测试:因为我们配置类中,有其它bean依赖于connectionUtils,你可以自己创建一个单独的bean测试,这里我只告诉方法,后面会重点讲源码
    Java 手写 Spring框架 IOC 和 AOP---SpringIoC高级应用面试知识点,源码_第52张图片

2.2 FactoryBean和BeanFactory

BeanFactory我们前面已经提到了,是IoC容器的顶级接口,定义了容器的一些基础行为,负责生产和管理Bean的一个工厂,具体使用它下面的子接口类型,例如ApplicationContext
FactoryBean:工厂bean,spring中有普通bean和工厂bean两种,FactoryBean可以生成某一类型的Bean实例返回给我们,也就是说,我们可以借助它自定义Bean的创建过程
前面我们介绍了创建bean的三种方式,构造方法,静态方法,实例方法,FactoryBean类似于静态方法和实例方法,但是FactoryBean是常用的方法,静态和实例方法,用的并不多
一般用来创建复杂对象,就是xml和注解没办法创建的对象
  1. 新建一个类Company
    Java 手写 Spring框架 IOC 和 AOP---SpringIoC高级应用面试知识点,源码_第53张图片
  2. 创建一个生产此对象的工厂,继承实现FactoryBean
    Java 手写 Spring框架 IOC 和 AOP---SpringIoC高级应用面试知识点,源码_第54张图片
如何使用呢,依然需要通过注解或者xml指定使用FactoryBean来生产复杂对象
  1. xml
    Java 手写 Spring框架 IOC 和 AOP---SpringIoC高级应用面试知识点,源码_第55张图片
    Java 手写 Spring框架 IOC 和 AOP---SpringIoC高级应用面试知识点,源码_第56张图片
  2. Annontation,就是简单的配置类中配置了
    Java 手写 Spring框架 IOC 和 AOP---SpringIoC高级应用面试知识点,源码_第57张图片
    Java 手写 Spring框架 IOC 和 AOP---SpringIoC高级应用面试知识点,源码_第58张图片

3. Spring Bean的生命周期

Created with Raphaël 2.3.0 第一步:实例化Bean 第二步:设置属性值 第三步:调用BeanNameAware的setBeanName方法 第四步:调用BeanFactoryAware的setBeanFactory方法 第五步:调用ApplicationContextAware的setApplicationContext方法 第六步:调用BeanPostProcessor的预初始化方法 第七步:调用InitializingBean的afterPropertiesSet方法 第八步:调用定制的初始方法init-method 第九步:调用BeanPostProcessor的后初始化方法
  1. 第九步,有两种可能,如果是prototype(原型模式)的bean,就直接交给调用者
  2. 如果是singleton(单例模式)的bean,就放入spring缓存池中准备就绪的bean
Created with Raphaël 2.3.0 Spring缓存池中准备就绪的bean,当Bean销毁时 调用destory-method属性配置的销毁方法(没有先后顺序) 调用DisposableBean的destory方法(没有先后顺序)
文字描述一下
  1. 根据配置情况调用Bean构造方法或工厂方法实例化Bean
  2. 利用依赖注入完成Bean中所有属性值的配置注入
  3. 如果Bean实现了BeanNameAware接口,则Spring调用Bean的setBeanName()方法传入当前Bean的id值
  4. 如果Bean实现了BeanFactoryAware接口,则Spring调用setBeanFactory()方法传入当前工厂实例的引用
  5. 如果Bean实现了ApplicationContextAware接口,则Spring调用setApplicationContext()方法传入当前ApplicationContext实例的引用。
  6. 如果BeanPostProcessor和Bean关联,则Spring将调用该接口的预初始化方法postProcessBeforeInitialzation()对Bean进行加工操作,此处非常重要,Spring的AOP就是利用它实现的
  7. 如果Bean实现了InitializingBean接口,则Spring将调用afterPropertiesSet()方法.
  8. 如果在配置文件中通过init-method属性指定了初始化方法,则调用该初始化方法。
  9. 如果BeanPostProcessor和Bean关联,则Spring将调用该接口的初始化方法postProcessAfterInitialization()。此时,Bean已经可以被应用系统使用了。
  10. 如果在< bean>中指定了该Bean的生命周期管理;如果在< bean>中指定了该Bean的作用范围为scope=“prototype”,则将实例交给调用者,剩下的不归Spring管
  11. 如果Bean实现了DisposableBean接口,则Spring会调用destory()方法将Spring中的Bean销毁;如果在配置文件中通过destory-method属性指定了Bean的销毁方法,则Spring将调用该方法对Bean进行销毁
注意,虽然Spring为Bean提供了细致全面的生命周期过程,通过实现特定的接口或< bean>的属性设置,都可以对Bean生命周期进行管理,虽然可以随意配置< bean>的属性,但是不建议过多使用Bean实现接口,因为这样会导致代码和Spring的高耦合

3.1 spring bean的生命周期测试

接下来我们写一个测试类,测试一下生命周期
  1. 第一、二、三步:创建Result类,即可测试生命周期第一步,而设置一些属性注入,即可测试生命周期第二步,调用BeanNameAware的setBeanName()为第三步,,所以我们实现BeanNameAware,并实现setBeanName方法
    Java 手写 Spring框架 IOC 和 AOP---SpringIoC高级应用面试知识点,源码_第59张图片

  2. 第四步:调用BeanFactoryAware的setBeanFactory方法
    Java 手写 Spring框架 IOC 和 AOP---SpringIoC高级应用面试知识点,源码_第60张图片

  3. 第五步:调用ApplicationContextAware的setApplicationContext方法
    Java 手写 Spring框架 IOC 和 AOP---SpringIoC高级应用面试知识点,源码_第61张图片

  4. 第六步:调用BeanPostProcessor的预初始化方法:这个是后置处理器,这里先略过,下面就会讲

  5. 第七步:调用InitializingBean的afterPropertiesSet方法
    Java 手写 Spring框架 IOC 和 AOP---SpringIoC高级应用面试知识点,源码_第62张图片

  6. 第八步:调用定制的初始方法init-method,可以用xml配置
    Java 手写 Spring框架 IOC 和 AOP---SpringIoC高级应用面试知识点,源码_第63张图片

  7. 第九步:调用BeanPostProcessor的后初始化方法

  8. Bena销毁前

  1. 如果是单例模式,调用DisposableBean的destory方法,调用destory-method属性配置的销毁方法
    Java 手写 Spring框架 IOC 和 AOP---SpringIoC高级应用面试知识点,源码_第64张图片
  1. 测试
    Java 手写 Spring框架 IOC 和 AOP---SpringIoC高级应用面试知识点,源码_第65张图片
    Java 手写 Spring框架 IOC 和 AOP---SpringIoC高级应用面试知识点,源码_第66张图片

3.2 实例化之前干的事

其实IoC容器,在第一步之前还干了一些事
  1. 实例化之前会读取xml,涉及到了一个对象BeanDefinition(Spring容器启动过程中,会将Bean解析成Spring内部的BeanDefinition结构)
    Java 手写 Spring框架 IOC 和 AOP---SpringIoC高级应用面试知识点,源码_第67张图片
  2. 其实就是将Bean的定义信息存储到BeanDefinition相应的属性中,类名、scope、属性、构造函数参数列表、依赖的bean、是否单例、是否懒加载等等
  3. 后面对Bean的操作,就是直接对BeanDefinition进行,例如拿到这个对象,根据类名,构造函数等利用反射,创建实例
当然,在Bean生命周期前,是有工厂已经存在的,那么针对工厂的后置处理器BeanFactoryPostProcessor,也会进行工作,具体看下面

4. 后置处理器

spring 提供两种后置处理bean的扩展接口,BeanPostProcessor(针对bean对象的后置处理器)和BeanFactoryPostProcessor(针对bean工厂的后置处理器)

3.1 BeanPostProcessor

针对Bean基本的处理,可以针对某个具体的bean,看下面源码
public interface BeanPostProcessor {
	//初始化方法前执行(初始化方法,类似于init-method所指定方法)
	@Nullable
	default Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
		return bean;
	}
	//初始化方法后执行
	@Nullable
	default Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
		return bean;
	}

}
编写后置处理器
  1. 创建类,然后实现BeanPostProcessor,让其特定监听lazyResult这个Bean
    Java 手写 Spring框架 IOC 和 AOP---SpringIoC高级应用面试知识点,源码_第68张图片

3.2 BeanFactoryPostProcessor

在BeanFactory初始化前后可以使用BeanFactoryPostProcessor进行后置处理,针对整个Bean工厂进行处理,典型应用是PropertyPlaceholderConfigurer。看下源码,不过多介绍了
源码如下,是个函数式接口,可以使用lambda表达式直接用,只有一个方法,方法参数是ConfigurableListableBeanFactory
@FunctionalInterface
public interface BeanFactoryPostProcessor {
	void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException;

}
ConfigurableListableBeanFactory ,提供了getBeanDefinition方法,可以根据此方法,找到我们定义bean的BeanDefinition对象,然后我们可以对定义的属性进行修改

Java 手写 Spring框架 IOC 和 AOP---SpringIoC高级应用面试知识点,源码_第69张图片

三、Spring IoC源码

由于篇幅限制,我将其放在这篇文章:https://blog.csdn.net/grd_java/article/details/122716151

四、Spring AOP 高级应用和源码

由于篇幅原因,我将其放在这篇文章:https://blog.csdn.net/grd_java/article/details/122858872

你可能感兴趣的:(源码,java,spring,开发语言)