说起spring,想必每个开发人员都用过,spring是除了jdk之外java编程中最必不可少的基础框架,其优秀的编码方式和编程思想,值得每个技术人员学习和参考。spring给我们提供了很多扩展接口,方便我们开发过程中spring容器启动时或者启动结束做一些事情。此篇我们想要讲的是BeanPostProcessor接口的原理和使用方式。
源码分析
先看一下BeanPostProcessor接口的源码:
public interface BeanPostProcessor {
Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException;
Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException;
}
BeanPostProcessor接口只有两个方法, postProcessBeforeInitialization和postProcessAfterInitialization:
1) postProcessBeforeInitialization方法:将此BeanPostProcessor应用于任何bean初始化回调,作用时机是InitializingBean的afterPropertiesSet方法或自定义init-method方法之前。bean已经被填充了属性值。返回的bean实例可以是围绕原件的包装器。
2) postProcessAfterInitialization方法:将此BeanPostProcessor应用于任何bean初始化回调,作用时机是InitializingBean的afterPropertiesSet方法或自定义init-method方法之后。bean已经被填充了属性值。返回的bean实例可以是围绕原件的包装器。
也就是说,实现了该接口的实例,可以在bean初始化之前和初始化之前做一些前置或者后置操作。
使用场景&编码实现
现在有很多优秀的持久层框架,但是有些公司或者项目架构比较轻量级一些,不想使用持久层框架,会选择自己封装一些DAO操作或者直接使用spring提供的jdbcTemplate操作数据库,在使用jdbcTemplate操作时为了避免每个DAO都写很多类似或者重复的sql操作,一般会抽象出一个通用的DAO操作类,例如BaseDao,里边会封装一些泛型的查询和更新操作,然后具体的调用和实现交给子类实现。
理论上,BaseDao层已经帮每个子类实现了大部分功能,子类只需要实现个性化逻辑,那么BaseDao就需要持有一个jdbcTemplate实例,这个jdbcTemplate实例要么是子类以方法调用的形式传进来,要么是BaseDao层持有,在容器启动的时候进行初始化。根据个人的使用经验来说,肯定是在容器启动的时候初始化,这样可以省去每次调用的时候重复性传参,并且非业务参数,接下来我们就使用代码实现:
BaseDao
public class BaseDao {
protected static final String select = "select ";
protected static final String from = " from ";
protected static final String where = " where ";
protected static final String and = " and ";
protected static final String orderBy = " order by ";
protected static final String oneEqOne = " 1=1 ";
protected static final String count = " count ";
protected static final String sum = " sum ";
protected static final String allSymbol = "(*) ";
protected static final String seqId = "seqId";
protected static final String star = " * ";
protected JdbcOperations jdbcTemplate;
protected JdbcOperations getJdbcTemplate() {
return jdbcTemplate;
}
public void setJdbcTemplate(JdbcOperations jdbcTemplate) {
this.jdbcTemplate = jdbcTemplate;
}
protected <T> T queryByPrimaryKey(String tableName, String primaryKey, Object value, Class<T> clazz) {
StringBuilder sqlBuilder = new StringBuilder();
sqlBuilder.append(select);
sqlBuilder.append(star);
sqlBuilder.append(from);
sqlBuilder.append(tableName);
if (null == primaryKey || 0 >= primaryKey.length()) {
primaryKey = "id";
}
sqlBuilder.append(where);
sqlBuilder.append(primaryKey);
sqlBuilder.append(" = ? limit 1 ");
log.debug("querySql-{}",sqlBuilder.toString());
return this.querySingleObj(clazz, sqlBuilder.toString(), new Object[]{value});
}
protected <T> T queryByPrimaryKey(String tableName, Object value, Class<T> clazz) {
return this.queryByPrimaryKey(tableName, null, value, clazz);
}
UserDao
@Repository("user2Dao")
public class User2Dao extends BaseDao {
private static final String TABLE_NAME = "User";
public User queryByPk(Long id) {
return this.queryByPrimaryKey(TABLE_NAME,id,User.class);
}
}
DataSourceConfig
/**
* 数据源配置类
*
*/
public class DataSourceConfig implements BeanPostProcessor {
@Autowired
private JdbcTemplate jdbcTemplate;
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
if(bean instanceof BaseDao) {
((BaseDao)bean).setJdbcTemplate(jdbcTemplate);
System.out.println(beanName + "完成设置jdbcTemplate");
}
return bean;
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
return bean;
}
}
SpringBootConfig
@Configuration
public class SpringBootConfig {
@Bean
public DataSourceConfig dataSourceConfig() {
DataSourceConfig dataSourceConfig = new DataSourceConfig();
return dataSourceConfig;
}
}
最重要的就是DataSourceConfig这个类,DataSourceConfig实现了BeanPostProcessor接口,在实现postProcessBeforeInitialization中,判断bean是不是BaseDao的实例,如果是就把jdbcTemplate注入进去。作用就是在spring容器实例化了所有实现了BaseDao的子类之后,在调用初始化方法之前把jdbcTemplate填充进去,这样的话在调用每个子类Dao的时候jdbcTemplate都完成了初始化,就可以进行查询和更新操作了。
测试验证
针对上一步中的编码,我们首先启动springboot服务:
可以看到,在spring容器启动过程中给所有BaseDao的子类实例都填充了jdbcTemplate属性。既然容器启动没问题,接着我们编写测试代码测试一下Dao操作有没有问题:
UserManager
@Service("userManager")
public class UserManager {
@Autowired
private User2Dao user2Dao;
public User queryById(Long id) {
return this.user2Dao.queryByPk(id);
}
}
UserController
@RestController
public class UserController {
@Autowired
private UserManager userManager;
@RequestMapping("user/get/{id}")
public User queryUser(@PathVariable("id") Long id) {
User user = this.userManager.queryById(id);
return user;
}
}
再次启动应用,并从浏览器发起请求:
得到正常响应,说明我们之前的jdbcTemplate注入没有问题,可以像之前的实现方式一样访问DB。
总结
BeanPostProcessor是Spring IOC容器给我们提供的一个扩展接口。BeanPostProcessor的实现类注册到Spring IOC容器后,对于该Spring IOC容器所创建的每个bean实例在初始化方法(如afterPropertiesSet和任意已声明的init方法)调用前,将会调用BeanPostProcessor中的postProcessBeforeInitialization方法,而在bean实例初始化方法调用完成后,则会调用BeanPostProcessor中的postProcessAfterInitialization方法。
spring扩展接口中还有一个BeanFactoryPostProcessor与BeanPostProcessor比较类似,使用方式基本相似但是作用时机有所不同,实现BeanFactoryPostProcessor接口,可以在spring的bean创建之前,修改bean的定义属性,BeanFactoryPostProcessor是在spring容器加载了bean的定义文件之后,在bean实例化之前执行的。
本文分享自 PersistentCoder 微信公众号,前往查看
如有侵权,请联系 cloudcommunity@tencent.com 删除。
本文参与 腾讯云自媒体同步曝光计划 ,欢迎热爱写作的你一起参与!