改变不能总是嘴上说,执行力才是人与人之间拉开距离的能力。
1.需求2.问题分析3.看源码解决拦截器不生效问题4.EmptyInterceptor原理5.Hibernate与Mybatis 框架的思考6.总结:
1.需求
系统获客后,会创建一条线索,现在要对创建线索进行扩展
2.问题分析
由于历史开发者的原因,没有做好抽象,代码里有大量创建线索的逻辑,现在要对创建的线索做一个扩展。如果找到所有的创建线索的代码块去修改,会非常的疲惫。
系统使用的是:SpringBoot+Hibernate 架构。
原本我想用切面解决这个问题,但是发现挺繁琐。
正在我一筹莫展时,灵光一现,Hibernate里有没有拦截器。百度一下果然有EmptyInterceptor
拦截器。但是根据网上的配置不生效。
3.看源码解决拦截器不生效问题
下面讲讲我是如何解决这个问题的
我通过idea
的find功能找到了源码中几处使用EmptyInterceptor
的代码块。 然后挨个源码查看。
在SessionFactoryBuilderImpl
中找到了了端倪
this.interceptor = strategySelector.resolveDefaultableStrategy(
Interceptor.class,
configurationSettings.get( INTERCEPTOR ),//自定义的拦截器
EmptyInterceptor.INSTANCE//默认的拦截器
);
复制代码
此处在构建SessionFactory过程中,会在此处设置interceptor
拦截器。这里算是用了一个简单策略模式模式吧。
逻辑:如果配置了自定义拦截器则使用自定义的拦截器。没有配置则适用默认的拦截器。
configurationSettings.get( INTERCEPTOR )
复制代码
此处取的就是配置的拦截器INTERCEPTOR 对应的配置key为
String INTERCEPTOR = "hibernate.session_factory.interceptor";
复制代码
也就说我们在配置自定义拦截器需要使用此配置key设置。
#配置
hibernate.session_factory.interceptor=com.wsjia.ms.doApplication.util.SerialIntercpter
@Value("${hibernate.session_factory.interceptor}")
private String INTERCEPTOR;
@Bean
public LocalSessionFactoryBean sessionFactory() {
LocalSessionFactoryBean sessionFactoryBean = new LocalSessionFactoryBean();
sessionFactoryBean.setDataSource(dataSource());
sessionFactoryBean.setPackagesToScan(ENTITYMANAGER_PACKAGES_TO_SCAN);
Properties hibernateProperties = new Properties();
hibernateProperties.put("hibernate.dialect", HIBERNATE_DIALECT);
hibernateProperties.put("hibernate.show_sql", HIBERNATE_SHOW_SQL);
hibernateProperties.put("hibernate.hbm2ddl.auto", HIBERNATE_HBM2DDL_AUTO);
hibernateProperties.put("hibernate.session_factory.interceptor",INTERCEPTOR);
sessionFactoryBean.setHibernateProperties(hibernateProperties);
return sessionFactoryBean;
}
复制代码
4.EmptyInterceptor原理
更进一步我又看了拦截器是如何生效的。
(1.首先:LocalSessionFactoryBean
做为一个FactoryBean
会创建一个SessionFactory
工厂用于创建Session
SessionFactory的实现类:SessionFactoryImpl
public SessionFactory build() {
metadata.validate();
return new SessionFactoryImpl( metadata, buildSessionFactoryOptions() );
}
复制代码
(2.其次
SessionFactoryImpl.openSession方法会调用session构建器SessionBuilderImpl
的openSession方法,构建一个session出来,此时就会把拦截器设置给session
public Session openSession() {
log.tracef( "Opening Hibernate Session. tenant=%s, owner=%s", tenantIdentifier, sessionOwner );
final SessionImpl session = new SessionImpl(
connection,
sessionFactory,
sessionOwner,
getTransactionCoordinator(),
getJdbcCoordinator(),
getTransaction(),
getTransactionCompletionProcesses(),
autoJoinTransactions,
sessionFactory.settings.getRegionFactory().nextTimestamp(),
interceptor,
statementInspector,
flushBeforeCompletion,
autoClose,
connectionReleaseMode,
tenantIdentifier
);
for ( SessionEventListener listener : listeners ) {
session.getEventListenerManager().addListener( listener );
}
return session;
}
复制代码
(3.session:
来看看SessionImpl.save方法
@Override
public Serializable save(Object obj) throws HibernateException {
return save( null, obj );
}
@Override
public Serializable save(String entityName, Object object) throws HibernateException {
return fireSave( new SaveOrUpdateEvent( entityName, object, this ) );
}
private Serializable fireSave(SaveOrUpdateEvent event) {
errorIfClosed();
checkTransactionSynchStatus();
checkNoUnresolvedActionsBeforeOperation();
for ( SaveOrUpdateEventListener listener : listeners( EventType.SAVE ) ) {
listener.onSaveOrUpdate( event );
}
checkNoUnresolvedActionsAfterOperation();
return event.getResultId();
}
复制代码
来看看这个save过程:
- 将当前entity 包装成一个SaveOrUpdateEvent事件
- 做一些check校验
- 调用
同步监听器
的onSaveOrUpdate方法,处理save事件 - 返回id
拦截器的调用是在监听器中发生的.AbstractSaveEventListener
protected boolean substituteValuesIfNecessary(
Object entity,
Serializable id,
Object[] values,
EntityPersister persister,
SessionImplementor source) {
//拦截器的调用
boolean substitute = source.getInterceptor().onSave(
entity,
id,
values,
persister.getPropertyNames(),
persister.getPropertyTypes()
);
}
复制代码
此时就会调用我们自定义的拦截器了
5.Hibernate与Mybatis 框架的思考
更进一步思考:Hibernate与Mybatis
读过Mybatis 源码和 少许Hibernate后,我产生了如下总结
- Mybatis 与 Hibernate 设计方式:配置化个性SessionFactory工厂的创建。使用工厂创建 Session。在这一点上大家是差不多的。
- Mybatis: 通过
Executor
执行器来处理增删改查 - Hibernate 使用
同步监听器
来处理操作,操作前先创建一个事件,然后调用监听器处理。
6.总结:
再复杂的框架也是一段段同步代码组织起来的。只要掌握了阅读源码的技巧,会发现多个框架的设计上有异曲同工之妙。阅读源码有助于帮助我们更好的解决问题