Mybatis之Interceptor原理分析
Mybatis之Interceptor原理分析
Interceptor介绍
Mybatis提供了一种插件plugin来实现拦截器功能,Interceptor是Mybatis的一个拦截是接口,通过实现Interceptor可以实现对sql的增强。例如加解密数据,分页等功能
Executor,ResultSetHandler,StatementHandler,ParameterHandler,这是Mybatis中的四大对象,也是拦截器的切入点。我们可以基于这四大对象的方法进行增强
MyBatis 允许使用插件来拦截的方法调用包括:
Executor (update, query, flushStatements, commit, rollback, getTransaction, close, isClosed)
ParameterHandler (getParameterObject, setParameters)
ResultSetHandler (handleResultSets, handleOutputParameters)
StatementHandler (prepare, parameterize, batch, update, query)
总体概括为:
- 拦截执行器的方法
- 拦截参数的处理
- 拦截结果集的处理
- 拦截sql语法构建的处理
Interceptor具体使用
Interceptor在工程中具体使用流程,下面已添加解密的dbKey为例进行分析
在执行任何sql语句是,都会在参数中添加dbKey这个参数
1.新增实现类
这个拦截器拦截StatementHandler接口的prepare方法,所有执行prepare方法都会被拦截
@Intercepts({@Signature(type = StatementHandler.class, method = "prepare", args = {Connection.class, Integer.class})})
public class MybatisStmtInterceptor implements Interceptor {
private String dbKey = null;
@Override
public Object intercept(Invocation invocation)
throws Throwable {
StatementHandler statementHandler = (StatementHandler) invocation.getTarget();
BoundSql boundSql = statementHandler.getBoundSql();
// 所有sql执行语句的参数中都增加一个dbKey参数
boundSql.setAdditionalParameter("dbKey", dbKey);
Object result = invocation.proceed();
return result;
}
@Override
public void setProperties(Properties properties) {
dbKey = properties.getProperty("dbKey");
}
@Override
public Object plugin(Object target) {
return Plugin.wrap(target, this);
}
}
这三个方法的执行分别是setProperties,plugin,intercept
其中,setProperties是在spring启动加载plugins成为bean时调用的,可以从中获取Properties中获取Interceptor配置的对应属性供拦截器使用(譬如在上述代码中只能获取dbKey,是因为配置文件中该Interceptor只配置了dbKey)
2.配置文件配置拦截器
在Spring的配置文件中能够添加
<bean id="sqlSessionFactory"
class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource"/>
<property name="configuration">
<bean class="org.apache.ibatis.session.Configuration">
<property name="mapUnderscoreToCamelCase" value="true"/>
</bean>
</property>
<!-- ORM对象存放的包地址 -->
<property name="typeAliasesPackage"
value="com.alliance.openhde.dao.bean"/>
<!-- sqlmap配置文件地址 -->
<property name="mapperLocations"
value="classpath:mybatis/mapping/*.xml"/>
<property name="plugins">
<array>
<bean class="com.commons.plugin.MybatisStmtInterceptor">
<property name="properties">
<value>dbKey=${db_key}</value>
</property>
</bean>
</array>
</property>
</bean>
这两部配置好后,拦截器即配置成功,以后在工程中就不用关心dbKey的问题,所有的sql参数都会自动默认添加dbKey这个参数。
源码分析
1.读取配置文件plugins加载到内存中
在工程启动时会通过SqlSessionFactoryBuilder.builder方法进行加载配置文件,解析xml文件,关键代码
如下
解析plugins标签
XMLConfigBuilder.java
private void parseConfiguration(XNode root) {
try {
this.propertiesElement(root.evalNode("properties"));
Properties settings = this.settingsAsProperties(root.evalNode("settings"));
this.loadCustomVfs(settings);
this.loadCustomLogImpl(settings);
this.typeAliasesElement(root.evalNode("typeAliases"));
this.pluginElement(root.evalNode("plugins"));
this.objectFactoryElement(root.evalNode("objectFactory"));
this.objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));
this.reflectorFactoryElement(root.evalNode("reflectorFactory"));
this.settingsElement(settings);
this.environmentsElement(root.evalNode("environments"));
this.databaseIdProviderElement(root.evalNode("databaseIdProvider"));
this.typeHandlerElement(root.evalNode("typeHandlers"));
this.mapperElement(root.evalNode("mappers"));
} catch (Exception var3) {
throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + var3, var3);
}
}
通过反射生成Interceptor对象,把Interceptor对象添加到InterceptorChain中的Interceptor数组中
XMLConfigBuilder.java
private void pluginElement(XNode parent) throws Exception {
if (parent != null) {
Iterator var2 = parent.getChildren().iterator();
while(var2.hasNext()) {
XNode child = (XNode)var2.next();
String interceptor = child.getStringAttribute("interceptor");
Properties properties = child.getChildrenAsProperties();
Interceptor interceptorInstance = (Interceptor)this.resolveClass(interceptor).newInstance();
interceptorInstance.setProperties(properties);
this.configuration.addInterceptor(interceptorInstance);
}
}
}
Configuration.java
public void addInterceptor(Interceptor interceptor) {
this.interceptorChain.addInterceptor(interceptor);
}
InterceptorChain.java
private final List<Interceptor> interceptors = new ArrayList();
public void addInterceptor(Interceptor interceptor) {
this.interceptors.add(interceptor);
}
可以看出Interceptor 实例化后最终添加到InterceptorChain类中的Interceptor的list中供后续代码使用