likes
comments
collection
share

Spring Bean生命周期和Aware接口

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

Spring Bean生命周期和Aware接口

铿然架构  |  作者  /  铿然一叶 这是  铿然架构  的第 114  篇原创文章

1. 介绍

为什么要学习bean生命周期和了解Aware?

在bean的生命周期过程中提供了一些钩子,使得我们有机会实现定制能力,例如封装已有的Bean,生成一个代理对象,从而添加一些行为。

aware有“意识到”、“感知到”的意思,应用层的bean可以通过实现xxxAware接口感知到spring内置的对象,利用它们完成一些操作,例如实现BeanFactoryAware接口可以获得BeanFactory对象,进一步获取到需要的bean实例。

因此,学习bean生命周期和了解aware的目的是为了实现定制能力,并且结合bean生命周期和aware的执行过程,才知道在何时能使用哪个aware,两者要结合起来看。

2. Bean生命周期和aware调用过程

Bean从创建到消亡的生命周期如下:

Spring Bean生命周期和Aware接口

蓝色部分为类中自行定义实现,绿色部分通过实现接口方法实现,黄色通过注解实现。

注意:jdk9以上已经不带javax.annotation包,以上涉及“@PostConstruct”和“@PreDestroy”注解,需要额外引入依赖包:

        <dependency>
            <groupId>javax.annotation</groupId>
            <artifactId>javax.annotation-api</artifactId>
            <version>1.3.2</version>
        </dependency>

2.1 启动spring日志

启动spring后,以上bean的生命周期方法自动调用,日志如下:

MyBean构造器初始化
BeanNameAware.setBeanName:myBean
serviceAddress=10.8.9.4
BeanClassLoaderAware.setBeanClassLoader
BeanFactoryAware.setBeanFactory
ApplicationContextAware.setApplicationContext
@PostConstruct
InitializingBean.afterPropertiesSet
@Bean.initMethod
BeanPostProcessor.postProcessBeforeInitialization, bean=org.springframework.boot.autoconfigure.web.servlet.ServletWebServerFactoryConfiguration$EmbeddedTomcat@1ecfcbc9, beanName=org.springframework.boot.autoconfigure.web.servlet.ServletWebServerFactoryConfiguration$EmbeddedTomcat
BeanPostProcessor.postProcessAfterInitialization, bean=org.springframework.boot.autoconfigure.web.servlet.ServletWebServerFactoryConfiguration$EmbeddedTomcat@1ecfcbc9, beanName=org.springframework.boot.autoconfigure.web.servlet.ServletWebServerFactoryConfiguration$EmbeddedTomcat
BeanPostProcessor.postProcessBeforeInitialization, bean=org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory@5a6482a9, beanName=tomcatServletWebServerFactory
BeanPostProcessor.postProcessBeforeInitialization, bean=org.springframework.boot.autoconfigure.websocket.servlet.WebSocketServletAutoConfiguration$TomcatWebSocketConfiguration@7f9ab969, beanName=org.springframework.boot.autoconfigure.websocket.servlet.WebSocketServletAutoConfiguration$TomcatWebSocketConfiguration
BeanPostProcessor.postProcessAfterInitialization, bean=org.springframework.boot.autoconfigure.websocket.servlet.WebSocketServletAutoConfiguration$TomcatWebSocketConfiguration@7f9ab969, beanName=org.springframework.boot.autoconfigure.websocket.servlet.WebSocketServletAutoConfiguration$TomcatWebSocketConfiguration
.....
ChildBean构造器初始化
BeanPostProcessor.postProcessBeforeInitialization, bean=com.kengcoder.springframeawsome.bean.ChildBean@3dd4a6fa, beanName=childBean
BeanPostProcessor.postProcessAfterInitialization, bean=com.kengcoder.springframeawsome.bean.ChildBean@3dd4a6fa, beanName=childBean
BeanPostProcessor.postProcessBeforeInitialization, bean=org.springframework.boot.autoconfigure.AutoConfigurationPackages$BasePackages@5cbe2654, beanName=org.springframework.boot.autoconfigure.AutoConfigurationPackages
BeanPostProcessor.postProcessAfterInitialization, bean=org.springframework.boot.autoconfigure.AutoConfigurationPackages$BasePackages@5cbe2654, beanName=org.springframework.boot.autoconfigure.AutoConfigurationPackages
......

从上面的例子可以看到:

● 属性初始化在构造器调用之后,代码如下:

地址信息通过配置参数获取:

    @Value("${address}")
    private String serviceAddress;

打印属性信息代码(实现了BeanNameAware接口):

    @Override
    public void setBeanName(String name) {
        System.out.println("BeanNameAware.setBeanName:" + name);
        System.out.println("serviceAddress=" + serviceAddress);
    }

打印日志结果:

MyBean构造器初始化
BeanNameAware.setBeanName:myBean
serviceAddress=10.8.9.4

可以看到,在BeanNameAware.setBeanName调用时已经获取到属性,推导出属性初始化在构造器调用之后,其他钩子方法之前执行。

● 几个aware的接口方法在bean构造器之后,同时在下列方法之前调用:

@PostConstruct
InitializingBean.afterPropertiesSet
@Bean.initMethod

● 通过如下类方法注入的bean实例化在@Bean.initMethod之后被调用。

    public void setChildBean(@Autowired ChildBean childBean) {
        System.out.println("setChildBean");
        this.childBean = childBean;
    }

● BeanPostProcessor.postProcessBeforeInitialization和BeanPostProcessor.postProcessAfterInitialization会因为多个bean初始化多次被调用,也因此有机会获取这些bean,并用来实现定制逻辑。

2.2 停止spring

停止spring,bean生命周期日志如下:

@PreDestroy
DisposableBean.destroy
@Bean.destroyMethod

3. 定制化例子

3.1 静态方法获取bean实例

通常静态方法存在于工具类中,这些类没有成员变量,因此不需要实例化,也就不需要使用@Component、@Service等注解来实例化,当工具类要使用一个三方bean实例时,由于没有使用上述注解,也无法使用诸如@Autowired的注解来注入三方bean,此时就要用到aware接口,下面来看例子。

3.1.1 类结构:

Spring Bean生命周期和Aware接口

3.1.2 代码

3.1.2.1 ThirdPartyBean

三方bean,模拟一个简单操作,通过spring注入:

@Component
public class ThirdPartyBean {
    public void exec() {
        System.out.println("ThirdPartyBean be called.");
    }
}

3.1.2.2 BeanFactoryHolder

实现BeanFactoryAware接口,获取传入的BeanFactory并持有它,让其他类可以获取到BeanFactory:

@Component
public class BeanFactoryHolder implements BeanFactoryAware {
    private static BeanFactory beanFactory;

    public static BeanFactory getBeanFactory() {
        return beanFactory;
    }

    @Override
    public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
        // 注意这里的写法,前面必须加上BeanFactoryHolder
        BeanFactoryHolder.beanFactory = beanFactory;
    }
}

3.1.2.3 ThirdPartyBeanUtil

工具类,获取三方bean并调用:

public class ThirdPartyBeanUtil {

    public static void exec() {
        ThirdPartyBean thirdPartyBean = BeanFactoryHolder.getBeanFactory().getBean(ThirdPartyBean.class);
        thirdPartyBean.exec();
        System.out.println("ThirdPartyBeanUtil be called.");
    }
}

3.1.3 调用日志

成功获取到三方bean,调用成功:

ThirdPartyBean be called.
ThirdPartyBeanUtil be called.

3.2 属性初始化之后完成bean的初始化

有时候bean的初始化需要参考bean的属性,这些属性通过@Value注解从配置文件中读取,此时按照上面的顺序以及语义来看,比较适合实现InitializingBean接口的afterPropertiesSet方法,在该方法中完成bean初始化。

3.3 改变已有bean的行为

例如已有一个Restful接口bean,用于发送resuful请求,想增加一些动作,例如上报统计信息,那么可以实现BeanPostProcessor接口的postProcessAfterInitialization方法生成一个代理类。

3.3.1 类结构

Spring Bean生命周期和Aware接口

3.3.2 代码

3.3.2.1 RestfulBean

restful请求类:

@Component
public class RestfulBean {
    public String post(String url, String jsonData) {
        System.out.println("url=" + url + ", jsonData=" + jsonData);
        return "success";
    }
}

3.3.2.2 RestfulBeanWrapper

代理类:

@Component
public class RestfulBeanWrapper extends RestfulBean implements BeanPostProcessor {

    private RestfulBean restfulBean;

    public String post(String url, String jsonData) {
        System.out.println("上报统计信息···");
        return restfulBean.post(url, jsonData);
    }

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        Class beanClass = bean.getClass();
        if (beanClass == RestfulBean.class) {
            restfulBean = (RestfulBean) bean;
            return this;
        }
        return bean;
    }
}

3.3.2.3 RestfulBeanController

用于触发调用RestfulBean:

@RestController
@RequestMapping(path = "/restfulBeanController", method = {RequestMethod.GET})
public class RestfulBeanController {

    @Autowired
    private RestfulBean restfulBean;

    @RequestMapping("/exec")
    public String exec() {
        return restfulBean.post("http://localhost:8080/restservice", "helo world!");
    }
}

3.3.3 调用日志

上报统计信息···
url=http://localhost:8080/restservice, jsonData=helo world!

可以看到代理类增加的操作被执行,RestfulBean的原有方法也正确执行。

4. 总结

● 本文探讨了Bean的生命周期,重点介绍了在生命周期各阶段可利用的钩子方法,这些方法为Bean的定制化提供了灵活性和扩展能力。

● 通过具体示例阐释了如何利用这些钩子实现定制功能,虽然这些示例可能还有更好的替代方法,但它们有效地展示了其中一种实现,在适当的业务场景下,可以根据需要选择使用这些方法。

● “aware”和钩子接口的处理机制,在某种程度上类似于发布/订阅模式。在此模式中,Spring提供一个接口供应用层订阅(即实现该接口)。当满足条件时,Spring会触发通知(调用实现类重载的接口方法),并将事件对象(接口方法参数)传递给订阅者,以供其使用,所谓万变不离其宗,最终都归于某种设计模式或设计原则。


其他阅读: