我们从第三方框架mybatis为引,看看如何往spring中注入一个bean
1、纯mybatis开发生成一个mapper对象
如果不使用spring的情况下,mybatis想生成一个mapper对象大概需要做下面的操作:
假设我们有了一个TMapper接口,此时获取该mapper对象
SqlSessionFactory sqlSessionFactory =new SqlSessionFactoryBuilder().build(configuration);
SqlSession sqlSession = sqlSessionFactory.openSession();
TMapper mapper = sqlSession.getMapper(TMapper.class);
通过sqlsession.getMapper()即可获取mapper对象,但我们知道TMapper是一个接口,接口是不能进行实例化的,那这个对象是怎么来的?
如果翻阅mybatis源码会看到,底层对该接口进行了jdk动态代理完成了对象的实例化,最后返回了代理对象。
好了,到这里我们大概知道mybatis生成一个mapper对象了,下面我们需要考虑的是如何将这个mapper对象注入到spring容器中。
2、如何将mapper对象注入到spring容器中
我们先不看mybatis如何将mapper对象注入到spring中去的,我们先来看看我们已知的可以往spring注入对象的方式。
注意:往spring中注入类和注入对象是不同的概念,注入类是spring会接管类对象实例化、初始化等过程,而注入对象则是将对象的实例化初始化交由自己掌控。
1)、@Component注解方式——这种方式是把类给spring管理
2)、xml方式,<bean id="" class="" ——同理这种方式也是把类给spring管理
3)、动态注册beanDefifinition——这种方式是把beanDefifinition对象给spring,并不是自己产的真实对 象 。
以上三种方式会被很多人误解可以把一个对象给到spring容器;当然这三种方式最后也是产生了一个对 象,并且存到了容器当中;但是问题的关键是这个对象的产生过程是我们无法去控制的;是由spring容 器去实例化的对象;比如上面我们的tMapper对象,是需要我们用动态代理这种特定方式来产生, spring肯定不会帮你完成。所以再来看下下面的三种方式:
4)、@Bean方式——通过在方法当中编码,可以自己产生对象;返回后即存在容器,符合要求。
5)、FactoryBean方式——通过实现FactoryBean接口也可以自己编码产生对象,符合要求。(只是能实现对mapper对象进行实例化,但注入spring需要借助xml配置、其他注解等其他方式注入到spring中)
6)、使用spring提供的api,beanFactory.registerSingleton(String name,Object object)——直接传自己的对象 符合要求
那么mybatis中是如何去选择的?(下面以xml的方式去解析整体流程)
我们可以看看mybatisxml怎么去配一个mapper的:
<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>
public class MapperFactoryBean implements FactoryBean{
private Class mapperInterface; //接收不同类型的mapper
//返回代理对象
public T getObject() throws Exception {
return getSqlSession().getMapper(this.mapperInterface);
}
//xml配置的时候,提供Mapper,实现代理
public void setMapperInterface(Class<T> mapperInterface) {
this.mapperInterface = mapperInterface;
}
}
mybatis选择的是FactoryBean的方式。
但是这个时候问题又来了,如果我要注入多个mapper对象,此时需要去xml配置多次?这会不会显得太麻烦了?此时基于FactoryBean的基础上(为了使得接口能被实例化、Mapper所有方法基本执行相同的操作——找到对应的SQL,绑定参数,执行SQL然后返回结果),我们需要解决多个mapper对象注入简易化的需求。
此时mybatis就抛弃了xml注入bean的配置化方式,提供了两个注解@MapperScan、@Mapper
@Mapper是队单个接口类的注解,单个操作,然后接口在编译之后都会生成相应的实现类。(接口少的时候适合使用,所以我们暂时不说他)
@MapperScan是对整个包之下的所有的接口类的注解,是批量的操作。使用@MapperScan之后,接口类就不需要再使用@Mapper注解了(正解决了注入多个mapper的简易化需求)
所以我们下面就得去看看@MapperScan是如何实现这么个过程的。
注意:FactoryBean+@Component并不能注入mapper对象,因为@Component只能将类交给spring生成bean,不能将对象交给spring。所以就不需要讨论这种情况了。而@Bean、beanFactory.registerSingleton()每配一个mapper就要编写一次,显然也不合适,所以就只剩下动态注册beanDefifinition。我们暂且不下定论,先看看mybatis是怎么做的。
3、@MapperScan如何进行多个mapper的注入
在说明@MapperScan注解的原理前,需要明白的一点,就是要满足接口能被实例化、接口方法能执行相应逻辑的前提,保证这一前提的是使用了FactoryBean方式在getObject()方法中返回了一个jdk动态代理实例化好的的mapper代理对象。有了这一前提我们才需要解决多mapper注入的问题。
由上面可知FactoryBean+xml配置可以将对象注入到spring中,不过这种情况在需要注入多个mapper的时候显得太麻烦了,所以mybatis就提供了一个扫描mapper的注解@MapperScan。
Mybatis的mapper对象注入到Spring容器中的过程_DanceDonkey的博客-CSDN博客_mybatis mapper注入
我们可以点进@MapperScan注解中:
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE})
@Documented
@Import({MapperScannerRegistrar.class})
@Repeatable(MapperScans.class) //在一个类、方法、属性上标注多个相同注解
public @interface MapperScan {}
可以看到一个@Import({MapperScannerRegistrar.class}),@Import注解是用来导入配置类或者一些需要前置加载的类。
对于@MapperScan注解的原理可以看下面文章:
通过@MapperScan源码了解Spring自定义注解扫描器[通俗易懂] - 腾讯云开发者社区-腾讯云
看过这篇文章,可以知道,mybatis确实是使用了动态注册beanDefifinition的方式将FactoryBean方式产生的mapper代理对象注入到了spring中。
那么我们下面需要看看什么是beanDefifinition、如何动态注册beanDefifinition?