Spring鉤子接口匯總分析使用示例
1、Aware 系列接口
Aware 系列接口是用來獲取 Spring 內部對象的接口。Aware 自身是一個頂級接口,它有一系列子接口,在一個 Bean 中實現(xiàn)這些子接口并重寫里面的 set 方法后,Spring 容器啟動時,就會回調該 set 方法,而相應的對象會通過方法參數(shù)傳遞進去。我們以其中的 ApplicationContextAware 接口為例。
ApplicationContextAware
大部分 Aware 系列接口都有一個規(guī)律,它們以對象名稱為前綴,獲取的就是該對象,所以 ApplicationContextAware 獲取的對象是 ApplicationContext 。
public interface ApplicationContextAware extends Aware { void setApplicationContext(ApplicationContext applicationContext) throws BeansException; }
ApplicationContextAware 繼承了 Aware 接口,并定義一個 set 方法,參數(shù)就是 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 對象,它表示整個的運行時環(huán)境,可以設置和獲取配置屬性。
- ApplicationEventPublisherAware:獲取 ApplicationEventPublisher 對象,它是用來發(fā)布事件的。
- ResourceLoaderAware:獲取 ResourceLoader 對象,它是獲取資源的工具。
2、InitializingBean
InitializingBean 是一個可以在 Bean 的生命周期執(zhí)行自定義操作的接口,凡是實現(xiàn)該接口的 Bean,在初始化階段都可以執(zhí)行自定義的操作。
public interface InitializingBean { void afterPropertiesSet() throws Exception; }
從 InitializingBean 源碼中可以看出它有一個 afterPropertiesSet 方法,當一個 Bean 實現(xiàn)該接口時,在 Bean 的初始化階段,會回調 afterPropertiesSet 方法,其初始化階段具體指 Bean 設置完屬性之后。
該接口使用方式如下:
@Component public class Test implements InitializingBean { @Override public void afterPropertiesSet() throws Exception { System.out.println("Test 執(zhí)行初始化"); } }
定義啟動類:
@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 執(zhí)行初始化
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 方法被執(zhí)行并打印輸出語句。
3、BeanPostProcessor
BeanPostProcessor 和 InitializingBean 有點類似,也是可以在 Bean 的生命周期執(zhí)行自定義操作,一般稱之為 Bean 的后置處理器,不同的是,BeanPostProcessor 可以在 Bean 初始化前、后執(zhí)行自定義操作,且針對的目標也不同,InitializingBean 針對的是實現(xiàn) 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 方法,入?yún)⑹钱斍罢诔跏蓟?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 + " 初始化前執(zhí)行操作"); return bean; } @Override public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { System.out.println(beanName + " 初始化后執(zhí)行操作"); 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 初始化前執(zhí)行操作
org.springframework.context.event.internalEventListenerProcessor 初始化后執(zhí)行操作
org.springframework.context.event.internalEventListenerFactory 初始化前執(zhí)行操作
org.springframework.context.event.internalEventListenerFactory 初始化后執(zhí)行操作
main 初始化前執(zhí)行操作
main 初始化后執(zhí)行操作
test 初始化前執(zhí)行操作
Test 執(zhí)行初始化
test 初始化后執(zhí)行操作
...
org.springframework.boot.autoconfigure.context.PropertyPlaceholderAutoConfiguration初始化前執(zhí)行操作
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 對象,然后可以依據(jù)該 Bean 對象做一些定制化的操作,如:判斷該 Bean 是否為某個特定對象、獲取 Bean 的注解元數(shù)據(jù)等。事實上,Spring 內部也正是這樣使用的,這部分也會在后面章節(jié)詳細討論。
4、BeanFactoryPostProcessor
BeanFactoryPostProcessor 是 Bean 工廠的后置處理器,一般用來修改上下文中的 BeanDefinition,修改 Bean 的屬性值。
public interface BeanFactoryPostProcessor { // 入?yún)⑹且粋€ Bean 工廠:ConfigurableListableBeanFactory。該方法執(zhí)行時,所有 BeanDefinition 都已被加載,但還未實例化 Bean。 // 可以對其進行覆蓋或添加屬性,甚至可以用于初始化 Bean。 void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException; }
BeanFactoryPostProcessor 提供了一個 postProcessBeanFactory 方法,當所有的 BeanDefinition 被加載時,該方法會被回調。值得注意的是,Spring 內置了許多 BeanFactoryPostProcessor 的實現(xiàn),以此來完善自身功能。
這里,我們來實現(xiàn)一個自定義的 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); } } }
主要是通過 Bean 工廠獲取所有的 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,它是用來創(chuàng)建 Bean 實例的,屬于工廠 Bean,不過它和普通的創(chuàng)建不同,它提供了更為靈活的方式,其實現(xiàn)有點類似于設計模式中的工廠模式和修飾器模式。
Spring 框架內置了許多 FactoryBean 的實現(xiàn),它們在很多應用如(Spring的AOP、ORM、事務管理)及與其它第三框架(ehCache)集成時都有體現(xiàn)。
public interface FactoryBean<T> { // 該方法會返回該 FactoryBean “生產”的對象實例,我們需要實現(xiàn)該方法以給出自己的對象實例化邏輯 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 { // 這里可以靈活的創(chuàng)建 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 的參數(shù)是 testFactoryBean ,從這可以看出,當容器中的 Bean 實現(xiàn)了 FactoryBean 后,通過 getBean(String BeanName) 獲取到的 Bean 對象并不是 FactoryBean 的實現(xiàn)類對象,而是這個實現(xiàn)類中的 getObject() 方法返回的對象。如果想獲取 FactoryBean 的實現(xiàn)類,需通過這種方式:getBean(&BeanName),在 BeanName 之前加上&。
6、ApplicationListener
ApplicationListener 是 Spring 實現(xiàn)事件機制的核心接口,屬于觀察者設計模式,一般配合 ApplicationEvent 使用。在 Spring 容器啟動過程中,會在相應的階段通過 ApplicationContext 發(fā)布 ApplicationEvent 事件,之后所有的 ApplicationListener 會被回調,根據(jù)事件類型,執(zhí)行不同的操作。
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 被回調時,判斷當前發(fā)布的事件類型是否是自定義的 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 發(fā)布 TestApplicationEvent 事件。當然也可以在業(yè)務代碼中通過 ApplicationContextAware 獲取 ApplicationContext 發(fā)布事件。
結果:
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 進行擴展,來實現(xiàn)自身特定的事件機制。這部分也在前面的文章討論過,感興趣的同學可自行翻閱。
到此這篇關于Spring鉤子接口匯總分析使用示例的文章就介紹到這了,更多相關Spring鉤子接口內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
Java實現(xiàn)DFA算法對敏感詞、廣告詞過濾功能示例
本篇文章主要介紹了Java實現(xiàn)DFA算法對敏感詞、廣告詞過濾功能示例,具有一定的參考價值,感興趣的小伙伴們可以參考一下2017-11-11使用注解@Validated和BindingResult對入?yún)⑦M行非空校驗方式
這篇文章主要介紹了使用注解@Validated和BindingResult對入?yún)⑦M行非空校驗方式,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2021-10-10SpringBoot整合GitLab-CI實現(xiàn)持續(xù)集成的過程
這篇文章主要介紹了SpringBoot整合GitLab-CI實現(xiàn)持續(xù)集成,本文詳細講述了 GitLab-CI 持續(xù)集成的安裝、部署、以及配置,需要的朋友可以參考下2022-12-12springmvc后臺基于@ModelAttribute獲取表單提交的數(shù)據(jù)
這篇文章主要介紹了springmvc后臺基于@ModelAttribute獲取表單提交的數(shù)據(jù),文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友可以參考下2019-10-10