Spring整合MyBatis(四)MapperFactoryBean 的创建

摘要: 本文结合《Spring源码深度解析》来分析Spring 5.0.6版本的源代码。若有描述错误之处,欢迎指正。

目录

一、MapperFactoryBean的初始化

二、获取 MapperFactoryBean 的实例

为了使用MyBatis功能,示例中的Spring配置文件提供了两个bean,除了之前分析的 SqlSessionFactoryBean 类型的 bean 以外,还有一个是 MapperFactoryBean 类型的 bean。

结合两个测试用例综合分析,对于单独使用MyBatis的时候调用数据库接口的方式是:

UserMapper userMapper = sqlSession.getMapper(UserMapper.class);

 而在这一过程中,其实是MyBatis在获取映射的过程中根据配置信息为UserMapper类型动态创建了代理类。而对于Spring的创建方式:

UserMapper userMapper = (UserMapper) context.getBean("userMapper");

Spring中获取的名为userMapper的bean,其实是与单独使用MyBatis完成了—样的功能,那么我们可以推断,在bean的创建过程中一定是使用了MyBatis中的原生方法sqlSession.getMapper(UserMapper.class)进行了再一次封装。结合配置文件,我们把分析目标转向org.mybatis.spring.mapper.MapperFactoryBean,初步推测其中的逻辑应该在此类中实现。同样,还是首先查看的类层次结构图MapperFactoryBean,如下图所示。

同样,在实现的接口中发现了我们感兴趣的两个接口InitializingBean与FactoryBean。我们的分析还是从bean的初始化开始。

一、MapperFactoryBean的初始化

因为实现了InitializingBean接口,Spring会保证在bean初始化时首先调用afterPropertiesSet方法来完成其初始化逻辑。追踪父类,发现afterPropertiesSet方法是在DaoSupport类中实现,代码如下:

@Override
public final void afterPropertiesSet() throws IllegalArgumentException, BeanInitializationException {
    // Let abstract subclasses check their configuration.
    checkDaoConfig();

    // Let concrete implementations initialize themselves.
    try {
        initDao();
    }
    catch (Exception ex) {
        throw new BeanInitializationException("Initialization of DAO failed", ex);
    }
}

但从函数名称来看我们大体推测,MapperFactoryBean的初始化包括对DAO配置的验证以及对DAO的初始工作,其中initDao()方法是模板方法,设计为留给子类做进一步逻辑处理。而checkDaoConfig()才是我们分析的重点。

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

 super.checkDaoConfig()在SqlSessionDaoSupport类中实现,代码如下:

@Override
protected void checkDaoConfig() {
    notNull(this.sqlSessionTemplate, "Property 'sqlSessionFactory' or 'sqlSessionTemplate' are required");
}

结合代码我们了解到对于DAO配置的验证,Spring做了以下几个方面的工作。

  • 父类中对于sqlSession不为空的验证。

sqlSession作为根据接口创建映射器代理的接触类一定不可以为空,而sqlSession的初始化工作是在设定其sqlSessionFactory属性时完成的。

public void setSqlSessionFactory(SqlSessionFactory sqlSessionFactory) {
    if (this.sqlSessionTemplate == null || sqlSessionFactory != this.sqlSessionTemplate.getSqlSessionFactory()) {
        this.sqlSessionTemplate = createSqlSessionTemplate(sqlSessionFactory);
    }
}

 也就是说,对于下面的配置如果忽略了对于sqlSessionFactoiy属性的设置,那么在此时就会被检测出来。

<bean id="userMapper" class="org.mybatis.spring.mapper.MapperFactoryBean">
    <property name="mapperInterface" value="org.cellphone.uc.repo.mapper.UserMapper"/>
    <property name="sqlSessionFactory" ref="sqlSessionFactory"/>
</bean>
  • 映射接口的验证。

接口是映射器的基础,sqlSession会根据接口动态创建相应的代理类,所以接口必不可少。

  •  映射文件存在性验证。

对于函数前半部分的验证我们都很容易理解,无非是对配置文件中的属性是否存在做验证,但是后面部分是完成了什么方面的验证呢?如果读者读过MyBatis源码,你就会知道,在MyBatis实现过程中并没有手动调用configuration.addMapper方法,而是在映射文件读取过程中一旦解析到如<mapper namespace="org.cellphone.uc.repo.mapper.UserMapper">,便会自动进行类型映射的注册。那么,Spring中为什么会把这个功能单独拿出来放在验证里呢?这是不是多此一举呢?

在上面的函数中,configuration.addMapper(this.mapperInterface)其实就是将 UserMapper 注册到映射类型中,如果你可以保证这个接口一定存在对应的映射文件,那么其实这个验证并没有必要。但是,由于这个是我们自行决定的配置,无法保证这里配罝的接口一定存在对应的映射文件,所以这里非常有必要进行验证。在执行此代码的时候,MyBatis会检査嵌人的映射接口是否存在对应的映射文件,如果没有回抛出异常,Spring正是在用这种方式来完成接口对应的映射文件存在性验证。

二、获取 MapperFactoryBean 的实例

由于MapperFactoryBean实现了FactoryBean接口,所以当通过getBean方法获取对应实例的时候其实是获取该类的getObject()函数返回的实例。

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

这段代码正是我们在提供MyBatis独立使用的时候的一个代码调用。Spring通过 FactoryBean 进行了封装。

原文地址:https://www.cnblogs.com/warehouse/p/9446054.html