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) 启动完成