当前位置: 首页 > 后端技术 > Java

Mybatis与springboot整合

时间:2023-04-02 09:37:23 Java

整合逻辑如下:利用springboot自动组装的特性,使用MybatisAutoConfiguration开启mybatis与springboot的整合在创建SqlSessionFactory前,尝试读取前缀为mybatis的配置文件(如mybatis-spring.xml)并记录到SqlSessionFactoryBean#configLocation;如果不读取配置文件,直接使用默认配置创建配置如果读取配置文件,使用配置信息创建配置通过FactoryBean创建SqlSessionFactory默认事务工厂使用SpringManagedTransactionFactory,以后会创建SpringManagedTransaction来创建SqlSessionTemplate来操作SqlSession,本质上是一层代理,目的是动态打开和关闭SqlSession——这样做的原因是SqlSession不是线程安全的。在MybatisAutoConfiguration中,@Mapper注解修改的类被Scanner扫描,记录在beanDefinitionMap中。通过源码实现具体情况观察。1、自动组装由于springboot自动组装的特点,找到mybatis-spring-boot-autoconfigure.jar下的配置文件:META-INF|__spring.factories文件内容:#AutoConfigureorg.springframework.boot.autoconfigure.EnableAutoConfiguration=\org.mybatis.spring.boot.autoconfigure.MybatisAutoConfigurationEnableAutoConfiguration指向MybatisAutoConfiguration,所以关键的配置必须在这个类中。propertiesconfiguration//导入属性配置@EnableConfigurationProperties({MybatisProperties.class})//导入数据源配置@AutoConfigureAfter({DataSourceAutoConfiguration.class})publicclassMybatisAutoConfigurationimplementsInitializingBean{进入MybatisProperties类,发现很多熟悉的属性名(typeAliases,typeHandler等)@ConfigurationProperties(prefix="mybatis")publicclassMybatisProperties{//配置文件前缀,比如mybatis-spring.xmlpublicstaticfinalStringMYBATIS_PREFIX="mybatis";//映射器地址privateString[]mapperLocations;//typeAliases类型别名包路径privateStringtypeAliasesPackage;私有类typeAliasesSuperType;//typeHandlers类型转换包路径privateStringtypeHandlersPackage;私有布尔checkConfigLocation=false;//执行器枚举:SIMPLE、REUSE、BATCHprivateExecutorTypeexecutorType;2、创建SqlSessionFactory回忆一下单独使用mybatis时的步骤:configuration->SqlSessionFactory->sqlSession->getMapper看看与springboot整合后的流程。@Bean@ConditionalOnMissingBeanpublicSqlSessionFactorysqlSessionFactory(DataSourcedataSource)throwsException{//通过FactoryBean创建SqlSessionFactorySqlSessionFactoryBeanfactory=newSqlSessionFactoryBean();factory.setDataSource(数据源配置文件);//--如果配置文件是指定资源if(StringUtils.hasText(this.properties.getConfigLocation())){}//--如果用户没有指定配置文件,则使用一些默认配置创建一个配置this.applyConfiguration(factory);如果(this.properties.getConfigurationProperties()!=null){factory.setConfigurationProperties(this.properties.getConfigurationProperties());}returnfactory.getObject();}spring与第三方集成时,经常会用到FactoryBean方法,比如这里的SqlSessionFactoryBean。观察其getObject()方法org.mybatis.spring.SqlSessionFactoryBean#getObjectpublicSqlSessionFactorygetObject()throwsException{if(this.sqlSessionFactory==null){//直接在afterPropertiesSet中创建sqlSessionFactorythis.afterPropertiesSet();}returnthis.sqlSessionFactory;}追踪afterPropertiesSet方法org.mybatis.spring.SqlSessionFactoryBean#afterPropertiesSetorg.mybatis.spring.SqlSessionFactoryBean#buildSqlSessionFactoryprotectedSqlSessionFactorybuildSqlSessionFactory()throwsException{XMLConfigBuilderxmlConfigBuilder=null;配置targetConfiguration;//--配置已创建if(this.configuration!=null){targetConfiguration=this.configuration;}//--配置没有创建,但是有配置文件地址:用XPath解析配置文件elseif(this.configLocation!=null){xmlConfigBuilder=newXMLConfigBuilder(this.configLocation.getInputStream(),(String)null,this.configurationProperties);targetConfiguration=xmlConfigBuilder.getConfiguration();}//--使用默认配置创建配置else{targetConfiguration=newConfiguration();}//--------跳过typeAliases、typeHandler等设置---------targetConfiguration.setEnvironment(//初始化环境newEnvironment(this.environment,(TransactionFactory)(this.transactionFactory==null?//###默认情况下,SpringManagedTransactionFactory用于管理事务newSpringManagedTransactionFactory():this.transactionFactory),this.dataSource));returnthis.sqlSessionFactoryBuilder.build(targetConfiguration);}SqlSessionFactory的创建过程和单独使用mybatis时几乎完全一样;唯一不同的是默认设置了SpringManagedTransactionFactory管理事务(mybatis默认使用jdbc管理事务,事务控制交给DB)3.SqlSessionTemplate接下来看SqlSession的处理晚sqlSessionTemplate(SqlSessionFactorysqlSessionFactory){ExecutorTypeexecutorType=this.properties.getExecutorType();返回executorType!=null?newSqlSessionTemplate(sqlSessionFactory,executorType)//由构造函数创建:newSqlSessionTemplate(sqlSessionFactory;看构造函数)whatorg.mybatis.spring.SqlSessionTemplate#SqlSessionTemplatepublicSqlSessionTemplate(SqlSessionFactorysqlSessionFactory,ExecutorTypeexecutorType,PersistenceExceptionTranslatorexceptionTranslator){this.sqlSessionFactory=sqlSessionFactory;}this.executorType=executorType;this.exceptionTranslator=exceptionTranslator;this.sqlSessionProxy=//创建sqlsession代理(SqlSession)Proxy.newProxyInstance(SqlSessionFactory.class.getClassLoader(),newClass[]{SqlSession.class},//##invocationHandlernewSqlSessionTemplate.SqlSessionInterceptor());}直接查看代理的invocationHandler的invoke()org.mybatis.spring.SqlSessionTemplate.SqlSessionInterceptor#invokepublicObjectinvoke(Objectproxy,Methodmethod,Object[]args)throwsThrowable{//1.sqlSession创建,内部实现:session=sessionFactory.openSession(executorType)SqlSessionsqlSession=SqlSessionUtils.getSqlSession(Sq}catch(Throwablevar11){unwrapped=ExceptionUtil.unwrapThrowable(var11);抛出(Throwable)展开;}finally{//3.sqlSession关闭if(sqlSession!=null){SqlSessionUtils.closeSqlSession(sqlSession,SqlSessionTemplate.this.sqlSession}Factory);}returnunwrapped;}当调用封装在SqlSessionTemplate中的一系列数据库操作方法(如下)时,会自动创建和销毁sqlSessionpublicTselectOne(Stringstatement){returnthis.sqlSessionProxy.selectOne(statement);}publicintinsert(Stringstatement){returnthis.sqlSessionProxy.insert(statement);}publicintupdate(Stringstatement){returnthis.sqlSessionProxy.update(statement);}Spring使用SqlSessionTemplate代替mybatis的SqlSession,主要是自动创建和销毁SqlSession,因为SqlSession是线程不安全的类,不能复用。4、Mapper创建SqlSessionTemplate类中提供的一系列方法,包括getMapper()方法:publicTgetMapper(Classtype){returnthis.getConfiguration().getMapper(type,this);}这似乎符合mybatis的使用方式。但是仔细一想,又觉得不对。我们在工作中不会以这种方式获取映射器。真正在spring环境下打开mapper的方法是这样的:@AutowireUserMapperuserMapper;翻译过来就是通过context获取,即:context.getMapper(Classclz);springboot如何自动组装Mapper?答案还在MybatisAutoConfiguration中。@Import({MybatisAutoConfiguration.AutoConfiguredMapperScannerRegistrar.class})publicstaticclassMapperScannerRegistrarNotFoundConfigurationimplementsInitializingBean追踪导入的MybatisAutoConfiguration.AutoConfiguredMapperScannerRegistrarorg.mybatis.spring.boot.autoconfigure.MybatisAutoConfiguration.AutoConfiguredMapperScannerRegistrar#registerBeanDefinitionspublicvoidregisterBeanDefinitions(AnnotationMetadataimportingClassMetadata,BeanDefinitionRegistryregistry){//其实这行日志已经解释的很清楚了MybatisAutoConfiguration.logger.debug("Searchingformappersannotatedwith@Mapper");Listpackages=AutoConfigurationPackages.get(this.beanFactory);//###Scanner:扫描包下所有被@Mapper或interface修饰的ClassPathMapperScannerscanner=newClassPathMapperScanner(registry);if(this.resourceLoader!=null){scanner.setResourceLoader(this.resourceLoader);}scanner.setAnnotationClass(Mappe类);扫描仪.registerFilters();scanner.doScan(StringUtils.toStringArray(packages));}观察scanner.doScan方法:org.mybatis.spring.mapper.ClassPathMapperScanner#doScanorg.springframework.context.annotation.ClassPathBeanDefinitionScanner#doScanorg.springframework.context.annotation.ClassPathBeanDefinitionScanner#registerBeanDefinitionorg.springframework.beans.factory.support.BeanDefinitionReaderUtils#registerBeanDefinitionorg.springframework.beans.factory.support.DefaultListableBeanFactory#registerBeanDefinition/anDefault”,初始化后的对象会被放入springcontextprivatefinalMapbeanDefinitionMap=newConcurrentHashMap(256);//——————以上都是属性——————//最后放入beanDefinitionMapthis.beanDefinitionMap.put(beanName,beanDefinition);在SqlSessionFactory的创建中,通过解析mapper.xml,xml中指定的mapper转换为代理对象(mybatis逻辑),在MybatisAutoConfiguration中,Scanner扫描@Mapper注解装饰类,完成注入(spring逻辑)附录P6-P7知识大集合