Bean 的生命周期的各个阶段

⽣命周期指的是⼀个对象从诞⽣到销毁的整个⽣命过程,我们把这个过程就叫做⼀个对象的⽣命周期. Bean的⽣命周期分为以下5个部分:

1. 实例化(为Bean分配内存空间)

2. 属性赋值(Bean注⼊和装配,⽐如 @AutoWired )

3. 初始化

        a. 执⾏各种通知,如 BeanNameAware , BeanFactoryAware , ApplicationContextAware 的接⼝⽅法.

        b. 执⾏初始化⽅法

                 ▪ xml定义 init-method

                ▪ 使⽤注解的⽅式 @PostConstruct

                ▪ 执⾏初始化后置⽅法( BeanPostProcessor )

4. 使⽤Bean

5. 销毁Bean a. 销毁容器的各种⽅法,如 @PreDestroy , DisposableBean 接⼝⽅法, destroy-method.

实例化和属性赋值对应构造⽅法和setter⽅法的注⼊.初始化和销毁是⽤⼾能⾃定义扩展的两个阶段, 可以在实例化之后,类加载完成之前进⾏⾃定义"事件"处理. ⽐如我们现在需要买⼀栋房⼦,那么我们的流程是这样的:

1. 先买房(实例化,从⽆到有)

2. 装修(设置属性)

3. 买家电,如洗⾐机,冰箱,电视,空调等([各种]初始化,可以⼊住);

4. ⼊住(使⽤Bean)

5. 卖房(Bean销毁)

执⾏流程如下图所⽰:

1 代码演⽰

@Component
public class BeanLifeComponent implements BeanNameAware {
    private UserComponent userComponent;

    public BeanLifeComponent() {
        System.out.println("执⾏构造函数");
    }

    @Autowired
    public void setUserComponent(UserComponent userComponent) {
        System.out.println("设置属性userComponent");
        this.userComponent = userComponent;
    }

    @Override
    public void setBeanName(String s) {
        System.out.println("执⾏了 setBeanName ⽅法:" + s);
    }

    /**
     * 初始化
     */
    @PostConstruct
    public void postConstruct() {
        System.out.println("执⾏ PostConstruct()");
    }

    public void use() {
        System.out.println("执⾏了use⽅法");
    }

    /**
     * 销毁前执⾏⽅法
     */
    @PreDestroy
    public void preDestroy() {
        System.out.println("执⾏:preDestroy()");
    }
}

执⾏结果

通过运⾏结果观察
1. 先执⾏构造函数
2. 设置属性
3. Bean初始化
4. 使⽤Bean
5. 销毁Bean

2 Spring Boot⾃动配置

SpringBoot的⾃动配置就是当Spring容器启动后, ⼀些配置类, bean对象等就⾃动存⼊到了IoC容器中,不需要我们⼿动去声明, 从⽽简化了开发, 省去了繁琐的配置操作.
SpringBoot⾃动配置, 就是指SpringBoot是如何将依赖jar包中的配置类以及Bean加载到Spring IoC容器中的.
我们学习主要分以下两个⽅⾯:
1. Spring 是如何把对象加载到SpringIoC容器中的
2. SpringBoot 是如何实现的

2.1 Spring 加载Bean

2.1.1 问题描述

需求: 使⽤Spring管理第三⽅的jar包的配置
引⼊第三⽅的包, 其实就是在该项⽬下,引⼊第三⽅的代码, 我们采⽤在该项⽬下创建不同的⽬录来模拟第三⽅的代码引⼊

数据准备:
1. 创建项⽬spring-autoconfig, 当前项⽬⽬录为 com.example.demo
2. 模拟第三⽅代码⽂件在 com.bite.autoconfig ⽬录下

第三⽅⽂件代码:

@Component
public class BiteConfig {
    public void study(){
        System.out.println("start study...");
    }
}

3. 获取 BiteConfig这个Bean
写测试代码

@SpringBootTest
class SpringAutoconfigApplicationTests {
    @Autowired
    private ApplicationContext applicationContext;
    @Test
    void contextLoads() {
        BiteConfig biteConfig = applicationContext.getBean(BiteConfig.class,"biteConfig");
        System.out.println(biteConfig);
    }
}

4. 运⾏程序:

观察⽇志: No qualifying bean of type 'com.bite.autoconfig.BiteConfig' available
没有 com.bite.autoconfig.BiteConfig 这个类型的Bean

2.1.2 原因分析

Spring通过五⼤注解和 @Bean 注解可以帮助我们把Bean加载到SpringIoC容器中, 以上有个前提就是这些注解类需要和SpringBoot启动类在同⼀个⽬录下 ( @SpringBootApplication 标注的类 就
是SpringBoot项⽬的启动类).
启动类所在⽬录为: com.example.demo , ⽽ BiteConfig 这个类在
com.bite.autoconfig 下, 所以SpringBoot并没有扫描到.

当我们引⼊第三⽅的Jar包时, 第三⽅的Jar代码⽬录肯定不在启动类的⽬录下, 如何告诉Spring帮我们管理这些Bean呢?

2.1.3 解决⽅案

我们需要指定路径或者引⼊的⽂件, 告诉Spring, 让Spring进⾏扫描到.
常⻅的解决⽅案有两种:
1. @ComponentScan 组件扫描
2. @Import 导⼊(使⽤@Import导⼊的类会被Spring加载到IoC容器中)
我们通过代码来看如何解决

2.1.3.1 @ComponentScan

通过 @ComponentScan 注解, 指定Spring扫描路径

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.ComponentScan;
@ComponentScan("com.bite.autoconfig")
@SpringBootApplication
public class SpringAutoconfigApplication {
    public static void main(String[] args) {
        SpringApplication.run(SpringAutoconfigApplication.class, args);
    }
}

可以指定扫描多个包
@ComponentScan({"com.bite.autoconfig","com.example.demo"})
运⾏程序:

可以看到, 这次 biteConfig Bean获取到了

2.1.3.2 @Import

@Import 导⼊主要有以下⼏种形式:
1. 导⼊类
2. 导⼊ ImportSelector 接⼝实现类

1. 导⼊类

@Import(BiteConfig.class)
@SpringBootApplication
public class SpringAutoconfigApplication {
    public static void main(String[] args) {
        SpringApplication.run(SpringAutoconfigApplication.class, args);
    }
}

运⾏程序:

可以看到, 这种⽅式也可以告诉Spring加载 biteConfig

问题: 如果⼜多了⼀些配置项呢?

Component
public class BiteConfig2 {
    public void study2(){
        System.out.println("start study2...");
    }
}

我们可以采⽤导⼊多个类

@Import({BiteConfig.class, BiteConfig2.class})
@SpringBootApplication
public class SpringAutoconfigApplication {
    public static void main(String[] args) {
        SpringApplication.run(SpringAutoconfigApplication.class, args);
    }
}

很明显, 这种⽅式也很繁琐.
所以, SpringBoot依然没有采⽤.


2. 导⼊ImportSelector接⼝实现类
ImportSelector 接⼝实现类

public class MyImportSelector implements ImportSelector {
    @Override
    public String[] selectImports(AnnotationMetadata importingClassMetadata) {
        //需要导⼊的全限定类名
        return new String[]
{"com.bite.autoconfig.BiteConfig","com.bite.autoconfig.BiteConfig2"};
    }
}

启动类:

@Import(MyImportSelector.class)
@SpringBootApplication
public class SpringAutoconfigApplication {
    public static void main(String[] args) {
        SpringApplication.run(SpringAutoconfigApplication.class, args);
    }
}

运⾏程序:

可以看到, 我们采⽤这种⽅式也可以导⼊第三⽅依赖提供的Bean.

问题:

但是他们都有⼀个明显的问题, 就是使⽤者需要知道第三⽅依赖中有哪些Bean对象或配置类. 如果漏掉其中⼀些Bean, 很可能导致我们的项⽬出现⼤的事故.
这对程序员来说⾮常不友好.

依赖中有哪些Bean, 使⽤时需要配置哪些bean, 第三⽅依赖最清楚, 那能否由第三⽅依赖来做这件事呢?
• ⽐较常⻅的⽅案就是第三⽅依赖给我们提供⼀个注解, 这个注解⼀般都以@EnableXxxx开头的注解,注解中封装的就是 @Import 注解
1. 第三⽅依赖提供注解

import org.springframework.context.annotation.Import;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Import(MyImportSelector.class)//指定要导⼊哪些类
public @interface EnableBiteConfig {
}

注解中封装 @Import 注解, 导⼊ MyImportSelector.class
2. 在启动类上使⽤第三⽅提供的注解

@EnableBiteConfig
@SpringBootApplication
public class SpringAutoconfigApplication {
    public static void main(String[] args) {
        SpringApplication.run(SpringAutoconfigApplication.class, args);
    }
}

3. 运⾏程序

可以看到, 这种⽅式也可以导⼊第三⽅依赖提供的Bean.
并且这种⽅式更优雅⼀点. SpringBoot采⽤的也是这种⽅式

3. 总结

1. Bean的作⽤域共分为6种: singleton, prototype, request, session, application和websocket.
2. Bean的⽣命周期共分为5⼤部分: 实例化, 属性复制, 初始化, 使⽤和销毁
3. SpringBoot的⾃动配置原理源码⼝是 @SpringBootApplication 注解, 这个注解封装了3个注

        ◦ @SpringBootConfiguration 标志当前类为配置类
        ◦ @ComponentScan 进⾏包扫描(默认扫描的是启动类所在的当前包及其⼦包)
        ◦ @EnableAutoConfiguration
                ▪ @Import 注解 : 读取当前项⽬下所有依赖jar包中 META-INF/spring.factories ,META-
                  INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports 两个                        ⽂件⾥⾯定义的配置类(配置类中定义了 @Bean 注解标识的⽅法)
                ▪ @AutoConfigurationPackage : 把启动类所在的包下⾯所有的组件都注⼊到 Spring容                       器中

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值