MyBatis 允许你在映射语句执行过程中的某一点进行拦截调用。默认情况下,MyBatis 允许使用插件来拦截的方法调用包括:
- Executor (update, query, flushStatements, commit, rollback, getTransaction, close, isClosed)
- ParameterHandler (getParameterObject, setParameters)
- ResultSetHandler (handleResultSets, handleOutputParameters)
- StatementHandler (prepare, parameterize, batch, update, query)(最常用的拦截对象)
使用插件
// ExamplePlugin.java
@Intercepts({@Signature(
type= Executor.class,
method = "update",
args = {MappedStatement.class,Object.class})})
public class ExamplePlugin implements Interceptor {
private Properties properties = new Properties();
public Object intercept(Invocation invocation) throws Throwable {
// implement pre processing if need
Object returnObject = invocation.proceed();
// implement post processing if need
return returnObject;
}
public void setProperties(Properties properties) {
this.properties = properties;
}
}
!-- mybatis-config.xml -->
<plugins>
<plugin interceptor="org.mybatis.example.ExamplePlugin">
<property name="someProperty" value="100"/>
</plugin>
</plugins>
上面的插件将会拦截在 Executor 实例中所有的 “update” 方法调用, 这里的 Executor 是负责执行底层映射语句的内部对象
具体示例可以参考pagehelper插件
MyBatis插件调用流程
与 Spring 整合分析
几个关键的问题,我们要弄清楚:
1、 SqlSessionFactory 是什么时候创建的?
2、 SqlSession 去哪里了?为什么不用它来 getMapper?
3、 为什么@Autowired 注入一个接口,在使用的时候却变成了代理对象?在 IOC 的容器里面我们注入的是什么? 注入的时候发生了什么事情?
关键配置
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="configLocation" value="classpath:mybatis-config.xml"></property>
<property name="mapperLocations" value="classpath:mapper/*.xml"></property>
<property name="dataSource" ref="dataSource"/>
</bean>
<bean id="mapperScanner" class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="basePackage" value="com.study.crud.dao"/>
</bean>
1、创建会话工厂
SqlSessionFactoryBean它实现了 InitializingBean 接口,所以要实现 afterPropertiesSet()方法,这个方法 会在 bean 的属性值设置完的时候被调用。 另外它实现了 FactoryBean 接口,所以它初始化的时候,实际上是调用 getObject() 方法,它里面调用的也是 afterPropertiesSet()方法。最后跟下去代码,返 回 了 一 个 DefaultSqlSessionFactory
2、创建 SqlSession
DefaultSqlSession它是线程不安全的。所以使用SqlSessionTemplate
public SqlSessionTemplate(SqlSessionFactory sqlSessionFactory, ExecutorType executorType,PersistenceExceptionTranslator exceptionTranslator) { this.sqlSessionFactory = sqlSessionFactory; this.executorType = executorType; this.exceptionTranslator = exceptionTranslator; this.sqlSessionProxy = (SqlSession) newProxyInstance( SqlSessionFactory.class.getClassLoader(), new Class[] { SqlSession.class }, new SqlSessionInterceptor()); }
所有的方法都会先走到内部代理类 SqlSessionInterceptor 的 invoke()方法。MyBatis 它提供了一个 SqlSessionDaoSupport,里面持有一个 SqlSessionTemplate 对象,并且提供了一个 getSqlSession()方法,让我们获得一个 SqlSessionTemplate。然后让dao的实现类继承它,并使用方法操作。(通常和spring结合后不这样用)
3、接口的扫描注册
MapperScannerConfigurer 实现了 BeanDefinitionRegistryPostProcessor 接口,重写 postProcessBeanDefinitionRegistry()方法,在这个方法里面: 调用了scanner.scan()中 调 用 了 processBeanDefinitions在注册 beanDefinitions 的时候,BeanClass 被改为 MapperFactoryBean。MapperFactoryBean 继 承 了 SqlSessionDaoSupport , 可 以 拿 到 SqlSessionTemplate
4、接口注入使用
Spring 会根据 Mapper 的名字从 BeanFactory 中获取它的 BeanDefination,再从 BeanDefination 中 获 取 BeanClass , demoMapper 对 应 的 BeanClass 是 MapperFactoryBean
因为实现了 FactoryBean 接口,同样是调 用 getObject()方法也就是说,我们注入到 Service 层的接口,实际上还是一个 MapperProxy 代理对象。 所以最后调用 Mapper 接口的方法,也是执行 MapperProxy 的 invoke()方法,后面的 流程就跟编程式的工程里面一模一样了