Mybatis源码分析

Mapper接口动态代理的实现原理

  • Mybatis关于包装Mapper的代码都在org.apache.ibatis.binding 这个包下面。其中有4个类: 

    • MapperRegistry :

      1. 该类主要维护了一个映射关系Map<Class<?>, MapperProxyFactory<?>> , 通过addMapper(Class<T> type)方法注册目标对象(接口对象) ,

      2. 通过getMapper(Class<T> type, SqlSession sqlSession)方法获取Mapper接口的实现类实例 , getMapper方法内部会调用MapperProxyFactory.newInstance

      3. MapperRegistry源码

        public class MapperRegistry {
        
          private final Configuration config;
          private final Map<Class<?>, MapperProxyFactory<?>> knownMappers = new HashMap<Class<?>, MapperProxyFactory<?>>();
        
          public MapperRegistry(Configuration config) {
            this.config = config;
          }
        
          @SuppressWarnings("unchecked")
          public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
            final MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory<T>) knownMappers.get(type);
            if (mapperProxyFactory == null) {
              throw new BindingException("Type " + type + " is not known to the MapperRegistry.");
            }
            try {
              return mapperProxyFactory.newInstance(sqlSession);
            } catch (Exception e) {
              throw new BindingException("Error getting mapper instance. Cause: " + e, e);
            }
          }
          
          public <T> boolean hasMapper(Class<T> type) {
            return knownMappers.containsKey(type);
          }
        
          public <T> void addMapper(Class<T> type) {
            if (type.isInterface()) {
              if (hasMapper(type)) {
                throw new BindingException("Type " + type + " is already known to the MapperRegistry.");
              }
              boolean loadCompleted = false;
              try {
                knownMappers.put(type, new MapperProxyFactory<T>(type));
                // It's important that the type is added before the parser is run
                // otherwise the binding may automatically be attempted by the
                // mapper parser. If the type is already known, it won't try.
                MapperAnnotationBuilder parser = new MapperAnnotationBuilder(config, type);
                parser.parse();
                loadCompleted = true;
              } finally {
                if (!loadCompleted) {
                  knownMappers.remove(type);
                }
              }
            }
          }
        
          /**
           * @since 3.2.2
           */
          public Collection<Class<?>> getMappers() {
            return Collections.unmodifiableCollection(knownMappers.keySet());
          }
        
          /**
           * @since 3.2.2
           */
          public void addMappers(String packageName, Class<?> superType) {
            ResolverUtil<Class<?>> resolverUtil = new ResolverUtil<Class<?>>();
            resolverUtil.find(new ResolverUtil.IsA(superType), packageName);
            Set<Class<? extends Class<?>>> mapperSet = resolverUtil.getClasses();
            for (Class<?> mapperClass : mapperSet) {
              addMapper(mapperClass);
            }
          }
        
          /**
           * @since 3.2.2
           */
          public void addMappers(String packageName) {
            addMappers(packageName, Object.class);
          }
          
        }

         

    • MapperProxyFactory

      1. 该类主要通过newInstance(MapperProxy<T> mapperProxy)创建Mapper接口的实现类实例

      2. MapperProxyFactory源码

        public class MapperProxyFactory<T> {
        
          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;
          }
        
          @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

      1. 该类实现了JDK动态代理的接口 InvocationHandler , 在invoke方法中实现了代理方法调用的细节

      2. 该类还对要执行的方法 MapperMethod 进行了缓存 , 缓存到一个不可变对象 final Map<Method, MapperMethod> 中

      3. MapperProxy源码

        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;
          }
        
          @Override
          public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            try {
              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();
          }
        }

         

    • MapperMethod

      1. 该类是整个代理机制的核心类,对Sqlsession当中的操作进行了封装

      2. 内部有两个重要的内部类 SqlCommand 和 MethodSignature

        1. SqlCommand 封装了具体执行的动作

        2. MethodSignature 封装了接口当中方法的 参数类型 返回值类型 等信息

      3. MapperMethod源码

        public class MapperMethod {
        
          private final SqlCommand command;
          private final MethodSignature method;
        
          public MapperMethod(Class<?> mapperInterface, Method method, Configuration config) {
            this.command = new SqlCommand(config, mapperInterface, method);
            this.method = new MethodSignature(config, mapperInterface, method);
          }
        
          public Object execute(SqlSession sqlSession, Object[] args) {
            Object result;
            switch (command.getType()) {
              case INSERT: {
            	Object param = method.convertArgsToSqlCommandParam(args);
                result = rowCountResult(sqlSession.insert(command.getName(), param));
                break;
              }
              case UPDATE: {
                Object param = method.convertArgsToSqlCommandParam(args);
                result = rowCountResult(sqlSession.update(command.getName(), param));
                break;
              }
              case DELETE: {
                Object param = method.convertArgsToSqlCommandParam(args);
                result = rowCountResult(sqlSession.delete(command.getName(), param));
                break;
              }
              case SELECT:
                if (method.returnsVoid() && method.hasResultHandler()) {
                  executeWithResultHandler(sqlSession, args);
                  result = null;
                } else if (method.returnsMany()) {
                  result = executeForMany(sqlSession, args);
                } else if (method.returnsMap()) {
                  result = executeForMap(sqlSession, args);
                } else if (method.returnsCursor()) {
                  result = executeForCursor(sqlSession, args);
                } else {
                  Object param = method.convertArgsToSqlCommandParam(args);
                  result = sqlSession.selectOne(command.getName(), param);
                }
                break;
              case FLUSH:
                result = sqlSession.flushStatements();
                break;
              default:
                throw new BindingException("Unknown execution method for: " + command.getName());
            }
            if (result == null && method.getReturnType().isPrimitive() && !method.returnsVoid()) {
              throw new BindingException("Mapper method '" + command.getName() 
                  + " attempted to return null from a method with a primitive return type (" + method.getReturnType() + ").");
            }
            return result;
          }
        ....
        }

         

    • 原理总结

      • mybatis通过jdk动态代理的方式动态生成目标接口的一个实现类,因此在调用所有目标接口的任意方法时,都会执行代理类MapperProxy中的invoke方法在invoke方法中有一个MapperMethod类,该类封装了Sqlsession中的CRUD操作 ,所以我们在调用Mapper接口时 ,真正被执行的是invoke中的MapperMethod.execute(SqlSession sqlSession, Object[] args)方法,从而进行数据库操作。

Mapper接口的实例化过程

在mybatis的Spring配置中 , 单个Mapper接口的配置如下

<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
        <property name="dataSource" ref="dataSource" />
        <property name="configLocation" value="classpath:sqlMapConfig.xml" />
</bean>
<bean id="userMapper" class="org.mybatis.spring.mapper.MapperFactoryBean">
        <property name="mapperInterface" value="com.tqd.dao.UserMapper"></property>
        <property name="sqlSessionFactory" ref="sqlSessionFactory" />
</bean>
  1. 首先找到MapperFactoryBean<T>这个类 , 该类实现了FactoryBean<T>接口 , 通过getObject方法得到mapper接口的实例
    @Override
      public T getObject() throws Exception {
        return getSqlSession().getMapper(this.mapperInterface);
      }

     

  2. 同时MapperFactoryBean<T>还继承了SqlSessionDaoSupport类 , 调用其父类的getSqlSession()方法得到SqlSession
    public abstract class SqlSessionDaoSupport extends DaoSupport {
      private SqlSession sqlSession;
      private boolean externalSqlSession;
      
      public void setSqlSessionFactory(SqlSessionFactory sqlSessionFactory) {
        if (!this.externalSqlSession) {
          this.sqlSession = new SqlSessionTemplate(sqlSessionFactory);
        }
      }
      public void setSqlSessionTemplate(SqlSessionTemplate sqlSessionTemplate) {
        this.sqlSession = sqlSessionTemplate;
        this.externalSqlSession = true;
      }
      /**
       * Users should use this method to get a SqlSession to call its statement methods
       * This is SqlSession is managed by spring. Users should not commit/rollback/close it
       * because it will be automatically done.
       *
       * @return Spring managed thread safe SqlSession
       */
      public SqlSession getSqlSession() {
        return this.sqlSession;
      }
    ......
    }

     

mybatis-config.xml加载过程

mapper.xml加载过程

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值