mybatis源码解析之拦截器 博客分类: mybatis mybatis代理
拦截器核心类:
先来看看jdk的代理是如何实现的:
java.lang.reflect.InvocationHandler java.lang.reflect.Proxy public class MyInvocationHandler implements InvocationHandler{ private Object target; public MyInvocationHandler(Object target) { super(); this.target = target; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println(Object.class); System.out.println(method.getDeclaringClass()); System.out.println("------------------before------------------"); // 执行目标对象的方法 Object result = method.invoke(target, args); // 在目标对象的方法执行之后简单的打印一下 System.out.println("-------------------after------------------"); return result; } public Object getProxy() { return Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(), target.getClass().getInterfaces(), this); } }
测试:
UserService userService = new UserServiceImpl(); // 实例化InvocationHandler MyInvocationHandler invocationHandler = new MyInvocationHandler(userService); // 根据目标对象生成代理对象 UserService proxy = (UserService) invocationHandler.getProxy(); // 调用代理对象的方法 proxy.add();
下面就看一下mybatis的拦截器是如何实现的:
在org.apache.ibatis.plugin 下有拦截器的核心代码
注解类 Interceptors 和 Signature
Ivocation接口是要求自定义的拦截器必须继承自这个接口重新他的方法
Plugin 这个类继承与InvocationHandler
InterceptorChain 是一个拦截器连,既然是连儿那么里面一定有结合来存储和注册拦截器的方法
现在我们先来用mybatis的拦截器做一个测试。
import java.util.HashMap; import java.util.Map; import java.util.Properties; import org.apache.ibatis.plugin.Interceptor; import org.apache.ibatis.plugin.Intercepts; import org.apache.ibatis.plugin.Invocation; import org.apache.ibatis.plugin.Plugin; import org.apache.ibatis.plugin.Signature; import org.junit.Test; public class PluginTest { public static void main(String[] args) { Map map = new HashMap(); map = (Map) new AlwaysMapPlugin().plugin(map); map.put("asdfadfs","ddddd"); System.out.println(map.getClass()); System.out.println( map.get("asdfadfs")); } @Intercepts({ @Signature(type = Map.class, method = "get", args = {Object.class})}) public static class AlwaysMapPlugin implements Interceptor { //连接器触发的方法 public Object intercept(Invocation invocation) throws Throwable { invocation.proceed(); System.out.println("拦截器被触发"); return "Always"; } //创建代理类 public Object plugin(Object target) { return Plugin.wrap(target, this); } public void setProperties(Properties properties) { } } }
上面的代码中有我们自定义的拦截器AlwaysMapPlugin ,它继承自Interceptor 实现了它的方法。
intercept 这个方法是在触发自定义拦截器注解中的类和方法的时候触发的。
Invocation 参数中定义了
private Object target; 目标类,也就是测试中的map
private Method method; 方法类 注解中的get方法
private Object[] args; 方法参数
plugin 这个方法中使用了Plugin这个代理类,Plugin.wrap(target, this); 返回一个拦截器代码,你可以测试一下 System.out.println(map.getClass());控制台输出class $Proxy5 是一个Map的代理对象。
整个拦截器很简单,唯一Plugin.wrap(target, this);这个类的调用还没有被看到,也是拦截器的核心。
public class Plugin implements InvocationHandler { private Object target; private Interceptor interceptor; private Map<Class<?>, Set<Method>> signatureMap; private Plugin(Object target, Interceptor interceptor, Map<Class<?>, Set<Method>> signatureMap) { .......... } //静态方法获取代理类 public static Object wrap(Object target, Interceptor interceptor) { ....... 返回代理类 return Proxy.newProxyInstance( type.getClassLoader(),//classloader interfaces,//工具类中处理的接口 new Plugin(target, interceptor, signatureMap)//实例化代理对象 ); .......... } //代理方法 执行被代理方法中的intercept实现 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {......... } //工具方法 获取自定义拦截器中注解中定义的接口类 private static Map<Class<?>, Set<Method>> getSignatureMap(Interceptor interceptor) {......... } //工具方法 type.getInterfaces() //type 是被代理对象 signatureMap 是上面的工具方法获取的要代理的接口类列表 // 方法返回自定义连接器中定义的接口和代理对象实例的接口列表 //说白了:这个方法就是来判断new AlwaysMapPlugin().plugin(map); 这个map实例的接口 //是否有 @Signature(type = Map.class, 这里的Map接口。 private static Class<?>[] getAllInterfaces(Class<?> type, Map<Class<?>, Set<Method>> signatureMap) {...... } }
如果要添加上连接器链:测试代码这样写
Map map = new HashMap(); InterceptorChain chain = new InterceptorChain(); //注册拦截器 chain.addInterceptor(new CustomInterceptor()); //获取代理类 map = (Map)chain.pluginAll(map); map.get("ddd");
那么我们来看看mybatis中的拦截器是合适调用的
操作数据库的步骤是 获取config资源,构建sqlsessionFactory,获取sqlsession,操作数据库。
我们在xml文件中定义好的拦截器就是在构建Configuration的时候注册到org.apache.ibatis.session.Configuration类中去的。在这个类里面有我们熟悉的InterceptorChain 拦截器连儿。
什么时候执行这个连接器呢,我们要看一下它的源码。
//方法1 public ParameterHandler newParameterHandler(MappedStatement mappedStatement, Object parameterObject, BoundSql boundSql) { ParameterHandler parameterHandler = mappedStatement.getLang().createParameterHandler(mappedStatement, parameterObject, boundSql); parameterHandler = (ParameterHandler) interceptorChain.pluginAll(parameterHandler); return parameterHandler; } //方法2 public ResultSetHandler newResultSetHandler(Executor executor, MappedStatement mappedStatement, RowBounds rowBounds, ParameterHandler parameterHandler, ResultHandler resultHandler, BoundSql boundSql) { ResultSetHandler resultSetHandler = mappedStatement.hasNestedResultMaps() ? new NestedResultSetHandler(executor, mappedStatement, parameterHandler, resultHandler, boundSql, rowBounds) : new FastResultSetHandler(executor, mappedStatement, parameterHandler, resultHandler, boundSql, rowBounds); resultSetHandler = (ResultSetHandler) interceptorChain.pluginAll(resultSetHandler); return resultSetHandler; } //方法3 public StatementHandler newStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) { StatementHandler statementHandler = new RoutingStatementHandler(executor, mappedStatement, parameterObject, rowBounds, resultHandler, boundSql); statementHandler = (StatementHandler) interceptorChain.pluginAll(statementHandler); return statementHandler; } //方法4 public Executor newExecutor(Transaction transaction, ExecutorType executorType, boolean autoCommit) { executorType = executorType == null ? defaultExecutorType : executorType; executorType = executorType == null ? ExecutorType.SIMPLE : executorType; Executor executor; if (ExecutorType.BATCH == executorType) { executor = new BatchExecutor(this, transaction); } else if (ExecutorType.REUSE == executorType) { executor = new ReuseExecutor(this, transaction); } else { executor = new SimpleExecutor(this, transaction); } if (cacheEnabled) { executor = new CachingExecutor(executor, autoCommit); } executor = (Executor) interceptorChain.pluginAll(executor); return executor; }
还记得我们自定义的例子中 map = (Map)chain.pluginAll(map); 返回代理类对象
这里的四个方法其实就是限制了,mybatis 中拦截器可以连接的接口,Executor,StatementHandler, ResultSetHandler,ParameterHandler ( 因为:interceptorChain.pluginAll(接口类型))
推荐阅读
-
mybatis源码解析之拦截器 博客分类: mybatis mybatis代理
-
mybatis源码解析之mapper接口代理 博客分类: mybatis mybatis
-
mybatis源码解析之拦截器 博客分类: mybatis mybatis代理
-
mybatis源码解析之mapper接口代理 博客分类: mybatis mybatis
-
MyBatis之动态SQL 博客分类: java
-
spring源码解析之七(spring-mybatis)
-
荐 [设计模式] 代理模式之 静态代理与动态代理 & Mybatis实例解析
-
MyBatis源码分析之——配置解析创建SqlSessionFactory的过程
-
mybatis源码解析之基础模块-log
-
mybatis源码解析之基础模块-TypeHandler