Mybatis的插件实现原理(JDK动态代理+责任链模式)
HelloService //目标接口
sayHello() //目标方法
HelloServiceImpl //目标接口实现类
Invocation //是目标方法的封装
public Object process() throws Exception{
return method.invoke(target,args);
}
Interceptor //拦截器接口
intercept(Invocation invocation); //拦截方法,执行拦截器逻辑
public Object plugin(Object target) {
return TargetProxy.wrap(target,this);
} //创建代理对象并返回
InterceptorChain //拦截器链类
private List<Interceptor> interceptorList = new ArrayList<>();
public Object pluginAll(Object target) {
for (Interceptor interceptor : interceptorList) {
target = interceptor.plugin(target);//插入拦截器
}
return target;
}
主要说明拦截器接口,包括两个重要方法,一个是intercept里面是拦截器的主要逻辑,还有一个是plugin方法,用来创建目标方法和拦截器的代理类。
subInvocationHandler implenents InvocationHandler //自定义代理对象需要实现代理接口
invoke(Object proxy, Method method, Object[] args){
Invocation invocation = new Invocation(target,method,args);
return interceptor.intercept(invocation);
}//代理类执行目标会回调invoke
//代理类的创建
public static Object wrap(Object target,Interceptor interceptor) {
SubInvocationHandler targetProxy = new SubInvocationHandler(target, interceptor);
return Proxy.newProxyInstance(target.getClass().getClassLoader(),
target.getClass().getInterfaces(),targetProxy);
}
代理类执行时,会回调InvocationHandler的invoke。在invoke中会调用拦截器的intercept方法。
//测试方法
public class Test {
public static void main(String[] args) {
HelloService target = new HelloServiceImpl();
Interceptor transactionInterceptor = new TransactionInterceptor();
LogInterceptor logInterceptor = new LogInterceptor();
InterceptorChain interceptorChain = new InterceptorChain();
interceptorChain.addInterceptor(transactionInterceptor);
interceptorChain.addInterceptor(logInterceptor);
target = (Target) interceptorChain.pluginAll(target);
target.sayHello();
}
}
创建目标对象和拦截器,将拦截器添加到拦截器链。调用拦截器链的pluginAll嵌套创建代理对象(调用拦截器的plugin方法)。target.sayHello()方法执行时会回调自定义代理类InvocationHandler的invoke方法,再invoke中会调用interceptor的intercept(invocation)方法,在最后一个拦截器中会执行invocation.process()调用目标方法。
mybatis Plugin
Plugin
类实现了InvocationHandler
接口;
调用ParameterHandler,ResultSetHandler,StatementHandler,Executor时会执行Plugin
的invoke
方法,并根据@Intercepts
的配置信息(方法名,参数等)动态判断是否需要拦截该方法.再然后使用需要拦截的方法Method封装成Invocation,并调用Interceptor的proceed
方法。
拦截器代理类对象->拦截器->目标方法
Executor.Method->Plugin.invoke->Interceptor.intercept->Invocation.proceed->method.invoke
Interceptor
接口 :org.apacheibatis.plugin.Interceptor
public interface Interceptor {
//当plugin函数返回代理,就可以对其中的方法进行拦截来调用intercept方法
Object intercept(Invocation invocation) throws Throwable;
//plugin方法是拦截器用于封装目标对象的,通过该方法我们可以返回目标对象本身,也可以返回一个它的代理。
Object plugin(Object target);
//在Mybatis配置文件中指定一些属性
void setProperties(Properties properties);
}
拦截器上注解接口配置
@Intercepts({@Signature( type= Executor.class, method = "update",
args ={MappedStatement.class,Object.class})})
- type:表示拦截的类,这里是Executor的实现类
- method:表示拦截的方法,这里是拦截Executor的query方法
- args:表示方法参数
这个拦截器会拦截Executor接口的update方法
mybatis的拦截器链的入口
在Configuration实例化Executor、ParameterHandler、ResultSetHandler、StatementHandler四大接口对象的时候调用interceptorChain.pluginAll()
方法插入进去的。其实就是循环执行拦截器链所有的拦截器的plugin() 方法,mybatis官方推荐的plugin方法是Plugin.wrap() 方法,这个类就是我们上面的TargetProxy类。
- Executor (update, query, flushStatements, commit, rollback, getTransaction, close, isClosed) MyBatis的执行器,用于执行增删改查操作;
- ParameterHandler (getParameterObject, setParameters) 处理SQL的参数对象;
- .ResultSetHandler (handleResultSets, handleOutputParameters) 处理SQL的返回结果集;
- StatementHandler (prepare, parameterize, batch, update, query) 拦截Sql语法构建的处理;
参考地址:https://www.cnblogs.com/qdhxhz/p/11390778.html