Spring钩子接口汇总分析使用示例

目录
  • 1、Aware 系列接口
  • 2、InitializingBean
  • 3、BeanPostProcessor
  • 4、BeanFactoryPostProcessor
  • 5、FactoryBean
  • 6、ApplicationListener

1、Aware 系列接口

Aware 系列接口是用来获取 Spring 内部对象的接口。Aware 自身是一个顶级接口,它有一系列子接口,在一个 Bean 中实现这些子接口并重写里面的 set 方法后,Spring 容器启动时,就会回调该 set 方法,而相应的对象会通过方法参数传递进去。我们以其中的 ApplicationContextAware 接口为例。

ApplicationContextAware

大部分 Aware 系列接口都有一个规律,它们以对象名称为前缀,获取的就是该对象,所以 ApplicationContextAware 获取的对象是 ApplicationContext 。

public interface ApplicationContextAware extends Aware {
	void setApplicationContext(ApplicationContext applicationContext) throws BeansException;
}

ApplicationContextAware 继承了 Aware 接口,并定义一个 set 方法,参数就是 ApplicationContext 对象,当然,其它系列的 Aware 接口也是类似的定义。其具体使用方式如下:

public class Test implements ApplicationContextAware {
    private ApplicationContext applicationContext;
    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
    }
}

在 Spring 启动过程中,会回调 setApplicationContext 方法,并传入 ApplicationContext 对象,之后就可对该对象进行操作。其它系列的 Aware 接口也是如此使用。具体的调用时机会在后面详细介绍。

以下是几种常用的 Aware 接口:

  • BeanFactoryAware:获取 BeanFactory 对象,它是基础的容器接口。
  • BeanNameAware:获取 Bean 的名称。
  • EnvironmentAware:获取 Environment 对象,它表示整个的运行时环境,可以设置和获取配置属性。
  • ApplicationEventPublisherAware:获取 ApplicationEventPublisher 对象,它是用来发布事件的。
  • ResourceLoaderAware:获取 ResourceLoader 对象,它是获取资源的工具。

2、InitializingBean

InitializingBean 是一个可以在 Bean 的生命周期执行自定义操作的接口,凡是实现该接口的 Bean,在初始化阶段都可以执行自定义的操作。

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 方法被执行并打印输出语句。

3、BeanPostProcessor

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 内部也正是这样使用的,这部分也会在后面章节详细讨论。

4、BeanFactoryPostProcessor

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 的属性等。

5、FactoryBean

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 之前加上&。

6、ApplicationListener

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钩子接口内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • SpringBoot 钩子接口的实现代码

    目录 Aware 接口族 InitializingBean BeanPostProcessor BeanFactoryPostProcessor ImportSelector ImportBeanDefinitionRegistrar FactoryBean ApplicationListener ApplicationRunner Aware 接口族 Aware 意为感知,实现 Aware 接口并重写其方法,可以从上下文中获取当前的运行环境 常见的 aware 接口 BeanNameAware

  • Spring钩子接口汇总分析使用示例

    目录 1.Aware 系列接口 2.InitializingBean 3.BeanPostProcessor 4.BeanFactoryPostProcessor 5.FactoryBean 6.ApplicationListener 1.Aware 系列接口 Aware 系列接口是用来获取 Spring 内部对象的接口.Aware 自身是一个顶级接口,它有一系列子接口,在一个 Bean 中实现这些子接口并重写里面的 set 方法后,Spring 容器启动时,就会回调该 set 方法,而相应的对

  • 常用的Docker命令及示例汇总分析

    目录 1.容器生命周期管理 (1)docker run (2)start/stop/restart (3)docker kill (4)docker rm (5)pause/unpause (6)create (7)docker exec (8)docker ps (9)docker inspect (10)top 1.容器生命周期管理 (1)docker run 命令说明     创建一个新的容器并运行一个命令 语法 docker run [OPTIONS] IMAGE [COMMAND] [

  • Spring Aware接口示例代码详解

    若 Spring 检测到 bean 实现了 Aware 接口,则会为其注入相应的依赖.所以通过让bean 实现 Aware 接口,则能在 bean 中获得相应的 Spring 容器资源. Spring 中提供的 Aware 接口有: BeanNameAware:注入当前 bean 对应 beanName BeanClassLoaderAware:注入加载当前 bean 的 ClassLoader BeanFactoryAware:注入 当前BeanFactory容器 的引用 BeanNameAw

  • 一个依赖搞定 Spring Boot 接口防盗刷的流程分析

    目录 系统要求​ 工作流程​ 命中规则后 接入使用 注意 配置一览表 kk-anti-reptile 是适用于基于 spring-boot 开发的分布式系统的反爬虫组件. 系统要求​ 基于 spring-boot 开发(spring-boot1.x, spring-boot2.x均可) 需要使用 redis 工作流程​ kk-anti-reptile 使用基于 Servlet 规范的的 Filter 对请求进行过滤,在其内部通过 spring-boot 的扩展点机制,实例化一个 Filter,并

  • React Hooks钩子中API的使用示例分析

    目录 hooks是什么 Hooks的作用 使用Hooks组件前后开发模式的对比 Hooks使用策略 为什么要有Hooks useState useEffect使用 useEffect依赖项 使用情景 useMemo使用 useMemo缓存组件方式 useMemo和useEffect的区别 useCallback使用 useContext使用 useRef使用 为什么在函数组件中无法使用ref 如何在类组件中使用ref属性 自定义hooks hooks是什么 hooks理解字面意思就是钩子,是一些

  • Spring事务失效问题分析及解决方案

    这篇文章主要介绍了Spring事务失效问题分析及解决方案,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 隔离级别 在 TransactionDefinition.java 接口中,定义了"四种"的隔离级别枚举: /** * [Spring 独有]使用后端数据库默认的隔离级别 * * MySQL 默认采用的 REPEATABLE_READ隔离级别 * Oracle 默认采用的 READ_COMMITTED隔离级别 */ int ISOL

  • 浅谈Spring Boot: 接口压测及简要优化策略

    工程做好之后,需要对接口进行压力测试.可以自己编写线程池模拟多用户访问测试,也可以使用jmeter进行压测.jmeter的好处是测试方便,并且有完善的结果分析功能.本次采用jmeter进行压力测试. 1.准备数据,为了测试准备200w条以上的数据.一个简单的方法是使用下面的sql快速创建. INSERT INTO table (user_name,address) SELECT user_name, address FROM table; 但这样创建的数据不同记录的重复部分太多,和实际业务不太相

  • Java开发SpringBoot集成接口文档实现示例

    目录 swagger vs smart-doc Swagger的代码侵入性比较强 原生swagger不支持接口的参数分组 简单罗列一下smart-doc的优点 SpringBoot集成 smart-doc 引入依赖,版本选择最新版本 新建配置文件smart-doc.json 通过执行maven 命令生成对应的接口文档 访问接口文档 功能增强 1. 开启调试 2. 通用响应体 3. 自定义Header 4. 参数分组 5. idea配置doc 6. 完整配置 小结 之前我在SpringBoot老鸟

  • Spring Security实现自动登陆功能示例

    当我们在登录像QQ邮箱这种大多数的网站,往往在登录按键上会有下次自动登录这个选项,勾选后登录成功,在一段时间内,即便退出浏览器或者服务器重启,再次访问不需要用户输入账号密码进行登录,这也解决了用户每次输入账号密码的麻烦. 接下来实现自动登陆. applicatio.properties配置用户名密码 spring.security.user.name=java spring.security.user.password=java controller层实现 @RestController pub

  • Spring Data Jpa框架最佳实践示例

    目录 前言 扩展接口用法 SPRINGDATAJPA最佳实践 一.继承SIMPLEJPAREPOSITORY实现类 二.集成QUERYDSL结构化查询 1.快速集成 2.丰富BaseJpaRepository基类 3.最终的BaseJpaRepository形态 三.集成P6SPY打印执行的SQL 结语 前言 Spring Data Jpa框架的目标是显著减少实现各种持久性存储的数据访问层所需的样板代码量. Spring Data Jpa存储库抽象中的中央接口是Repository.它需要领域实

随机推荐