徐刘根
2017-11-09 09:22
采纳率: 0%
浏览 3.9k

MyBatis动态代理实现无接口实现类操作数据库实现原理

MyBatis动态代理

我们知道MyBatis通过动态代理的方式,实现了只通过Mapper接口而无接口的实现类的方式操作数据库。

熟悉源码的同学应该知道如下代码(MapperProxy.invoke()):

 @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        try {
            //如果目标方法继承自Object,则直接调用目标方法
            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);
        }
        //从缓存中取出MapperMethod对象,如果缓存中没有,则创建新的MapperMethod对象并添加到缓存中
        final MapperMethod mapperMethod = cachedMapperMethod(method);
        return mapperMethod.execute(sqlSession, args);
    }

这种方式类似于截获了真正的代理对象,而去执行其他的逻辑,通过MapperMethod实现数据库的访问操作!

代理对象的获取

MyBatis中的代理对象的获取是通过如下代码实现的:

 public T newInstance(SqlSession sqlSession) {
        //创建MapperProxy对象,每次调用都会创建新的MapperProxy对象
        final MapperProxy<T> mapperProxy = new MapperProxy<T>(sqlSession, mapperInterface, methodCache);
        return newInstance(mapperProxy);
    }

    /**
     * 创建实现了mapperInterface接口的代理对象
     * @param mapperProxy
     * @return
     */
    @SuppressWarnings("unchecked")
    protected T newInstance(MapperProxy<T> mapperProxy) {
        T t = (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[]{mapperInterface}, mapperProxy);
        return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[]{mapperInterface}, mapperProxy);
    }

返回的记过是这样的:

图片说明

也就是说,无论调用的是哪一个Mapper接口,返回的代理对象都是MapperProxy类型的。

自己实现MyBatis动态代理模拟上述过程

图片说明

 public class MapperRegistry {

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

    public <T> T getMapper(Class<T> type) {
        //模拟初始化knownMappers集合
        knownMappers.put(type, new MapperProxyFactory<T>(type));

        final MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory<T>) knownMappers.get(type);
        return (T) mapperProxyFactory.newInstance();
    }
}

 public class MapperProxyFactory<T> {

    private final Class<T> mapperInterface;

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

    public T newInstance() {
        final MapperProxy<T> mapperProxy = new MapperProxy<>(mapperInterface);
        return newInstance(mapperProxy);
    }

    protected T newInstance(MapperProxy<T> mapperProxy) {
        return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[]{mapperInterface}, mapperProxy);
    }

}
 public class MapperProxy<T> implements InvocationHandler, Serializable {

    private static final long serialVersionUID = -6424540398559729838L;

    //真正的业务对象,也就是RealSubject对象
    private Class<T> mapperInterface;

    /**
     * 构造函数
     */
    public MapperProxy(Class<T> target) {
        this.mapperInterface = target;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

        final MapperMethod mapperMethod = new MapperMethod();
        return mapperMethod.execute(args);
    }
}
 public class MapperMethod {

    public Object execute(Object[] args) {
        return new User("123", "234");
    }
}
 public class DefaultSqlSession {

    private Configuration configuration = new Configuration();

    public <T> T getMapper(Class<T> type) {
        return configuration.getMapper(type);
    }
}
 public class Configuration {

    final MapperRegistry mapperRegistry = new MapperRegistry();

    public <T> T getMapper(Class<T> type) {
        return mapperRegistry.getMapper(type);
    }
}
 public class UserService {

    public static void main(String[] args) {
        final DefaultSqlSession sqlSession = new DefaultSqlSession();
        UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
        User user = userMapper.selectUser();
        System.out.println(user.toString());
    }
}

 public interface UserMapper {

    User selectUser();

}

自己写的代码中抽取了MyBatis下binding包的主要内容实现的一个简单的动态获取实例,但是获取代理对象的时候,返回的记过是这样的:

图片说明

可以看出,上述的过程的代理返回的类型并不是MapperProxy,虽然可以获取到正确的执行结果,但是返回的代理的对象类型是出现了转换异常的。

问题描述

1、为什么sqlSession.getMapper()返回的代理对象结果都是MapperProxy类型的,设计的初衷?

2、为什么我按照上述相同的代码过程自己实现的动态代理,为什么会出现转换异常?

还请各位大哥哥大姐姐帮忙研究一下,谢谢!

  • 点赞
  • 写回答
  • 关注问题
  • 收藏
  • 邀请回答

2条回答 默认 最新

  • x4666666 2017-11-17 02:08

    1.假设我们用真正意义上的代理模式,如果很多mapper,你是不是要给所有的mapper写一个InvocationHandler
    2.首先你是调用了proxy的object方法,而实际上是不管什么方法你都执行了MapperMethod的execute方法,造成类型不匹配,你把invoke代码改成
    if("selectUser".equals(method.getName())){
    return mapperMethod.execute(args);
    }

    点赞 2 打赏 评论
  • 徐刘根 2017-11-09 09:36

    自己顶一下自己,大哥哥大姐姐看到了帮忙看一下

    点赞 打赏 评论

相关推荐 更多相似问题