Spring Boot-002生命周期和一些基本用法

112 阅读3分钟

Spring Bean的生命周期

Spring 初始化 Bean
1.资源定位:@ComponentScan所定义的扫描包
2.Bean定义:@Bean的定义保存到BeanDefinition的实例中
3.发布Bean定义:IoC容器装载Bean定义
4.实例化:创建Bean的实例对象
5.依赖注入:如@Autowired注入的各类资源

Spring Bean的生命周期流程图

Pasted image 20241024160830.png

生命周期的接口方法

package org.greenfred.springboot.demos.lifecycle.impl;  
  
  
import org.greenfred.springboot.demos.lifecycle.interfaces.Animal;  
import org.greenfred.springboot.demos.lifecycle.interfaces.Person;  
import org.springframework.beans.BeansException;  
import org.springframework.beans.factory.*;  
import org.springframework.beans.factory.annotation.Autowired;  
import org.springframework.beans.factory.annotation.Qualifier;  
import org.springframework.context.ApplicationContext;  
import org.springframework.context.ApplicationContextAware;  
  
import org.springframework.stereotype.Component;  
  
import javax.annotation.PostConstruct;  
import javax.annotation.PreDestroy;  
  
  
  
@Component  
public class LifeOfPerson implements Person, BeanNameAware, BeanFactoryAware, ApplicationContextAware, InitializingBean, DisposableBean {  
  
    @Autowired  
    @Qualifier("dog")  
    private Animal animal = null;  
  
    @Override  
    public void service() {  
        this.animal.use();  
    }  
  
    @Override  
    public void setAnimal(Animal animal) {  
        System.out.println("延迟依赖注入");  
        this.animal = animal;  
    }  
  
    @Override  
    public void setBeanName(String name) {  
        System.out.println(this.getClass().getSimpleName()+"调用 BeanNameAware 的 setBeanName");  
    }  
  
    @Override  
    public void setBeanFactory(BeanFactory beanFactory) throws BeansException {  
        System.out.println(this.getClass().getSimpleName()+"调用 BeanNameAware 的 setBeanFactory");  
    }  
  
    @Override  
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {  
        System.out.println(this.getClass().getSimpleName()+"调用 ApplicationContextAware 的 setApplicationContext");  
    }  
  
    @Override  
    public void afterPropertiesSet() throws Exception {  
        System.out.println(this.getClass().getSimpleName()+"调用 InitializingBean 的 afterPropertiesSet");  
    }  
  
    @PostConstruct  
    public void init() {  
        System.out.println(this.getClass().getSimpleName()+"注解日 PostConstruct 定义的自定义初始化方法");  
    }  
  
    @PreDestroy  
    public void customDestroy() {  
        System.out.println(this.getClass().getSimpleName()+"注解@PreDestroy定义的自定义销毁方法");  
    }  
  
    @Override  
    public void destroy() throws Exception {  
        System.out.println(this.getClass().getSimpleName()+"DisposableBean方法");  
    }  
}

Pasted image 20241025160805.png

注意:测试类要这么写
注意:如果有相同名称的 Bean 或者接口的包不要导入错误,如之前的import org.greenfred.springboot.demos.lifecycle.interfaces.Animal;

@SpringBootTest(classes = Application.class)  
class ApplicationTests {  
      
    @Test  
    void lifeCycleText() {  
        ApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);  
        Person person = context.getBean(LifeOfPerson.class);  
        System.out.println(person);  
    }  
      
}

//其中AppConfig配置类无特殊实例化需求就配置即可
@Configuration  
@ComponentScan(basePackages = "org.greenfred.springboot.demos.lifecycle")  
public class AppConfig {  
}


二者的典型使用场景

  • @Configuration 常用于配置类或主类中,将特定的 Bean 或配置注入到 Spring 容器中,通常适用于那些需要手动控制实例化过程的类。
  • @ComponentScan 则用于组件自动扫描,配合 @SpringBootApplication 使用,能够扫描指定包路径下所有带有特定注解的类,并自动注册为 Spring Bean,从而实现自动装配。

在 Spring Boot 中,@SpringBootApplication 自动包含 @ComponentScan,所以在很多情况下可以省略 @ComponentScan

使用属性文件

添加依赖

<dependency>  
    <groupId>org.springframework.boot</groupId>  
    <artifactId>spring-boot-configuration-processor</artifactId>  
    <optional>true</optional>  
</dependency>  
  
<dependency>  
    <groupId>mysql</groupId>  
    <artifactId>mysql-connector-java</artifactId>  
    <version>8.0.23</version>  
</dependency>

注意:如果自定义配置类,再加上手动创建,应该使用 @PropertySource 标注位置

@Configuration  
@ComponentScan(basePackages = "org.greenfred.springbootlifecycle")  
@PropertySource("classpath:application.properties")  
public class AppConfig {  
}

@Value 注入 properties 中的属性

package org.greenfred.springbootlifecycle.po;  
  
import org.springframework.beans.factory.annotation.Value;  
import org.springframework.stereotype.Component;  
  
  
@Component  
public class DataBaseProperties {  
    @Value("${spring.datasource.driver-class-name}")  
    private String driverName = null;  
  
    public String getDriverName() {  
        return driverName;  
    }  
  
    public void setDriverName(String driverName) {  
        this.driverName = driverName;  
        System.out.println(this.driverName);  
    }  
}

配置文件

Pasted image 20241027135722.png

打印结果 Pasted image 20241027135631.png

@ConfigurationProperties装配

注意:使用这个注解装配,要与 properties 中属性基本一致,比如属性这样写 Pasted image 20241027142228.png 那代码中就应该这样写 Pasted image 20241027142429.png 打印 Pasted image 20241027142512.png 如果写成这样 Pasted image 20241027142637.png 则获取失败,拿到 null Pasted image 20241027142715.png

Bean 的作用域

Bean 作用域表 Pasted image 20241030152610.png 简单测试 singleton 效果

package org.greenfred.beancontext;  
  
import org.springframework.stereotype.Component;  
  
@Component  
public class ContextBean {  
}

package org.greenfred.beancontext;  
  
import org.junit.jupiter.api.Test;  
  
import org.springframework.boot.test.context.SpringBootTest;  
import org.springframework.context.annotation.AnnotationConfigApplicationContext;  
  
@SpringBootTest  
class BeanContextApplicationTests {  
  
    @Test  
    void contextLoads() {  
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(BeanContextApplication.class);  
        ContextBean bean1 = context.getBean(ContextBean.class);  
        ContextBean bean2 = context.getBean(ContextBean.class);  
        System.out.println( bean1 == bean2 );  
    }  
  
}

打印结果 Pasted image 20241030154606.png Pasted image 20241030154747.png 可以看到 bean1 和 bean2 地址是相同的 当 Scope 修改为 SCOPE_PROTOTYPE 打印结果则不再相同

@Component  
@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)  
public class ContextBean {  
}

Pasted image 20241031144827.png

Spring EL

为了更加灵活,Spring 提供了表达式语言 SpringEL。通过 SpringEL可以拥有更为强大的运算规则来更好地装配 Bean

占位符

@Value("${....}")
它会读取上下文的属性值装配到属性中

package org.greenfred.springbootlifecycle.po;  
  
import org.springframework.beans.factory.annotation.Value;  
import org.springframework.stereotype.Component;  
  
  
@Component  
public class DataBaseProperties {  
    @Value("${spring.datasource.driver-class-name}")  
    private String driverName = null;  
  
    public String getDriverName() {  
        return driverName;  
    }  
  
    public void setDriverName(String driverName) {  
        this.driverName = driverName;  
        System.out.println(this.driverName);  
    }  
}

Spring表达式

@Value("#{...}")
它将具有运算的功能,还可以调用方法

@Component  
public class ElBean {  
  
    @Value("#{T(System).currentTimeMillis()}")  
    private Long initTime = null;  
  
    public Long getInitTime() {  
        return initTime;  
    }  
  
    public void setInitTime(Long initTime) {  
        this.initTime = initTime;  
    }  
}

T(.....)代表的是引入类: System 是java.lang.* 包的类
打印可以得到如下结果 Pasted image 20241031153156.png 还可以有这些赋值方式

package org.greenfred.beancontext;  
  
import org.springframework.beans.factory.annotation.Value;  
import org.springframework.stereotype.Component;  
  
@Component  
public class ElBean {  
  
    @Value("#{T(System).currentTimeMillis()}")  
    private Long initTime = null;  
  
    // 赋值字符串  
    @Value("#{'张三'}")  
    private String name = null;  
  
    // 科学计数法赋值  
    @Value("#{9.3E3}")  
    private double point;  
  
    // 其他Bean赋值  
    @Value("#{contextBean.context}")  
    private String otherBean = null;  
  
    public String getName() {  
        return name;  
    }  
  
    public void setName(String name) {  
        this.name = name;  
    }  
  
    public double getPoint() {  
        return point;  
    }  
  
    public void setPoint(double point) {  
        this.point = point;  
    }  
  
    public String getOtherBean() {  
        return otherBean;  
    }  
  
    public void setOtherBean(String otherBean) {  
        this.otherBean = otherBean;  
    }  
  
    public Long getInitTime() {  
        return initTime;  
    }  
  
    public void setInitTime(Long initTime) {  
        this.initTime = initTime;  
    }  
}

package org.greenfred.beancontext;  
  
import org.springframework.beans.factory.annotation.Value;  
import org.springframework.beans.factory.config.ConfigurableBeanFactory;  
  
import org.springframework.context.annotation.Scope;  
import org.springframework.stereotype.Component;  
  
@Component  
@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)  
public class ContextBean {  
    @Value("这是个上下文")  
    private String context;  
  
    public String getContext() {  
        return context;  
    }  
  
    public void setContext(String context) {  
        this.context = context;  
    }  
}


Pasted image 20241031154654.png 打印可以拿到以下结果 Pasted image 20241031154747.png