[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/>
以上就是本章内容,若是文章中有错误或者须要补充的请及时提出,本人感激涕零。