zoukankan      html  css  js  c++  java
  • mybatis如何通过接口查找对应的mapper.xml及方法执行详解

    转:http://www.jb51.net/article/116402.htm

    本文主要介绍的是关于mybatis通过接口查找对应mapper.xml及方法执行的相关内容,下面话不多说,来看看详细的介绍:

    在使用mybatis的时候,有一种方式是

    1
    BookMapper bookMapper = SqlSession().getMapper(BookMapper.class)

    获取接口,然后调用接口的方法。只要方法名和对应的mapper.xml中的id名字相同,就可以执行sql。

    那么接口是如何与mapper.xml对应的呢?

    首先看下,在getMapper()方法是如何操作的。

    在DefaultSqlSession.Java中调用了configuration.getMapper()

    1
    2
    3
    public <T> T getMapper(Class<T> type) {
     return configuration.<T>getMapper(type, this);
     }

    在Configuration.java中调用了mapperRegistry.getMapper(type, sqlSession);

    1
    2
    3
    public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
     return mapperRegistry.getMapper(type, sqlSession);
     }

    下面重点来了,在MapperRegistry.java中实现了动态代理

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    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);
     }
     }

    这个函数分两部分来看,首先是从map集合中获取接口代理,map集合的来源,第二部分获取代理后实例化,获取接口的方法,执行sql。

    对于第一部分:集合的来源。

    这个MapperRegistry.java中有个方法是addMappers();共有两个重载。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    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);
     }
     }
     
     //解析包名下的接口
     public void addMappers(String packageName) {
     addMappers(packageName, Object.class);
     }

    往上追溯该方法的调用是在SqlSessionFactory.build();时对配置文件的解析,其中对节点mappers的解析,这里先不赘述,

    1
    mapperElement(root.evalNode("mappers"));
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    private void mapperElement(XNode parent) throws Exception {
     if (parent != null) {
      for (XNode child : parent.getChildren()) {
      //使用package节点进行解析配置
      if ("package".equals(child.getName())) {
       String mapperPackage = child.getStringAttribute("name");
       //注册包下的接口
       configuration.addMappers(mapperPackage);
      } else {
      //使用mapper节点
       String resource = child.getStringAttribute("resource");
       String url = child.getStringAttribute("url");
       String mapperClass = child.getStringAttribute("class");
       if (resource != null && url == null && mapperClass == null) {
       ErrorContext.instance().resource(resource);
       InputStream inputStream = Resources.getResourceAsStream(resource);
       XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, resource, configuration.getSqlFragments());
       mapperParser.parse();
       } else if (resource == null && url != null && mapperClass == null) {
       ErrorContext.instance().resource(url);
       InputStream inputStream = Resources.getUrlAsStream(url);
       XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, url, configuration.getSqlFragments());
       mapperParser.parse();
       } else if (resource == null && url == null && mapperClass != null) {
       Class<?> mapperInterface = Resources.classForName(mapperClass);
       configuration.addMapper(mapperInterface);
       } else {
       throw new BuilderException("A mapper element may only specify a url, resource or class, but not more than one.");
       }
      }
      }
     }
     }

    这是调用addMapper()的顺序。

    同时在改方法中还有一个方法很重要

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    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));
     //根据接口名寻找同包下同名的xml或者mapper的namespace是该接口的xml
     //找到对用的xml后进行解析mapper节点里面的节点
     MapperAnnotationBuilder parser = new MapperAnnotationBuilder(config, type);
     parser.parse();
     loadCompleted = true;
     } finally {
     if (!loadCompleted) {
      knownMappers.remove(type);
     }
     }
    }
    }

    这是通过接口的全路径来查找对应的xml。这里有两种方式解析,也就是我们平常xml文件放置位置的两种写法。

    第一种是不加namespace,把xml文件放在和接口相同的路径下,同时xml的名字与接口名字相同,如接口名为Student.java,xml文件为Student.xml。在相同的包下。这种当时可以不加namespace.

    第二种是加namespace,通过namespace来查找对应的xml.

    到这就是接口名和xml的全部注册流程。

    下面再说下第二部分就是通过动态代理获取接口名字来对应xml中的id。

    主要有两个类MapperProxyFactory.java和MapperProxy.java

    对于MapperProxyFactory.java

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    public class MapperProxyFactory<T> {
     
     private final Class<T> mapperInterface;
     private 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.java中进行方法的执行

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
     if (Object.class.equals(method.getDeclaringClass())) {
      try {
      return method.invoke(this, 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;
     }

    至此,就是mybatis所有接口和xml的加载,以及通过动态代理来进行接口的执行的过程。

    总结

    以上就是这篇文章的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,如果有疑问大家可以留言交流,谢谢大家对脚本之家的支持。

  • 相关阅读:
    Python-sokect 示例
    Python装饰器
    javascript权威指南第22章高级技巧
    javascript权威指南第21章 Ajax和Comet
    javascript权威指南第20章 JSON
    javascript权威指南第17章 错误异常处理
    javascript权威指南第16章 HTML5脚本编程
    Bootstrap 表单布局示例
    javascript权威指南第15章 使用Canvas绘图
    贪心算法学习笔记
  • 原文地址:https://www.cnblogs.com/wangle1001986/p/9072630.html
Copyright © 2011-2022 走看看