细读Spring源码(四)---什么是IOC?

往期回顾:

IOC是什么?看一下百度百科中的专家贡献,感觉写的还是比较简单明了,拿过来引用:

控制反转(Inversion of Control,缩写为IoC),是面向对象编程中的一种设计原则,可以用来减低计算机代码之间的耦合度。其中最常见的方式叫做依赖注入(Dependency Injection,简称DI),还有一种方式叫“依赖查找”(Dependency Lookup)。通过控制反转,对象在被创建的时候,由一个调控系统内所有对象的外界实体将其所依赖的对象的引用传递给它。也可以说,依赖被注入到对象中。

Class A中用到了Class B的对象b,一般情况下,需要在A的代码中显式的new一个B的对象。
采用依赖注入技术之后,A的代码只需要定义一个私有的B对象,不需要直接new来获得这个对象,而是通过相关的容器控制程序来将B对象在外部new出来并注入到A类里的引用中。而具体获取的方法、对象被获取时的状态由配置文件(如XML)来指定。
 
上面的解释中提到的容器,就是Spring,它负责管理所有的bean,同时维护了bean之间的依赖关系,使得开发人员不需要手动去new,而是定义好,按照容器约定的方式去使用即可。先看一张图,这是我总结的关于IOC的核心知识图谱,今天的文章就按照这张图中的顺序进行介绍~

 一、接口

ApplicationContext和BeanFactory中两个非常重要的接口,后面在源码中会详细介绍,这里先大概说一下:

BeanFactory是Spring中管理Bean的工厂,也是Spring内置的工厂,负责管理Bean从创建到销毁的整个生命周期,BeanFactory中管理的bean,可以在使用时进行创建;

ApplicationContext也是个接口,它继承了BeanFactory以及其他的几个接口(后面介绍),从而提供了比BeanFactory更加强大的功能,但是ApplcationContext中管理的bean,在容器初始化的时候就创建好了;

二、实现原理

我们知道创建一个对象的方式除了new之外,还可以通过动态代理、工厂模式等方式创建,而Spring中就是通过工厂模式+动态代理的方式实现bean的依赖注入,工厂模式和动态代理分别在细读Spring源码(二)---关于Spring中用到的设计模式细读Spring源码(二)---关于Spring中用到的设计模式 中已经介绍过,其实最底层还是靠反射来支撑。

三、依赖注入DI(Dependency Injection)

依赖注入是IOC的实现方式,在Spring中有两种注入方式,基于XML和基于注解,虽然现在都是通过注解方式,但我觉得还是有必要说一下XML,毕竟spring源码还得从XML看起,更重要的是注解是对XML的优化方式,所以知道了XML,也就能更好地理解注解的方式了。

Talk is cheap,Show your my code!

3.1 基于XML的实现

首先,定义两个存在依赖关系的类DiOrder和DiProduct,前者依赖后者

1、DiProduct.java

 1 package com.spring.reading.ioc.di;
 2 
 3 import java.math.BigDecimal;
 4 
 5 /**
 6  * @author: cyhua
 7  * @createTime: 2021/12/1
 8  * @description: 定义一个商品类
 9  */
10 public class DiProduct {
11 
12     /**
13      * 商品id
14      */
15     private String productId;
16 
17     /**
18      * 商品名称
19      */
20     private String productName;
21 
22     /**
23      * 商品价格
24      */
25     private BigDecimal productPrice;
26 
27     public DiProduct() {
28     }
29 
30     public String getProductId() {
31         return productId;
32     }
33 
34     public void setProductId(String productId) {
35         this.productId = productId;
36     }
37 
38     public String getProductName() {
39         return productName;
40     }
41 
42     public void setProductName(String productName) {
43         this.productName = productName;
44     }
45 
46     public BigDecimal getProductPrice() {
47         return productPrice;
48     }
49 
50     public void setProductPrice(BigDecimal productPrice) {
51         this.productPrice = productPrice;
52     }
53 
54     @Override
55     public String toString() {
56         StringBuilder sb = new StringBuilder("DiProduct={");
57         sb.append("productId='").append(productId).append("'");
58         sb.append(",productName='").append(productName).append("'");
59         sb.append(",productPrice='").append(productPrice).append("'");
60         sb.append("}");
61         return sb.toString();
62     }
63 }

2、DiOrder.Java

 1 package com.spring.reading.ioc.di;
 2 
 3 /**
 4  * @author: cyhua
 5  * @createTime: 2021/12/1
 6  * @description: 定义一个订单类
 7  */
 8 public class DiOrder {
 9 
10     /**
11      * 订单编号
12      */
13     private String orderNumber;
14 
15     /**
16      * 订单对应商品:依赖商品类
17      */
18     private DiProduct diProduct;
19 
20     /**
21      * 商品个数
22      */
23     private int productNum;
24 
25 
26     public DiOrder() {
27     }
28 
29     public String getOrderNumber() {
30         return orderNumber;
31     }
32 
33     public void setOrderNumber(String orderNumber) {
34         this.orderNumber = orderNumber;
35     }
36 
37     public DiProduct getDiProduct() {
38         return diProduct;
39     }
40 
41     public void setDiProduct(DiProduct diProduct) {
42         this.diProduct = diProduct;
43     }
44 
45     public int getProductNum() {
46         return productNum;
47     }
48 
49     public void setProductNum(int productNum) {
50         this.productNum = productNum;
51     }
52 
53     @Override
54     public String toString() {
55         StringBuilder sb = new StringBuilder("DiOrder={");
56         sb.append("orderNumber='").append(orderNumber).append("'");
57         sb.append(",productNum='").append(productNum).append("'");
58         sb.append(",diProduct='").append(diProduct).append("'");
59         sb.append("}");
60         return sb.toString();
61     }
62 }

其次,在spring-di.xml中定义bean

3、spring-di.xml

 1 <?xml version="1.0" encoding="UTF-8"?>
 2 <beans xmlns="http://www.springframework.org/schema/beans"
 3        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 4        xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">
 5 
 6 
 7     <!--定义一个name为diProduct的商品bean-->
 8     <bean id="diProduct" class="com.spring.reading.ioc.di.DiProduct">
 9         <property name="productId" value="1"/>
10         <property name="productName" value="MacbookPro max"/>
11         <property name="productPrice" value="26999"/>
12     </bean>
13 
14     <!--定义一个name为diOrder的订单bean-->
15     <bean id="diOrder" class="com.spring.reading.ioc.di.DiOrder">
16         <property name="orderNumber" value="0123456789"/>
17         <property name="diProduct" ref="diProduct"/>
18         <property name="productNum" value="2"/>
19     </bean>
20 
21 </beans>

最后,通过diOrder获取订单对象,并打印里面的商品对象

4、测试类

 1 package com.spring.reading.ioc.di;
 2 
 3 import org.springframework.context.ApplicationContext;
 4 import org.springframework.context.support.ClassPathXmlApplicationContext;
 5 
 6 /**
 7  * @author: cyhua
 8  * @createTime: 2021/12/1
 9  * @description: 测试基于XML的注入
10  */
11 public class DiXmlCaller {
12 
13     public static void main(String[] args) {
14         ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring-di.xml");
15         DiOrder diOrder = (DiOrder) applicationContext.getBean("diOrder");
16         System.out.println("商品详情:" + diOrder.getDiProduct());
17         System.out.println("订单详情:" + diOrder);
18     }
19 }

查看结果:

 可以看到,DiOrder依赖的DiProduct,已经完成自注入,通过diOrder实例能够获取到diProduct实例了。上面这种方式是基于get、set方法的注入,还可以通过有参构造函数注入,配置如下:

5、通过构造器填充属性

1 <!--定义一个name为diProduct的商品bean-->
2     <bean id="diProduct1" class="com.spring.reading.ioc.di.DiProduct">
3         <constructor-arg value="2"/>
4         <constructor-arg value="iphone"/>
5         <constructor-arg value="6000"/>
6     </bean>

通过diProduct1获取并打印的结果如下:

 还可以通过byName和byType实现自动注入,而不需要通过property属性指定

6、通过byName自动装配依赖

1  <!-- 通过byName自动装配DiProduct:会查找名称和属性名diProduct相同的bean-->
2     <bean id="diOrder1" class="com.spring.reading.ioc.di.DiOrder" autowire="byName">
3         <property name="orderNumber" value="0123456789-1"/>
4         <property name="productNum" value="2"/>
5     </bean>

修改的地方有以下两点:

  • bean中增加autowire属性,value设置为byName
  • 去掉name为diProduct的property配置

缺点:被依赖对象的bean名称必须和依赖对象中的属性值一致,就像在DiOrder中定义了名为diProduct的商品类,在xml中就要定义成diProduct,否则就找不到了!

7、通过byType自动装配依赖

1 <!-- 通过byType自动装配DiProduct:会查找类型与属性diProduct相同的bean进行自动装配,如果定义了多个类型相同的bean,则编译不通过 -->
2     <bean id="diOrder2" class="com.spring.reading.ioc.di.DiOrder" autowire="byType">
3         <property name="orderNumber" value="0123456789-1"/>
4         <property name="productNum" value="2"/>
5     </bean>

修改的地方有以下两点:

  • bean中增加autowire属性,value设置为byType
  • 去掉name为diProduct的property配置

缺点:被依赖对象只能创建一个bean,否则编译报错,就像上面的例子中,如果xml中定义两个DiProduct,即使名称不一样,通过byType自动装配的时候也会报错,报错信息如下:

Could not autowire. There is more than one bean of 'DiProduct' type. Beans: diProduct1,diProduct. Properties: 'diProduct'

上面是针对引用类型的注入方式,当配置的属性值较多时,可以通过引入外部文件的方式注入,比如数据库配置信息

8、通过外部文件注入属性

 1  <!--引入外部定义文件-->
 2     <bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
 3         <property name="locations">
 4             <list>
 5                 <value>classpath:config/database.properties</value>
 6             </list>
 7         </property>
 8         <property name="fileEncoding" value="utf-8"/>
 9     </bean>
10 
11     <!--注入文件中定义的属性-->
12     <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource"
13           destroy-method="close">
14         <!-- 基本属性 url、user、password -->
15         <property name="url" value="${spring.reading.jdbc.url}"/>
16         <property name="username" value="${spring.reading.jdbc.username}"/>
17         <property name="password" value="${spring.reading.jdbc.password}"/>
18         <!-- 配置初始化大小、最小、最大 -->
19         <property name="initialSize" value="${spring.reading.jdbc.initialSize}"/>
20         <property name="minIdle" value="${spring.reading.jdbc.minIdle}"/>
21         <property name="maxActive" value="${spring.reading.jdbc.maxActive}"/>
22         <!-- 配置获取连接等待超时的时间 -->
23         <property name="maxWait" value="${spring.reading.jdbc.maxWait}"/>
24     </bean>

9、定义外部文件database.properties

spring.reading.jdbc.url=jdbc:mysql://localhost:3306/test?useUnicode=true&amp;characterEncoding=utf8&allowMultiQueries=true
spring.reading.jdbc.username=test
spring.reading.jdbc.password=123
spring.reading.jdbc.initialSize=10
spring.reading.jdbc.minIdle=2000
spring.reading.jdbc.maxActive=50
spring.reading.jdbc.maxWait=6000

到这里,基于XML的依赖注入介绍的也差不多了,下面来介绍基于注解的实现方式

3.2 基于注解的实现

3.2.1 注解是什么?

简单来讲有三点:

  • 注解是java中的特殊标记:@注解名称(属性名称=属性值,属性名称=属性值)
  • 可以作用在类、属性和方法上
  • 为了简化xml配置的一种发明

3.2.2 实现

 基于纯注解的实现需要下面几步:

  • 创建配置类,配置要注入的Bean,如配置DiProduct
  • 在依赖类中通过注解方式注入(基本类型使用$value注解,引用类型可以使用@Autowired、@Resource、@Qualifer)
  • 开启包扫描,指定要扫描的包路径
  • 通过创建AnnotationConfigApplicationContext获取bean 

下面直接上代码:

1、DiProduct.java

 1 package com.spring.reading.ioc.di;
 2 
 3 import java.math.BigDecimal;
 4 
 5 /**
 6  * @author: cyhua
 7  * @createTime: 2021/12/1
 8  * @description: 定义一个商品类
 9  */
10 public class DiProduct {
11 
12     /**
13      * 商品id
14      */
15     private String productId;
16 
17     /**
18      * 商品名称
19      */
20     private String productName;
21 
22     /**
23      * 商品价格
24      */
25     private BigDecimal productPrice;
26 
27     public DiProduct() {
28     }
29 
30     public DiProduct(String productId, String productName, BigDecimal productPrice) {
31         this.productId = productId;
32         this.productName = productName;
33         this.productPrice = productPrice;
34     }
35 
36     public String getProductId() {
37         return productId;
38     }
39 
40     public void setProductId(String productId) {
41         this.productId = productId;
42     }
43 
44     public String getProductName() {
45         return productName;
46     }
47 
48     public void setProductName(String productName) {
49         this.productName = productName;
50     }
51 
52     public BigDecimal getProductPrice() {
53         return productPrice;
54     }
55 
56     public void setProductPrice(BigDecimal productPrice) {
57         this.productPrice = productPrice;
58     }
59 
60     @Override
61     public String toString() {
62         StringBuilder sb = new StringBuilder("DiProduct={");
63         sb.append("productId='").append(productId).append("'");
64         sb.append(",productName='").append(productName).append("'");
65         sb.append(",productPrice='").append(productPrice).append("'");
66         sb.append("}");
67         return sb.toString();
68     }
69 }

2、DiOrder.java

 1 package com.spring.reading.ioc.di;
 2 
 3 import org.springframework.beans.factory.annotation.Autowired;
 4 import org.springframework.beans.factory.annotation.Value;
 5 
 6 /**
 7  * @author: cyhua
 8  * @createTime: 2021/12/1
 9  * @description: 定义一个订单类
10  */
11 public class DiOrder {
12 
13     /**
14      * 订单编号
15      */
16     @Value("1234567890")
17     private String orderNumber;
18 
19     /**
20      * 订单对应商品:依赖商品类
21      */
22     @Autowired
23     private DiProduct diProduct;
24 
25     /**
26      * 商品个数
27      */
28     @Value("5")
29     private int productNum;
30 
31 
32     public DiOrder() {
33     }
34 
35     public DiOrder(String orderNumber, DiProduct diProduct, int productNum) {
36         this.orderNumber = orderNumber;
37         this.diProduct = diProduct;
38         this.productNum = productNum;
39     }
40 
41     public String getOrderNumber() {
42         return orderNumber;
43     }
44 
45     public void setOrderNumber(String orderNumber) {
46         this.orderNumber = orderNumber;
47     }
48 
49     public DiProduct getDiProduct() {
50         return diProduct;
51     }
52 
53     public void setDiProduct(DiProduct diProduct) {
54         this.diProduct = diProduct;
55     }
56 
57     public int getProductNum() {
58         return productNum;
59     }
60 
61     public void setProductNum(int productNum) {
62         this.productNum = productNum;
63     }
64 
65     @Override
66     public String toString() {
67         StringBuilder sb = new StringBuilder("DiOrder={");
68         sb.append("orderNumber='").append(orderNumber).append("'");
69         sb.append(",productNum='").append(productNum).append("'");
70         sb.append(",diProduct='").append(diProduct).append("'");
71         sb.append("}");
72         return sb.toString();
73     }
74 }

上面的diProduct通过@Autowired注解注入,orderNumber和productNum通过@Value注解注入

3、DiConfig.java

 1 package com.spring.reading.ioc.di;
 2 
 3 import org.springframework.context.annotation.Bean;
 4 import org.springframework.context.annotation.Configuration;
 5 
 6 import java.math.BigDecimal;
 7 
 8 /**
 9  * @author: cyhua
10  * @createTime: 2021/12/1
11  * @description: 通过注解@Configuration定义配置类,
12  */
13 @Configuration
14 public class DiConfig {
15 
16     @Bean
17     public DiOrder diOrder() {
18         return new DiOrder();
19     }
20 
21     @Bean
22     public DiProduct diProduct() {
23         return new DiProduct("111", "康师傅泡面", new BigDecimal(20));
24     }
25 }

关键点:

第13行,定义这是一个配置类

第16、21行,定义两个bean,默认beanName为方法名

4、测试类

 1 package com.spring.reading.ioc.di;
 2 
 3 import org.springframework.context.ApplicationContext;
 4 import org.springframework.context.annotation.AnnotationConfigApplicationContext;
 5 import org.springframework.context.annotation.ComponentScan;
 6 
 7 /**
 8  * @author: cyhua
 9  * @createTime: 2021/12/1
10  * @description: 测试基于注解的注入
11  */
12 @ComponentScan(basePackages = "com.spring.reading.ioc")
13 public class DiAnnotationCaller {
14 
15     public static void main(String[] args) {
16         ApplicationContext context = new AnnotationConfigApplicationContext(DiConfig.class);
17         DiOrder diOrder = context.getBean("diOrder", DiOrder.class);
18         System.out.println("商品详情:" + diOrder.getDiProduct());
19         System.out.println("订单详情:" + diOrder);
20     }
21 }

关键点:

第12行,开启注解扫描,配置要扫描的基础包

第16行,获取一个基于注解的引用上下文

5、查看结果

 与定义的符合,可以看到DiOrder类的属性值没有手动调用set或有参构造函数,通过使用几个注解就完成了自动注入,而且比起XML更加方便。

 3.2.3 注意点

下面来验证几个需要注意的点

1、@bean注解使用的beanName默认是方法名,如果方法名与属性名或要获取的名称不一致,则会报错

比如我将上面配置类中定义Diorder类的方法名改成diOrder1,执行结果如下:

2、@Autowired是通过类型注入,即在XML中介绍的额byType,如果同一个类有多个bean,则自动注入会失败

比如我在DiConfig类中再定义一个DiProduct类型的bean,执行结果如下:

如上图,会提示DiProduct的bean适配到了两个,这时候就可以通过@Autowired+@Qualifier结合的方式注入,优先根据beanType查找,当根据类型找到多个时,再通过@Qualifier按照指定beanName确定使用哪一个bean

3、当同一个类有多个bean时,通过@Autowired+@Qualifier结合的方式注入

只需要修改DiOrder中的注解即可,修改和执行结果如下:

5、通过注解@Resource替换@Autowired+@Qualifier,修改后执行结果如下:

 

 至此,两种DI方式基本差不多了,主要的总结都在上面的思维导图,看图比更直观,这里就不再赘述。

四、Bean管理

4.1 Bean的类型

Spring中的Bean分为普通Bean和FactoryBean,普通Bean的定义类型和返回类型一致,而FactoryBean的定义类型可能和返回类型不一致,因为FactoryBean它本身也是一个工厂,可以生产不同的Bean,开发者可以通过实现该接口重写里面个getObject接口决定返回什么类型的Bean。

4.2 Bean的创建过程

Spring中一个Bean的创建过程分为【创建实例,分配空间-->填充属性-->初始化Bean,将bean名称指向开辟的内存空间】,像我们平时直接通过new创建对象,很多细节都被隐藏在new的背后,而这背后的奥义在Spring中可以看到显示的实现,这也就是后面分析源码中重点讲解的内容,这里先提一下。

4.3 Bean的作用域

Bean的作用域有singleton、prototype、request、session等,前两者在设计模式那篇中已经介绍过,后两者是web应用中使用的,这里再简单总结一下这四个作用域:

  • singleton:单例,sping默认的作用域,而且默认在容器启动过程中创建,但是如果设置成懒加载,则会在获取时才创建,相同的beanName在全局只有一个实例,当多个线程根据同一个beanName获取时,每次都能取同一个实例;
  • prototype:多例,需要手动开启,每次通过同一个beanName获取时都会创建一个新的实例;
  • request:请求,在一次http请求过程中只创建一个实例;
  • session:会话,在一次回话期呢只创建一个实例;

4.4 Bean的生命周期

Spring中一个bean从创建到销毁需要经历的步骤如上图:

  1. 创建实例:此时会在内存中给该bean开辟一块内存空间
  2. 注入属性:通过set方法设置bean中的各个属性
  3. 后置处理器前置处理:在初始化之前可以对bean做一些修改,比如修改修改某个属性等
  4. 初始化:调用初始化方法,如果不指定初始化方法,则使用默认的
  5. 后置处理器后置处理:在初始化之后做一些操作
  6. 使用:此时一个bean就创建好了, 我们就能调用它里面的方法了
  7. 销毁:当容器关闭时,bean会被销毁,同样可以自定义销毁的方法

下面通过一个例子来看一下Bean生命周期的实现:

4.5 测试Bean的生命周期

创建公共类,一个基本类LifeCycleVo.java和一个后置处理器类

1、LifeCycleVo.java

 1 package com.spring.reading.lifecycle;
 2 
 3 /**
 4  * @author: cyhua
 5  * @createTime: 2021/12/1
 6  * @description: 定义一个测试生命周期的类
 7  */
 8 public class LifeCycleVo {
 9 
10     /**
11      * 定义当前阶段
12      */
13     private String description;
14 
15     /**
16      * 无参构造方法,实例化
17      */
18     public LifeCycleVo() {
19         System.out.println("第一步:创建bean");
20     }
21 
22     /**
23      * 自定义初始化方法
24      */
25     public void init() {
26         System.out.println("第四步:初始化bean");
27     }
28 
29     /**
30      * 自定义销毁方法
31      */
32     public void destroy() {
33         System.out.println("第七步:销毁bean");
34     }
35 
36     public String getDescription() {
37         return description;
38     }
39 
40     public void setDescription(String description) {
41         System.out.println("第二步:注入属性");
42         this.description = description;
43     }
44 
45     @Override
46     public String toString() {
47         return "LifeCycleVo{" +
48                 "description='" + description + '\'' +
49                 '}';
50     }
51 }

2、LifeCycleBeanPost.java

 1 package com.spring.reading.lifecycle;
 2 
 3 import org.springframework.beans.BeansException;
 4 import org.springframework.beans.factory.config.BeanPostProcessor;
 5 
 6 /**
 7  * @author: cyhua‰
 8  * @createTime: 2021/11/26
 9  * @description: bean后置处理器,在初始化前后执行一些操作
10  */
11 public class LifeCycleBeanPost implements BeanPostProcessor {
12 
13     @Override
14     public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
15         System.out.println("第三步:初始化bean之前执行后置处理器");
16         return BeanPostProcessor.super.postProcessBeforeInitialization(bean, beanName);
17     }
18 
19     @Override
20     public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
21         System.out.println("第五步:初始化bean之后执行后置处理器");
22         return BeanPostProcessor.super.postProcessAfterInitialization(bean, beanName);
23     }
24 }

4.5.1 基于XML的实现

1、spring-lifecycle.xml中配置bean

 1 <?xml version="1.0" encoding="UTF-8"?>
 2 <beans xmlns="http://www.springframework.org/schema/beans"
 3        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 4        xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">
 5 
 6 
 7     <!--定义一个name为lifeCycleVo的bean-->
 8     <bean id="lifeCycleVo" class="com.spring.reading.lifecycle.LifeCycleVo" init-method="init" destroy-method="destroy">
 9         <property name="description" value="基于XML文件测试bean生命周期"/>
10     </bean>
11 
12     <!--定义一个后置处理器-->
13     <bean id="lifeCycleBeanPost" class="com.spring.reading.lifecycle.LifeCycleBeanPost"/>
14 
15 </beans>

上面代码中的第9行,自定义初始化和销毁方法

2、测试类

 1 package com.spring.reading.lifecycle;
 2 
 3 import org.springframework.context.support.ClassPathXmlApplicationContext;
 4 
 5 /**
 6  * @author: cyhua
 7  * @createTime: 2021/12/1
 8  * @description: 基于XML的声明周期测试类
 9  */
10 public class LifeBaseXmlCaller {
11 
12     public static void main(String[] args) {
13         ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("spring-lifecycle.xml");
14         LifeCycleVo lifeCycleVo = (LifeCycleVo) context.getBean("lifeCycleVo");
15         System.out.println("第六步:开始使用Bean \nlifeCycleVo=" + lifeCycleVo);
16         context.close();
17     }
18 }

3、查看效果

 

 七个步骤都成功执行,符合预期,binggo!!!

4.5.2 基于注解的实现方式

 1、LifeCycleConfig.java

 1 package com.spring.reading.lifecycle;
 2 
 3 import org.springframework.context.annotation.Bean;
 4 import org.springframework.context.annotation.Configuration;
 5 
 6 /**
 7  * @author: cyhua
 8  * @createTime: 2021/12/1
 9  * @description: 创建配置类,通过注解注入bean
10  */
11 @Configuration
12 public class LifeCycleConfig {
13 
14     @Bean(initMethod = "init", destroyMethod = "destroy")
15     public LifeCycleVo lifeCycleVo() {
16         LifeCycleVo lifeCycleVo = new LifeCycleVo();
17         lifeCycleVo.setDescription("基于配置类测试bean生命周期");
18         return lifeCycleVo;
19     }
20 
21     @Bean
22     public LifeCycleBeanPost lifeCycleBeanPost() {
23         return new LifeCycleBeanPost();
24     }
25 }

在注解@Bean中自定义初始化和销毁方法

2、测试类

 1 package com.spring.reading.lifecycle;
 2 
 3 import org.springframework.context.annotation.AnnotationConfigApplicationContext;
 4 import org.springframework.context.annotation.ComponentScan;
 5 
 6 /**
 7  * @author: cyhua
 8  * @createTime: 2021/12/1
 9  * @description: 基于XML的声明周期测试类
10  */
11 @ComponentScan(basePackages = "com.spring.reading.lifecycle")
12 public class LifeBaseConfigCaller {
13 
14     public static void main(String[] args) {
15         AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(LifeCycleConfig.class);
16         LifeCycleVo lifeCycleVo = (LifeCycleVo) context.getBean("lifeCycleVo");
17         System.out.println("第六步:开始使用Bean \nlifeCycleVo=" + lifeCycleVo);
18         context.close();
19     }
20 }

3、查看效果

 同样成功执行,符合预期!

到此为止,spring中的IOC以及DI介绍的差不多了,本文只是读源码的一个前置条件,后面会跟源代码进行剖析,先知道怎么做,再去研究底层的实现机制。

posted @ 2021-12-01 22:21  bug改了我  阅读(194)  评论(0编辑  收藏  举报