上文中讲到,通过sqlSession的getMapper方法获得的对象是jdk的动态代理对象,
如果和spring整合之后,再通过这种方式获取显然是不恰当的,
那么我们如何实现非spring产生的对象交给spring来管理呢?
mybatis是如何实现这种方式的呢?
首先先说明一下我现在的理解
可能有人会说使用@controller @service @component
@repository。。等等注解标注类之后就可以将我们自己产生的对象交给spring容器管理。
但是这种方式只会使被注解标注的类能够被spring容器扫描到,
然后spring将扫描到的bean定义生成一个单例对象,
这个过程我们是无法控制的,是spring帮助我们创建的对象。
那么我们如何将自己创建的对象交个spring管理,或者更宽泛的说如何将第三方的对象交个spring管理?
方式一:可以通过@bean注解
方式二:可以通过实现factorybean接口,并重写其中的getObject方法,在这个方法中就可以自定义创建对象的逻辑,并将返回的对象交给spring容器管理 mapperFactoryBean就是这种方式
<bean id="userMapper" class="org.mybatis.spring.mapper.MapperFactoryBean">
<property name="mapperInterface" value="org.mybatis.spring.sample.mapper.UserMapper" />
<property name="sqlSessionFactory" ref="sqlSessionFactory" />
</bean>
MapperFactoryBean实现了factoryBean接口,那么配置之后会将UserMapper的代理对象创建出来,并交给spring容器管理,然后就可以使用@autowired自动注入。
但是一个项目中一定会有多个mapper,这种配置的方式无疑只能是在demo中使用,因为我们不可能为每一个mapper都配置一个MapperFactoryBean,这是重复的劳动。
那么我们就使用spring的后置处理器进行处理。@mapperScan实际上就是利用扩展spring的生命周期阶段的方法进行批量处理注入mapper的
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Import(MapperScannerRegistrar.class)
public @interface MapperScan {
}
利用@import注解导入MapperScannerRegistrar,并重写了registerBeanDefinitions方法,手动的将 符合条件的bean定义注入到spring容器中
public class MapperScannerRegistrar implements ImportBeanDefinitionRegistrar, ResourceLoaderAware {
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
//获取mapperScan注解上对应的值
AnnotationAttributes annoAttrs = AnnotationAttributes.fromMap(importingClassMetadata.getAnnotationAttributes(MapperScan.class.getName()));
//利用ClassPathMapperScanner进行扫描,扫描后通过BeanDefinitionRegistry进行注册
ClassPathMapperScanner scanner = new ClassPathMapperScanner(registry);
//..省略其中部分代码 然后利用scanner进行扫描
scanner.doScan(StringUtils.toStringArray(basePackages));
}
}
public class ClassPathMapperScanner extends ClassPathBeanDefinitionScanner{
@Override
public Set<BeanDefinitionHolder> doScan(String... basePackages) {
//调用父类进行扫描bean定义
Set<BeanDefinitionHolder> beanDefinitions = super.doScan(basePackages);
if (beanDefinitions.isEmpty()) {
logger.warn("No MyBatis mapper was found in '" + Arrays.toString(basePackages) + "' package. Please check your configuration.");
} else {
//对bean定义进行后续的处理
processBeanDefinitions(beanDefinitions);
}
return beanDefinitions;
}
}
从前的某个文章讲过ClassPathBeanDefinitionScanner 是spring的bean扫描器,mybatis对其进行了扩展,扫描出自己的mapper接口进行处理,然后对得到的beandefinition进行处理,处理的方式在processBeanDefinitions方法中 那么核心的思想在于
//通过构造器将要代理的接口类型传递进去
definition.getConstructorArgumentValues().addGenericArgumentValue(definition.getBeanClassName());
// 设置Bean的真实类型MapperFactoryBean
definition.setBeanClass(this.mapperFactoryBean.getClass());
definition.getPropertyValues().add("addToConfig", this.addToConfig);
这里面再留个小问题吧,以后慢慢学着解决,为什么springboot应用没有配置@mapperscan注解,mybatis也好用了呢?