读完本文后你的收获:
1、了解Mybatis和Spring整合的底层原理
2、知道为什么我们在spring中定义了接口就可以直接操作数据库
3、了解Spring中的拓展点和FactoryBean的使用
首先来分析要解决的问题,如下图所示,我们没有在Admapper上加@Component等注解,我们为什么可以从bean工厂中获取AdMapper的实例,并且可以通过@Resource注入对象;
首先来解决第一个问题,没有加注解是怎么加入到容器中的。
在mybatis和spring整合时,一般会在配置类上加@MapperScan,我们点进去这个注解发现,它@Import(MapperScannerRegistrar.class)了另一个类。MapperScannerRegistrar这个类呢做的就是把所有的mapper变成BeanDefinition,从而后边可以被创建。
通过继承关系,我们发现这个类实现了ImportBeanDefinitionRegistrar这个接口,这个接口是spring当中的一个扩展点,基于接口中的registerBeanDefinitions方法我们可以做到向bean工厂中注入BeanDefinition。
看一下这个方发的实现,主要是doScan方法,首先调用父类的doScan扫描我们配置的包路径,获得所有的class,包装成BeanDefinition,假如说我们这些接口都有实现类,那么这时我们把这个BD直接放入到BDMap中,spring就会帮助我们创建对象,这就解释了第一个疑问,这些接口的定义信息是怎么放进去的。
我们虽然拿到了BD并且把它放到了BDMap中去,如果spring直接使用当前的bd是没办法创建成功对象的,因为我们只写了接口,这时我们就要改变BeanDefinition的属性,来让bean工厂可以创建对象。
processBeanDefinitions方法就是做的这个事,我们看到这个方法的接口参数是Set<BeanDefinitionHolder> beanDefinitions,也就是上一步扫描的结果,循环修改BD的信息
主要解析以下三行代码
代码1: definition.getPropertyValues().add("mapperInterface", definition.getBeanClassName());
代码2: definition.setBeanClass(this.mapperFactoryBean.getClass());
代码3: definition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE);
解析代码直接,因为 它设置了一个mapperFactoryBean,所以需要知道mapperFactoryBean的结构
可以看到MapperFactoryBean实现了继承了 SqlSessionDaoSupport 实现了FactoryBean;FactoryBean这个接口大家都不陌生,可以通过getObject方法获得我们想要的对象,实时上也是这么做的。
代码一 definition.getPropertyValues().add("mapperInterface", definition.getBeanClassName());此处相当于我们直接调用setMapperInterface方法,只不过是spring会把字符串转换成class对象,为什么要设置class类型,因为我们的mapper通常有好多接口,而且我们也不知道具体的名字,需要程序动态的注入进来;
代码2: definition.setBeanClass(this.mapperFactoryBean.getClass());设置bd的beanClass类型,bd在创建的时候会根据这个类型去创建对象,因为mapperFactoryBean是个对象,所以可以被创建。
代码3: definition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE); 修改默认的装配类型为byType,这样我们就可以通过@Autowired来自动装配
到现在实际上已经把bd的定义修改完了,然后在创建对象时回调用getObject方法生成代理对象,并放到容器中。所以容器中放的是接口的代理对象。那么我们也可以直接拿出来使用
上边已经对文章开头处的疑问做了阐述,希望能对大家有所帮助,其实还有很多细节比如为什么要设置AutowireMode,不设置会怎么样,大家可以查看一下源码去解决这个问题,并且会很有收获发现spring默认的装配类型不是按类型装配。
再次总结一下文章中所用的知识点
ImportBeanDefinitionRegistrar:这个接口可以帮助我们动态的向bean工厂中添加bd或者修改bd
FactoryBean:这个接口可以让我们通过getObject方法获取对象并且执行是不是单例
AutowireMode:装配类型,可以实现自动装配