深化代理模式,Mybaits如何做到调用接口

Mybaits如何做到调用接口?

毫无疑问,动态代理,本文将透过一些源码深化代理模式

1.源码篇

1)生成代理

先上测试代码,相关包扫描和Mybaits的配置文件都已经写好了

public class MapperTest {
    @Test
    public void testMapper() throws IOException, MyException {
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("classpath:/spring/applicationContext-dao.xml");
        SqlSessionFactory factory = (SqlSessionFactory) context.getBean("sqlSessionFactory");
        //可DEBUG这一行,这里创建并返回了Mapper的代理
        TestMapper mapper = factory.openSession().getMapper(TbItemMapper.class);
        //发送一个查询请求,这一行揭示了如何做到调用接口
        mapper.selectByExample(new TestMapper ());
    }
}

这里不必细看,知道在哪进入即可

public class MapperRegistry {
 public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
        //检索Mapper是否注册代理工厂
        MapperProxyFactory<T> 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);
            }
        }
    }
        //此处省略其他代码
}

注意重载方法,该对象已经装配好了配置文件中的接口信息,还配置了一个缓存

  • MapperProxy就是一个InvocationHandler
  • 再用他生成真正的JDK代理对象并返回
    至此动态代理对象已经生成
public class MapperProxyFactory<T> {
    private final Class<T> mapperInterface;
    private Map<Method, MapperMethod> methodCache = new ConcurrentHashMap();

  protected T newInstance(MapperProxy<T> mapperProxy) {
        //JDK代理
        ***return Proxy.newProxyInstance(this.mapperInterface.getClassLoader(), new Class[]{this.mapperInterface}, mapperProxy);***
    }
   
     //DEBUG进入在这一行
    public T newInstance(SqlSession sqlSession) {
        MapperProxy<T> mapperProxy = new MapperProxy(sqlSession, this.mapperInterface, this.methodCache);
        return this.newInstance(mapperProxy);
    }
    //此处省略其他代码
}

2)方法如何调用

invoke方法接收到方法调用,只做了两件事

  • 创建并缓存MapperMethod类
  • 调用MapperMethod.execute()方法
//实现了InvocationHandler
public class MapperProxy<T> implements InvocationHandler, Serializable {
    private static final long serialVersionUID = -6424540398559729838L;
    private final SqlSession sqlSession;
    private final Class<T> mapperInterface;
    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;
    }

    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        //如果调用了Object类的方法
        if (Object.class.equals(method.getDeclaringClass())) {
            try {
                return method.invoke(this, args);
            } catch (Throwable var5) {
                throw ExceptionUtil.unwrapThrowable(var5);
            }
        } else {
            //缓存并创建一个MapperMethod对象,KEY method VALUE mapperMethod
            MapperMethod mapperMethod = this.cachedMapperMethod(method);
            //把session和方法参数委托给MapperMethod
            return mapperMethod.execute(this.sqlSession, args);
        }
    }

    private MapperMethod cachedMapperMethod(Method method) {
        MapperMethod mapperMethod = (MapperMethod)this.methodCache.get(method);
        if (mapperMethod == null) {
            //用接口信息,方法信息,配置信息构造MapperMethod 
            mapperMethod = new MapperMethod(this.mapperInterface, method, this.sqlSession.getConfiguration());
            this.methodCache.put(method, mapperMethod);
        }

        return mapperMethod;
    }
}

MapperMethod 类做了什么?(指令模式?)

  • 将方法调用信息包装成SqlCommand
  • 将方法xml配置信息包装成MethodSignature
  • 发送SQL语句,获取结果返回
public class MapperMethod {
 private final MapperMethod.SqlCommand command;
    private final MapperMethod.MethodSignature method;

    public MapperMethod(Class<?> mapperInterface, Method method, Configuration config) {
        this.command = new MapperMethod.SqlCommand(config, mapperInterface, method);
        this.method = new MapperMethod.MethodSignature(config, method);
    }
     //进入这个方法,匹配指令执行SQL
    public Object execute(SqlSession sqlSession, Object[] args) {
        Object param;
        Object result;
        if (SqlCommandType.INSERT == this.command.getType()) {
            param = this.method.convertArgsToSqlCommandParam(args);
            result = this.rowCountResult(sqlSession.insert(this.command.getName(), param));
        } else if (SqlCommandType.UPDATE == this.command.getType()) {
            param = this.method.convertArgsToSqlCommandParam(args);
            result = this.rowCountResult(sqlSession.update(this.command.getName(), param));
        } else if (SqlCommandType.DELETE == this.command.getType()) {
            param = this.method.convertArgsToSqlCommandParam(args);
            result = this.rowCountResult(sqlSession.delete(this.command.getName(), param));
        } else {
            if (SqlCommandType.SELECT != this.command.getType()) {
                throw new BindingException("Unknown execution method for: " + this.command.getName());
            }
            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 {
                param = this.method.convertArgsToSqlCommandParam(args);
                result = sqlSession.selectOne(this.command.getName(), param);
            }
        }

        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;
        }
    }
}


自然而然,这个类之后的事就和我们使用JDBC一样,拼装SQL,调用SQL,装配返回结果
但这里的动态代理和以往我所见过的不同,以往InvocationHandler需要持有代理对象的引用,在这里却没有,因为Mapper本身并不存在实现,就是这一点引发了我的思考


2.深化代理模式

代理是什么?我的思想转变(不包括CGLIB)

阶段一:初识AOP
这个问题在我初学AOP的时候遇到,当时我的理解跟随着AOP,生成代理的目的就是给方法进行增强,在方法前后添加业务逻辑,接口,所以代理就是一种方法增强的模式,而事务很适合用这一点。毕竟就是在方法前后添加开启事务,提交事务。
而Proxy.newProxyInstance(classloader,interface,invocationhandler)这个API,拿到手我是看不懂的。

阶段二:印象强化
在网上看了一些文章,理解到了静态代理,才感知到动态代理,是Java运用反射机制,沿用代理的思想的一个实现,目的就是为了解耦。
Proxy.newProxyInstance的参数就很明显了

  • classloader和interface 运行时反射创建代理类
  • invocationhandler 持有代理对象的引用,负责转发的请求处理
    当然我以为这样就结束了

阶段三:代理模式
走马观花看了一遍《headfirst设计模式》,才知道代理的玩法太多了,代理更像是一个影武者(傀儡),方法增强不过是冰山一角,比如保护代理,在代理类中编写如果调用了XXX方法就抛出异常等等。

阶段四:开拓视野
我看的源码肯定不多,所以Mybaits中的代理着实让我开了眼界,对于没有实现类的接口也可以代理,传递方法和参数,委托自己的类来进行处理,方法信息和SQL语句绑定,调用方法就会执行相关的Statement,原来SQL语句是这么调用的,当然过程比我说的复杂得多。


3.总结

1.Mybaits中的Mapper接口使用动态代理来实现方法调用
2.Mapper的动态代理并不处理方法,而是转发给MapperMethod。
3.Mapper接口不需要实现类,但需要有相关的XML配置,因为MapperMethod根据方法匹配XML配置

原文地址:https://www.cnblogs.com/haon/p/11353434.html