[toc]java
Spring 提供了很是多的擴展接口,官方將這些接口稱之爲鉤子,這些鉤子會在特定的時間被回調,以此來加強 Spring 功能,衆多優秀的框架也是經過擴展這些接口,來實現自身特定的功能,如 SpringBoot、mybatis 等。web
Aware 從字面意思理解就是「知道」、「感知」的意思,是用來獲取 Spring 內部對象的接口。Aware 自身是一個頂級接口,它有一系列子接口,在一個 Bean 中實現這些子接口並重寫裏面的 set 方法後,Spring 容器啓動時,就會回調該 set 方法,而相應的對象會經過方法參數傳遞進去。咱們以其中的 ApplicationContextAware 接口爲例。spring
ApplicationContextAware設計模式
大部分 Aware 系列接口都有一個規律,它們以對象名稱爲前綴,獲取的就是該對象,因此 ApplicationContextAware 獲取的對象是 ApplicationContext 。數組
public interface ApplicationContextAware extends Aware { void setApplicationContext(ApplicationContext applicationContext) throws BeansException; }
ApplicationContextAware 源碼很是簡單,其繼承了 Aware 接口,並定義一個 set 方法,參數就是 ApplicationContext 對象,固然,其它系列的 Aware 接口也是相似的定義。其具體使用方式以下:tomcat
public class Test implements ApplicationContextAware { private ApplicationContext applicationContext; @Override public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { this.applicationContext = applicationContext; } }
在 Spring 啓動過程當中,會回調 setApplicationContext 方法,並傳入 ApplicationContext 對象,以後就可對該對象進行操做。其它系列的 Aware 接口也是如此使用。具體的調用時機會在後面詳細介紹。springboot
如下是幾種經常使用的 Aware 接口:mybatis
InitializingBean 是一個能夠在 Bean 的生命週期執行自定義操做的接口,凡是實現該接口的 Bean,在初始化階段均可以執行自定義的操做。app
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 方法被執行並打印輸出語句。
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 內部也正是這樣使用的,這部分也會在後面章節詳細討論。
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); } } }
主要是經過 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 的屬性等。
ImportSelector 是一個較爲重要的擴展接口,經過該接口可動態的返回須要被容器管理的類,不過通常用來返回外部的配置類。可在標註 @Configuration 註解的類中,經過 @Import 導入 ImportSelector 來使用。
public interface ImportSelector { // 方法入參是註解的元數據對象,返回值是類的全路徑名數組 String[] selectImports(AnnotationMetadata importingClassMetadata); }
selectImports 方法返回的是類的全路徑名。
自定義 ImportSelector:
public class TestImportSelector implements ImportSelector { @Override public String[] selectImports(AnnotationMetadata importingClassMetadata) { if (importingClassMetadata.hasAnnotation("")) { // 判斷是否包含某個註解 } // 返回 Test 的全路徑名,Test 會被放入到 Spring 容器中 return new String[]{"com.loong.diveinspringboot.test.Test"}; } }
selectImports 方法中能夠針對經過 AnnotationMetadata 對象進行邏輯判斷,AnnotationMetadata 存儲的是註解元數據信息,根據這些信息能夠動態的返回須要被容器管理的類名稱。
定義的 Test 類:
public class Test { public void hello() { System.out.println("Test -- hello"); } }
這裏,咱們沒有對 Test 標註 @Component 註解,因此,Test 不會自動加入到 Spring 容器中。
@SpringBootApplication @Import(TestImportSelector.class) public class Main { public static void main(String[] args) { SpringApplication springApplication = new SpringApplication(); ConfigurableApplicationContext run = springApplication.run(Main.class); Test bean = run.getBean(Test.class); bean.hello(); } }
以後經過 @Import 導入自定義的 TestImportSelector ,前面也說過,@Import 通常配合 @Configuration 使用,而 @SpringBootApplication 中包含了 @Configuration 註解。以後,經過 getBean 方法從容器中獲取 Test 對象,並調用 hello 方法。
2020-02-26 08:01:41.712 INFO 29546 --- [ main] o.s.j.e.a.AnnotationMBeanExporter : Registering beans for JMX exposure on startup 2020-02-26 08:01:41.769 INFO 29546 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat started on port(s): 8080 (http) with context path '' 2020-02-26 08:01:41.773 INFO 29546 --- [ main] com.loong.diveinspringboot.test.Main : Started Main in 4.052 seconds (JVM running for 4.534) Test -- hello
最終,結果正確輸出。
該接口和 ImportSelector 相似,也是配合 @Import 使用,不過 ImportBeanDefinitionRegistrar 更爲直接一點,它能夠直接把 Bean 註冊到容器中。
public interface ImportBeanDefinitionRegistrar { public void registerBeanDefinitions( AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry); }
入參除了註解元數據對象 AnnotationMetadata 外,還多了一個 BeanDefinitionRegistry 對象,在前面的文章講過,該對象定義了關於 BeanDefinition 的一系列的操做,如:註冊、移除、查詢等。
自定義 ImportBeanDefinitionRegistrar:
public class TestRegistrar implements ImportBeanDefinitionRegistrar { // 通常經過 AnnotationMetadata 進行業務判斷,而後經過 BeanDefinitionRegistry 直接註冊 Bean @Override public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) { RootBeanDefinition beanDefinition = new RootBeanDefinition(Test.class); beanDefinition.setLazyInit(true); registry.registerBeanDefinition(Test.class.getName(), beanDefinition); } }
這裏,主要經過 BeanDefinitionRegistry 手動註冊 Test 類的 BeanDefinition,並設置懶加載屬性。
ImportSelector 和 ImportBeanDefinitionRegistrar 是實現 @Enable 模式註解的核心接口,而 @Enable 模式註解在 Spring、SpringBoot、SpringCloud 中被大量使用,其依靠這些註解來實現各類功能及特性,是較爲重要的擴展接口,咱們會在後面的文章中反覆討論,包括 ImportSelector 和 ImportBeanDefinitionRegistrar 是如何被 Spring 調用的、以及一些重要的 @Enable 註解實現。
值得注意的是,SpringBoot 外部化配置、自動裝配特性就是經過 @Enable 註解配合 ImportSelector 和 ImportBeanDefinitionRegistrar 接口來實現的,這部分在前面的 SpringBoot 系列的文章中已經討論過,感興趣的同窗可自行翻閱。
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 以前加上&。
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 進行擴展,來實現自身特定的事件機制。這部分也在前面的文章討論過,感興趣的同窗可自行翻閱。
Spring 的鉤子接口就介紹到這,值得注意的是,Spring 的許多核心功能也是經過其內置的鉤子接口來實現的,特別是一些核心註解,如:@Component 和 @Bean 的實現,這些都會在後面的文章一一討論。
<br/><br/><br/>
以上就是本章內容,若是文章中有錯誤或者須要補充的請及時提出,本人感激涕零。