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

mac2022-06-30  79

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

相关资源:JAVA上百实例源码以及开源项目
最新回复(0)