mybatis源码分析之Mapper代理实现分析
来源:互联网 发布:js color 编辑:程序博客网 时间:2024/06/01 21:17
0 概述
使用过mybatis框架的人都知道,我们只是写了一个个mapper接口但是没有写它的实现类,但是我们可以直接使用它调用其对应的接口执行相应的sql语句。其实很容易想到它是使用代理来实现的,那么究竟是怎么实现的呢?本文主要来揭开这一神秘面纱。
1 代理模式
之前写过一篇代理模式的简介,一般使用代理方式如下,有个Target接口以及其实现类,Proxy中会调用具体Target实现类。具体实例可见:代理模式
2 Mapper接口代理实现的源码分析
我们知道Mapper接口是没有具体的实现类,那么是怎么个代理法?其实是一种约定,但是正是因为这种约定可以大大的简化开发。约定Mapper接口XML文件一一对应,这样在invoke方法中就可以通过相应的类名、方法名获取到相应的xml文件配置的sql语句,从而执行对应的sql语句。
如下面实例,不难发现mapper类名和xml 中namespace对应上,mapper中方法名和对应的id对应上。
package com.hsc.dao;import com.hsc.entity.Book;/** * Created by hsc on 17/7/22. */public interface BookMapper { int insert(Book book);}
对应xml文件
<?xml version="1.0" encoding="UTF-8" ?><!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"><mapper namespace="com.hsc.dao.BookMapper"> <sql id="Base_Column_List"> `id`,`name` </sql> <insert id="insert" useGeneratedKeys="true" keyProperty="id" parameterType="com.hsc.entity.Book"> insert into book(name) values( #{name}) </insert></mapper>
MapperProxyFactory 工厂类
/** * @author Lasse Voss */public class MapperProxyFactory<T> { //Mapper接口 private final Class<T> mapperInterface; private final Map<Method, MapperMethod> methodCache = new ConcurrentHashMap<Method, MapperMethod>(); public MapperProxyFactory(Class<T> mapperInterface) { this.mapperInterface = mapperInterface; } public Class<T> getMapperInterface() { return mapperInterface; } public Map<Method, MapperMethod> getMethodCache() { return methodCache; } //返回接口的代理对象,基于JDK的原生的代理方式 @SuppressWarnings("unchecked") protected T newInstance(MapperProxy<T> mapperProxy) { return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy); } public T newInstance(SqlSession sqlSession) { final MapperProxy<T> mapperProxy = new MapperProxy<T>(sqlSession, mapperInterface, methodCache); return newInstance(mapperProxy); }}
MapperProxy 是实现InvocationHandler接口,重点关注下invoke方法的实现
public class MapperProxy<T> implements InvocationHandler, Serializable { private static final long serialVersionUID = -6424540398559729838L; private final SqlSession sqlSession; //mapper 接口 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; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { try { // 这里是为了做兼容,因为MapperProxyFactory生成的代理对象其祖先也是Object,比如代理对象调用toString方法就会默认调用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); } //根据配置找到对应执行方法(进行包装) final MapperMethod mapperMethod = cachedMapperMethod(method); //执行 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; } @UsesJava7 private Object invokeDefaultMethod(Object proxy, Method method, Object[] args) throws Throwable { final Constructor<MethodHandles.Lookup> constructor = MethodHandles.Lookup.class .getDeclaredConstructor(Class.class, int.class); if (!constructor.isAccessible()) { constructor.setAccessible(true); } final Class<?> declaringClass = method.getDeclaringClass(); return constructor .newInstance(declaringClass, MethodHandles.Lookup.PRIVATE | MethodHandles.Lookup.PROTECTED | MethodHandles.Lookup.PACKAGE | MethodHandles.Lookup.PUBLIC) .unreflectSpecial(method, declaringClass).bindTo(proxy).invokeWithArguments(args); } /** * Backport of java.lang.reflect.Method#isDefault() */ private boolean isDefaultMethod(Method method) { return ((method.getModifiers() & (Modifier.ABSTRACT | Modifier.PUBLIC | Modifier.STATIC)) == Modifier.PUBLIC) && method.getDeclaringClass().isInterface(); }}
3 小结
mybatis通过这种约定以及使用代理模式这种巧妙的设计方式是值得我们思考和学习的。
阅读全文
0 0
- mybatis源码分析之Mapper代理实现分析
- Mybatis源码分析(一)--Mapper的动态代理
- Mybatis源码分析获取Mapper
- SprignMVC+myBatis整合+mybatis源码分析+动态代理实现流程+如何根据mapper接口生成其实现类
- Mybatis3源码分析(21)-Mapper实现-动态代理
- Mybatis3源码分析(21)-Mapper实现-动态代理
- Mybatis3源码分析(21)-Mapper实现-动态代理
- MapReudce源码分析之Mapper
- 转 MyBatis Mapper 接口如何通过JDK动态代理来包装SqlSession 源码分析
- MyBatis Mapper 接口如何通过JDK动态代理来包装SqlSession 源码分析
- MyBatis Mapper 接口如何通过JDK动态代理来包装SqlSession 源码分析
- 【转载】MyBatis Mapper 接口如何通过JDK动态代理来包装SqlSession 源码分析
- Mybatis源码分析之插件责任链、动态代理
- Mybatis源码中Mapper的动态代理实现原理
- Tomcat源码阅读之Mapper分析
- mybatis源码学习之执行过程分析(3)——mapper接口的获取
- 【mybatis】--mapper代理实现dao
- easy-mapper 源码分析
- 集成iOS的支付宝遇到的问题及解决方案
- C指针(转)
- debugview的使用方法
- 我的学习记录42
- 重载(Overload)和重写(Override)解析
- mybatis源码分析之Mapper代理实现分析
- os x 10.12 安装caffe
- Java命令注入之防护
- 写一个监听器类监听对象
- 面向对象
- 论文:Faster R-CNN
- 深入理解Java PriorityQueue
- Unity Shader 编程之一
- 每秒处理10万订单乐视集团支付架构