spring framework具備很高的擴展性,咱們能夠經過這些接口對spring作靈活的擴展。java
Spring中提供了各類Aware接口,方便從上下文中獲取當前的運行環境,比較常見的幾個子接口有: ApplicationContextAware、BeanClassLoaderAware、BeanFactoryAware、BeanNameAware、EnvironmentAware、ImportAware、MessageSourceAware、ServletConfigAware、ServletContextAware等等。web
例如:要想經過應用上下文獲取bean對象,能夠經過以下方式。spring
@Component public class ApplicationContextAssistor implements ApplicationContextAware { private ApplicationContext applicationContext; @Override public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { this.applicationContext = applicationContext; } public Object getBeanDefinition(String name) { return this.applicationContext.getBean(name); } public <T> T getBeanDefinition(String name, Class<T> clazz) { return this.applicationContext.getBean(name, clazz); } }
InitializingBean接口只有一個方法afterPropertiesSet( ):當BeanFactory 設置完全部的Bean屬性以後纔會調用,能夠作一些資源初始化操做。apache
DisposableBean接口只有一個方法destroy( ):單例銷燬時由BeanFactory調用,能夠作一些資源釋放操做。編程
例子:tomcat
@Component public class ConnectBean implements InitializingBean, DisposableBean { @Override public void destroy() throws Exception { System.out.println("單例銷燬時由BeanFactory調用"); } @Override public void afterPropertiesSet() throws Exception { System.out.println("當BeanFactory 設置完全部的Bean屬性以後纔會調用"); } }
BeanPostProcessor能夠在bean初始化( 注意初始化不包括init方法 )以後修改bean實例中的屬性,對bean實例作一些特定操做 。springboot
例如:先定義一個bean,包含屬性count咱們將經過BeanPostProcessor修改該屬性的值。app
@Component public class ConnectBean{ private Integer count; public void setCount(int count) { this.count = count; } public Integer getCount() { return this.count; } }
定義BeanPostProcessor接口實現類實現接口方法postProcessBeforeInitialization在bean實例初始化以後調用和postProcessAfterInitialization在bean初始化以後調用,先找出ConnectBean實例而後對調用bean實例的setCount( )方法,ide
@Order(value = 1) @Component public class ConnectBeanPostProcessor implements BeanPostProcessor { @Override public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { if (bean instanceof ConnectBean) { System.out.println("給ConnectBean設置初始count"); ((ConnectBean)bean).setCount(100); } return BeanPostProcessor.super.postProcessBeforeInitialization(bean, beanName); } @Override public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { if (bean instanceof ConnectBean) { System.out.println("給ConnectBean設置count完成" + ((ConnectBean)bean).getCount()); } return BeanPostProcessor.super.postProcessAfterInitialization(bean, beanName); } }
調用啓動方法:post
. ____ _ __ _ _ /\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \ ( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \ \\/ ___)| |_)| | | | | || (_| | ) ) ) ) ' |____| .__|_| |_|_| |_\__, | / / / / =========|_|==============|___/=/_/_/_/ :: Spring Boot :: (v2.2.5.RELEASE) 2020-03-24 08:47:43.417 INFO 187788 --- [ main] com.example.demo.DemoApplication : Starting DemoApplication on ZZ-WZ112019 with PID 187788 (E:\java_work\demo\target\classes started by jiafeng in E:\java_work\demo) 2020-03-24 08:47:43.419 INFO 187788 --- [ main] com.example.demo.DemoApplication : No active profile set, falling back to default profiles: default 2020-03-24 08:47:44.236 INFO 187788 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat initialized with port(s): 8080 (http) 2020-03-24 08:47:44.244 INFO 187788 --- [ main] o.apache.catalina.core.StandardService : Starting service [Tomcat] 2020-03-24 08:47:44.244 INFO 187788 --- [ main] org.apache.catalina.core.StandardEngine : Starting Servlet engine: [Apache Tomcat/9.0.31] 2020-03-24 08:47:44.316 INFO 187788 --- [ main] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring embedded WebApplicationContext 2020-03-24 08:47:44.316 INFO 187788 --- [ main] o.s.web.context.ContextLoader : Root WebApplicationContext: initialization completed in 860 ms 給ConnectBean設置初始count 給ConnectBean設置count完成100 2020-03-24 08:47:44.428 INFO 187788 --- [ main] o.s.s.concurrent.ThreadPoolTaskExecutor : Initializing ExecutorService 'applicationTaskExecutor' 2020-03-24 08:47:44.537 INFO 187788 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat started on port(s): 8080 (http) with context path '' 2020-03-24 08:47:44.539 INFO 187788 --- [ main] com.example.demo.DemoApplication : Started DemoApplication in 1.395 seconds (JVM running for 1.724)
Spring IoC容器容許BeanFactoryPostProcessor在容器實際實例化任何其它的bean以前讀取配置元數據,並有可能修改它。 換句話說BeanFactoryPostProcessor是bean工廠的bean屬性處理容器,咱們能夠管理bean工廠內全部的beandefinition(未實例化)數據,能夠爲所欲爲的修改屬性。
例如:咱們修改ConnectBean類,在其中新增init方法,而後咱們再BeanFactoryPostProcessor中把init方法設置爲ConnectBean的初始方法。
@Component public class ConnectBean { private Integer count; public void setCount(int count) { this.count = count; } public Integer getCount() { return this.count; } public void init() { System.out.println("這是ConnectBean的初始化方法:init"); } }
定義BeanFactoryPostProcessor接口實現類,實現接口方法postProcessBeanFactory( )改方法參數ConfigurableListableBeanFactory對象,該對象能夠操做全部bean工廠中的BeanDefinition(bean元數據)。你也能夠經過設置@order設定beanFactoryPostProcessor的執行順序。
@Order(value = 1) @Component public class ConnectBeanFactoryPostProcessor implements BeanFactoryPostProcessor { @Override public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException { System.out.println("調用自定義BeanFactoryPostProcessor" + beanFactory.getClass()); BeanDefinition beanDefinition = beanFactory.getBeanDefinition("connectBean"); beanDefinition.setInitMethodName("init"); System.out.println("調用自定義BeanFactoryPostProcessor結束" + beanFactory.getClass()); } }
執行結果:能夠看出BeanFactoryPostProcessor的調用早於BeanPostProcessor的調用。
. ____ _ __ _ _ /\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \ ( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \ \\/ ___)| |_)| | | | | || (_| | ) ) ) ) ' |____| .__|_| |_|_| |_\__, | / / / / =========|_|==============|___/=/_/_/_/ :: Spring Boot :: (v2.2.5.RELEASE) 2020-03-24 09:48:21.732 INFO 186536 --- [ main] com.example.demo.DemoApplication : Starting DemoApplication on ZZ-WZ112019 with PID 186536 (E:\java_work\demo\target\classes started by jiafeng in E:\java_work\demo) 2020-03-24 09:48:21.734 INFO 186536 --- [ main] com.example.demo.DemoApplication : No active profile set, falling back to default profiles: default 調用自定義BeanFactoryPostProcessorclass org.springframework.beans.factory.support.DefaultListableBeanFactory 調用自定義BeanFactoryPostProcessor結束class org.springframework.beans.factory.support.DefaultListableBeanFactory 2020-03-24 09:48:22.540 INFO 186536 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat initialized with port(s): 8080 (http) 2020-03-24 09:48:22.548 INFO 186536 --- [ main] o.apache.catalina.core.StandardService : Starting service [Tomcat] 2020-03-24 09:48:22.548 INFO 186536 --- [ main] org.apache.catalina.core.StandardEngine : Starting Servlet engine: [Apache Tomcat/9.0.31] 2020-03-24 09:48:22.614 INFO 186536 --- [ main] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring embedded WebApplicationContext 2020-03-24 09:48:22.614 INFO 186536 --- [ main] o.s.web.context.ContextLoader : Root WebApplicationContext: initialization completed in 841 ms 給ConnectBean設置初始count 這是ConnectBean的初始化方法:init 給ConnectBean設置count完成100 2020-03-24 09:48:22.742 INFO 186536 --- [ main] o.s.s.concurrent.ThreadPoolTaskExecutor : Initializing ExecutorService 'applicationTaskExecutor' 2020-03-24 09:48:22.865 INFO 186536 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat started on port(s): 8080 (http) with context path '' 2020-03-24 09:48:22.867 INFO 186536 --- [ main] com.example.demo.DemoApplication : Started DemoApplication in 1.417 seconds (JVM running for 1.76)
@Import註解的參數能夠是一個類,也能夠是一個ImportSelector接口,也能夠是ImportBeanDefinitionRegistrar接口,若是是一個類就會建立該類對應的實例到IOC容器;若是是一個 ImportSelector接口就會根據接口邏輯篩選知足條件的類建立bean實例;若是是ImportBeanDefinitionRegistrar也會根據接口實現方法的邏輯建立bean實例。
官方解釋:
1. 當處理Java編程式配置類(使用了@Configuration的類)的時候,ImportBeanDefinitionRegistrar接口的實現類能夠註冊額外的bean definitions。
2. ImportBeanDefinitionRegistrar接口的實現類必須提供給@Import註解或者是ImportSelector接口返回值 。
3. ImportBeanDefinitionRegistrar接口的實現類可能還會實現下面org.springframework.beans.factory.Aware接口中的一個或者多個,它們各自的方法優先於ImportBeanDefinitionRegistrar的registerBeanDefinitions( )f方法被調用。
手動把類註冊成bean實例
首先定義一個類,後面該類會被註冊到bean容器裏面
public class CollectService { public void hello() { System.out.println("hello ! I am collect"); } }
自定義ImportBeanDefinitionRegistrar類,實現手動註冊bean
public class CollectImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar { /** * * @author jiafeng * @date 2020年3月24日 下午3:43:46 * @param importingClassMetadata 當前類的註解信息 * @param registry 註冊類 */ @Override public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) { GenericBeanDefinition beanDefinition = new GenericBeanDefinition(); beanDefinition.setBeanClass(CollectService.class); beanDefinition.setSynthetic(true); registry.registerBeanDefinition("collectService", beanDefinition); } }
最後定義一個配置類,用於發現上面的手動註冊類
@Configuration @Import(CollectImportBeanDefinitionRegistrar.class) public class CollectConfiguration { }
springboot啓動類裏面調用被手動註冊的bean實例方法,驗證是否註冊成功
public static void main(String[] args) { ConfigurableApplicationContext applicationContext = SpringApplication.run(DemoApplication.class, args); System.out.println("啓動完成"); CollectService collectService = (CollectService)applicationContext.getBean("collectService"); collectService.hello(); }
執行結果:調用被註冊類的hello( )方法。
. ____ _ __ _ _ /\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \ ( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \ \\/ ___)| |_)| | | | | || (_| | ) ) ) ) ' |____| .__|_| |_|_| |_\__, | / / / / =========|_|==============|___/=/_/_/_/ :: Spring Boot :: (v2.2.5.RELEASE) 2020-03-24 16:14:10.498 INFO 192624 --- [ main] com.example.demo.DemoApplication : Starting DemoApplication on ZZ-WZ112019 with PID 192624 (E:\java_work\demo\target\classes started by jiafeng in E:\java_work\demo) 2020-03-24 16:14:10.500 INFO 192624 --- [ main] com.example.demo.DemoApplication : No active profile set, falling back to default profiles: default org.springframework.beans.factory.support.DefaultListableBeanFactory 2020-03-24 16:14:11.304 INFO 192624 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat initialized with port(s): 8080 (http) 2020-03-24 16:14:11.312 INFO 192624 --- [ main] o.apache.catalina.core.StandardService : Starting service [Tomcat] 2020-03-24 16:14:11.312 INFO 192624 --- [ main] org.apache.catalina.core.StandardEngine : Starting Servlet engine: [Apache Tomcat/9.0.31] 2020-03-24 16:14:11.379 INFO 192624 --- [ main] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring embedded WebApplicationContext 2020-03-24 16:14:11.379 INFO 192624 --- [ main] o.s.web.context.ContextLoader : Root WebApplicationContext: initialization completed in 850 ms 2020-03-24 16:14:11.498 INFO 192624 --- [ main] o.s.s.concurrent.ThreadPoolTaskExecutor : Initializing ExecutorService 'applicationTaskExecutor' 2020-03-24 16:14:11.607 INFO 192624 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat started on port(s): 8080 (http) with context path '' 2020-03-24 16:14:11.609 INFO 192624 --- [ main] com.example.demo.DemoApplication : Started DemoApplication in 1.374 seconds (JVM running for 1.71) 啓動完成 hello ! I am collect
自定義註解實現spring把類加載成bean實例
spring 官方也是經過ImportBeanDefinitionRegistrar實現@Component、@service等註解的動態注入機制。具體過程以下:
首先定義一個註解@Zyservice,配置該註解的類會被註冊到bean容器中。
/** * @description <描述> * @author jiafeng * @date 2020年3月24日 下午6:32:46 */ @Documented @Inherited @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.TYPE, ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER}) public @interface Zyservice { }
定義使用該註解的類,該類會被註冊到容器中
/** * @description <描述> * @author jiafeng * @date 2020年3月24日 下午6:34:58 */ @Zyservice public class ZycfcService { public void hello() { System.out.println("zycfcService 註冊成功"); } }
定義ImportBeanDefinitionRegistrar接口實現類,實現掃描將配置@Zyservice註解的類實例化到bean容器中
/** * @description <描述> * @author jiafeng * @date 2020年3月24日 下午6:35:35 */ public class ZycfcImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar { @Override public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) { Map<String, Object> annotationAttributes = importingClassMetadata.getAnnotationAttributes(ComponentScan.class.getName()); String[] basePackages = (String[])annotationAttributes.get("basePackages"); ClassPathBeanDefinitionScanner scanner = new ClassPathBeanDefinitionScanner(registry, false); scanner.addIncludeFilter(new AnnotationTypeFilter(Zyservice.class));// 掃描對應註解的類 scanner.scan(basePackages); } }
定義Configuration配置類引入ZycfcImportBeanDefinitionRegistrar註冊器,配置掃描包路徑
@Configuration @ComponentScan(basePackages = {"com.example.demo.bean"}) @Import(ZycfcImportBeanDefinitionRegistrar.class) public class ZycfcServiceConfiguration { }
啓動類,springboot啓動以後從bean容器中獲取ZycfcService類實例,調用方法驗證是否動態注入到bean容器中
public static void main(String[] args) { ConfigurableApplicationContext applicationContext = SpringApplication.run(DemoApplication.class, args); System.out.println("啓動完成"); CollectService collectService = applicationContext.getBean(CollectService.class); collectService.hello(); ZycfcService zycfcService = applicationContext.getBean(ZycfcService.class); zycfcService.hello(); }
執行結果:
. ____ _ __ _ _ /\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \ ( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \ \\/ ___)| |_)| | | | | || (_| | ) ) ) ) ' |____| .__|_| |_|_| |_\__, | / / / / =========|_|==============|___/=/_/_/_/ :: Spring Boot :: (v2.2.5.RELEASE) 2020-03-24 19:03:27.374 INFO 190632 --- [ main] com.example.demo.DemoApplication : Starting DemoApplication on ZZ-WZ112019 with PID 190632 (E:\java_work\demo\target\classes started by jiafeng in E:\java_work\demo) 2020-03-24 19:03:27.375 INFO 190632 --- [ main] com.example.demo.DemoApplication : No active profile set, falling back to default profiles: default org.springframework.beans.factory.support.DefaultListableBeanFactory 2020-03-24 19:03:28.179 INFO 190632 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat initialized with port(s): 8080 (http) 2020-03-24 19:03:28.187 INFO 190632 --- [ main] o.apache.catalina.core.StandardService : Starting service [Tomcat] 2020-03-24 19:03:28.187 INFO 190632 --- [ main] org.apache.catalina.core.StandardEngine : Starting Servlet engine: [Apache Tomcat/9.0.31] 2020-03-24 19:03:28.259 INFO 190632 --- [ main] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring embedded WebApplicationContext 2020-03-24 19:03:28.259 INFO 190632 --- [ main] o.s.web.context.ContextLoader : Root WebApplicationContext: initialization completed in 856 ms 2020-03-24 19:03:28.377 INFO 190632 --- [ main] o.s.s.concurrent.ThreadPoolTaskExecutor : Initializing ExecutorService 'applicationTaskExecutor' 2020-03-24 19:03:28.487 INFO 190632 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat started on port(s): 8080 (http) with context path '' 2020-03-24 19:03:28.490 INFO 190632 --- [ main] com.example.demo.DemoApplication : Started DemoApplication in 1.392 seconds (JVM running for 1.716) 啓動完成 hello ! I am collect zycfcService 註冊成功
BeanDefinitionRegistryPostProcessor類能夠當作是ImportBeanDefinitionRegistrar類和BeanFactoryPostProcessor類的綜合體,既能夠 實現BeanDefinition的註冊操做將類註冊到bean容器中,也能夠實現BeanDefinition的修改、移除操做。
例如:首先建立類,用於被手動註冊到容器中
/** * @description <描述> * @author jiafeng * @date 2020年3月24日 下午8:02:12 */ public class GreatService { public void test() { System.out.println("這是GreatService的方法:test"); } }
定義BeanDefinitionRegistryPostProcessor的實現類,實現postProcessBeanDefinitionRegistry和postProcessBeanFactory方法實現手動註冊bean實例功能和修改bean元數據功能。
/** * @description <描述> * @author jiafeng * @date 2020年3月24日 下午7:46:22 */ @Component public class GreatBeanDefinitionRegistryPostProcessor implements BeanDefinitionRegistryPostProcessor { @Override public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException { //從beanFactory中取出bean元數據,進行修改或其餘操做 BeanDefinition beanDefinition = beanFactory.getBeanDefinition("greatService"); beanDefinition.setInitMethodName("test"); System.out.println("調用GreatBeanDefinitionRegistryPostProcessor的postProcessBeanFactory( )方法"+beanDefinition.getAttribute("type")); } @Override public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException { //建立bean元數據並註冊到容器中 GenericBeanDefinition beanDefinition = new GenericBeanDefinition(); beanDefinition.setBeanClass(GreatService.class); beanDefinition.setAttribute("type", "test"); registry.registerBeanDefinition("greatService", beanDefinition); System.out.println("調用GreatBeanDefinitionRegistryPostProcessor的postProcessBeanDefinitionRegistry( )方法"); } }
執行啓動類從執行結果能夠看到,先執行postProcessBeanDefinitionRegistry( )方法註冊bean實例到容器中,而後執行postProcessBeanFactory( )方法從beanFactory中能夠取出beanDefinition進行修改操做。
. ____ _ __ _ _ /\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \ ( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \ \\/ ___)| |_)| | | | | || (_| | ) ) ) ) ' |____| .__|_| |_|_| |_\__, | / / / / =========|_|==============|___/=/_/_/_/ :: Spring Boot :: (v2.2.5.RELEASE) 2020-03-24 20:16:36.621 INFO 194764 --- [ main] com.example.demo.DemoApplication : Starting DemoApplication on ZZ-WZ112019 with PID 194764 (E:\java_work\demo\target\classes started by jiafeng in E:\java_work\demo) 2020-03-24 20:16:36.623 INFO 194764 --- [ main] com.example.demo.DemoApplication : No active profile set, falling back to default profiles: default 調用GreatBeanDefinitionRegistryPostProcessor的postProcessBeanDefinitionRegistry( )方法 調用GreatBeanDefinitionRegistryPostProcessor的postProcessBeanFactory( )方法test org.springframework.beans.factory.support.DefaultListableBeanFactory 2020-03-24 20:16:37.424 INFO 194764 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat initialized with port(s): 8080 (http) 2020-03-24 20:16:37.432 INFO 194764 --- [ main] o.apache.catalina.core.StandardService : Starting service [Tomcat] 2020-03-24 20:16:37.432 INFO 194764 --- [ main] org.apache.catalina.core.StandardEngine : Starting Servlet engine: [Apache Tomcat/9.0.31] 2020-03-24 20:16:37.499 INFO 194764 --- [ main] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring embedded WebApplicationContext 2020-03-24 20:16:37.499 INFO 194764 --- [ main] o.s.web.context.ContextLoader : Root WebApplicationContext: initialization completed in 847 ms 2020-03-24 20:16:37.619 INFO 194764 --- [ main] o.s.s.concurrent.ThreadPoolTaskExecutor : Initializing ExecutorService 'applicationTaskExecutor' 這是GreatService的方法:test 2020-03-24 20:16:37.731 INFO 194764 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat started on port(s): 8080 (http) with context path '' 2020-03-24 20:16:37.733 INFO 194764 --- [ main] com.example.demo.DemoApplication : Started DemoApplication in 1.381 seconds (JVM running for 1.718) 啓動完成