spring framework具有很高的扩展性,我们可以通过这些接口对spring做灵活的扩展。
1. Aware接口
Spring中提供了各种Aware接口,方便从上下文中获取当前的运行环境,比较常见的几个子接口有: ApplicationContextAware、BeanClassLoaderAware、BeanFactoryAware、BeanNameAware、EnvironmentAware、ImportAware、MessageSourceAware、ServletConfigAware、ServletContextAware等等。
例如:要想通过应用上下文获取bean对象,可以通过如下方式。
@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);
}
}
2. InitializingBean和DisposableBean
InitializingBean接口只有一个方法afterPropertiesSet( ):当BeanFactory 设置完所有的Bean属性之后才会调用,可以做一些资源初始化操作。
DisposableBean接口只有一个方法destroy( ):单例销毁时由BeanFactory调用,可以做一些资源释放操作。
例子:
@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属性之后才会调用");
}
}
3. BeanPostProcessor
BeanPostProcessor可以在bean初始化( 注意初始化不包括init方法 )之后修改bean实例中的属性,对bean实例做一些特定操作 。
例如:先定义一个bean,包含属性count我们将通过BeanPostProcessor修改该属性的值。
@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( )方法,
@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);
}
}
调用启动方法:
. ____ _ __ _ _
/\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
\\/ ___)| |_)| | | | | || (_| | ) ) ) )
' |____| .__|_| |_|_| |_\__, | / / / /
=========|_|==============|___/=/_/_/_/
:: 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)
4. BeanFactoryPostProcessor
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)
5. ImportBeanDefinitionRegistrar
@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 注册成功
来源:oschina
链接:https://my.oschina.net/u/3100849/blog/3210344