【Java架构师】Mybatis 动态代理源码解析

代理模式在设计模式中还是很常见的,因为他可以动态生成一个类,所以非常灵活,接手一个需求,可以不通过修改之前的代码就完成逻辑的修改,那么代理模式就派上用场了,jdk代理模式又分动态和静态,除了jdk动态代理外还有cglib等代理,现在就来说说Mybatic中对jdk动态代理的使用。

public void test_query() {
    String resource = "spring/mybatis-config-datasource.xml";
    Reader reader;
    try {
        //1 将资源文件写入到字符流中
        reader = Resources.getResourceAsReader(resource);
        //2 通过SqlSessionFactoryBuilder创建SqlSessionFactory
        SqlSessionFactory sqlMapper = new SqlSessionFactoryBuilder().build(reader);

        SqlSession session = sqlMapper.openSession();
        try {
            User user = session.selectOne("com.didspace.queryUserInfoById", 1L);

        } finally {
            session.close();
            reader.close();
        }
    } catch (IOException e) {
        e.printStackTrace();
    }
}

1 将资源文件写入到字符流中

2 通过SqlSessionFactoryBuilder创建SqlSessionFactory,如果了解Mybatis的人,估计大部分都会知道SqlSessionFactory是Mybatis的核心,那下面我们就具体分析一下。对于新手小白想更轻松的学好Java提升,Java架构,web开发、大数据,数据分析,人工智能等技术,这里给大家分享系统教学资源,扩列下我尉(同英):CGMX9880 【教程/工具/方法/解疑】

new SqlSessionFactoryBuilder().build(reader); 看下这段代码的调用链路

image

再看下SqlSessionFactory的实现

image

从实现上看SqlSessionFactory和DefaultSqlSessionFactory有很大联系,先不管,我们继续看
new SqlSessionFactoryBuilder().build(reader)

  //这个方法是我们调用的
  public SqlSessionFactory build(Reader reader) {
    return this.build((Reader)reader, (String)null, (Properties)null);
}

public SqlSessionFactory build(Reader reader, String environment) {
    return this.build((Reader)reader, environment, (Properties)null);
}

public SqlSessionFactory build(Reader reader, Properties properties) {
    return this.build((Reader)reader, (String)null, properties);
}

public SqlSessionFactory build(Reader reader, String environment, Properties properties) {
    SqlSessionFactory var5;
    try {
        XMLConfigBuilder parser = new XMLConfigBuilder(reader, environment, properties);
        var5 = this.build(parser.parse());
    } catch (Exception var14) {
        throw ExceptionFactory.wrapException("Error building SqlSession.", var14);
    } finally {
        ErrorContext.instance().reset();

        try {
            reader.close();
        } catch (IOException var13) {
            ;
        }

    }

    return var5;
}

我们看到builer是四个的,但是是用了方法的重载,方法名称相同,但是参数不同,最终调用的是

public SqlSessionFactory build(Reader reader, String environment, Properties properties) {
    SqlSessionFactory var5;
    try {
    XMLConfigBuilder parser = new XMLConfigBuilder(reader, environment, properties);
    var5 = this.build(parser.parse());
} catch (Exception var14) {
    throw ExceptionFactory.wrapException("Error building SqlSession.", var14);
} finally {
    ErrorContext.instance().reset();

    try {
        reader.close();
    } catch (IOException var13) {
        ;
    }

}

这个不是设计模式,但是这种写法,是很不错的,值得借鉴,然后我们看下代码
先说new XMLConfigBuilder(reader, environment, properties);

这是一个构造方法,源码中很多都是通过构造方法来初始化对象

     public XMLConfigBuilder(Reader reader, String environment, Properties props) {
    this(new XPathParser(reader, true, props, new XMLMapperEntityResolver()), environment, props);
  }

这里面又套了一层,也是和上面的处理方式类似,一个构造方法又去调用另外一个构造方法,另一个构造方法如下

   private XMLConfigBuilder(XPathParser parser, String environment, Properties props) {
    super(new Configuration());
    ErrorContext.instance().resource("SQL Mapper Configuration");
    this.configuration.setVariables(props);
    this.parsed = false;
    this.environment = environment;
    this.parser = parser;
  }

这里我们看到初始化了Configuration对象,最终我们创建了XMLConfigBuilder对象,然后我们执行下一步

  public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {
    try {
      XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);
      return build(parser.parse());

build(parser.parse()) 方法,先执行parser.parse(),我们会调用到下面的方法

   private void parseConfiguration(XNode root) {
    try {
      //issue #117 read properties first
      propertiesElement(root.evalNode("properties"));
      Properties settings = settingsAsProperties(root.evalNode("settings"));
      loadCustomVfs(settings);
      typeAliasesElement(root.evalNode("typeAliases"));
      pluginElement(root.evalNode("plugins"));
      objectFactoryElement(root.evalNode("objectFactory"));
      objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));
      reflectorFactoryElement(root.evalNode("reflectorFactory"));
      settingsElement(settings);
      // read it after objectFactory and objectWrapperFactory issue #631
      environmentsElement(root.evalNode("environments"));
      databaseIdProviderElement(root.evalNode("databaseIdProvider"));
      typeHandlerElement(root.evalNode("typeHandlers"));
      mapperElement(root.evalNode("mappers"));
    } catch (Exception e) {
      throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);
    }
  }

这里调动了 mapperElement(root.evalNode(“mappers”)),看下代码,就是在这里将Mapperxml文件中内容放到Configuration对象中,也就是之前初始化的对象并返回

   private void mapperElement(XNode parent) throws Exception {
    if (parent != null) {
      for (XNode child : parent.getChildren()) {
        if ("package".equals(child.getName())) {
          String mapperPackage = child.getStringAttribute("name");
          configuration.addMappers(mapperPackage);
        } else {
          String resource = child.getStringAttribute("resource");
          String url = child.getStringAttribute("url");
          String mapperClass = child.getStringAttribute("class");
          if (resource != null && url == null && mapperClass == null) {

现在我们看看 build(parser.parse())的build方法代码

  public SqlSessionFactory build(Configuration config) {
    return new DefaultSqlSessionFactory(config);
  }

这里面最终我们返回了DefaultSqlSessionFactory对象,而在最开始时的类关系,我们知道DefaultSqlSessionFactory实现了SqlSessionFactory,所以也就等于返回了一个SqlSessionFactory,这时候我们的SqlSessionFactory初始化也就完成了。对于新手小白想更轻松的学好Java提升,Java架构,web开发、大数据,数据分析,人工智能等技术,这里给大家分享系统教学资源,扩列下我尉(同英):CGMX9880 【教程/工具/方法/解疑】

那Mybatis既然使用了动态代理,那是在哪里使用了呢,回到之前提到的方法,这个方法就包含了代理类的生成以及mapper的加载

   //   通过package方式自动搜索加载,生成对应的mapper代理类
   private void mapperElement(XNode parent) throws Exception {
    if (parent != null) {
      for (XNode child : parent.getChildren()) {
      //mybatis框架提供的搜索classpath下指定package以及子package中符合条件(注解或者继承于某个类/接口)的类,默认使用Thread.currentThread().getContextClassLoader()返回的加载器,和spring的工具类殊途同归。
        if ("package".equals(child.getName())) {
          String mapperPackage = child.getStringAttribute("name");
          configuration.addMappers(mapperPackage);
        } else {
        ......

我们看下调用链路

image

从这个链路中我们看到addMapper方法,看他做了什么

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 {
   // 为mapper接口创建一个MapperProxyFactory代理
    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);
    }
  }
}
  }

knownMappers.put(type, new MapperProxyFactory(type)),这里就创建了代理,注意他是没有生成代理类的,他做了一个接口类和代理类的映射,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);
  }

注意这个newInstance方法,我们看他的参数是MapperProxy 对象,看下他的源码

  public class MapperProxy<T> implements InvocationHandler, Serializable { 

他实现了InvocationHandler,也就是动态代理一定要实现的类,说明newInstance这个方法就是创建动态代理类的方法的,通过查看他的调用会发现,他不是在初始化容器的时候被调用,而是在查询方法执行时被调用。

 public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
 //从初始化时放入map中的接口代理类映射中取出代理核心类
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);
}

}

通过查看调用,我们发现是由这个方法调用,在查询时,mybatis会根据mapper.xml文件中配置的nameSpace中得到类路径找到类,然后进行动态代理。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值