一、关于BeanPostProcessor

1.1:它是什么?

首先它是一个接口,定义了两个方法:


public interface BeanPostProcessor {
@Nullable //所有bean初始化之前触发该方法
default Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
return bean;
} @Nullable //所有bean初始化之后触发该方法
default Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
return bean;
}
}

它定义了两个方法,分别是:

postProcessBeforeInitialization:bean初始化前置处理

postProcessAfterInitialization:bean初始化后置处理

注:这里的初始化是指一个被实例化后的bean的完成其一些初始化方法的调用(最基本的就是通过@PostConstruct预设的初始化方法),上面两个方法的before和after就是针对这个状态来区分触发时机的。

我们可以定义一个实现了该接口的bean,来达到对其他bean做一些初始化前后要做的事情。

1.2:什么时候触发?

首先看下spring beans的生命周期(图片来源于网络):

图1

上图中标红的位置就是BeanPostProcessor两个方法的触发点,可以看到这些方法的触发是在初始化阶段。

那么,如何定义一个类似的bean的初始化阶段的后置处理器呢?很简单,让一个bean实现BeanPostProcessor接口并重写其before、after方法即可,可以搞很多个这样的bean,触发过程就是,容器里的任何bean在实例化后初始化前,都会触发一次所有实现了BeanPostProcessor接口的bean的before方法,初始化以后都会触发一次所有实现了BeanPostProcessor接口的bean的after方法,也就是说,spring在启动时,会预先加载实现了该接口的对象(通过registerBeanPostProcessors方法注册这类bean),这样,其他任何bean在初始化时,都可以通过之前已经加载好的逻辑,逐个触发一遍(当然如果想要保证实现顺序,还可以通过实现Order接口,来定义触发顺序)。

1.3:可以用来做什么?

了解了它的触发时机,那么它通常可以用来做哪些事情呢?一般来说,可以利用其做一些通用性的bean属性注入,下面通过一个实例来说下其应用方式和场景。

二、使用方式

实战一下,给目前项目内所有的SqlSessionFactory对象都加一个拦截器。

2.1:定义一个Mybatis拦截器

现在来定义一个Mybatis里的拦截器,它的作用就是简单拿到sql,然后打印出该sql执行耗时:


@Slf4j
@Intercepts({@Signature(type = StatementHandler.class, method = "query", args = {Statement.class, ResultHandler.class}),
@Signature(type = StatementHandler.class, method = "update", args = {Statement.class}),
@Signature(type = StatementHandler.class, method = "batch", args = {Statement.class})})
public class SqlInterceptor implements Interceptor { @Override
public Object intercept(Invocation invocation) throws Throwable { //拦截每次的sql执行
Object target = invocation.getTarget();
StatementHandler statementHandler = (StatementHandler) target;
BoundSql boundSql = statementHandler.getBoundSql();
String sql = boundSql.getSql(); //获取sql
long start = System.currentTimeMillis();
try {
return invocation.proceed(); //sql运行
} catch (Throwable t) {
System.out.println(String.format("错误SQL=%s", sql));
throw t;
} finally {
System.out.println(String.format("耗时%s ms, SQL=%s", (System.currentTimeMillis() - start), sql));
} } @Override
public Object plugin(Object target) {
return Plugin.wrap(target, this);
} @Override
public void setProperties(Properties properties) { }
}

Mybatis的拦截器需要预先往SqlSessionFactory设置:


@Bean(name = "sqlSession")
public SqlSessionFactory sqlSession(@Qualifier("dataSource") DataSource dataSource) throws Exception {
SqlSessionFactoryBean bean = new SqlSessionFactoryBean();
bean.setDataSource(dataSource);
bean.setVfs(SpringBootVFS.class);
bean.getObject().getConfiguration().addInterceptor(new SqlInterceptor()); //手动加入
return bean.getObject();
}

2.2:借助BeanPostProcessor操作相关Bean

这时项目模块如果很多,但是这个拦截器又要求对所有项目所有的SqlSessionFactory都生效,一个个去改每个项目里的SqlSessionFactory类型的bean太过繁琐,这个时候就可以在公共模块里定义一个BeanPostProcessor去干这件事,比如可以定义成下面这样:


@Slf4j
public class SqlSessionFactoryBeanPostProcessor implements BeanPostProcessor { @Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
if (bean instanceof SqlSessionFactory) { //所有bean初始化之后都会进入这个方法,这个时候需要滤出需要的类型,比如这次就只需要拿到SqlSessionFactory类型的对象对其设置拦截器就行了
SqlSessionFactory nowBean = (SqlSessionFactory) bean;
nowBean.getConfiguration().addInterceptor(new SqlInterceptor(nowBean //设置拦截器
.getConfiguration()
.getEnvironment()
.getDataSource()));
}
return bean; //完成后返回出去,可能直接进入容器,也可能会去执行其他的BeanPostProcessor
}
}

然后再把它也定义成一个bean,其本身也是一个bean,才能被spring扫到去装载,否则只是实现BeanPostProcessor接口spring是没办法察觉做管理的:


@ConditionalOnClass({SqlSessionFactory.class}) //存在SqlSessionFactory类型时,才会触发下面bean的装载
public class MysqlAutoConfiguration {
@Bean
public SqlSessionFactoryBeanPostProcessor sqlSessionFactoryBeanPostProcessor() {
return new SqlSessionFactoryBeanPostProcessor();
}
}

这样写完,就不用去一个个的改SqlSessionFactory对象了,只要引入该公共模块,那么在bean初始化完成后,就会走这段逻辑,然后滤出自己需要的类型,对其进行修改就好,这样,所有SqlSessionFactory就在不修改别的地方初始化SqlSessionFactory代码的情况下,全局生效了。

【框架】利用Spring的BeanPostProcessor来修改bean属性的更多相关文章

  1. Spring IoC(三)bean属性、方法注释

    1.环境配置 使用注解开发jdk1.5.Spring2.5支持,在xml中添加context相关的是四个配置; <beans default-lazy-init="true" ...

  2. Spring学习笔记--自动装配Bean属性

    Spring提供了四种类型的自动装配策略: byName – 把与Bean的属性具有相同名字(或者ID)的其他Bean自动装配到Bean的对应属性中. byType – 把与Bean的属性具有相同类型 ...

  3. Spring思维课程导图——bean属性的设置

  4. Spring学习(五)-----注入bean属性的三种方式( 1: 正常的方式 2: 快捷方式 3: “p” 模式)

    在Spring中,有三种方式注入值到 bean 属性. 正常的方式 快捷方式 “p” 模式 看到一个简单的Java类,它包含两个属性 - name 和 type.稍后将使用Spring注入值到这个 b ...

  5. 如何注入值到Spring bean属性

    在Spring中,有三种方式注入值到 bean 属性. 正常的方式 快捷方式 “p” 模式 看到一个简单的Java类,它包含两个属性 - name 和 type.稍后将使用Spring注入值到这个 b ...

  6. Spring5源码解析-Spring框架中的单例和原型bean

    Spring5源码解析-Spring框架中的单例和原型bean 最近一直有问我单例和原型bean的一些原理性问题,这里就开一篇来说说的 通过Spring中的依赖注入极大方便了我们的开发.在xml通过& ...

  7. 手撸Spring框架,设计与实现资源加载器,从Spring.xml解析和注册Bean对象

    作者:小傅哥 博客:https://bugstack.cn 沉淀.分享.成长,让自己和他人都能有所收获! 一.前言 你写的代码,能接的住产品加需求吗? 接,是能接的,接几次也行,哪怕就一个类一片的 i ...

  8. 【Spring学习笔记-MVC-5】利用spring MVC框架,实现ajax异步请求以及json数据的返回

    作者:ssslinppp      时间:2015年5月26日 15:32:51 1. 摘要 本文讲解如何利用spring MVC框架,实现ajax异步请求以及json数据的返回. Spring MV ...

  9. 可用来修改bean对象的BeanPostProcessor

    可用来修改bean对象的BeanPostProcessor 11.1 简介 BeanPostProcessor是Spring中定义的一个接口,其与之前介绍的InitializingBean和Dispo ...

随机推荐

  1. 【React】354- 一文吃透 React 事件机制原理

    大纲 主要分为4大块儿,主要是结合源码对 react事件机制的原理 进行分析,希望可以让你对 react事件机制有更清晰的认识和理解. 当然肯定会存在一些表述不清或者理解不够标准的地方,还请各位大神. ...

  2. Nginx学习一路向西

    Nginx 学习一路向北 Java大猿帅成长手册,GitHub JavaEgg ,N线互联网开发必备技能兵器谱 1. Nginx简介 1.1 Nginx 概述 NGINX是一个免费.开源.高性能.轻量 ...

  3. C语言每日一练——第1题

    一.程序功能 程序的功能是:将大于整数m且紧靠m的k个素数存入数组xx.并把in.dat文件的内容输入到程序,并把输出结果输出道out.dat文件夹中例如:若输入17,5 则应该输入:19,23,29 ...

  4. cygwin报错 /bin/bash: Operation not permitted

    如题,使用Cygwin过程中本来好好的,突然就不能登录了,每个用户登录都报错 /bin/bash: Operation not permitted.开始也以为是没有权限之类的,重装弄了很久也不行.后面 ...

  5. NodeJS4-8静态资源服务器实战_构建cli工具

    Cli(command-line interface),中文是 命令行界面,简单来说就是可以通过命令行快速生成自己的项目模板等功能(比较熟悉的是vue-cli脚手架这些),把上述写的包做成Cli工具. ...

  6. 2016/09/29 SQL中的join

    1.建表 首先建tb_a并插入数据. )); insertinto tb_a(symbol, sname) values ('A','B'); insertinto tb_a(symbol, snam ...

  7. VS2019 开发Django(八)------视图

    导航:VS2019开发Django系列 这几天学习了一下Django的视图和模板,从这几天的学习进度来看,视图这里并没有花很多的时间,相反的,模板花费了大量的时间,主要原因还是因为对Jquery操作d ...

  8. PDF提取表格的网页工具——Excalibur

      在之前的文章另类爬虫:从PDF文件中爬取表格数据中,我们知道如何利用Python的camelot模块,通过写Python程序来提取PDF中的表格数据.本文我们将学习如何用更便捷的工具从PDF中提取 ...

  9. 基于windows server 2016和sqlserver 2016 AlwaysOn的群集配置

    文档:基于windows server 2016和sqlserver 2...链接:http://note.youdao.com/noteshare?id=4f07c1c3f7d0e32b7631d7 ...

  10. 参加杭州 2019 AI Bootcamp有感与总结(1)

    上周末参加了微软人工智能的活动,感慨多多. 感谢活动主讲和主办方. 通过参加活动,对微软认知服务和ML.NET的现状与发展有了更多的认识.文章不是活动内容的堆叠,总结少点,更多的大概是感慨. 微软认知 ...