前言
在上一篇文章中,我们使用了老老实实的方式对两个框架进行了整合,我们在每一个服务层组件都声明了一个SqlSessionTemplate对象,在调用数据层的时候通过getMapper()方法映射得到某个接口,然后调用里面的方法。
直接在业务组件调用getMapper()方法并不是最佳选择,每一次调用这个方法就会做一次反射。mybatis-spring整合包中提供了相关组件,可以不必每次都调用getMapper()方法,而是通过配置的方式直接为业务对象注入映射器实现。接下来我们就对整合做一下优化。
优化前的要求
1、映射的命名空间(即映射文件的命名空间)要与映射器接口的包路径对应;
2、映射元素的id必须和映射器接口的方法名一致。
3、以下代码、修改都是基于上一篇博文的,各位可以先看一下上一篇博文的目录结构以及里面的代码,因为只需要在一些地方做修改,没必要再把所有代码都拿过来。
开始优化
第一种优化——使用MapperFactoryBean注入映射器
我们不再在服务层实现类中定义SqlSessionTemplate对象,而是定义它所依赖的接口,此处以UserServiceImpl为例。所需要的代码、以及目录结构与上一篇博文都一样,没有代码的朋友可以去上一篇博文找代码,或者自己撸。
package com.Blog.ServiceImpl;
import com.Blog.Dao.StudentDao;
import com.Blog.Entity.Student;
import com.Blog.Service.StudentService;
import org.apache.ibatis.annotations.Param;
import org.mybatis.spring.SqlSessionTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Service;
@Service("studentService") //使用注解定义服务层Bean,可以删除配置文件中的配置
public class StudentServiceImpl implements StudentService {
@Autowired
@Qualifier("studentDao")
private StudentDao studentDao; //所依赖的Dao层接口
@Override
public Student getStudent(@Param("id") int id) {
return studentDao.getStudent(id);
}
}
因为我们用注解的方式定义了Service层的一个Bean,所以可以将之前在配置文件中定义的id为studentService的Bean删除了。又因为我们不再显式直接使用SqlSessionTemplate对象,所以也可进行删除。综上,我们需要在spring-mybatis.xml中做的操作:
删除第三步、第四步,并在第二步之后加上以下代码:
<!--定义一个Bean,类即是org.mybatis.spring.mapper.MapperFactoryBean,指定它里面的mapperInterface是我们要指向的接口-->
<bean id="studentDao" class="org.mybatis.spring.mapper.MapperFactoryBean">
<property name="mapperInterface" value="com.Blog.Dao.StudentDao" />
<property name="sqlSessionFactory" ref="factory" />
</bean>
因为我们是用注解定义的组件,所以不要忘了扫描我们的包:
<context:component-scan base-package="com.Blog.ServiceImpl" />
其他的都不需要动,运行测试类,是没有问题的。
那么MapperFactoryBean到底是一个什么东西呢?
MapperFactoryBean是SqlSessionDaoSupport的子类,需要通过一个SqlSessionFactory实例来创建SqlSessionTemplate实例,所以我们为其注入了一个id为factory的Bean组件。这个类还有一个属性是mapperInterface属性,即映射器接口,我们只需要指定为要映射的接口的路径,就相当于调用了getMapper()方法反射得到这个接口的信息。最后我们在StudentServiceImpl类中通过注解的方式为studentDao属性注入依赖,就可以调用相关的方法了。
需要注意的是,如果映射器对应的SQL映射文件与映射器的类路径相同,那么该映射文件可以自动被MapperFactoryBean解析,在配置SqlSessionFactoryBean时可以不用mapperLocations属性来扫描SQL映射文件。反之,仍然需要进行扫描引入。因为我们定义的接口和对应的映射文件不在同一包下,名称也不一样,所以我们还是手动扫描了。
第二种——使用MapperScannerConfigurer注入映射器
在上面的优化中,我们使用MapperFactoryBean对映射器进行配置,简化了DAO模块的编码。但是,如果有多个接口,即需要配置多个映射器的时候,相应的配置项就会变得很多。为了简化配置工作量,mybatis-spring整合包中提供了MapperScannerConfigurer,它可以扫描指定包中的接口,并为它们直接注册为MapperFactoryBean。
将第一种优化中配置MapperFactoryBean的代码替换为以下代码:
<!--使用MapperScannerConfigurer扫描指定包的接口,并自动生成MapperFactoryBean-->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="basePackage" value="com.Blog.Dao" />
</bean>
运行也是没问题的。
basePackage属性中可以指定多个包,用逗号或分号隔开;
MapperScannerConfigurer会为所有由它创建的映射器开启自动装配。也就是说我们不用显式注入SqlSessionFactory实例,它会自动从容器中扫描。如果容器中有多个SqlSessionFactory实例,此时需要显式指定所依赖的SqlSessionFactory,如下
<!--使用MapperScannerConfigurer扫描指定包的接口,并自动生成MapperFactoryBean-->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="sqlSessionFactoryBeanName" value="factory" />
<property name="basePackage" value="com.Blog.Dao" />
</bean>
映射器被注入到容器中之后,Spring会根据其接口名称为其命名,规则是首字母小写的非完全限定类名。例如StudentDao接口,会被命名为studentDao,所以我们可以直接在StudentServiceImpl中为studentDao属性注入依赖。所以这就需要我们命名规范,养成良好的命名习惯。
总结
多对比几种实现方式,选择最好的方式!