• MyBatis框架的使用及源码分析(七) MapperProxy,MapperProxyFactory


    从上文<MyBatis框架中Mapper映射配置的使用及原理解析(六) MapperRegistry> 中我们知道DefaultSqlSession的getMapper方法,最后是通过MapperRegistry对象获得Mapper实例:

    public <T> T getMapper(Class<T> type, SqlSession sqlSession) {  
      final MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory<T>) knownMappers.get(type);  
      if (mapperProxyFactory == null) {  //说明这个Mapper接口没有注册
        throw new BindingException("Type " + type + " is not known to the MapperRegistry.");  
      }  
      try {  //生成一个MapperProxy对象  
        return mapperProxyFactory.newInstance(sqlSession);  
      } catch (Exception e) {  
        throw new BindingException("Error getting mapper instance. Cause: " + e, e);  
      }  
    }  

     从代码中我们看到试图从一个叫knownMappers的变量取出MapperProxyFactory。

    我们看看这个knownMapper在MapperRegistry中的定义: 

    private final Map<Class<?>, MapperProxyFactory<?>> knownMappers = new HashMap<Class<?>, MapperProxyFactory<?>>();  

     有getMapper方法,那么必然后addMapper方法:

    public <T> void addMapper(Class<T> type) {  
      if (type.isInterface()) {  
        if (hasMapper(type)) {  
          throw new BindingException("Type " + type + " is already known to the MapperRegistry.");  
        }  
        boolean loadCompleted = false;  
        try {  
          knownMappers.put(type, new MapperProxyFactory<T>(type));  
          // It's important that the type is added before the parser is run  
          // otherwise the binding may automatically be attempted by the  
          // mapper parser. If the type is already known, it won't try.  
          MapperAnnotationBuilder parser = new MapperAnnotationBuilder(config, type);  
          parser.parse();  
          loadCompleted = true;  
        } finally {  
          if (!loadCompleted) {  
            knownMappers.remove(type);  
          }  
        }  
      }  
    }  

     在<MyBatis框架中Mapper映射配置的使用及原理解析(四) 解析Mapper接口映射xml文件> 文章中,解析玩xml后,绑定命名空间bindMapperForNamespace()方法

    //绑定到命名空间
    private void bindMapperForNamespace() {
      String namespace = builderAssistant.getCurrentNamespace();
      if (namespace != null) {
        Class<?> boundType = null;
        try {
          boundType = Resources.classForName(namespace);
        } catch (ClassNotFoundException e) {
          //ignore, bound type is not required
        }
        if (boundType != null) {
          if (!configuration.hasMapper(boundType)) {
            // Spring may not know the real resource name so we set a flag
            // to prevent loading again this resource from the mapper interface
            // look at MapperAnnotationBuilder#loadXmlResource
            configuration.addLoadedResource("namespace:" + namespace);
            configuration.addMapper(boundType);
          }
        }
      }
    }

    我们看到 

    configuration.addMapper(boundType);

    正是调用MapperRegistry.addMapper方法

      public <T> void addMapper(Class<T> type) {
        mapperRegistry.addMapper(type);
      }

    我们在回过头来看getMapper是如何获得Mapper对象的: 

    1.先获取MapperProxyFactory

    2.再调用MapperProxyFactory对象的newInstance方法获得Mapper。

     我们看MapperProxyFactory代码:

    public T newInstance(SqlSession sqlSession) {  
    //创建一个Mapperxy对象,这个方法实现了JDK动态代理中的InvocationHandler接口
    final MapperProxy<T> mapperProxy = new MapperProxy<T>(sqlSession, mapperInterface, methodCache); return newInstance(mapperProxy); } protected T newInstance(MapperProxy<T> mapperProxy) {
    //mapperInterface,说明Mapper接口被代理了,这样子返回的对象就是Mapper接口的子类,方法被调用时会被mapperProxy拦截,也就是执行mapperProxy.invoke()方法 
    return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy); }

    这里就是返回的一个代理类实例MapperProxy。

    package org.apache.ibatis.binding;
    
    import java.io.Serializable;
    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.Method;
    import java.util.Map;
    
    import org.apache.ibatis.reflection.ExceptionUtil;
    import org.apache.ibatis.session.SqlSession;
    
    /**
     * @author Clinton Begin
     * @author Eduardo Macarron
     */
    public class MapperProxy<T> implements InvocationHandler, Serializable {
    
      private static final long serialVersionUID = -6424540398559729838L;
      private final SqlSession sqlSession;
    //Mapper接口 
    private final Class<T> mapperInterface;

        /*
         * Mapper接口中的每个方法都会生成一个MapperMethod对象, methodCache维护着他们的对应关系
         */  
        private final Map<Method, MapperMethod> methodCache;

      public MapperProxy(SqlSession sqlSession, Class<T> mapperInterface, Map<Method, MapperMethod> methodCache) {
        this.sqlSession = sqlSession;
        this.mapperInterface = mapperInterface;
        this.methodCache = methodCache;
      }
    
    //这里会拦截Mapper接口的所有方法 
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { if (Object.class.equals(method.getDeclaringClass())) { //如果是Object中定义的方法,直接执行。如toString(),hashCode()等 try { return method.invoke(this, args);// } catch (Throwable t) { throw ExceptionUtil.unwrapThrowable(t); } } final MapperMethod mapperMethod = cachedMapperMethod(method); //其他Mapper接口定义的方法交由mapperMethod来执行 return mapperMethod.execute(sqlSession, args); } private MapperMethod cachedMapperMethod(Method method) { MapperMethod mapperMethod = methodCache.get(method); if (mapperMethod == null) { mapperMethod = new MapperMethod(mapperInterface, method, sqlSession.getConfiguration()); methodCache.put(method, mapperMethod); } return mapperMethod; } }

    要使用Java的动态代理就必须得实现InvocationHandler接口:

    @Override  
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {  
      try {  
        if (Object.class.equals(method.getDeclaringClass())) {  
          return method.invoke(this, args);  
        } else if (isDefaultMethod(method)) {  
          return invokeDefaultMethod(proxy, method, args);  
        }  
      } catch (Throwable t) {  
        throw ExceptionUtil.unwrapThrowable(t);  
      }  
      final MapperMethod mapperMethod = cachedMapperMethod(method);  
      return mapperMethod.execute(sqlSession, args);  
    }  

    首先判断代理对象是一个接口还是一个类,显然我们没有对mapper接口进行任何实现,那么它将执行

    final MapperMethod mapperMethod = cachedMapperMethod(method);  
    return mapperMethod.execute(sqlSession, args);  

    生成一个MapperMethod对象,接着调用其execute方法,把sqlSession和参数传递进去,执行Mapper方法。

  • 相关阅读:
    js键盘按钮keyCode及示例大全
    HTML 禁止复制文字
    【Layui】formSelects下拉多选框取值
    JQuery 判断访问的浏览器是pc还是手机
    php发送post请求的方法
    用PHP实现小写金额转换大写金额【精确到分】
    PHP获取文件大小的几种方法!
    js jquery 数组的合并 对象的合并
    web页面长时间未操作自动退出登录
    获取公众号素材管理列表-教程
  • 原文地址:https://www.cnblogs.com/zsg88/p/7551987.html
Copyright © 2020-2023  润新知