likes
comments
collection
share

【Spring_01】Bean的生命周期

作者站长头像
站长
· 阅读数 17

本文主要有以下内容:

  • Bean的生命周期

故事从一道美味的外卖说起:

买菜

天微微亮、勤奋的我就去菜市场和菜贩子斗智斗勇、争取拿到最新鲜的肉和蔬菜、做出最美味可口的菜肴以卷死附近的其他商家。经过一番唇枪舌战之后。我终于选出了满意的菜、回到店里开始处理。

择菜

起了个大早、上了个大当、原以为趁着人少我可以选出一些好菜、结果这些菜贩子趁着天还不咋亮、欺负由于多年敲代码导致视力老眼昏花的我、塞了一些不新鲜的配菜在我的菜篮子里面!真是一个奸商、要是我的店倒闭了就有这部分奸商的责任!说好的人与人之间的信任呢?

为了让我的顾客吃到新鲜美味的菜肴、虽然被菜贩子坑了但我还是一个踏踏实实做生意的人、于是乎我把不新鲜的菜丢到、重新购置了一部分菜、以备不时之需。

按照店里菜单上的菜品、将各种菜品的原材料备好、土豆切成丝、肉是肉片各准备一部分、切点回锅肉。就开始等待客户从美团上下单了。

对应实例化之前的代码

炒菜

在早上11点10分左右、接到今天的第一单、红烧肉。接到第一单自然是开心的。

起锅开火、热锅凉油、先把锅润一遍、等到差不多的时候倒出油、将事先切成小块且已焯水的精品五花肉放入锅中、煎至肉块上出现金黄色、用勺子盛出多余的油之后、进行下一步工作。

调味上色

肉已经煎得差不多了,放豆瓣酱炒出红油、放入干海椒、八角、老抽上色、一丢丢盐、以及事先炒好的糖色和山楂片、加热水大火烧开、小火慢炖二十分钟后、加入土豆块炖二十分钟之后大火收汁。

起锅装盘

在完成上述的操作之后、将菜品放入打包盒、贴上密封条、加入打包的袋子里面、放入好评返现券。

呼唤骑手

将打包好的菜放在指定的位置等待骑手取餐送货、至此一个外卖就做好了、当外卖送到客户手里时、被他吃掉之后、一个bean的生命周期也就结束了!

惊不惊喜?意不意外?

接下来就来来好好捋捋这个bean的生命周期!

bean的生命周期总结

学习 Spring 的过程当中、我们肯定会会知道通过 xml方式去配置一个bean、也会了解IOC容器等基本常识概念。我们知道配置的bean(不管是xml方式、还是注解@bean、@Service、@Controller)会被IOC容器创建且管理。

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">
    <context:component-scan base-package="com.supersist.spring.tutorial" />
    <bean name="supersist" class="com.supersist.spring.tutorial.domain.User" init-method="doInit" destroy-method="doDestroy">
        <property name="age" value="100"/>
        <property name="name" value="壹贰是只猫"/>

    </bean>
</beans>

那必然就会有一个地方装载着这些数据的原始信息。这个地方就是我们用来装菜的菜蓝子

菜市场理解为IOC容器初始化之后、可对应BeanFactoryPostProcessor的代码。

菜篮子里面的菜就是我们的原始信息、不同的蔬菜相互组合就成了菜单上的菜。如果将菜单上的菜比喻成一个Bean的话、那么这道菜的所需要的原材料以及其他信息组成了BeanDefinition

因此如果要修改一个Bean的信息就可以通过修改BeanDefinition的值方式去修改。这一步就对应了择菜阶段。【实例化之前的准备】

BeanDefinition 包含了 bean 的各种属性信息,如类名、作用域、初始化方法、销毁方法、构造器参数、属性值等。

炒菜之前的洗菜阶段可对应InstantiationAwareBeanPostProcessor中的postProcessBeforeInstantiation方法。

炒菜阶段:这个阶段就是实例化的过程、即调用实体类的构造函数的过程。【实例化】

  • 肉下锅之后会煎至金黄再放其他调味品【实例化之后的阶段】

调味上色:这一步可以分为两个阶段

  • 备料阶段:准备好干辣椒、糖色、检查老抽、生抽等这一段。【属性注入前的准备】
  • 下锅和肉炒的阶段【属性注入这一阶段】

起锅装盘:不管点什么外卖、商家的打包盒上都会有商家相关的信息、如店名、下单小票等。

  • 实际上这一阶段就是对应【Aware】接口这一部分的回调
  • setBeanName
  • setBeanFactory
  • setApplicationContext

在上述步骤完成之后、出餐会做一些额外的处理、比如添加餐具、添加一些饮料。出餐前的最后初始化阶段

  • 这一部分对应的就是初始化这一阶段

骑手拿到餐之后、稍微检查一下、这就是初始化之后的调用阶段。客户签收之后就是使用阶段、客户处理掉这个外卖的时候就是销毁阶段。

如上所述生命周期可以分为如下几个阶段:

  • 实例化
    • 实例化之前阶段
    • 实例化
    • 实例化之后的阶段
  • 属性注入
    • 属性注入前的阶段
    • 属性注入
  • 初始化
    • 初始化之前的调用
    • 接口的初始化方法
    • 手动指定的初始化方法
    • 初始化之后的回调
  • 使用
  • 销毁

文字很抽象、用下图总结说明。从上往下、从左到右

【Spring_01】Bean的生命周期

图片说明:

BeanFactoryPostProcessor: 是一个在 Spring 容器标准初始化之后调用的回调接口,可以在此接口中的方法修改BeanDefinition。

@Component
public class MyBeanFactoryPostProcessor implements BeanFactoryPostProcessor {

    private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(MyBeanFactoryPostProcessor.class);

    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory)
            throws BeansException {

        log.info("1.execute BeanFactoryPostProcessor#postProcessBeanFactory");
        // 可以在这里修改BeanDefinition的信息
        BeanDefinition bd = configurableListableBeanFactory.getBeanDefinition("supersist");
    }
}

InstantiationBeanAwarePostProcessor:该接口是BeanPostProcessor接口的一个子接口、其中有三个方法、对每一个bean都起作用。

  • postProcessBeforeInstantiation():这个方法在所有Bean的实例化之前调用。可以在此方法修改属性注入的值
  • postProcessAfterInstantiation():这个方法在所有Bean实例化之后调用。返回true or false决定是否进行属性填充
  • postProcessProperties():在属性填充之前调用,可以修改属性值
@Component
public class MyInstantiationAwareBeanPostProcessor implements InstantiationAwareBeanPostProcessor {
    @Override
    public Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) throws BeansException {

        log.info("2.execute InstantiationAwareBeanPostProcessor#postProcessBeforeInstantiation for {}", beanName);
        return InstantiationAwareBeanPostProcessor.super.postProcessBeforeInstantiation(beanClass, beanName);
    }

    @Override
    public boolean postProcessAfterInstantiation(Object bean, String beanName) throws BeansException {

        System.out.println("4.实例化之后调用");
        log.info("4.execute InstantiationAwareBeanPostProcessor#postProcessAfterInstantiation for {}", beanName);

        return InstantiationAwareBeanPostProcessor.super.postProcessAfterInstantiation(bean, beanName);
    }

    @Override
    public PropertyValues postProcessProperties(PropertyValues pvs, Object bean, String beanName) throws BeansException {

        System.out.println("5.属性填充之前调用-修改注入的属性");
        log.info("5.execute InstantiationAwareBeanPostProcessor#postProcessProperties for {}",pvs.getPropertyValues());
        log.info("5.execute InstantiationAwareBeanPostProcessor#postProcessProperties for {}", beanName);
        if ("supersist".equals(beanName)) {
            System.out.println("Properties for bean 'supersist':");
            MutablePropertyValues mutablePropertyValues = (MutablePropertyValues) pvs;
            mutablePropertyValues.getPropertyValueList().forEach(propertyValue -> {
                System.out.println("Property name: " + propertyValue.getName() + ", value: " + propertyValue.getValue());
            });
            mutablePropertyValues.addPropertyValue(new PropertyValue("name", "Updated Name"));
            mutablePropertyValues.addPropertyValue(new PropertyValue("age", 40));

            System.out.println("Modified properties for bean 'supersist':");
            mutablePropertyValues.getPropertyValueList().forEach(propertyValue -> {
                System.out.println("Property name: " + propertyValue.getName() + ", value: " + propertyValue.getValue());
            });

            return mutablePropertyValues;
        }
        return InstantiationAwareBeanPostProcessor.super.postProcessProperties(pvs, bean, beanName);
    }


}

如果实体类实现了如下接口:

  • BeanNameAware: 则属性填充后会调用setBeanName()
  • BeanFactoryAware:则调用setBeanFactory()
  • ApplicationContextAware:则调用setApplicationContextAware()

BeanPostProcessor:此接口定义了初始化前后的回调函数、针对所有的Bean

@Component
public class MyBeanPostProcessor implements BeanPostProcessor {

    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        //log.info("10.初始化之前调用");
        log.info("10.execute BeanPostProcessor#postProcessBeforeInitialization for {}", beanName);
        return BeanPostProcessor.super.postProcessBeforeInitialization(bean, beanName);
    }

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        log.info("13:初始化之后调用");
        log.info("13.execute BeanPostProcessor#postProcessAfterInitialization for {}", beanName);
        return BeanPostProcessor.super.postProcessAfterInitialization(bean, beanName);
    }
}

初始化执行顺序如下:

  • InitialzingBean:如果实现了此接口、则调用afterPropertiesSet()

  • 如果在xml配置中指定了初始化方法:则调用init-method="doInit"此方法

上述方法执行完毕之后、就会回调执行postProcessAfterInitialization此方法。

接着被使用阶段被销毁

如果实体类实现了DisposableBean、且指定了destory方法则执行指定的destroy方法

@Override
public void destroy() throws Exception {
	log.info("execute DisposableBean#destroy");
}
public void doDestroy() {
  log.info("14.销毁");
  log.info("execute User#doDestroy");
}

主类:

public static void main(String[] args) {
    log.info("Init application context");

    ConfigurableApplicationContext context = new ClassPathXmlApplicationContext("spring-beans.xml");
    User user = (User) context.getBean("supersist");

    log.info(user.toString());
    log.info("Shutdown application context");
    context.close();
}

配置的xml文件见最上面。

运行结果如下图:

【Spring_01】Bean的生命周期

参考资料:

转载自:https://juejin.cn/post/7398150089556721676
评论
请登录