Spring钩子接口汇总分析使用示例

网友投稿 160 2024-02-01


Spring钩子接口汇总分析使用示例

1、Aware 系列接口

Aware 系列接口是用来获取 Spring 内部对象的接口。Aware 自身是一个顶级接口,它有一系列子接口,在一个 Bean 中实现这些子接口并重写里面的 set 方法后,Spring 容器启动时,就会回调该 set 方法,而相应的对象会通过方法参数传递进去。我们以其中的 ApplicationContextAware 接口为例。

ApplicationContextAware

大部分 Aware 系列接口都有一个规律,它们以对象名称为前缀,获取的就是该对象,所以 ApplicationContextAware 获取的对象是 ApplicationContext 。

public interface ApplicationContextAware extends Aware { void setApplicationContext(ApplicationContext applicationContext) throws BeansException; }

ApplicationContextAware 继承了 Aware 接口,并定义一个 set 方法,参数就是 ApplicationContext 对象,当然,其它系列的 Aware 接口也是类似的定义。其具体使用方式如下:

public class Test implements ApplicationContextAware { private ApplicationContext applicationContext; @Override public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { this.applicationContext = applicationContext; } }

在 Spring 启动过程中,会回调 setApplicationContext 方法,并传入 ApplicationContext 对象,之后就可对该对象进行操作。其它系列的 Aware 接口也是如此使用。具体的调用时机会在后面详细介绍。

以下是几种常用的 Aware 接口:

BeanFactoryAware:获取 BeanFactory 对象,它是基础的容器接口。BeanNameAware:获取 Bean 的名称。EnvironmentAware:获取 Environment 对象,它表示整个的运行时环境,可以设置和获取配置属性。ApplicationEventPublisherAware:获取 ApplicationEventPublisher 对象,它是用来发布事件的。ResourceLoaderAware:获取 ResourceLoader 对象,它是获取资源的工具。

2、InitializingBean

InitializingBean 是一个可以在 Bean 的生命周期执行自定义操作的接口,凡是实现该接口的 Bean,在初始化阶段都可以执行自定义的操作。

public interface InitializingBean { void afterPropertiesSet() throws Exception; }

从 InitializingBean 源码中可以看出它有一个 afterPropertiesSet 方法,当一个 Bean 实现该接口时,在 Bean 的初始化阶段,会回调 afterPropertiesSet 方法,其初始化阶段具体指 Bean 设置完属性之后。

该接口使用方式如下:

@Component public class Test implements InitializingBean { @Override public void afterPropertiesSet() throws Exception { System.out.println("Test 执行初始化"); } }

定义启动类:

@SpringBootApplication public class Main { public static void main(String[] args) { SpringApplication.run(Main.class, args); } }

结果:

...

2020-02-24 08:43:41.435 INFO 26193 --- [ost-startStop-1] o.s.b.w.servlet.FilterRegistrationBean : Mapping filter: httpTraceFilter to: [/*]

2020-02-24 08:43:41.435 INFO 26193 --- [ost-startStop-1] o.s.b.w.servlet.FilterRegistrationBean : Mapping filter: webMvcMetricsFilter to: [/*]

Test 执行初始化

2020-02-24 08:43:41.577 INFO 26193 --- [ main] o.s.w.s.handler.SimpleUrlHandlerMapping : Mapped URL path [/**/favicon.ico] onto handler of type [class org.springframework.web.servlet.resource.ResourceHttpRequestHandler]

2020-02-24 08:43:41.756 INFO 26193 --- [ main] s.w.s.m.m.a.RequestMappingHandlerAdapter : Looking for @ControllerAdvice: org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext@23529fee: startup date [Mon Feb 24 08:43:39 CST 2020]; root of context hierarchy

...

最终,afterPropertiesSet 方法被执行并打印输出语句。

3、BeanPostProcessor

BeanPostProcessor 和 InitializingBean 有点类似,也是可以在 Bean 的生命周期执行自定义操作,一般称之为 Bean 的后置处理器,不同的是,BeanPostProcessor 可以在 Bean 初始化前、后执行自定义操作,且针对的目标也不同,InitializingBean 针对的是实现 InitializingBean 接口的 Bean,而 BeanPostProcessor 针对的是所有的 Bean。

public interface BeanPostProcessor { // Bean 初始化前调用 default Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { return bean; } // Bean 初始化后调用 default Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { return bean; } }

所有的 Bean 在初始化前、后都会回调接口中的 postProcessBeforeInitialization 和 postProcessAfterInitialization 方法,入参是当前正在初始化的 Bean 对象和 BeanName。值得注意的是 Spring 内置了非常多的 BeanPostProcessor ,以此来完善自身功能,这部分会在后面文章深入讨论。

这里通过自定义 BeanPostProcessor 来了解该接口的使用方式:

// 一般自定义的 BeanPostProcessor 命名格式都是以 BeanPostProcessor 为后缀。 @Component public class TestBeanPostProcessor implements BeanPostProcessor { @Override public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { System.out.println(beanName + " 初始化前执行操作"); return bean; } @Override public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { System.out.println(beanName + " 初始化后执行操作"); return bean; } }

启动类:

@SpringBootApplication public class Main { public static void main(String[] args) { SpringApplication.run(Main.class); } }

结果:

...

2020-02-24 23:37:08.949 INFO 26615 --- [ main] com.loong.diveinspringboot.test.Main : No active profile set, falling back to default profiles: default

2020-02-24 23:37:08.994 INFO 26615 --- [ main] ConfigServletWebServerApplicationContext : Refreshing org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext@2133814f: startup date [Mon Feb 24 23:37:08 CST 2020]; root of context hierarchy

2020-02-24 23:37:09.890 INFO 26615 --- [ main] .s.d.r.c.RepositoryConfigurationDelegate : Multiple Spring Data modules found, entering strict repository configuration mode!

org.springframework.context.event.internalEventListenerProcessor 初始化前执行操作

org.springframework.context.event.internalEventListenerProcessor 初始化后执行操作

org.springframework.context.event.internalEventListenerFactory 初始化前执行操作

org.springframework.context.event.internalEventListenerFactory 初始化后执行操作

main 初始化前执行操作

main 初始化后执行操作

test 初始化前执行操作

Test 执行初始化

test 初始化后执行操作

...

org.springframework.boot.autoconfigure.context.PropertyPlaceholderAutoConfiguration初始化前执行操作

2020-02-24 23:37:13.097 INFO 26615 --- [ main] o.s.j.e.a.AnnotationMBeanExporter : Registering beans for JMX exposure on startup

2020-02-24 23:37:13.195 INFO 26615 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat started on port(s): 8080 (http) with context path

2020-02-24 23:37:13.207 INFO 26615 --- [ main] com.loong.diveinspringboot.test.Main : Started Main in 4.657 seconds (JVM running for 5.078)

...

可以看到,输出的结果中不仅包括自定义的 Test,还包括 Spring 内部的 Bean 。

BeanPostProcessor 使用场景其实非常多,因为它可以获取正在初始化的 Bean 对象,然后可以依据该 Bean 对象做一些定制化的操作,如:判断该 Bean 是否为某个特定对象、获取 Bean 的注解元数据等。事实上,Spring 内部也正是这样使用的,这部分也会在后面章节详细讨论。

4、BeanFactoryPostProcessor

BeanFactoryPostProcessor 是 Bean 工厂的后置处理器,一般用来修改上下文中的 BeanDefinition,修改 Bean 的属性值。

public interface BeanFactoryPostProcessor { // 入参是一个 Bean 工厂:ConfigurableListableBeanFactory。该方法执行时,所有 BeanDefinition 都已被加载,但还未实例化 Bean。 // 可以对其进行覆盖或添加属性,甚至可以用于初始化 Bean。 void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException; }

BeanFactoryPostProcessor 提供了一个 postProcessBeanFactory 方法,当所有的 BeanDefinition 被加载时,该方法会被回调。值得注意的是,Spring 内置了许多 BeanFactoryPostProcessor 的实现,以此来完善自身功能。

这里,我们来实现一个自定义的 BeanFactoryPostProcessor:

@Component public class TestBeanFactoryPostProcessor implements BeanFactoryPostProcessor { @Override public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException { String beanNames[] = beanFactory.getBeanDefinitionNames(); for (String beanName : beanNames) { BeanDefinition beanDefinition = beanFactory.getBeanDefinition(beanName); System.out.println(beanDefinition); } } }

接着启动程序:

@SpringBootApplication public class Main { public static void main(String[] args) { SpringApplication.run(Main.class); } }

结果:

2020-02-25 21:46:00.754 INFO 28907 --- [ main] ConfigServletWebServerApplicationContext : ...

2020-02-25 21:46:01.815 INFO 28907 --- [ main] .s.d.r.c.RepositoryConfigurationDelegate : ...

Root bean: class [org.springframework.context.annotation.ConfigurationClassPostProcessor]; scope=; abstract=false; lazyInit=false; autowireMode=0; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=null; factoryMethodName=null; initMethodName=null; destroyMethodName=null

Root bean: class [org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor]; scope=; abstract=false; lazyInit=false; autowireMode=0; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=null; factoryMethodName=null; initMethodName=null; destroyMethodName=null

...

2020-02-25 21:46:04.926 INFO 28907 --- [ main] o.s.j.e.a.AnnotationMBeanExporter : ...

2020-02-25 21:46:04.989 INFO 28907 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : ...

2020-02-25 21:46:04.993 INFO 28907 --- [ main] com.loong.diveinspringboot.test.Main : ...

可以看到,BeanDefinition 正确输出,里面是一些 Bean 的相关定义,如:是否懒加载、Bean 的 Class 以及 Bean 的属性等。

5、FactoryBean

FactoryBean 也是一种 Bean,不同于普通的 Bean,它是用来创建 Bean 实例的,属于工厂 Bean,不过它和普通的创建不同,它提供了更为灵活的方式,其实现有点类似于设计模式中的工厂模式和修饰器模式。

Spring 框架内置了许多 FactoryBean 的实现,它们在很多应用如(Spring的AOP、ORM、事务管理)及与其它第三框架(ehCache)集成时都有体现。

public interface FactoryBean<T> { // 该方法会返回该 FactoryBean “生产”的对象实例,我们需要实现该方法以给出自己的对象实例化逻辑 T getObject() throws Exception; // Bean的类型 Class<?> getObjectType(); // 是否是单例 default boolean isSingleton() { return true; } }

自定义 FactoryBean:

@Component public class TestFactoryBean implements FactoryBean<Test> { @Override public Test getObject() throws Exception { // 这里可以灵活的创建 Bean,如:代理、修饰 return new Test(); } @Override public Class<?> getObjectType() { return null; } }

Test 类:

public class Test { public void hello() { System.out.println("Test -- hello"); } }

启动类:

@SpringBootApplication public class Main { public static void main(String[] args) { SpringApplication springApplication = new SpringApplication(); ConfigurableApplicationContext run = springApplication.run(Main.class); Test bean = (Test) run.getBean("testFactoryBean"); bean.hello(); } }

输出:

2020-02-27 23:16:00.334 INFO 32234 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat started on port(s): 8080 (http) with context path

2020-02-27 23:16:00.338 INFO 32234 --- [ main] com.loong.diveinspringboot.test.Main : Started Main in 3.782 seconds (JVM running for 4.187)

Test -- hello

可以看到,启动类中 getBean 的参数是 testFactoryBean ,从这可以看出,当容器中的 Bean 实现了 FactoryBean 后,通过 getBean(String BeanName) 获取到的 Bean 对象并不是 FactoryBean 的实现类对象,而是这个实现类中的 getObject() 方法返回的对象。如果想获取 FactoryBean 的实现类,需通过这种方式:getBean(&BeanName),在 BeanName 之前加上&。

6、ApplicationListener

ApplicationListener 是 Spring 实现事件机制的核心接口,属于观察者设计模式,一般配合 ApplicationEvent 使用。在 Spring 容器启动过程中,会在相应的阶段通过 ApplicationContext 发布 ApplicationEvent 事件,之后所有的 ApplicationListener 会被回调,根据事件类型,执行不同的操作。

public interface ApplicationListener<E extends ApplicationEvent> extends EventListener { void onApplicationEvent(E event); }

在 onApplicationEvent 方法中,通过 instanceof 判断 event 的事件类型。

自定义 ApplicationListener:

@Component public class TestApplicationListener implements ApplicationListener { @Override public void onApplicationEvent(ApplicationEvent event) { if (event instanceof TestApplicationEvent) { TestApplicationEvent testApplicationEvent = (TestApplicationEvent) event; System.out.println(testApplicationEvent.getName()); } } }

当自定义的 TestApplicationListener 被回调时,判断当前发布的事件类型是否是自定义的 TestApplicationEvent,如果是则输出事件名称。

自定义 TestApplicationEvent:

public class TestApplicationEvent extends ApplicationEvent { private String name; public TestApplicationEvent(Object source, String name) { super(source); this.name = name; } public String getName() { return name; } public void setName(String name) { this.name = name; } }

启动类:

@SpringBootApplication public class Main { public static void main(String[] args) { SpringApplication springApplication = new SpringApplication(); ConfigurableApplicationContext run = springApplication.run(Main.class); run.publishEvent(new TestApplicationEvent(new Main(),"Test 事件")); } }

通过 ApplicationContext 发布 TestApplicationEvent 事件。当然也可以在业务代码中通过 ApplicationContextAware 获取 ApplicationContext 发布事件。

结果:

2020-02-27 08:37:10.972 INFO 30984 --- [ main] o.s.j.e.a.AnnotationMBeanExporter : Registering beans for JMX exposure on startup

2020-02-27 08:37:11.026 INFO 30984 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat started on port(s): 8080 (http) with context path

2020-02-27 08:37:11.029 INFO 30984 --- [ main] com.loong.diveinspringboot.test.Main : Started Main in 3.922 seconds (JVM running for 4.367)

Test 事件

ApplicationListener 也被 SpringBoot 进行扩展,来实现自身特定的事件机制。这部分也在前面的文章讨论过,感兴趣的同学可自行翻阅。

关于Sp

Java编程技术

前言这周学习下Flink相关的知识,学习到一个读写Kafka消息的示例, 自己动手实践了一下,别人示例使用的是普通的Java Main方法,没有用到spring bo ...


版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。

上一篇:如何实现controller接口跳转到另一个controller接口
下一篇:C# 使用接口进行排序
相关文章

 发表评论

暂时没有评论,来抢沙发吧~