欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

mybatis源码解析之拦截器 博客分类: mybatis mybatis代理 

程序员文章站 2024-03-14 08:43:34
...

拦截器核心类:

先来看看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 代理