`
projecttian
  • 浏览: 32637 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

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(接口类型)

 

 

分享到:
评论
1 楼 secondriver 2013-11-26  
" target="_blank">" />" target="_blank">" wmode="" quality="high" menu="false" pluginspage="http://www.macromedia.com/go/getflashplayer" type="application/x-shockwave-flash" width="200" height="200">

相关推荐

Global site tag (gtag.js) - Google Analytics