Spring中你应该要知道的的回调方式

前言

本篇按照bean的声明周期顺序罗列出了其中所执行的回调方式,也是按照这个顺序进行排版的,有一些并不能作为我们开发所需要的就省略了,个别是因为同一个接口就整合到了一起,但并不影响。

BeanDefinitionRegistryPostProcessor

image-20231219221229830

这个后置处理器是在bean工厂初始化准备后执行的,一般是进行内置的bean解析,用在了应用初始化器上,也就是SPI,由tomcat启动spring容器时使用,也有在监听器配置里,添加默认的后置处理器,也是spring所必须的后置处理器,所以我们一般是用不到的,可以作为了解,如果是说需要进行框架开发,可以深入了解。

BeanFactoryPostProcessor

bean工厂的后置处理,在上一个处理器之后执行,我们使用的@Component,@Import,@Configuration等创建的bean就是在这里被扫描解析并注册的。

image-20231218214238863

它的参数是bean工厂,所以它的权限范围较大,因为在这一层spring容器还未实例化bean,所以这里我们可以修改beanDefinition,甚至自定义自己的beanDefinition,又或者只是做一写简单操作,比如,根据校验我们自定义的规则校验benaDefinition是否合法,或者是删除benaDefinition

定义一个 普通的类:

public class CustomTest {

    private String name = "3";

    private String tt;

    private CustomConfig config;

    public String getName(){
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public void init() {
        System.out.println("初始化方法");
    }

    public CustomConfig getConfig() {
        return config;
    }

    public void setConfig(CustomConfig config) {
        this.config = config;
    }

    @Override
    public String toString() {
        return "CustomTest{" +
            "name='" + name + '\'' +
            ", config=" + config +
            '}';
    }
}

定义3个bean

@Component
public class CustomConfig {
}

@Component
public class CustomConfig2 {
}
@Component
public class CustomConfig3 {
}

@Component
public class CustomBeanFactoryPostProcessor implements BeanFactoryPostProcessor {
    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
        GenericBeanDefinition customBean = new GenericBeanDefinition();
        customBean.setBeanClassName(CustomTest.class.getName());
        // 设置初始化方法
        customBean.setInitMethodName("init");
        // 设置自定义bean的class
        customBean.setBeanClass(CustomTest.class);
        // 设置自动注入
        customBean.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE);
        ((BeanDefinitionRegistry)beanFactory).registerBeanDefinition("customTestBean", customBean);
        // 提前实例化
        Object customTestBean = beanFactory.getBean("customTestBean");
        System.out.println("自定义bean:" + customTestBean);

        // 判断某个bean是否存在
        if (beanFactory.containsBean("customConfig")) {
            System.out.println("包含bean[customConfig]");
        }

        // 移除某个bean
        ((BeanDefinitionRegistry) beanFactory).removeBeanDefinition("customConfig2");

        // 提前实例一个bean
        CustomConfig3 bean = beanFactory.getBean(CustomConfig3.class);
        System.out.println("实例化:" + bean);
    }
}

实现后置处理器,做几个操作:

  1. 自定义一个bean定义
  2. 提前实例化自定义的bean
  3. 判断

image-20231218221904082

InstantiationAwareBeanPostProcessor

接口定义如下:

public interface InstantiationAwareBeanPostProcessor extends BeanPostProcessor {

	@Nullable
	default Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) throws BeansException {
		return null;
	}

	default boolean postProcessAfterInstantiation(Object bean, String beanName) throws BeansException {
		return true;
	}

	@Nullable
	default PropertyValues postProcessProperties(PropertyValues pvs, Object bean, String beanName)
			throws BeansException {

		return null;
	}
}

postProcessBeforeInstantiation

如果我们实现这个接口并返回了一个bean的话,可以打断后面的执行动作,什么意思呢?

在spring初始化阶段,它有一个bean的生命周期,周期中bean实例化是其中一环,之后还有依赖注入,aware回调,实例化回调等的操作,如果我们这里返回了bean,那么那些操作将不会执行,这些操作包含aware回调,postConstruct,initMethod

定义一个测试bean,这里实现了InitializingBean接口

@Component
public class CustomConfig4 implements InitializingBean {
    @Override
    public void afterPropertiesSet() throws Exception {
        System.out.println("customConfig4 afterPropertiesSet");
    }
}

实现实例化前的处理器方法,直接new一个对象

@Component
public class CustomInstantPostProcessor implements InstantiationAwareBeanPostProcessor {
    @Override
    public Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) throws BeansException {
        if (Objects.equals(beanName, "customConfig4")) {
            return new CustomConfig4();
        }
        return null;
    }
}

需要注意的是: 这个操作执行后,在spring中的后续的一些回调方法不会执行,如InitializingBean.afterPropertiesSetaware接口等,但是spring aop依然是生效的。

postProcessAfterInstantiation

当我们修改后,也就是实现了这个接口,默认返回true,返回true就表示,不在进行属性填充,也就是依赖注入不会发生。

postProcessProperties

这个处理器用做bean的属性填充,也就是我们@Autowired,@Resource注解注入时,属性填充执行的地方;

这里能做的可能只有校验了,比如你在应用初始化时对应用监控时可能会用到。

@Component
public class CustomBeanPropertiesPostProcessor implements InstantiationAwareBeanPostProcessor {
    @Override
    public PropertyValues postProcessProperties(PropertyValues pvs, Object bean, String beanName)
    throws BeansException {
        
        return pvs;
    }
}

需要注意的是: 这里虽然参数有bean对象,但是这个bean对象只是一个副本,并不是容器上下文里使用的,所以直接修改是不会生效的。

Supplier

在实例化bean时,Spring还提供了一个Supplier方法,用于实例创建的,如果没有的话,就会走spring默认的实例化逻辑,我们先看个例子

@Component
public class CustomSupplierPostProcessor implements BeanFactoryPostProcessor {
    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
        GenericBeanDefinition customBean = new GenericBeanDefinition();
        customBean.setBeanClassName(CustomTest2.class.getName());
        // 设置初始化方法
        customBean.setInitMethodName("init");
        // 设置自定义bean的class
        customBean.setBeanClass(CustomTest2.class);
        // 设置自动注入
        customBean.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE);
        // supplier
        customBean.setInstanceSupplier(()-> {
            CustomTest2 temp = new CustomTest2();
            temp.setConfig(beanFactory.getBean(CustomConfig.class));
            temp.setName("0");
            return temp;
        });
        ((BeanDefinitionRegistry)beanFactory).registerBeanDefinition("customTestBean2", customBean);
    }
}

这里的实现就是依赖于beanFactory后置处理器实现的,因为它需要操作beanDefinition,也或者是实现接口BeanDefinitionRegistryPostProcessor,这并不影响。

打印一下:

@Component
public class TestRunner implements ApplicationRunner {
    @Autowired
    private CustomConfig4 customConfig4;
    @Autowired
    private CustomTest2 customTest2;
    @Override
    public void run(ApplicationArguments args) throws Exception {
        customConfig4.t();
        System.out.println("customTest2:" + customTest2);
    }
}

image-20231219203715479

BeanPostProcessor

image-20231219221522093

初始化bean的操作接口,只能修改bean里的内容。

@Component
public class CustomBeaPostProcessor implements BeanPostProcessor {
    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        return 我们修改的bean对象;
    }
}

@Lookup

当我们打上注解@Component时,被标注的类会生成单例bean,那么这个bean中的属性在非人为干扰的情况下就不会在变化,如下:

@Data
@Component
public class CustomTest3 {

    @Autowired
    public CustomTest4 customTest4;

    public void print() {
        System.out.println(customTest4());
    }
}

@Component
@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public class CustomTest4 {

}

当调用customTest3.print()时,打印出的hashCode是不会变的。

那么如果说,有一个场景,需要customTest3每次获取customTest4都是一个新的bean对象,要怎么实现?

你可能会想到实现接口ApplicationContextAware,如下:

@Data
@Component
public class CustomTest3 implements ApplicationContextAware {
    private ApplicationContext applicationContext;
    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        applicationContext = applicationContext;
    }

    public void print() {
        CustomTest4 bean = applicationContext.getBean(CustomTest4.class);
        System.out.println(bean);
    }
}

或者自己手动创建一个实例,这个过程稍微复杂了点,但是在spring中,只需要一个注解@Lookup就能解决:

@Data
@Component
public class CustomTest3 {

    @Lookup
    public CustomTest4 customTest4(){return null;}

    public void print() {
        System.out.println(customTest4());
    }
}

@Component
@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public class CustomTest4 {

}

image-20231219210601804

另外它也可以这样写:

@Component
public interface CustomTest33 {

    @Lookup
    CustomTest4 customTest4();

}
@Component
public abstract class CustomTest34 {

    @Lookup
    abstract CustomTest4 customTest4();

}

它的原理是由接口生成了代理类,所需可以不需要实现类。

需要注意的是:

  1. 它只能打在方法上
  2. 标注的方法可以是接口,但不能是private
  3. 方法需是无参的

MergedBeanDefinitionPostProcessor

image-20231219221653345

postProcessMergedBeanDefinition提供我们修改beanDefinition的功能,如下:

@Component
public class CustomMergedBeanDefinitionPostProcessor implements MergedBeanDefinitionPostProcessor {
    @Override
    public void postProcessMergedBeanDefinition(RootBeanDefinition beanDefinition, Class<?> beanType, String beanName) {
        if (!Objects.equals(beanName, "customConfig5")) {
            return;
        }
        beanDefinition.setInitMethodName("t");
    }
}

@Component
public class CustomConfig5 {

    public void t() {
        System.out.println("customConfig5 init method.");
    }

}

这里需要注意的是: 在这步之前,bean已经实例化了,所以在这里我们修改Bean class是无效的。

resetBeanDefinition这个方法是重置元数据信息,我们已经提到过了,bean已经实例化了,所以只能重置bean内的元数据信息。

Aware接口

该接口定义为spring组件获取接口,比如:

  1. BeanClassLoaderAware设置beanClass类加载器
  2. BeanFactoryAware设置beanFactory
  3. ApplicationContextAware设置容器上下文

还有很多的接口,我们常用的可能是ApplicationContextAware这一类,不过如果你有对这个spring初始化流程有过理解的话,你可能会发现一个问题,我常见到会有这样的用法:

@Component
public class SpringUtil implements ApplicationContextAware {
    
    private static ApplicationContext applicationContext;
    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        SpringUtil.applicationContext = applicationContext;
    }
    
    public <T> T getBean(Class<T> clazz) {
        return applicationContext.getBean(clazz);
    }
}

不管对不对,我们假设有一个类:

@Component
public class CustomConfig6 implements InitializingBean {

    private CustomConfig config;

    @Override
    public void afterPropertiesSet() throws Exception {
        config = SpringUtil.getBean(CustomConfig.class);
    }
}

问:应用是否正常?

如果不正常,该怎么修改?

答案在本篇已经给出了

@PostConstruct/@PreDestroy

在xml配置bean的方式中

<bean id="xx" class="xxxxx" init-method="" destroy-method="">

在注解方式中,@PostConstruct就是init-method@PreDestroydestroy-method

注意: 它是在bean初始化完后,InitializingBean之前执行的;并且他们可以存在多个,执行顺序是:
@PostConstruct -> 父类内从先到后,子类内从先到后
@PreDestory -> 子类内从先到后,父类内从先到后

@Component
public class CustomConfig7 {

    @PostConstruct
    public void t() {
        System.out.println("customConfig7 init");
    }
    
    @PostConstruct
    public void t2() {
        System.out.println("customConfig7 init2");
    }

    @PreDestroy
    public void d() {
		System.out.println("customConfig7 destroy");
    }
    
    @PreDestroy
    public void d2() {
		System.out.println("customConfig7 destroy2");
    }
}

InitializingBean

image-20231219223850562

这个接口也是常用的一个,一般是做数据缓存回调,或者其他的一些需要预先执行的操作。

@Component
public class CustomConfig4 implements InitializingBean {
    @Override
    public void afterPropertiesSet() throws Exception {
        System.out.println("customConfig4 afterPropertiesSet");
    }

}

注意: 它在初始化完bean后就会进行回调,这时的bean是完整的bean

FactoryBean

自定义bean的创建的工厂模式,执行时机在单例bean创建完成后,即当CustomFactoryBean作为完整bean生成完后,进行FactoryBean的判断,如果时属于,那么才执行getObject()方法,创建bean

public class CustomTest5 {

    private String name;

    public CustomTest5(String name) {
        this.name = name;
    }
}
@Component
public class CustomFactoryBean implements FactoryBean<CustomTest5> {
    @Override
    public CustomTest5 getObject() throws Exception {
        return new CustomTest5("0");
    }

    @Override
    public Class<?> getObjectType() {
        return CustomTest5.class;
    }
}

这个接口和Supplier是一样的功能,只是实现不一样。

SmartInitializingSingleton

image-20231219224732117

注意: 这个接口是在所有bean实例并初始化完后执行的,我不知有什么场景使用。

ApplicationRunner

image-20231220205054945

spring应用启动完后执行,表示应用已经是正常可以立即使用的状态,用于需要程序启动完成后才能执行的操作,比如通知其他系统,或程序。

@Component
public class TestRunner2 implements ApplicationRunner {
    @Override
    public void run(ApplicationArguments args) throws Exception {
        
    }
}

CommandLineRunner

image-20231220205430511

spring应用启动完后执行,表示应用已经是正常可以立即使用的状态,用于需要程序启动完成后才能执行的操作,比如通知其他系统,或程序。

@Component
public class TestRunner3 implements CommandLineRunner {
    @Override
    public void run(String... args) throws Exception {
        
    }
}

  • 9
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值