mybatis之MapperFactoryBean源码解读

来源:互联网 发布:廖雪峰的javascript 编辑:程序博客网 时间:2024/06/06 17:03

  在我们使用mybatis开发dao层的时候,我们可以直接写好dao层接口,然后借助MapperFactoryBean直接代理,这样我们就不需要显示的实现dao接口,刚接触到这个技术点的时候就对这个MapperFactoryBean十分感兴趣,所以在了解了背后的实现机制时,就写下我的所见所得。

  先来看看MapperFactoryBean的继承关系

  

     从类的的继承图中可以看出来实现了FactoryBean,这是一个spring提供的工厂接口,其次继承了SqlSessionDaoSupport,SqlSessionSurpport最顶层实现了InitializingBean,InitializingBean接口定义了afterProper'tiesSet()方法,当一个类实现这个接口,spring容器在初始化一个Bean时,在初始化Bean并设置好相关的属性后就会调用这个方法,我们就可以利用这个接口来实现我们的一些初始化工作。

   在DaoSupport中实现afterPropertiesSet()调用了CheckDaoConfig()

    public final void afterPropertiesSet() throws IllegalArgumentException, BeanInitializationException {        this.checkDaoConfig();        try {            this.initDao();        } catch (Exception var2) {            throw new BeanInitializationException("Initialization of DAO failed", var2);        }    }
   所以在MapperFactoryBean中重写了afterPropertiesSet()方法

 protected void checkDaoConfig() {    super.checkDaoConfig();    notNull(this.mapperInterface, "Property 'mapperInterface' is required");    Configuration configuration = getSqlSession().getConfiguration();    if (this.addToConfig && !configuration.hasMapper(this.mapperInterface)) {      try {        configuration.addMapper(this.mapperInterface);      } catch (Exception e) {        logger.error("Error while adding the mapper '" + this.mapperInterface + "' to configuration.", e);        throw new IllegalArgumentException(e);      } finally {        ErrorContext.instance().reset();      }    }  }

  在Configuration类中添加代理的接口,所以在初始化Bean时,spring通过getObject()获得代理的接口。

 @Override  public T getObject() throws Exception {    return getSqlSession().getMapper(this.mapperInterface);  }

  通过SqlSession获得代理类,我们继续在DefaultSqlSession中查看

 public <T> T getMapper(Class<T> type) {    return configuration.<T>getMapper(type, this); }
  在代码中可以按到代理接口通过SqlSession所对应的Configuration获得,Configuration是一个保存XML文件相关信息的类,与SqlSession绑定。

 public <T> T getMapper(Class<T> type, SqlSession sqlSession) {    return mapperRegistry.getMapper(type, sqlSession); }
  Configuration是保存mybatis中XML的信息,要获得接口的代理需要通过MapperRegistry获得

  

 @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);    }  }

  看到这里终于找到了代理的终点,通过MapperProxyFactory类,从类名中就可以看出来这是一个代理工厂,最终mapperProxyFactory.newInstance()获得接口的代理

 @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);  }
  newInstance方法通过new 一个MapperProxy,然后通过JDK中Proxy获得接口的代理,我们知道常用的代理有两种,一种是JDK代理,一种是cglib代理,那么mybatis是使用哪种代理呢?我们可以查看MapperProxy类来看看到底是使用哪种代理

public class MapperProxy<T> implements InvocationHandler, Serializable 
  从代码中可以明确看出来是使用JDK动态代理的,到这里我们就分析完了mybatis的接口自动代理了。