拦截器核心类:
先来看看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(接口类型))