1.引入
我们在使用Mybatis创建四大对象的时候,都会有插件进行介入。那么在这里使用插件的好处是插件可以利用动态代理机制一层层的包装目标对象,而实现在目标对象执行目标方法之前进行拦截的效果(实质就是起到一个拦截器的效果)。因为MyBatis允许在已映射语句执行过程中的某一点进行拦截调用。这样如果我们需要在执行的过程中需要进行别的操作就会有一个更加方便的效果。
MyBatis 允许使用插件来拦截的方法调用包括内容如下:
• Executor (update, query, flushStatements, commit, rollback, getTransaction, close, isClosed)
• ParameterHandler (getParameterObject, setParameters)
• ResultSetHandler (handleResultSets, handleOutputParameters)
• StatementHandler (prepare, parameterize, batch, update, query)
2.一个插件开发的实例以及插件的开始步骤
(1).编写插件实现Interceptor接口,同时完成其接口中的方法重写。
①:编写第一个插件类:TestFirstPlugin.java
②:实现Interceptor接口,然后重写里面的内容信息。方法内容简介如下:
public class TestFirstPlugin implements Interceptor {
/**
* 拦截目标对象的目标方法的执行
*
*/
@Override
public Object intercept(Invocation invocation) throws Throwable {
// TODO Auto-generated method stub
//执行目标方法
Object proceed = invocation.proceed();
//返回执行结果
return proceed;
}
/**
* 四大对象在调用的时候调用的plugin方法。
* 其作用是包装目标对象,也就是为目标对象创建一个代理对象。
*
*/
@Override
public Object plugin(Object target) {
// TODO Auto-generated method stub
//我们在使用的时候可以借助Plugin的wrap方法来调用当前的Interceptor包装我们的目标对象
Object wrap = Plugin.wrap(target, this);
//返回为当前target创建的动态代理
return wrap;
}
/**
* 作用是将插件注册时的Property属性设置进来
*
*/
@Override
public void setProperties(Properties pro) {
// TODO Auto-generated method stub
System.out.println("属性信息为:" + pro);
}
}
(2).使用 @Intercepts注解完成插件签名。
我们知道Interceptor可以拦截每一个对象的每一个方法,那么如果我们需要实际让他去拦截一个对象的具体的方法的时候应该要告知他,你具体要拦截对象的方法是哪一个。
这一个时候就需要使用注解来完成插件的签名。具体的实现操作如下:
@Intercepts({
@Signature(type=StatementHandler.class,
method="parameterize",
args=java.sql.Statement.class)
})
public class TestFirstPlugin implements Interceptor {
//省略代码...
}
注解内容说明:
1.Intercepts注解内容是一个Signature数组(见最后)。
2.type值的是需要拦截的四大对象中的哪一个对象。
3.method指定对象的具体的方法。
4.方法中的参数列表信息
Signature数组接口
public @interface Signature {
Class<?> type();
String method();
Class<?>[] args();
}
(3).在全局配置文件中注册插件(mybatis-config.xml文件中)。
如果我们仅仅只是把这些内容写在类上,MyBatis是不知道的,那么这个时候我们需要把我们做的写一些信息告诉Mybatis,也就是把它写入到全局配置文件中去。具体内容如下:
<!--plugins:注册插件 -->
<plugins>
<plugin interceptor="com.mybatis.mybatis.dao.TestFirstPlugin.java">
<!--可以传递一些参数信息,也可以省略-->
<property name="username" value="root"/>
<property name="password" value="123456"/>
</plugin>
</plugins>
(4).测试以及结果输出
3.多个插件使用测试
(1).编写插件实现Interceptor接口,同时完成其接口中的方法重写。并使用@Intercepts注解完成插件签名。创建MySecondPlugin.java类。
@Intercepts(
{
@Signature(
type=StatementHandler.class,
method="parameterize",
args=java.sql.Statement.class
)
})
public class MySecondPlugin implements Interceptor{
@Override
public Object intercept(Invocation invocation) throws Throwable {
System.out.println("MySecondPlugin...intercept:"+invocation.getMethod());
return invocation.proceed();
}
@Override
public Object plugin(Object target) {
// TODO Auto-generated method stub
System.out.println("MySecondPlugin...plugin:"+target);
return Plugin.wrap(target, this);
}
@Override
public void setProperties(Properties properties) {
// TODO Auto-generated method stub
}
}
(2).在全局配置文件中注册插件(mybatis-config.xml文件中)。
<!--plugins:注册插件 -->
<plugins>
<plugin interceptor="com.mybatis.mybatis.dao.TestFirstPlugin.java">
<!--可以传递一些参数信息,也可以省略-->
<property name="username" value="root"/>
<property name="password" value="123456"/>
<plugin interceptor="com.mybatis.mybatis.dao.MySecondPlugin"></plugin>
</plugin>
</plugins>
(3).执行结果如下:
4.插件执行总结:
(1).按照插件注解声明,按照插件配置顺序调用插件plugin方 法,生成被拦截对象的动态代理。
(2).多个插件依次生成目标对象的代理对象,层层包裹,先声 明的先包裹;形成代理链。
(3).目标方法执行时依次从外到内执行插件的intercept方法。
(4).多个插件情况下,我们往往需要在某个插件中分离出目标 对象。可以借助MyBatis提供的SystemMetaObject类来进行获 取最后一层的h以及target属性的值。
5.插件开发的实际应用(基于单个拦截器使用):拦截sql,实现sql当中分页的动态参数调整
/**
* 完成插件签名:
* 告诉MyBatis当前插件用来拦截哪个对象的哪个方法
*/
@Intercepts(
{
@Signature(type=StatementHandler.class,method="parameterize",args=java.sql.Statement.class)
})
public class MyFirstPlugin implements Interceptor{
/**
* intercept:拦截:
* 拦截目标对象的目标方法的执行;
*/
@Override
public Object intercept(Invocation invocation) throws Throwable {
// TODO Auto-generated method stub
System.out.println("MyFirstPlugin...intercept:"+invocation.getMethod());
//动态的改变一下sql运行的参数:以前1号员工,实际从数据库查询3号员工
Object target = invocation.getTarget();
System.out.println("当前拦截到的对象:"+target);
//拿到:StatementHandler==>ParameterHandler===>parameterObject
//拿到target的元数据
MetaObject metaObject = SystemMetaObject.forObject(target);
Object value = metaObject.getValue("parameterHandler.parameterObject");
System.out.println("sql语句用的参数是:"+value);
//修改完sql语句要用的参数
metaObject.setValue("parameterHandler.parameterObject", 11);
//执行目标方法
Object proceed = invocation.proceed();
//返回执行后的返回值
return proceed;
}
/**
* plugin:
* 包装目标对象的:包装:为目标对象创建一个代理对象
*/
@Override
public Object plugin(Object target) {
// TODO Auto-generated method stub
//我们可以借助Plugin的wrap方法来使用当前Interceptor包装我们目标对象
System.out.println("MyFirstPlugin...plugin:mybatis将要包装的对象"+target);
Object wrap = Plugin.wrap(target, this);
//返回为当前target创建的动态代理
return wrap;
}
/**
* setProperties:
* 将插件注册时 的property属性设置进来
*/
@Override
public void setProperties(Properties properties) {
// TODO Auto-generated method stub
System.out.println("插件配置的信息:"+properties);
}