MapperFactoryBean
使用 MapperFactoryBean 可以向 Spring 容器中导入一个 Mapper 接口的代理实现类
如:向 Spring 容器中导入 com.springdemo.dao.UserDao 接口的代理实现类
<bean id="userDao" class="org.mybatis.spring.mapper.MapperFactoryBean">
<property name="mapperInterface" value="com.spring-mybatis.dao.UserDao"/> <!-- 要代理的目标接口 -->
<property name="sqlSessionFactory" ref="sqlSessionFactory"/>
</bean>
这样的配置其实是向 Spring 容器中注入了一个 MapperFactoryBean ,而 MapperFactoryBean 是一个 FactoryBean ,当获取 userDao 时,其实调用的是 MapperFactoryBean 的 getObject() 方法进行的获取。
- 注:因为 MapperFactoryBean 是 SqlSessionDaoSupport 的子类,所以要为其配置 sqlSessionFactory 来构建成员属性 SqlSessionTemplate ,同时在 MapperFactoryBean 类中可以通过 getSqlSession() 方法来获取这个 SqlSessionTemplate,【关于MapperFactoryBean、SqlSessionDaoSupport 的详细介绍,可以看上一篇文章】
注意到,通过配置 MapperFactoryBean 的方式,只能向容器中注入一个接口代理对象。
当有很多 DAO 接口时,就需要为每一个 DAO 接口配置 MapperFactoryBean:
<bean id="catDao" class="org.mybatis.spring.mapper.MapperFactoryBean">
<property name="mapperInterface" value="com.spring-mybatis.dao.CatDao"/>
<property name="sqlSessionFactory" ref="sqlSessionFactory"/>
</bean>
<bean id="dogDao" class="org.mybatis.spring.mapper.MapperFactoryBean">
<property name="mapperInterface" value="com.spring-mybatis.dao.DogDao"/>
<property name="sqlSessionFactory" ref="sqlSessionFactory"/>
</bean>
<bean id="studentDao" class="org.mybatis.spring.mapper.MapperFactoryBean">
<property name="mapperInterface" value="com.spring-mybatis.dao.StudentDao"/>
<property name="sqlSessionFactory" ref="sqlSessionFactory"/>
</bean>
....
这种方式显然是很低效的,为了解决这个问题,我们使用 MapperScannerConfigurer
,让它扫描一个包下的所有接口,成批地向容器中注入接口代理对象。
MapperScannerConfigurer
MapperScannerConfigurer 可以为一个包下的所有 Mapper 接口创建代理,并注入Spring 容器
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<!--指定Mapper接口所在的包-->
<property name="basePackage" value="com.spring-mybatis.dao" />
</bean>
使用这种方式,com.spring-mybatis.dao 包下的所有 DAO 接口,Spring 都会为其创建代理对象并注入容器
原理:
可以看到 MapperScannerConfigurer 实现了 BeanDefinitionRegistryPostProcessor
接口
(实现其 postProcessBeanDefinitionRegistry()
方法可以向容器中注册 BeanDefinition)。
关注 MapperScannerConfigurer 对 postProcessBeanDefinitionRegistry 方法的实现:
进入 scan() 方法:
进入 doScan():
进入 processBeanDefinitions() : 处理这些 Mapper 接口所对应的 BeanDefinition:
可以看到:
1、MapperScannerConfigurer 利用 ClassPathMapperScanner 扫描 basePackage 包下的 Mapper 接口,将这些 Mapper 接口解析成一个个 BeanDefinition。
2、由于接口不能直接创建对象,所以在处理 BeanDefinition 的过程中,将这些 BeanDefinition 的 BeanClass 类型设置成了 MapperFactoryBean 类型。所以后续 Spring 在依据 BeanDefinition 创建对象时,实际上创建的是一批 MapperFactoryBean 对象。
3、这些 MapperFactoryBean 的构造函数中传入的值是 Mapper 接口的类名(相当于为我们自动配置了 MapperFactoryBean 的 mapperInterface 属性)
4、同时设置了自动装配的类型为按类型注入,这说明这些 MapperFactoryBean 在创建的过程中,Spring 会为类中的 Set 方法注入相应类型的 Bean(这也就是为什么在配置 MapperScannerConfigurer 时,无需为其配置 SqlSessionFactory 的原因)