前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Spring IoC 容器扩展

Spring IoC 容器扩展

作者头像
李鸿坤
发布2020-07-18 18:40:37
5220
发布2020-07-18 18:40:37
举报
文章被收录于专栏:泛泛聊后端泛泛聊后端

托管给Spring IoC 容器的Bean虽然不知道容器的存在,但是容器也提供了完整的扩展点,让使用者动态干预bean的定义和实例化,以及生命周期相关的钩子。

生命周期

Bean实例化和销毁的时候,容器提供了默认的Hook,它们分别是InitializingBean和DisposableBean。实现后,容器将在bean实例化和销毁的时候进行调用。如果不实现这两个接口,同样可以使用该功能,那就是在配置对应的init-method和destroy-method。JSR-250定义了两个注解@PostConstruct and @PreDestroy同样是可以实现这样的功能。

代码语言:javascript
复制
package com.lihongkun.labs.spring.container.lifecycle;
import org.springframework.beans.factory.DisposableBean;import org.springframework.beans.factory.InitializingBean;
import javax.annotation.PostConstruct;import javax.annotation.PreDestroy;
public class LifeCycleBean implements InitializingBean, DisposableBean {
    private String propertyValue;
    public void setPropertyValue(String propertyValue) {        this.propertyValue = propertyValue;        System.out.println("[lifecycle event] setter inject");    }
    public LifeCycleBean(){        System.out.println("[lifecycle event] constructor");    }
    @PostConstruct    public void postConstruct(){        System.out.println("[lifecycle event] PostConstruct");    }
    @PreDestroy    public void preDestroy(){        System.out.println("[lifecycle event] PreDestroy");    }
    public void initMethod(){        System.out.println("[lifecycle event] initMethod");    }
    public void destroyMethod(){        System.out.println("[lifecycle event] destroyMethod");    }
    @Override    public void destroy() throws Exception {        System.out.println("[lifecycle event] destroy");    }
    @Override    public void afterPropertiesSet() throws Exception {        System.out.println("[lifecycle event] afterPropertiesSet");    }}

bean的配置

代码语言:javascript
复制
<?xml version="1.0" encoding="UTF-8"?><beans xmlns="http://www.springframework.org/schema/beans"       xmlns:context="http://www.springframework.org/schema/context"       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"       xsi:schemaLocation="http://www.springframework.org/schema/beans        https://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="org.example"/>
    <bean id="lifeCycleBean"          init-method="initMethod"          destroy-method="destroyMethod"          class="com.lihongkun.labs.spring.container.lifecycle.LifeCycleBean" >        <property name="propertyValue" value="hello" />    </bean></beans>

执行后的结果是

代码语言:javascript
复制
[lifecycle event] constructor[lifecycle event] setter inject[lifecycle event] PostConstruct[lifecycle event] afterPropertiesSet[lifecycle event] initMethod[lifecycle event] PreDestroy[lifecycle event] destroy[lifecycle event] destroyMethod

可以看出执行顺序是 :构造函数 => 属性注入 => PostConstruct => InitializingBean => init-method配置 => PreDestroy => DisposableBean => destroy-method配置。

InitializingBean 和 DisposableBean 的实现方式是和Spring容器耦合的。推荐的是JSR-250的注解,跟容器无关,切换其他容器的时候也是有对应的功能。如果不能使用的话,次优选择是init-method配置和destroy-method配置,保持类的干净,也是不耦合于容器。

容器扩展点

除了单个bean本身的事件,Spring容器提供了BeanPostProcessor和BeanFactoryPostProcessor两个容器级别的扩展点。它们被大量用在和Spring结合的一些基础框架上。

BeanPostProcessor

BeanPostProcessor 作用在实例化后的bean上,允许使用者在容器实例化bean的后,对其进行修改。它提供了两个方法,传入bean和beanName,返回处理后的bean。用法示例如下:

代码语言:javascript
复制
import org.springframework.beans.BeansException;import org.springframework.beans.factory.config.BeanPostProcessor;
public class HelloBeanPostProcessor implements BeanPostProcessor {    @Override    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {        System.out.println("[lifecycle event] postProcessBeforeInitialization");        return bean;    }
    @Override    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {        System.out.println("[lifecycle event] postProcessAfterInitialization");        return bean;    }}

通常应用于两种场景,对实现了某个接口的bean进行填充或者该接口相关的操作。Spring容器使用这个扩展点提供了一些特性。如postProcessBeforeInitialization实现了ApplicationContext相关的Aware机制。

代码语言:javascript
复制
class ApplicationContextAwareProcessor implements BeanPostProcessor {
  private final ConfigurableApplicationContext applicationContext;
  private final StringValueResolver embeddedValueResolver;

  /**   * Create a new ApplicationContextAwareProcessor for the given context.   */  public ApplicationContextAwareProcessor(ConfigurableApplicationContext applicationContext) {    this.applicationContext = applicationContext;    this.embeddedValueResolver = new EmbeddedValueResolver(applicationContext.getBeanFactory());  }

  @Override  @Nullable  public Object postProcessBeforeInitialization(final Object bean, String beanName) throws BeansException {    AccessControlContext acc = null;
    if (System.getSecurityManager() != null &&        (bean instanceof EnvironmentAware || bean instanceof EmbeddedValueResolverAware ||            bean instanceof ResourceLoaderAware || bean instanceof ApplicationEventPublisherAware ||            bean instanceof MessageSourceAware || bean instanceof ApplicationContextAware)) {      acc = this.applicationContext.getBeanFactory().getAccessControlContext();    }
    if (acc != null) {      AccessController.doPrivileged((PrivilegedAction<Object>) () -> {        invokeAwareInterfaces(bean);        return null;      }, acc);    }    else {      invokeAwareInterfaces(bean);    }
    return bean;  }
  private void invokeAwareInterfaces(Object bean) {    if (bean instanceof Aware) {      if (bean instanceof EnvironmentAware) {        ((EnvironmentAware) bean).setEnvironment(this.applicationContext.getEnvironment());      }      if (bean instanceof EmbeddedValueResolverAware) {        ((EmbeddedValueResolverAware) bean).setEmbeddedValueResolver(this.embeddedValueResolver);      }      if (bean instanceof ResourceLoaderAware) {        ((ResourceLoaderAware) bean).setResourceLoader(this.applicationContext);      }      if (bean instanceof ApplicationEventPublisherAware) {        ((ApplicationEventPublisherAware) bean).setApplicationEventPublisher(this.applicationContext);      }      if (bean instanceof MessageSourceAware) {        ((MessageSourceAware) bean).setMessageSource(this.applicationContext);      }      if (bean instanceof ApplicationContextAware) {        ((ApplicationContextAware) bean).setApplicationContext(this.applicationContext);      }    }  }
  @Override  public Object postProcessAfterInitialization(Object bean, String beanName) {    return bean;  }
}

上述代码即是Spring容器使用了BeanPostProcessor实现了Aware机制里面的EnvironmentAware、EmbeddedValueResolverAware、ResourceLoaderAware、ApplicationEventPublisherAware、MessageSourceAware、ApplicationContextAware。

另外一种场景是需要对实例化后的Bean进行代理包装,postProcessAfterInitialization很容易想到可以直接把bean的实现给替换了,实际上Spring的AOP机制也是用此扩展来实现的。

BeanFactoryPostProcessor

BeanFactoryPostProcessor主要使用在修改容器的bean definition上面,它的阶段在更前面的环节,可以达到动态修改bean配置的能力。它只有一个postProcessBeanFactory方法,使用示例如下

代码语言:javascript
复制
import org.springframework.beans.BeansException;import org.springframework.beans.factory.config.BeanFactoryPostProcessor;import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
public class HelloBeanFactoryPostProcessor implements BeanFactoryPostProcessor {    @Override    public void postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException {        System.out.println("[lifecycle event] postProcessBeanFactory");    }}

典型的应用即Spring容器中@Value("${property}")的实现。它是通过PropertyPlaceholderConfigurer类实现,继承自PropertyResourceConfigurer实现了BeanFactoryPostProcessor。

代码语言:javascript
复制
@Override  public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {    try {      // 合并本地属性和外部指定的属性文件资源中的属性       Properties mergedProps = mergeProperties();
      // Convert the merged properties, if necessary.      // 将属性的值做转换(仅在必要的时候做)      convertProperties(mergedProps);
      // Let the subclass process the properties.      // 对容器中的每个bean定义进行处理,也就是替换每个bean定义中的属性中的占位符       processProperties(beanFactory, mergedProps);    }    catch (IOException ex) {      throw new BeanInitializationException("Could not load properties", ex);    }  }

小结

Bean生命周期方法和容器级别的扩展点可以做资源初始化和销毁、动态修改bean对象以及动态修改bean定义。它们以一定的顺序作用在bean上。以下结果是示例中所输出的顺序。

代码语言:javascript
复制
[lifecycle event] postProcessBeanFactory[lifecycle event] constructor[lifecycle event] setter inject[lifecycle event] postProcessBeforeInitialization[lifecycle event] PostConstruct[lifecycle event] afterPropertiesSet[lifecycle event] initMethod[lifecycle event] postProcessAfterInitialization[lifecycle event] PreDestroy[lifecycle event] destroy[lifecycle event] destroyMethod
本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2020-06-01,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 泛泛聊后端 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 生命周期
  • 容器扩展点
    • BeanPostProcessor
      • BeanFactoryPostProcessor
      • 小结
      相关产品与服务
      容器服务
      腾讯云容器服务(Tencent Kubernetes Engine, TKE)基于原生 kubernetes 提供以容器为核心的、高度可扩展的高性能容器管理服务,覆盖 Serverless、边缘计算、分布式云等多种业务部署场景,业内首创单个集群兼容多种计算节点的容器资源管理模式。同时产品作为云原生 Finops 领先布道者,主导开源项目Crane,全面助力客户实现资源优化、成本控制。
      领券
      问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档