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.总结:
再复杂的框架也是一段段同步代码组织起来的。只要掌握了阅读源码的技巧,会发现多个框架的设计上有异曲同工之妙。阅读源码有助于帮助我们更好的解决问题