Spring Bean生命周期
①BeanFactoryPostProcessor
介绍
实现该接口可以在 Spring 容器加载 Bean 定义之后、实例化 Bean 之前修改 Bean 的定义信息。通过重写 postProcessBeanFactory() 方法,可以对 Bean 的定义进行修改,例如修改属性值、添加额外的 Bean 定义等。
BeanFactoryPostProcessor接口与bean定义进行交互和修改,但是不会与bean实例进行交互。这样做可能导致过早的bean实例化,违反容器规则并导致意外的副作用。如果需要与bean实例进行交互,则应该实现BeanPostProcessor接口。
关于执行顺序?
- ApplicationContext会根据PriorityOrdered和Ordered语义对自动检测到的BeanFactoryPostProcessor进行排序。
- 而通过ConfigurableApplicationContext以编程方式注册的BeanFactoryPostProcessor将按照注册的顺序应用;通过实现PriorityOrdered或Ordered接口来表达的任何排序语义将被忽略。
- 此外,@Order注解对BeanFactoryPostProcessor不起作用。
应用场景
- 修改属性值。如加解密。
- 注册额外的Bean定义。注册新的Bean定义到Bean工厂中,这样,后续的Bean实例化过程就可以使用这些新定义的Bean。
- Bean名称的重命名,实现自定义命名策略。
- 条件化Bean注册:根据条件判断是否注册这个Bean。
- 对Bean的注解进行处理:读取Bean的特殊注解,对Bean定义进行二次处理。
- 动态加载配置文件
- 实现国际化
- …
案例
动态修改配置类属性
配置类
@Component
@ConfigurationProperties(prefix = "my")
public class MyConfig {
private String myProperty;
public String getMyProperty() {
return myProperty;
}
public void setMyProperty(String myProperty) {
this.myProperty = myProperty;
}
}
修改属性
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.stereotype.Component;
@Component
public class MyBeanFactoryPostProcessor implements BeanFactoryPostProcessor {
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
// 获取MyConfig bean
MyConfig myConfig = beanFactory.getBean(MyConfig.class);
// 修改属性值
myConfig.setMyProperty("Updated Value from BeanFactoryPostProcessor");
}
}
修改Bean的初始化方法
@Component
public class MyBean {
@PostConstruct
public void init() {
System.out.println("Initializing MyBean...");
}
@PreDestroy
public void destroy() {
System.out.println("Destroying MyBean...");
}
public void customInit() {
System.out.println("Custom Initialization ");
}
public void customDestroy() {
System.out.println("Custom Destruction " );
}
}
@Component
public class MyBeanFactoryPostProcessor implements BeanFactoryPostProcessor {
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
// 获取名为 "myBean" 的 Bean 的定义
BeanDefinition beanDefinition = beanFactory.getBeanDefinition("myBean");
// 修改 Bean 的初始化方法为 "customInit"
beanDefinition.setInitMethodName("customInit");
// 修改 Bean 的销毁方法为 "customDestroy"
beanDefinition.setDestroyMethodName("customDestroy");
}
}
重命名Bean
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.stereotype.Component;
@Component
public class MyBeanFactoryPostProcessor implements BeanFactoryPostProcessor {
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
// 获取名为 "myBean" 的 Bean 的定义
BeanDefinition beanDefinition = beanFactory.getBeanDefinition("myBean");
// 修改 Bean 的名称为 "renamedMyBean"
beanFactory.registerSingleton("renamedMyBean", beanFactory.getBean("myBean"));
}
}
②Aware系列接口
介绍
通过实现Aware接口,Bean可以获取到Spring容器的一些关键资源或设置相关信息,从而能够在运行时更灵活地与容器进行交互和定制。
常用的接口
- ApplicationContextAware:实现该接口的Bean可以获取到ApplicationContext,它是Spring容器的顶层接口,包含了所有容器的功能。通过ApplicationContext,Bean可以在运行时获取其他Bean、资源、国际化消息等。
- BeanNameAware:实现该接口的Bean可以获取自己在Spring容器中的Bean名称。这在需要在Bean中获取自己的名称时非常有用,例如在日志记录、动态注册Bean等场景中。
- BeanFactoryAware:实现该接口的Bean可以获取到Bean所属的BeanFactory,也就是Spring容器本身。这可以让Bean在运行时获取其他Bean或进行Bean的动态注册。
- EnvironmentAware:实现该接口的Bean可以获取Spring的Environment对象,它表示了Spring的环境信息,包含了配置文件中的属性、系统属性等。这在需要根据不同的环境进行动态配置时非常有用。
- ResourceLoaderAware:实现该接口的Bean可以获取ResourceLoader,用于加载外部资源。通过ResourceLoader,Bean可以加载classpath中的文件、URL资源等。
- MessageSourceAware:实现该接口的Bean可以获取MessageSource,用于获取国际化消息。这在需要根据不同的语言或地区返回不同的消息时非常有用。
案例
获取ApplicationContext
@RestController
public class ProxyTestController implements ApplicationContextAware{
private static ApplicationContext applicationContext;
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
}
}
③⑥BeanPostProcessor
介绍
实现该接口可以在 Bean 实例化后、初始化前、后对 Bean 进行增强。提供了在 Bean 初始化之前、后干预的机会,可以对 Bean 进行定制化处理。
实例化:是指创建一个对象的过程
初始化:初始化包括属性的赋值、调用初始化方法(例如使用 @PostConstruct 标记的方法)、进行依赖注入等。初始化是对象生命周期中的一个关键阶段,它确保对象在使用之前处于合适的状态。
通过重写 postProcessBeforeInitialization() 和 postProcessAfterInitialization() 方法,可以在 Bean 初始化之前和之后进行一些自定义逻辑,返回处理过的Bean实例。
应用场景
- 修改属性:加解密、校验。
- 属性扩展附加信息,比如动态生成的一些属性值;
- AOP相关处理:在方法前后添加自定义逻辑,比如,动态生成代理对象、方法前后拦截等。
- 处理自定义注解:根据自定义注解,对Bean进行二次处理。
- 包装对象:对对象进行包装,增强、扩展Bean功能。
- …
案例
动态生成代理对象
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.stereotype.Component;
@Component
public class LoggingBeanPostProcessor implements BeanPostProcessor {
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
return bean;
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
return new LoggingProxy().getLoggingProxy(bean);
}
}
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class LoggingProxy implements InvocationHandler {
private Object target;
public Object getLoggingProxy(Object target) {
if(target instanceof LoggingInterface) {
this.target = target;
return Proxy.newProxyInstance(
target.getClass().getClassLoader(),
target.getClass().getInterfaces(),
this
);
}
return target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("Before executing method: " + method.getName());
Object result = method.invoke(target, args);
System.out.println("After executing method: " + method.getName());
return result;
}
}
public interface LoggingInterface {
}
public interface ProxyTestService extends LoggingInterface {
void proxyTest();
}
@Service
public class ProxyTestServiceImpl implements ProxyTestService {
@Override
public void proxyTest() {
System.out.println("被代理方法");
}
}
@RestController
public class ProxyTestController {
@Resource
private ProxyTestService proxyTestService;
@GetMapping("a")
public String proxyTest() {
proxyTestService.proxyTest();
return "ok";
}
}
④PostConstruct
依赖注入完成后立即执行,通常用于执行一些初始化操作,比如连接数据库、加载配置文件、缓存预热等。
注解并不是 Spring 特有的,它们是 JSR-250 规范中定义的注解
注意事项:被标记方法不能是静态,也不能传递参数
⑤InitializingBean
实现该接口可以在 Bean 属性设置完成后执行初始化操作。通过重写 afterPropertiesSet() 方法,可以在 Bean 初始化时执行自定义的逻辑。
⑦SmartInitializingSingleton
Spring 提供的一个扩展接口,用于在所有单例 Bean 初始化完成后执行一些特定的逻辑。它提供了一个 afterSingletonsInstantiated() 方法,当所有的单例 Bean 初始化完成后,Spring 容器会自动调用该方法。
⑧PreDestroy
在 Spring 容器销毁该 Bean 前执行。通常用于执行一些清理操作,比如释放资源、关闭连接等。
注解并不是 Spring 特有的,它们是 JSR-250 规范中定义的注解
注意事项:被标记方法不能是静态,也不能传递参数
⑨DisposableBean
实现该接口可以在容器销毁 Bean 实例前执行清理操作。通过重写 destroy() 方法,可以在 Bean 销毁前执行自定义的清理逻辑。
FactoryBean
介绍
实现了FactoryBean了实现类,通过 getObject() 方法,返回一个自定义Bean到容器,提供给其它Bean 实例使用(依赖注入)。
应用场景
- 对象的缓存:FactoryBean可以实现对象的缓存,例如在每次调用时返回同一个对象实例,提高性能。
- 动态代理:FactoryBean可以用于创建动态代理对象,实现一些特定的功能,例如权限控制、事务管理等。
- 对象的延迟加载:FactoryBean可以在需要时才创建实例,实现对象的延迟加载,从而减少系统启动时间和资源占用。
- 对象的创建和配置:FactoryBean允许开发人员在创建Bean实例时进行更复杂的逻辑处理,例如从外部配置文件中读取参数,进行条件判断,创建不同的实例等。这样可以将对象的创建和配置逻辑从业务代码中分离出来,提高了代码的可维护性和可扩展性。
案例
提供一个Bean
例如,下面FactoryBean
的实现类MyBean
,就提供了SomeService
的实现给OtherBean
使用:
public class SomeService {
// SomeService的具体实现
}
public class MyBean implements FactoryBean<SomeService> {
@Override
public SomeService getObject() throws Exception {
// 在这里创建并返回SomeService的实例
return new SomeService();
}
@Override
public Class<?> getObjectType() {
return SomeService.class;
}
@Override
public boolean isSingleton() {
return true;
}
}
@Configuration
public class AppConfig {
@Bean
public MyBean myBean() {
return new MyBean();
}
@Bean
public OtherBean otherBean(SomeService someService) {
// OtherBean依赖于SomeService实例
return new OtherBean(someService);
}
}
public class OtherBean {
private final SomeService someService;
public OtherBean(SomeService someService) {
this.someService = someService;
}
// 其他操作...
}