mybatis-mapper的动态代理原理

一、配置mapper

在进行动态代理前,我们需要在mybatis的全局配置环境中配置mapper标签,告诉mybatis我的mapper都在哪,同时需要在项目中创建mapper的xml文件和对应的接口

注意:mapper的xml文件要与接口名一样,namespace也需要设置为接口的全路径名,只有这样才能在为mapper接口做动态代理的时候找到正确的sql语句

二、源码分析

涉及到的模块

1. DefaultSqlSession:sqlsession的实现类,对jdbc的connection进行了封装

2. Configuation:mabatis配置文件对应的类,记录了mybatis的配置信息

3. MapperRegistry:Mapper接口的注册,通过内部的addMapper方法将指定位置的mapper接口存储到
knownMappers(该类的一个字段,Map
*< Class , MapperProxyFactory >)中 **

4. MapperProxyFactory:该类是代理工厂类,内置了有mapper接口的class和Map methodCache,该类的作用是创建代理对象

5. MapperProxy:代理对象实现类,内置了一个methodCache的map存放键值对(method,MapperMethod),还有mapper接口的class和sqlsession对象

6. MapperMethod:mapper接口方法对应的实现方法,当我们执行mapper中的方法时,都是通过mapperProxy中的methodCache找到method对应的MapperMethod,最后MapperMethod的execute方法(execute内部是执行sqlsession的方法)

7. 注意:

public class MapperMethod {
	//command根据configuration处理mapper接口方法对应xml文件中的sql语句
	//method根据configuation处理方法的参数以及根据返回类型将sqlsession返回的结果进行封装
	//这也是要求mapper接口与xml文件同名并且namespace为接口全路径的原因所在
    private final MapperMethod.SqlCommand command;
    private final MapperMethod.MethodSignature method;
}

业务代码

FlowerMapper mapper = sqlSession.getMapper(FlowerMapper.class);

DefaultSqlSession

public  T getMapper(Class type) {
		//此时传入了this,也就是我们业务代码中的sqlSession
        return this.configuration.getMapper(type, this);
    }

Configuration

public  T getMapper(Class type, SqlSession sqlSession) {
        return this.mapperRegistry.getMapper(type, sqlSession);
    }

上面的步骤比较简单,就是通过模块之间的调用将mapper的class和sqlsession传入了mapperRegistry的getMapper中

mapperRegistry

public  T getMapper(Class type, SqlSession sqlSession) {
		//knownMapper包含了扫描到的mapper的接口
		//判断传进来的class是否在knownMapper中,是的话则将其转化为代理工厂,不是则抛出异常
        MapperProxyFactory mapperProxyFactory = (MapperProxyFactory)this.knownMappers.get(type);
        if (mapperProxyFactory == null) {
            throw new BindingException("Type " + type + " is not known to the MapperRegistry.");
        } else {
            try {
                return mapperProxyFactory.newInstance(sqlSession);
            } catch (Exception var5) {
                throw new BindingException("Error getting mapper instance. Cause: " + var5, var5);
            }
        }
    }

MapperProxyFactory

public class MapperProxyFactory {
	//传进来的mapper的class
    private final Class mapperInterface;
 
    private final Map methodCache = new ConcurrentHashMap();

    public MapperProxyFactory(Class mapperInterface) {
        this.mapperInterface = mapperInterface;
    }

    public Class getMapperInterface() {
        return this.mapperInterface;
    }

    public Map getMethodCache() {
        return this.methodCache;
    }

    protected T newInstance(MapperProxy mapperProxy) {
        return Proxy.newProxyInstance(this.mapperInterface.getClassLoader(), new Class[]{this.mapperInterface}, mapperProxy);
    }
    
	
    public T newInstance(SqlSession sqlSession) {
 		//Mapper的代理对象实现类   
        MapperProxy mapperProxy = new MapperProxy(sqlSession, this.mapperInterface, this.methodCache);
        //返回动态代理对象
        return this.newInstance(mapperProxy);
    }
}

MapperProxy

public class MapperProxy implements InvocationHandler, Serializable {
    private static final long serialVersionUID = -6424540398559729838L;
    private final SqlSession sqlSession;
    private final Class mapperInterface;
    private final Map methodCache;

    public MapperProxy(SqlSession sqlSession, Class mapperInterface, Map methodCache) {
        this.sqlSession = sqlSession;
        this.mapperInterface = mapperInterface;
        this.methodCache = methodCache;
    }
	//任何mapper对象的方法都是通过invoke来实现调用的
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        try {
            if (Object.class.equals(method.getDeclaringClass())) {
                return method.invoke(this, args);
            }

            if (method.isDefault()) {
                return this.invokeDefaultMethod(proxy, method, args);
            }
        } catch (Throwable var5) {
            throw ExceptionUtil.unwrapThrowable(var5);
        }
		//通过原mapper接口的方法(接口的方法没有方法体)得到对应的实现方法(有方法体)
        MapperMethod mapperMethod = this.cachedMapperMethod(method);
        //重点分析这一句
        //执行该方法
        return mapperMethod.execute(this.sqlSession, args);
    }
	//将method和mapperMethod作为键值对传入methodCache中
    private MapperMethod cachedMapperMethod(Method method) {
    	//若该method对应的mapperMethod为空则构造一个mapperMethod
        return (MapperMethod)this.methodCache.computeIfAbsent(method, (k) -> {
            return new MapperMethod(this.mapperInterface, method, this.sqlSession.getConfiguration());
        });
    }

    private Object invokeDefaultMethod(Object proxy, Method method, Object[] args) throws Throwable {
        Constructor constructor = Lookup.class.getDeclaredConstructor(Class.class, Integer.TYPE);
        if (!constructor.isAccessible()) {
            constructor.setAccessible(true);
        }

        Class declaringClass = method.getDeclaringClass();
        return ((Lookup)constructor.newInstance(declaringClass, 15)).unreflectSpecial(method, declaringClass).bindTo(proxy).invokeWithArguments(args);
    }
}

MapperMethod

  public Object execute(SqlSession sqlSession, Object[] args) {
        Object result;
        Object param;
        switch(this.command.getType()) {
        case INSERT:
        	//将参数封装成一个object,传进sqlSession的方法中
            param = this.method.convertArgsToSqlCommandParam(args);
            result = this.rowCountResult(sqlSession.insert(this.command.getName(), param));
            break;
        case UPDATE:
            param = this.method.convertArgsToSqlCommandParam(args);
            result = this.rowCountResult(sqlSession.update(this.command.getName(), param));
            break;
        case DELETE:
            param = this.method.convertArgsToSqlCommandParam(args);
            result = this.rowCountResult(sqlSession.delete(this.command.getName(), param));
            break;
        case SELECT:
            if (this.method.returnsVoid() && this.method.hasResultHandler()) {
                this.executeWithResultHandler(sqlSession, args);
                result = null;
            } else if (this.method.returnsMany()) {
                result = this.executeForMany(sqlSession, args);
            } else if (this.method.returnsMap()) {
                result = this.executeForMap(sqlSession, args);
            } else if (this.method.returnsCursor()) {
                result = this.executeForCursor(sqlSession, args);
            } else {
                param = this.method.convertArgsToSqlCommandParam(args);
                result = sqlSession.selectOne(this.command.getName(), param);
                if (this.method.returnsOptional() && (result == null || !this.method.getReturnType().equals(result.getClass()))) {
                    result = Optional.ofNullable(result);
                }
            }
            break;
        case FLUSH:
            result = sqlSession.flushStatements();
            break;
        default:
            throw new BindingException("Unknown execution method for: " + this.command.getName());
        }

        if (result == null && this.method.getReturnType().isPrimitive() && !this.method.returnsVoid()) {
            throw new BindingException("Mapper method '" + this.command.getName() + " attempted to return null from a method with a primitive return type (" + this.method.getReturnType() + ").");
        } else {
            return result;
        }
    }

三、总结

通过上面的源码简单分析了mapper的动态代理过程,大致如下:

  1. sqlSession.getMapper(xxxMapper.class)—>DefaultSqlSession.getMapper(xxxMapper.class,this)
  2. mapperRegistry.getMapper(type, sqlSession)–>mapperProxyFactory.newInstance(sqlSession)
    以上是得到mapper代理对象的流程,下面分析mapper代理对象中的方法是如何执行的。

  1. MapperProxy.invoke(Object proxy, Method method, Object[] args)—>MapperMethod.execute(SqlSession sqlSession, Object[] args)

而execute方法内部还是通过调用sqlSession的api来执行mapper操作的,也就是mapper接口中方法的实现最终还是依靠sqlsession,mapper的存在是为了让程序员的开发效率更高

你可能感兴趣的:(mybatis,java,mybatis,mapper动态代理,mybatis动态代理)