Spring Bean的生命周期
Spring 初始化 Bean
1.资源定位:@ComponentScan所定义的扫描包
2.Bean定义:@Bean的定义保存到BeanDefinition的实例中
3.发布Bean定义:IoC容器装载Bean定义
4.实例化:创建Bean的实例对象
5.依赖注入:如@Autowired注入的各类资源
Spring Bean的生命周期流程图
生命周期的接口方法
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方法");
}
}
注意:测试类要这么写
注意:如果有相同名称的 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);
}
}
配置文件
打印结果
@ConfigurationProperties装配
注意:使用这个注解装配,要与 properties 中属性基本一致,比如属性这样写
那代码中就应该这样写
打印
如果写成这样
则获取失败,拿到 null
Bean 的作用域
Bean 作用域表
简单测试 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 );
}
}
打印结果
可以看到 bean1 和 bean2 地址是相同的
当 Scope 修改为 SCOPE_PROTOTYPE 打印结果则不再相同
@Component
@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public class ContextBean {
}
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.* 包的类
打印可以得到如下结果
还可以有这些赋值方式
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;
}
}
打印可以拿到以下结果