在Java中万物皆对象,既然是一个实例对象,那么就会有生命历程:被创建–>被使用–>被销毁…但是这说的太过于简洁了,以至于我们根本不能从本质上认清它具体的执行流程、生命历程,今天就来简单了解下Spring中的Bean生命周期具体流程。
我们知道,spring的ioc是创建和管理对象的关键容器,那么项目启动时,一定是spring容器先被创建出来,然后紧接着就是要放入spring容器中管理的各个组件bean,根据机制(是否为懒加载模式)来确定某些bean是否需要在spring容器被创建后立即创建出来进行管理。
总览图
那么Bean被创建的时候
IOC通过反射机制调用Bean的构造函数(创建完Bean对象)
进行属性填充,set方法注入等属性注入
处理各种实现自xxxAware的实现类
如果bean中设置了@PostConstruct方法,那么开始执行
BeanPostProcessor的postProcessBeforeInitialization()方法执行,前置处理器
如果bean实现了InitializingBean,重写了afterPropertiesSet(),那么开始执行
自定义的init方法,如创建Bean时指定的init-method
BeanPostProcessor的postProcessAfterInitialization()方法执行,后置处理器
bean对象被使用
Spring容器销毁
实现自DisposableBean的bean执行重写的destroy
方法
自定义的销毁方法,destroy-method
执行
前两条估计不用说了,先是调用对象的构造函数,然后属性注入执行set方法。
那么第3条,来自xxxAware的实现类目的其实就是让bean获取spring容器中的组件,当我们需要从spring中获取一些组件时可以使用
比如有个BeanUtil工具类,通过这个工具类的getBean方法能获取spring中的组件
@Component
public class BeanUtil implements ApplicationContextAware {
private ApplicationContext context;
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
context = applicationContext;
}
// 对外方法
public <T> T getBean (Class<T> clazz) {
return context.getBean(clazz);
}
}
BeanPostProcessor的前置和后置处理器:
spring中的所有bean在初始化前后都会执行前置和后置的处理器,有点类似是全局方式,比如我创建一个类实现了BeanPostProcessor接口,重写了前置和后置的处理,那么所有的bean初始化前后都会执行它,且方法的返回值可以是bean本身,亦可以是bean的包装类对象(代理对象等),一般使用较少,还是要看项目的实际情况使用
其实像实现一些xxxAware
或BeanPostProcessor
在bean初始化过程中横插一脚,多数情况下是没有必要的,除非是项目情况需求万不得已,而且实现这些接口将会使我们应用层的代码耦合到spring框架中,会增加耦合度。
上面大概了解下了Bean的整个生命周期流程,再来详细介绍下项目启动时要进行一些初始化数据或者注意一些bean的正确用法。
刚开始学习spring时,注入一些属性,基本类型多用了@Value,引用类型基本就是在用@Autowired,而在spring中现在已经不建议去使用@Autowired方式了,先来复习下属性注入的几种方式:
(推荐使用)
官方不推荐使用属性注入,属性注入其实有一个显而易见的缺点,那就是对于 IOC 容器以外的环境,除了使用反射来提供它需要的依赖之外,无法复用该实现类。因为该类没有提供该属性的 set 方法或者相应的构造方法来完成该属性的初始化。换言之,要是使用属性注入,那么你这个类就只能在 IOC 容器中使用,要是想自己 new 一下这个类的对象,那么相关的依赖无法完成注入。
当然,他不推荐归为不推荐,各位想用他也拦住啊,O(∩_∩)O哈哈~
个人目前构造方法注入用的多一些,配合lombok的注解,用的嘎嘎爽,给个例子
@RestController
@RequestMapping("/snow/score")
@AllArgsConstructor
public class SnowScoreController {
private final SnowRefereeService refereeService;
}
介绍个场景:项目启动时,需要将数据库中的一些配置信息数据读取出来加载到java的配置类中,完成初始化数据的操作。
列举下可以使用的方式:
@PostConstruct: bean中可以使用@PostConstruct来完成初始化,注意:此bean必须被spring管理,且方法必须是无返回值的
// 翻转到spring中
@Component
public class AliyunConfig {
private String endpoint;
private String accessKeyId;
private String accessKeySecret;
private String bucketName;
// 阿里云域名(bucket域名或者自定义域名)
private String aliyunDomain;
@Autowired
private SystemConfigService configService;
@PostConstruct
void initProperties() {
configService.list();
// 以下完成初始化数据操作
}
}
也阔以将此Bean通过不同的方式反转到ioc中
@Data
class BeanTest {
private String name;
@Autowired
private SystemConfigService configService;
private void setConfigName() {
System.out.println(configService);
System.out.println("BeanTest初始化数据");
}
}
@Configuration
@Slf4j
public class AliyunConfig {
@Bean(initMethod = "setConfigName")
public BeanTest beanTest () {
return new BeanTest();
}
}
还可以实现InitializingBean
,重写方法
@Data
@Component
class BeanTest implements InitializingBean {
private String name;
@Autowired
SystemConfigService systemConfigService;
@Override
public void afterPropertiesSet() throws Exception {
System.out.println("BeanTest初始化");
System.out.println(systemConfigService);
}
}
还可以实现ApplicationRunner或者
CommandLineRunner,这两个是springboot启动流程中最后被执行的runner,有兴趣可以了解下我的一篇文章Springboot启动流程(源码解析)、自动装配流程(源码解析)、总结、SrpringBoot初始化数据扩展
@Component
public class MyInitRunner1 implements ApplicationRunner {
@Autowired
private DispatcherService dispatcherService;
@Override
public void run(ApplicationArguments args) throws Exception {
System.out.println("我是runner1");
System.out.println(dispatcherService);
}
}
实现CommandLineRunner
@Component
public class MyInitRunner2 implements CommandLineRunner {
@Autowired
private DispatcherService dispatcherService;
@Override
public void run(String... args) throws Exception {
System.out.println("我是runner2");
System.out.println(dispatcherService);
}
}
@PostConstruct
> 实现InitializingBean
接口 > 实现ApplicationRunner
或CommandLineRunner
接口
按源码来看,ApplicationRunner的子类执行是优先于CommandLineRunner的子类的,可以通过@Order设置类的加载顺序。
介绍了5种springboot项目启动初始化数据的方式,基本开发中差不多是够用的。