Spring生命周期回调与容器扩展详解

Spring生命周期回调与容器扩展详解

在Spring框架中,Bean的生命周期回调与容器扩展是非常重要的一部分。Spring框架有一个完整的标准初始化和销毁Bean的流程, 我们可以根据自己的业务需求去扩展这个流程,实现一些自定义的处理。

Bean的生命周期回调

在Spring中,一个Bean的创建与销毁都是由容器来管理的, 容器会自动的调用Bean的一些方法来完成Bean的初始化和销毁。我们可以在Bean的生命周期的多个阶段插入我们自己的处理逻辑。这些阶段包括:Bean实例化、Bean属性设置、Bean初始化前、Bean初始化后、容器销毁前和容器销毁后等。

Bean实例化

在Bean实例化阶段,Spring会通过调用Bean的构造方法来实例化一个对象。如果Bean类的构造方法中存在参数,则Spring会先通过它们来实例化出所需的参数,并进行递归实例化。接着Spring会调用Bean实例化回调接口:InstantiationAwareBeanPostProcessor#postProcessBeforeInstantiation方法,以便让用户在Bean实例化之前进行一些处理。如果该方法返回了非 null 的 Bean,则 Spring 容器将不再调用它后面的 Bean 构造方法,直接使用返回的 Bean 对象。如果该方法返回了 null,则 Spring 容器仍会继续进行 Bean 的构造方法调用,直到构造出实例为止。

public class MyInstantiationAwareBeanPostProcessor implements InstantiationAwareBeanPostProcessor {
    public Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) throws BeansException {
        if ("user".equals(beanName)) {
            System.out.println("InstantiationAwareBeanPostProcessor postProcessBeforeInstantiation");
        }
        return null;
    }
}

@Configuration
@ComponentScan("com.example.demo")
public class AppConfig {
    @Bean
    public MyInstantiationAwareBeanPostProcessor myInstantiationAwareBeanPostProcessor() {
        return new MyInstantiationAwareBeanPostProcessor();
    }
}

Bean属性设置

在Bean属性设置阶段,Spring会调用Bean的setter方法来完成依赖注入。Spring通过反射机制查找 Bean 中特定的 set 方法,并将配置文件或者注释配置中所定义的属性值通过这些set方法进行注入。如果Bean实现了BeanNameAware接口或BeanFactoryAware接口,则在属性注入完成后,Spring容器会调用相应的回调方法:BeanNameAware#setBeanName()或BeanFactoryAware#setBeanFactory()。

public class User implements BeanNameAware, BeanFactoryAware {
    private String name;
    private int age;

    public void setBeanName(String name) {
        System.out.println(this.getClass().getSimpleName() + " setBeanName");
    }

    public void setBeanFactory(BeanFactory beanFactory) {
        System.out.println(this.getClass().getSimpleName() + " setBeanFactory");
    }

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

    public void setAge(int age) {
        this.age = age;
    }
}

@Configuration
@ComponentScan("com.example.demo")
public class AppConfig {
    @Bean
    public User user() {
        User user = new User();
        user.setName("Nate");
        user.setAge(30);
        return user;
    }
}

Bean初始化前

在Bean初始化前阶段,Spring容器会调用BeanPostProcessor#postProcessBeforeInitialization()该回调方法,让用户可以在Bean初始化的前面添加自己的逻辑。 Spring容器必须在Bean属性注入完成后,Bean实现了Aware的回调方法后,才会回调这个 BeanPostProcessor 的回调方法。这可以保证调用之前的 Bean 所有属性都被正确的设置了。

public class MyBeanPostProcessor implements BeanPostProcessor {
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        if ("user".equals(beanName)) {
            System.out.println("BeanPostProcessor postProcessBeforeInitialization");
        }
        return bean;
    }
}

@Configuration
@ComponentScan("com.example.demo")
public class AppConfig {
    @Bean
    public MyBeanPostProcessor myBeanPostProcessor() {
        return new MyBeanPostProcessor();
    }
}

Bean初始化后

在Bean初始化后阶段,Spring容器会调用BeanPostProcessor#postProcessAfterInitialization()该回调方法,让用户可以在Bean初始化的后面添加自己的逻辑。 Spring容器必须在调用BeanPostProcessor#postProcessBeforeInitialization()之后,才会回调这个 BeanPostProcessor 的回调方法。

public class MyBeanPostProcessor implements BeanPostProcessor {
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        if ("user".equals(beanName)) {
            System.out.println("BeanPostProcessor postProcessAfterInitialization");
        }
        return bean;
    }
}

@Configuration
@ComponentScan("com.example.demo")
public class AppConfig {
    @Bean
    public MyBeanPostProcessor myBeanPostProcessor() {
        return new MyBeanPostProcessor();
    }
}

容器销毁前

在容器销毁前阶段,Spring容器会调用DisposableBean#destroy()方法和DestroyBean接口的destroy()方法,以便进行Bean销毁前的一些逻辑处理。

public class Dog implements DisposableBean {
    public void destroy() throws Exception {
        System.out.println("DisposableBean destroy");
    }
}

@Configuration
@ComponentScan("com.example.demo")
public class AppConfig {
    @Bean(destroyMethod = "destroy")
    public Dog dog() {
        return new Dog();
    }
}

容器销毁后

在容器销毁后阶段,Spring容器会调用BeanPostProcessor#postProcessBeforeDestruction()方法和DestroyBean接口的destroy()方法,以便进行销毁Bean后的一些逻辑处理。

public class MyDestructionAwareBeanPostProcessor implements DestructionAwareBeanPostProcessor {
    public void postProcessBeforeDestruction(Object bean, String beanName) throws BeansException {
        if ("user".equals(beanName)) {
            System.out.println("DestructionAwareBeanPostProcessor postProcessBeforeDestruction");
        }
    }

    public boolean requiresDestruction(Object bean) {
        return true;
    }
}

@Configuration
@ComponentScan("com.example.demo")
public class AppConfig {
    @Bean
    public MyDestructionAwareBeanPostProcessor myDestructionAwareBeanPostProcessor() {
        return new MyDestructionAwareBeanPostProcessor();
    }
}

容器扩展

在Spring中,我们还可以通过继承一些扩展点的抽象类来实现我们自己的扩展。这些扩展点包括:BeanFactoryPostProcessor、BeanPostProcessor、ApplicationListener和ResourceLoaderAware等。

BeanFactoryPostProcessor

BeanFactoryPostProcessor用于对BeanFactory进行处理。在Spring容器初始化完成之后,BeanFactory 调用BeanFactoryPostProcessor回调函数对BeanFactory进行外部扩展。我们可以通过实现这个接口实现对Bean的修改,比如替换Bean的内容,增加Bean的内容等等。注意:这个扩展只是针对BeanFactory的扩展,而不是Bean的扩展。

public class MyBeanFactoryPostProcessor implements BeanFactoryPostProcessor {
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
        BeanDefinition bd = beanFactory.getBeanDefinition("dog");
        MutablePropertyValues pvs = bd.getPropertyValues();
        if (pvs.contains("name")) {
            PropertyValue pv = pvs.getPropertyValue("name");
            pv.setConvertedValue("Tommy");
        }
    }
}

@Configuration
@ComponentScan("com.example.demo")
public class AppConfig {
    @Bean
    public MyBeanFactoryPostProcessor myBeanFactoryPostProcessor() {
        return new MyBeanFactoryPostProcessor();
    }
}

BeanPostProcessor

前面已经讲解了BeanPostProcessor,它主要用于在Bean的初始化前后添加自己的处理逻辑。

ApplicationListener

ApplicationListener用于监听ApplicationEvent类型的事件。当相关事件发生时,Spring容器就会调用该接口的回调函数。我们可以利用这个机制自定义自己需要的事件,并在事件发生时实现自己的业务逻辑。

public class MyApplicationListener implements ApplicationListener<MyApplicationEvent> {
    public void onApplicationEvent(MyApplicationEvent event) {
        System.out.println("MyApplicationListener onApplicationEvent");
    }
}

@Configuration
@ComponentScan("com.example.demo")
public class AppConfig {
    @Bean
    public MyApplicationListener myApplicationListener() {
        return new MyApplicationListener();
    }
}

public class MyApplicationEvent extends ApplicationEvent {
    public MyApplicationEvent(Object source) {
        super(source);
    }
}

@Component
public class User {
    @Autowired
    private ApplicationContext applicationContext;

    public void print() {
        MyApplicationEvent event = new MyApplicationEvent(this);
        applicationContext.publishEvent(event);
    }
}

ResourceLoaderAware

ResourceLoaderAware用于在加载Bean时获取ResourceLoader(资源加载器)来加载文件或URL等资源。

public class MyResourceLoaderAware implements ResourceLoaderAware {
    private ResourceLoader resourceLoader;

    public void setResourceLoader(ResourceLoader resourceLoader) {
        this.resourceLoader = resourceLoader;
    }

    public void print() throws IOException {
        Resource resource = resourceLoader.getResource("classpath:test.txt");
        InputStream inputStream = resource.getInputStream();
        System.out.println(inputStream);
    }
}

@Configuration
@ComponentScan("com.example.demo")
public class AppConfig {
    @Bean
    public MyResourceLoaderAware myResourceLoaderAware() {
        return new MyResourceLoaderAware();
    }
}

总结

Spring中的Bean生命周期回调和容器扩展提供了很好的扩展机制, 通过实现一些扩展点的接口,我们可以在Bean的实例化,初始化,销毁的不同阶段插入我们自己的处理逻辑,实现一些自定义的处理。

本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:Spring生命周期回调与容器扩展详解 - Python技术站

(0)
上一篇 2023年6月27日
下一篇 2023年6月27日

相关文章

  • b站解除港澳台限制油猴脚本无法授权找不到ip地址

    以下是关于B站解除港澳台限制油猴脚本无法授权找不到IP地址的完整攻略,包括基本知识和两个示例说明。 基本知识 B站是一个中国的在线视频分享平台,它在某些地区(如港澳台地区)可能会受到地区限制。为了解除这些限制,一些用户可能会使用油猴脚本。然而,有些用户可能会遇到油猴脚本无法授权的问题,这可能是由于无法找到IP地址导致的。 示例说明 以下是两个B站解除港澳台限…

    other 2023年5月7日
    00
  • redis实现分布式session的解决方案

    下面是关于“redis实现分布式session的解决方案”的完整攻略: 什么是分布式session? Session一般指的是“会话”,分布式session指的就是用户的会话信息存储在多个节点上,而不是只存储在一台服务器上。分布式session可以让多个服务器共同维护用户状态,同时也可以分担单个服务器的压力,降低服务的单点故障。 为什么要使用redis实现分…

    other 2023年6月26日
    00
  • java栈实现二叉树的非递归遍历的示例代码

    让我们来详细讲解一下“Java栈实现二叉树的非递归遍历的示例代码”的完整攻略。 什么是非递归遍历? 在讲解“Java栈实现二叉树的非递归遍历的示例代码”之前,我们先来了解一下什么是非递归遍历。 二叉树的遍历有三种方式: 前序遍历:根节点 → 左子树 → 右子树。 中序遍历:左子树 → 根节点 → 右子树。 后序遍历:左子树 → 右子树 → 根节点。 在使用递…

    other 2023年6月27日
    00
  • C++成员初始化列表

    C++中的成员初始化列表是定义构造函数时经常使用的技巧,它可以让我们在对象构造的过程中直接初始化成员变量,而不需要在构造函数里手动赋值。使用成员初始化列表可以提高程序的运行效率,也更加方便直观。 成员初始化列表使用冒号连接,语法如下: class MyClass { public: MyClass(int num1, int num2) : member1(…

    other 2023年6月20日
    00
  • 用PHP实现递归循环每一个目录

    要用PHP实现递归循环每一个目录,可以遵循以下步骤: 使用PHP中的opendir()函数打开目录,并使用readdir()函数读取目录中的文件和文件夹; 判断读取的目录项是否为文件夹,如果是文件夹,则使用递归的方式进入该文件夹,继续读取其中的文件和文件夹; 如果读取到的是文件,则根据需要进行操作,比如输出文件名等; 在每次调用自身完成递归读取后,需要使用c…

    other 2023年6月27日
    00
  • (网页)js实现alert中显示换行的方法

    (网页)js实现alert中显示换行的方法 在网页中,我们常常需要通过alert()函数弹出提示框来向用户展示一些信息。然而,在某些情况下,我们需要在提示框中展示一些较长的文本信息,而默认的alert()函数只能显示单行文本,这就会导致文本被截断而影响信息展示。本篇文章将介绍一种实现在alert()函数中实现换行的方法。 解决方案 为了实现换行,我们可以在需…

    其他 2023年3月28日
    00
  • 闪退重启不断!苹果iPhone 6用TLC有多不靠谱(史上最详细全面解析)

    闪退重启不断!苹果iPhone 6用TLC有多不靠谱(史上最详细全面解析) 如果你正在使用苹果iPhone 6,但是你的手机在使用过程中频繁出现闪退或者重启的情况,那么这篇文章就是给你的。我们将从硬件的角度来分析这个问题,并对使用TLC闪存的iPhone 6进行详细解析,帮助你更好地理解这个问题。 什么是TLC闪存? 在了解为什么TLC闪存不靠谱之前,我们需…

    other 2023年6月27日
    00
  • mysql5.0版本下载地址集合

    MySQL 5.0版本下载地址集合攻略 MySQL是一种流行的开源关系型数据库管理系统,MySQL 5.0版本是其较旧的版本之一。在本攻略中,我将为您提供MySQL 5.0版本的下载地址集合,并提供两个示例说明。 下载地址集合 您可以从以下来源之一下载MySQL 5.0版本: 官方网站:您可以访问MySQL官方网站(https://www.mysql.com…

    other 2023年8月4日
    00
合作推广
合作推广
分享本页
返回顶部