0%

熟悉Spring钩子方法和钩子接口使用,简化你的开发

Spring提供了非常多的钩子方法和钩子接口,善用它们可以极大地方便我们开发,本篇文章将会详细介绍各种钩子方法和钩子接口的使用方式。

Aware接口

Spring中提供了各种Aware接口,方便从上下文中获取当前的运行环境,比较常见的几个子接口有: BeanFactoryAware,BeanNameAware,ApplicationContextAware,EnvironmentAwareBeanClassLoaderAware等,这些Aware的作用都可以从命名得知,并且其使用也是十分简单。

接口名 描述
ApplicationContextAware 实现了这个接口的类都可以获取到一个 ApplicationContext 对象. 可以获取容器中的所有Bean
ApplicationEventPublisherAware bean中可以得到应用上下文的事件发布器, 从而可以在Bean中发布应用上下文的事件。
BeanClassLoaderAware 获取bean的类加载器
BeanFactoryAware 获取bean的工厂
BeanNameAware 获取bean在容器中的名字
BootstrapContextAware 获取BootstrapContext
LoadTimeWeaverAware 加载Spring Bean时织入第三方模块, 如AspectJ
MessageSourceAware 主要用于获取国际化相关接口
NotificationPublisherAware 用于获取通知发布者
ResourceLoaderAware 初始化时注入ResourceLoader
ServletConfigAware web开发过程中获取ServletConfig
ServletContextAware web开发过程中获取ServletContext信息
EnvironmentAware 获取当前的应用的环境,从而用来获取配置信息、当前profile等信息
EmbeddedValueResolverAware 获取StringValueResolver,用来解析 ${app.test} 的字符串值。

InitializingBean接口和DisposableBean接口

  1. InitializingBean接口只有一个方法#afterPropertiesSet:当一个Bean实现InitializingBean#afterPropertiesSet方法里面可以添加自定义的初始化方法或者做一些资源初始化操作。当BeanFactory设置完所有的Bean属性之后才会调用#afterPropertiesSet方法

  2. DisposableBean接口只有一个方法#destroy:当一个单例Bean实现DisposableBean#destroy可以添加自定义的一些销毁方法或者资源释放操作。

使用示例:

1
2
3
4
5
6
7
8
9
10
11
@Component
public class ConcreteBean implements InitializingBean,DisposableBean {
@Override
public void destroy() throws Exception {
System.out.println("释放资源");
}
@Override
public void afterPropertiesSet() throws Exception {
System.out.println("初始化资源");
}
}

ImportBeanDefinitionRegistrar接口

  1. 当处理Java编程式配置类(使用了@Configuration的类)的时候,ImportBeanDefinitionRegistrar接口的实现类可以注册额外的bean definitions

  2. ImportBeanDefinitionRegistrar接口的实现类必须提供给@Import注解或者是ImportSelector接口返回值。

  3. ImportBeanDefinitionRegistrar接口的实现类可能还会实现下面org.springframework.beans.factory.Aware接口中的一个或者多个,它们各自的方法优先于ImportBeanDefinitionRegistrar#registerBeanDefinitions被调用。

    • org.springframework.context.EnvironmentAware(读取或者修改Environment的变量)
    • org.springframework.beans.factory.BeanFactoryAware (获取Bean自身的Bean工厂)
    • org.springframework.beans.factory.BeanClassLoaderAware(获取Bean自身的类加载器)
    • org.springframework.context.ResourceLoaderAware(获取Bean自身的资源加载器)

BeanPostProcessor接口和BeanFactoryPostProcessor接口

一般我们叫这两个接口为Spring的Bean后置处理器接口,作用是为Bean的各个处理操作前后提供可扩展的空间

BeanFactoryPostProcessor

1
2
3
public interface BeanFactoryPostProcessor {
void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException;
}

在标准初始化之后修改应用程序上下文的内部bean工厂。 此时所有bean定义都已经加载完成,但尚未实例化任何bean。 这允许覆盖或添加属性,甚至是初始化bean

BeanPostProcessor

BeanPostProcessor接口是最顶层的接口,接口定义:

1
2
3
4
5
6
public interface BeanPostProcessor {
// 初始化之前执行
Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException;
// 初始化之后执行
Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException;
}
  1. postProcessBeforeInitialization是指bean在初始化之前需要调用的方法
  2. postProcessAfterInitialization是指bean在初始化之后需要调用的方法
  3. postProcessBeforeInitializationpostProcessAfterInitialization方法被调用的时候。这个时候bean已经被实例化,并且所有该注入的属性都已经被注入,是一个完整的bean。

InstantiationAwareBeanPostProcessor

InstantiationAwareBeanPostProcessor接口继承自BeanPostProcessor接口。多出了3个方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public interface InstantiationAwareBeanPostProcessor extends BeanPostProcessor {
// 在目标 bean 被实例化之前应用这个 BeanPostProcessor。 返回的 bean 对象可能是一个代理来代替目标 bean,有效地抑制目标 bean 的默认实例化。
default Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) throws BeansException {
return null;
}

// 在 bean 被实例化之后,通过构造函数或工厂方法,但在 Spring 属性填充(从显式属性或自动装配)发生之前执行操作。
default boolean postProcessAfterInstantiation(Object bean, String beanName) throws BeansException {
return true;
}

// 在工厂将给定的属性值应用于给定的 bean 之前对给定的属性值进行后处理,无需任何属性描述符。
default PropertyValues postProcessProperties(PropertyValues pvs, Object bean, String beanName)
throws BeansException {

return null;
}
}
  1. postProcessBeforeInstantiation方法是最先执行的方法,它在目标对象实例化之前调用,该方法的返回值类型是Object,我们可以返回任何类型的值。由于这个时候目标对象还未实例化,所以这个返回值可以用来代替原本该生成的目标对象的实例(比如代理对象)。如果该方法的返回值代替原本该生成的目标对象,后续只有postProcessAfterInitialization方法会调用,其它方法不再调用。
  2. postProcessAfterInstantiation方法在目标对象实例化之后调用,这个时候对象已经被实例化,但是该实例的属性还未被设置,都是null。如果该方法返回false,会忽略属性值的设置;如果返回true,会按照正常流程设置属性值
  3. postProcessProperties方法对属性值进行修改(这个时候属性值还未被设置,但是我们可以修改原本该设置进去的属性值)。如果postProcessAfterInstantiation方法返回false,该方法不会被调用。可以在该方法内对属性值进行修改。

SmartInstantiationAwareBeanPostProcessor

SmartInstantiationAwareBeanPostProcessor接口继承InstantiationAwareBeanPostProcessor接口。多出了3个方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public interface SmartInstantiationAwareBeanPostProcessor extends InstantiationAwareBeanPostProcessor {

// 预测最终从此处理器的 postProcessBeforeInstantiation 回调返回的 bean 的类型。
@Nullable
default Class<?> predictBeanType(Class<?> beanClass, String beanName) throws BeansException {
return null;
}

// 确定用于给定 bean 的候选构造函数。
@Nullable
default Constructor<?>[] determineCandidateConstructors(Class<?> beanClass, String beanName)
throws BeansException {

return null;
}

// 获取对指定 bean 的早期访问的引用,通常用于解析循环引用。
default Object getEarlyBeanReference(Object bean, String beanName) throws BeansException {
return bean;
}

}
  1. predictBeanType方法用于预测Bean的类型,返回第一个预测成功的Class类型,如果不能预测返回null。主要在于BeanDefinition无法确定Bean类型的时候调用该方法来确定类型。
  2. determineCandidateConstructors方法用于选择合适的构造器,比如类有多个构造器,可以实现这个方法选择合适的构造器并用于实例化对象。
  3. getEarlyBeanReference主要用于解决循环引用问题。只有单例对象才会调用此方法。

DestructionAwareBeanPostProcessor

DestructionAwareBeanPostProcessor接口继承BeanPostProcessor接口。多出了2个方法:

1
2
3
4
5
6
7
8
9
10
11
public interface DestructionAwareBeanPostProcessor extends BeanPostProcessor {

// 在销毁之前将此 BeanPostProcessor 应用于给定的 bean 实例,例如 调用自定义销毁回调。
void postProcessBeforeDestruction(Object bean, String beanName) throws BeansException;

// 确定给定的 bean 实例是否需要此后处理器销毁。
default boolean requiresDestruction(Object bean) {
return true;
}

}

MergedBeanDefinitionPostProcessor

DestructionAwareBeanPostProcessor接口继承BeanPostProcessor接口。多出了2个方法:

1
2
3
4
5
6
7
8
9
10
public interface MergedBeanDefinitionPostProcessor extends BeanPostProcessor {

// 对指定 bean 的给定合并 bean 定义进行后处理。
void postProcessMergedBeanDefinition(RootBeanDefinition beanDefinition, Class<?> beanType, String beanName);

// 指定名称的 bean 定义已重置的通知,并且此后处理器应清除受影响 bean 的所有元数据。
default void resetBeanDefinition(String beanName) {
}

}

BeanDefinitionRegistryPostProcessor

BeanDefinitionRegistryPostProcessor 接口可以看作是BeanFactoryPostProcessorImportBeanDefinitionRegistrar的功能集合,既可以获取和修改BeanDefinition的元数据,也可以实现BeanDefinition的注册、移除等操作

1
2
3
4
public interface BeanDefinitionRegistryPostProcessor extends BeanFactoryPostProcessor {
// 在标准初始化之后修改应用程序上下文的内部 bean 定义注册表。 所有常规 bean 定义都将被加载,但尚未实例化任何 bean。 这允许在下一个后处理阶段开始之前添加更多的 bean 定义。
void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException;
}

FactoryBean接口

首先第一眼要注意,是FactoryBean接口而不是BeanFactory接口。一般情况下,Spring通过反射机制利用beanclass属性指定实现类来实例化bean ,实例化bean过程比较复杂。FactoryBean接口就是为了简化此过程,把bean的实例化定制逻辑下发给使用者

在该接口中还定义了以下3个方法。

  1. T getObject():返回由FactoryBean创建的bean实例,如果isSingleton()``返回true,则该实例会放到Spring容器中单实例缓存池中。
  2. boolean isSingleton():返回由FactoryBean创建的bean实例的作用域是singleton还是prototype
  3. Class<T> getObjectType():返回FactoryBean创建的bean类型。

注意一点:通过Spring容器的getBean()方法返回的不是FactoryBean本身,而是FactoryBean#getObject()方法所返回的对象,相当于FactoryBean#getObject()代理了getBean()方法。如果希望获取FactoryBean的实例,则需要在使用getBean(beanName) 方法时在beanName前显示的加上 “&” 前缀。

ApplicationListener

ApplicationListener是一个接口,里面只有一个onApplicationEvent(E event)方法,这个泛型E必须是ApplicationEvent的子类,而ApplicationEvent是Spring定义的事件,继承于EventObject,构造要求必须传入一个Object类型的source,这个source可以作为一个存储对象。将会在ApplicationListeneronApplicationEvent里面得到回调。如果在上下文中部署一个实现了ApplicationListener接口的bean,那么每当在一个ApplicationEvent发布到 ApplicationContext时,这个bean得到通知。其实这就是标准的Oberver设计模式。另外,ApplicationEvent的发布由ApplicationContext通过#publishEvent方法完成。其实这个实现从原理和代码上看都有点像Guavaeventbus

贴一个例子:
EmailEvent:

1
2
3
4
5
6
@Data
public class EmailEvent extends ApplicationEvent {
private String author;
private String content;
private String date;
}

EmailApplicationListener:

1
2
3
4
5
6
7
8
9
10
11
@Component
public class EmailApplicationListener implements ApplicationListener<EmailEvent> {
@Override
public void onApplicationEvent(EmailEvent event) {
System.out.println("EmailApplicationListener callback!!");
System.out.println("EmailEvent --> source: " + event.getSource());
System.out.println("EmailEvent --> author: " + event.getAuthor());
System.out.println("EmailEvent --> content: " + event.getContent());
System.out.println("EmailEvent --> date: " + event.getDate());
}
}

测试类:

1
2
3
4
5
6
7
8
9
10
11
@SpringBootTest(classes = Application.class)
@RunWith(SpringJUnit4ClassRunner.class)
public class EmailApplicationListenerTest {
@Autowired
private ApplicationContext applicationContext;
@Test
public void onApplicationEvent() throws Exception {
applicationContext.publishEvent(new EmailEvent("this is source",
"throwable","here is emailEvent","2017-5-16"));
}
}

原创不易,觉得文章写得不错的小伙伴,点个赞👍 鼓励一下吧~

欢迎关注我的开源项目:一款适用于SpringBoot的轻量级HTTP调用框架