- 前言
- 第一部分 核心实现
- 第 1 章 Spring 整体架构和环境搭建
- 第 2 章 容器的基本实现
- 第 3 章 默认标签的解析
- 第 4 章 自定义标签的解析
- 第 5 章 bean 的加载
- 第 6 章 容器的功能扩展
- 第 7 章 AOP
- 第二部分 企业应用
- 第 8 章 数据库连接 JDBC
- 第 9 章 整合 MyBatis
- 第 10 章 事务
- 第 11 章 SpringMVC
- 第 12 章 远程服务
- 第 13 章 Spring 消息
9.3.2 MapperFactoryBean 的创建
为了使用 MyBatis 功能,示例中的 Spring 配置文件提供了两个 bean,除了之前分析的 SqlSssionFactoryBean 类型的 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,如图 9-3 所示。

图 9-3 MapperFactoryBean 类的层次结构图
同样,在实现的接口中发现了我们感兴趣的两个接口 InitializingBean 与 FactoryBean。我们的分析还是从 bean 的初始化开始。
1.MapperFactoryBean 的初始化
因为实现了 InitializingBean 接口,Spring 会保证在 bean 初始化时首先调用 afterPropertiesSet 方法来完成其初始化逻辑。追踪父类,发现 afterPropertiesSet 方法是在 DaoSupport 类中实现,代码如下:
public final void afterPropertiesSet() throws IllegalArgumentException, BeanInitialization
Exception {
// 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 (Throwable t) {
logger.error("Error while adding the mapper '" + this.mapperInterface + "' to
configuration.", t);
throw new IllegalArgumentException(t);
} finally {
ErrorContext.instance().reset();
}
}
}
super.checkDaoConfig() 在 SqlSessionDaoSupport 类中实现,代码如下:
protected void checkDaoConfig() {
notNull(this.sqlSession, "Property 'sqlSessionFactory' or 'sqlSessionTemplate' are
required");
}
结合代码我们了解到对于 DAO 配置的验证,Spring 做了以下几个方面的工作。
父类中对于 sqlSession 不为空的验证。
sqlSession 作为根据接口创建映射器代理的接触类一定不可以为空,而 sqlSession 的初始化工作是在设定其 sqlSessionFactory 属性时完成的。
public void setSqlSessionFactory(SqlSessionFactory sqlSessionFactory) {
if (!this.externalSqlSession) {
this.sqlSession = new SqlSessionTemplate(sqlSessionFactory);
}
}
也就是说,对于下面的配置如果忽略了对于 sqlSessionFactory 属性的设置,那么在此时就会被检测出来。
<bean id="userMapper" class="org.mybatis.Spring.mapper.MapperFactoryBean">
<property name="mapperInterface" value="test.mybatis.dao.UserMapper"></property>
<property name="sqlSessionFactory" ref="sqlSessionFactory"></property>
</bean>
映射接口的验证。
接口是映射器的基础,sqlSession 会根据接口动态创建相应的代理类,所以接口必不可少。
映射文件存在性验证。
对于函数前半部分的验证我们都很容易理解,无非是对配置文件中的属性是否存在做验证,但是后面部分是完成了什么方面的验证呢?如果读者读过 MyBatis 源码,你就会知道,在 MyBatis 实现过程中并没有手动调用 configuration.addMapper 方法,而是在映射文件读取过程中一旦解析到如<mapper namespace="Mapper.UserMapper"> ,便会自动进行类型映射的注册。那么,Spring 中为什么会把这个功能单独拿出来放在验证里呢?这是不是多此一举呢?
在上面的函数中,configuration.addMapper(this.mapperInterface) 其实就是将 UserMapper 注册到映射类型中,如果你可以保证这个接口一定存在对应的映射文件,那么其实这个验证并没有必要。但是,由于这个是我们自行决定的配置,无法保证这里配置的接口一定存在对应的映射文件,所以这里非常有必要进行验证。在执行此代码的时候,MyBatis 会检查嵌入的映射接口是否存在对应的映射文件,如果没有回抛出异常,Spring 正是在用这种方式来完成接口对应的映射文件存在性验证。
2.获取 MapperFactoryBean 的实例
由于 MapperFactoryBean 实现了 FactoryBean 接口,所以当通过 getBean 方法获取对应实例的时候其实是获取该类的 getObject() 函数返回的实例。
public T getObject() throws Exception {
return getSqlSession().getMapper(this.mapperInterface);
}
这段代码正是我们在提供 MyBatis 独立使用的时候的一个代码调用。Spring 通过 FactoryBean 进行了封装。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论