springboot集成下,mybatis的mapper代理对象究竟是如何生成的

mybatis操作数据库的流程:先创建SqlSessionFactory,然后创建SqlSession,然后再创建获取mapper代理对象,最后利用mapper代理对象完成数据库的操作;Mapper代理对象的创建,利用的是JDK的动态代理,InvocationHandler是MapperProxy,后续Mapper代理对象方法的执行都会先经过MapperProxy的invoke方法。

但是,此时SqlSessionFactory的创建、SqlSession的创建以及mapper代理对象的获取都是我们手动操作的,实际应用中,mybatis往往也不会单独使用,绝大多数都是集成在spring中,也就是说我们无需手动去管理mybatis相关对象的生命周期,全部都由spring容器统一管理,那么spring是什么时候在哪创建的mybatis的相关对象的呢?尤其是mapper代理对象MapperProxy的创建

Springboot集成mybatis

当springboot(其实还是spring)集成mybatis后,mybatis的对象是交给spring容器管理的,只会实例化一次,然后伴随着spring容器一直存在,直到spring容器销毁

自动配置:MybatisAutoConfiguration

springboot集成下,mybatis的mapper代理对象究竟是如何生成的

MybatisAutoConfiguration会被当做配置类被spring解析,我们来看看spring容器会从此配置类中解析到什么

springboot集成下,mybatis的mapper代理对象究竟是如何生成的

创建了SqlSessionFactory实例(实际类型:DefaultSqlSessionFactory),并注册到了spring容器;此时我们应该还注意到

@Import({ AutoConfiguredMapperScannerRegistrar.class })

AutoConfiguredMapperScannerRegistrar继承了ImportBeanDefinitionRegistrar(注意看类注释,有兴许的可以更深入的研究下),那么它的registerBeanDefinitions也会被调用

@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
 logger.debug("Searching for mappers annotated with @Mapper");
 ClassPathMapperScanner scanner = new ClassPathMapperScanner(registry);
 try {
 if (this.resourceLoader != null) {
 scanner.setResourceLoader(this.resourceLoader);
 }
 // 获取启动类所在的包,如:com.lee.shiro,会作为扫描开始的base package,一般只会有一个,但支持多个
 List<String> packages = AutoConfigurationPackages.get(this.beanFactory);
 if (logger.isDebugEnabled()) {
 for (String pkg : packages) {
 logger.debug("Using auto-configuration base package '{}'", pkg);
 }
 }
 scanner.setAnnotationClass(Mapper.class); // 设置扫谁,Mapper注解是被扫描对象
 scanner.registerFilters();
 scanner.doScan(StringUtils.toStringArray(packages)); // 扫描所有mapper,进行bean定义处理
 } catch (IllegalStateException ex) {
 logger.debug("Could not determine auto-configuration package, automatic mapper scanning disabled.", ex);
 }
}

springboot集成下,mybatis的mapper代理对象究竟是如何生成的

以我们启动类所在的包(com.lee.shiro)为基包,扫描所有的mapper,然后修改所有mapper在spring容器中的bean定义,将mapper的beanClass全部指向了MapperFactoryBean

mapper代理对象的创建:MapperFactoryBean

MapperFactoryBean继承SqlSessionDaoSupport,SqlSessionDaoSupport有两个方法用来设置SqlSession

public void setSqlSessionFactory(SqlSessionFactory sqlSessionFactory) {
 if (!this.externalSqlSession) {
 this.sqlSession = new SqlSessionTemplate(sqlSessionFactory);
 }
}
public void setSqlSessionTemplate(SqlSessionTemplate sqlSessionTemplate) {
 this.sqlSession = sqlSessionTemplate;
 this.externalSqlSession = true;
}

可以看到SqlSession的实际类型是:SqlSessionTemplate,SqlSessionTemplate在MybatisAutoConfiguration以@Bean方式创建的

Spring在创建Service实例:UserServiceImpl的时候,发现依赖mapper(可能还有其他的实例依赖mapper),那么就会去spring容器获取mapper实例,没有则进行创建,然后注入进来(依赖注入);具体创建过程如下

if (mbd.isSingleton()) {
 sharedInstance = getSingleton(beanName, () -> {
 try {
 // 创建mapper对象,beanName:com.lee.shiro.mapper.UserMapper,创建出来的实例实际上是MapperFactoryBean类型
 return createBean(beanName, mbd, args); 
 }
 catch (BeansException ex) {
 // Explicitly remove instance from singleton cache: It might have been put there
 // eagerly by the creation process, to allow for circular reference resolution.
 // Also remove any beans that received a temporary reference to the bean.
 destroySingleton(beanName);
 throw ex;
 }
 });
 // 获取给定bean实例的对象,如果是FactoryBean,则获取bean实例本身或其创建的对象
 bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
}

springboot集成下,mybatis的mapper代理对象究竟是如何生成的

因为Spring在mapper扫描的时候,将所有mapper bean定义中的beanClass设置成了MapperFactoryBean(继承了FactoryBean),所以通过createBean方法创建的mapper实例实际上是MapperFactoryBean对象,然后通过

bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
protected Object getObjectForBeanInstance(
 Object beanInstance, String name, String beanName, @Nullable RootBeanDefinition mbd) {
 // Don't let calling code try to dereference the factory if the bean isn't a factory.
 // isFactoryDereference方法判断name中是否有&字符
 if (BeanFactoryUtils.isFactoryDereference(name)) {
 if (beanInstance instanceof NullBean) {
 return beanInstance;
 }
 if (!(beanInstance instanceof FactoryBean)) {
 throw new BeanIsNotAFactoryException(transformedBeanName(name), beanInstance.getClass());
 }
 }
 // Now we have the bean instance, which may be a normal bean or a FactoryBean.
 // If it's a FactoryBean, we use it to create a bean instance, unless the
 // caller actually wants a reference to the factory.
 // 此时的beanInstance可能是一个普通bean,也可能是一个FactoryBean
 // 如果是一个FactoryBean,那么就用它创建想要的bean实例
 // 此if表示,如果beanInstance是普通bean,或者本来就想要FactoryBean实例,则直接返回beanInstance
 if (!(beanInstance instanceof FactoryBean) || BeanFactoryUtils.isFactoryDereference(name)) {
 return beanInstance;
 }
 Object object = null;
 if (mbd == null) {
 object = getCachedObjectForFactoryBean(beanName);
 }
 // 此时表明beanInstance是一个FactoryBean,并且不是想要FactoryBean实例
 if (object == null) {
 // Return bean instance from factory.
 FactoryBean<?> factory = (FactoryBean<?>) beanInstance;
 // Caches object obtained from FactoryBean if it is a singleton.
 if (mbd == null && containsBeanDefinition(beanName)) {
 mbd = getMergedLocalBeanDefinition(beanName);
 }
 boolean synthetic = (mbd != null && mbd.isSynthetic());
 // 通过FactoryBean实例创建我们想要的实例
 object = getObjectFromFactoryBean(factory, beanName, !synthetic);
 }
 return object;
}

获取真正想要的bean实例,如果beanInstance是普通bean,或者本来就想要FactoryBean实例(beanName中有&),那么直接返回beanInstance,否则用FactoryBean实例来创建我们想要的实例对象。说回来就是会调用MapperFactoryBean的getObject()方法来获取Mapper的代理对象

总结

1、自动配置的过程中,spring会扫描所有的mapper,并将所有mapper bean定义中的beanClass指向MapperFactoryBean;

2、创建mapper实例的时候,根据bean定义创建的实例实际上是MapperFactoryBean实例,然后再利用MapperFactoryBean获取mapper实例(调用MapperFactoryBean的getObject方法,mybatis会利用jdk的动态代理创建mapper代理对象);

相关推荐