Spring中Mybatis的配置方案一
2.1 多數(shù)據(jù)源配置案例
(1)數(shù)據(jù)源配置(2)創(chuàng)建sqlSessionFactory(3)配置掃描器,掃描指定路徑的mapper生成數(shù)據(jù)庫操作代理類(4)數(shù)據(jù)源配置(5)創(chuàng)建sqlSessionFactory(6)配置掃描器,掃描指定路徑的mapper生成數(shù)據(jù)庫操作代理類
(1)(2)(3)是一組配置布蔗, (4)(5)(6)是一組配置友驮,配置指定的數(shù)據(jù)源到對應的包下掃描配置文件生成數(shù)據(jù)庫操作代理類疼鸟。
(1)(4)分別創(chuàng)建了兩個數(shù)據(jù)源殴蓬,(2)(5)根據(jù)對應的數(shù)據(jù)源創(chuàng)建SqlSession工廠立润,(3)(6)配置mybaits掃描器狂窑,根據(jù)對應的SqlSession工廠和包路徑生成代理后的數(shù)據(jù)庫操作類。
2.1 原理簡單介紹
2.1.1 SqlSessionFactory原理
(2)(5)作用是根據(jù)配置創(chuàng)建一個SqlSessionFactory桑腮,看下SqlSessionFactoryBean的代碼知道它實現(xiàn)了FactoryBean和InitializingBean類泉哈,由于實現(xiàn)了InitializingBean,所以自然它的afterPropertiesSet方法破讨,由于實現(xiàn)了FactoryBean類丛晦,所以自然會有getObject方法。下面看下時序圖:
screenshot.png
從時序圖可知提陶,SqlSessionFactoryBean類主要是通過屬性配置創(chuàng)建SqlSessionFactory實例采呐,具體是解析配置中所有的mapper文件放到configuration,然后作為構造函數(shù)參數(shù)實例化一個DefaultSqlSessionFactory作為SqlSessionFactory。
2.1.2 MapperScannerConfigurer原理
掃描指定路徑的mapper生成數(shù)據(jù)庫操作代理類
MapperScannerConfigurer 實現(xiàn)了 BeanDefinitionRegistryPostProcessor, InitializingBean, ApplicationContextAware, BeanNameAware接口搁骑,所以會重寫一下方法:
//在bean注冊到ioc后創(chuàng)建實例前修改bean定義和新增bean注冊斧吐,這個是在context的refresh方法調(diào)用voidpostProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry)throwsBeansException;//在bean注冊到ioc后創(chuàng)建實例前修改bean定義或者屬性值voidpostProcessBeanFactory(ConfigurableListableBeanFactory beanFactory)throwsBeansException;//set屬性設置后調(diào)用voidafterPropertiesSet()throwsException;//獲取IOC容器上下文,在context的prepareBeanFactory中調(diào)用voidsetApplicationContext(ApplicationContext applicationContext)throwsBeansException;//獲取bean在ioc容器中名字仲器,在context的prepareBeanFactory中調(diào)用voidsetBeanName(String name);
先上個掃描mapper生成代理類并注冊到ioc時序圖:
screenshot.png
首先MapperScannerConfigurer實現(xiàn)的afterPropertiesSet方法用來確保屬性basePackage不為空
publicvoidafterPropertiesSet()throwsException{? ? notNull(this.basePackage,"Property 'basePackage' is required");? }
postProcessBeanFactory里面啥都沒做煤率,setBeanName獲取了bean的名字,setApplicationContext里面獲取了ioc上下文乏冀。下面看重要的方法postProcessBeanDefinitionRegistry蝶糯,由于mybais是運行時候才通過解析mapper文件生成代理類注入到ioc,所以postProcessBeanDefinitionRegistry正好可以干這個事情辆沦。
publicvoidpostProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry){if(this.processPropertyPlaceHolders) {? ? ? processPropertyPlaceHolders();? ? }//構造一個ClassPathMapperScanner查找mapperClassPathMapperScanner scanner =newClassPathMapperScanner(registry);? ? scanner.setAddToConfig(this.addToConfig);//javax.annotation.Resourcescanner.setAnnotationClass(this.annotationClass);? ? scanner.setMarkerInterface(this.markerInterface);//引用sqlSessionFactoryscanner.setSqlSessionFactory(this.sqlSessionFactory);? ? scanner.setSqlSessionTemplate(this.sqlSessionTemplate);? ? scanner.setSqlSessionFactoryBeanName(this.sqlSessionFactoryBeanName);? ? scanner.setSqlSessionTemplateBeanName(this.sqlSessionTemplateBeanName);//ioc上下文scanner.setResourceLoader(this.applicationContext);? ? scanner.setBeanNameGenerator(this.nameGenerator);? ? scanner.registerFilters();//basePackage=com.alibaba.***.dal.***.mapper,com.alibaba.rock.auth.mapper,com.alibaba.rock.workflow.dal.workflow.mapperscanner.scan(StringUtils.tokenizeToStringArray(this.basePackage, ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS));? }
下面重點看下scan方法:
publicSet doScan(String... basePackages) {//根據(jù)指定路徑去查找對應mapper的接口類昼捍,并轉化為beandefinationSet 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的beandefinationprocessBeanDefinitions(beanDefinitions);? ? }returnbeanDefinitions;? }
其中super.doScan(basePackages);根據(jù)指定路徑查找mapper接口類,并生成bean的定義對象肢扯,對象中包含beanclassname,beanclass屬性妒茬,最后注冊該bean到ioc容器。下面看下最重要的processBeanDefinitions方法對bean定義的改造蔚晨。
privatevoidprocessBeanDefinitions(Set beanDefinitions){? ? GenericBeanDefinition definition;for(BeanDefinitionHolder holder : beanDefinitions) {? ? ? definition = (GenericBeanDefinition) holder.getBeanDefinition();// 上面講的掃描后beanclass設置的為mapper接口類乍钻,但是這里修改為MapperFactoryBean,MapperFactoryBean代理了mapper接口類,并且實際mapper接口類作為構造函數(shù)傳入了? ? ? definition.getConstructorArgumentValues().addGenericArgumentValue(definition.getBeanClassName()); definition.setBeanClass(this.mapperFactoryBean.getClass());? ? ? definition.getPropertyValues().add("addToConfig",this.addToConfig);//設置屬性配置中的sqlSessionFactorybooleanexplicitFactoryUsed =false;if(StringUtils.hasText(this.sqlSessionFactoryBeanName)) {? ? ? ? definition.getPropertyValues().add("sqlSessionFactory",newRuntimeBeanReference(this.sqlSessionFactoryBeanName));? ? ? ? explicitFactoryUsed =true;? ? ? }elseif(this.sqlSessionFactory !=null) {? ? ? ? definition.getPropertyValues().add("sqlSessionFactory",this.sqlSessionFactory);? ? ? ? explicitFactoryUsed =true;? ? ? }if(StringUtils.hasText(this.sqlSessionTemplateBeanName)) {if(explicitFactoryUsed) {? ? ? ? ? logger.warn("Cannot use both: sqlSessionTemplate and sqlSessionFactory together. sqlSessionFactory is ignored.");? ? ? ? }? ? ? ? definition.getPropertyValues().add("sqlSessionTemplate",newRuntimeBeanReference(this.sqlSessionTemplateBeanName));? ? ? ? explicitFactoryUsed =true;? ? ? }elseif(this.sqlSessionTemplate !=null) {if(explicitFactoryUsed) {? ? ? ? ? logger.warn("Cannot use both: sqlSessionTemplate and sqlSessionFactory together. sqlSessionFactory is ignored.");? ? ? ? }? ? ? ? definition.getPropertyValues().add("sqlSessionTemplate",this.sqlSessionTemplate);? ? ? ? explicitFactoryUsed =true;? ? ? }if(!explicitFactoryUsed) {if(logger.isDebugEnabled()) {? ? ? ? ? logger.debug("Enabling autowire by type for MapperFactoryBean with name '"+ holder.getBeanName() +"'.");? ? ? ? }? ? ? ? definition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE);? ? ? }? ? }? }
注:這里修改了mapper接口類的beandefination中的beanclass為MapperFactoryBean,它則負責生產(chǎn)數(shù)據(jù)類操作代理類银择,實際mapper接口類作為構造函數(shù)傳入了 多糠。由于只修改了beanclass,沒有修改beanname,所以我們從容器中獲取時候無感知的浩考。
在上一個代理bean如何構造的時序圖:
screenshot.png
下面看下MapperFactoryBean是如何生成代理類的:
首先夹孔,上面代碼設置了MapperFactoryBean的setSqlSessionFactory方法:
publicvoidsetSqlSessionFactory(SqlSessionFactory sqlSessionFactory){if(!this.externalSqlSession) {this.sqlSession =newSqlSessionTemplate(sqlSessionFactory);? ? }? }
上面方法創(chuàng)建了sqlSession,由于MapperFactoryBean為工廠bean所以實例化時候會調(diào)用getObject方法:
publicTgetObject()throwsException{returngetSqlSession().getMapper(this.mapperInterface);? }
其實是調(diào)用了SqlSessionTemplate->getMapper,其中mapperInterface就是創(chuàng)建MapperFactoryBean時候的構造函數(shù)參數(shù)析孽。
publicTgetMapper(Class type){returngetConfiguration().getMapper(type,this);? }
這里調(diào)用getConfiguration().getMapper(type, this);實際是DefaultSqlSessionFactory里面的configration的getMapper方法:
public T getMapper(Classtype,SqlSessionsqlSession){//knownMappers是上面時序圖中步驟6設置進入的析蝴。finalMapperProxyFactory mapperProxyFactory = (MapperProxyFactory) knownMappers.get(type);if(mapperProxyFactory ==null) {thrownewBindingException("Type "+ type +" is not known to the MapperRegistry.");? ? }try{returnmapperProxyFactory.newInstance(sqlSession);? ? }catch(Exceptione) {thrownewBindingException("Error getting mapper instance. Cause: "+ e, e);? ? }? }protectedT newInstance(MapperProxy mapperProxy) {return(T) Proxy.newProxyInstance(mapperInterface.getClassLoader(),newClass[]{ mapperInterface }, mapperProxy);? }publicT newInstance(SqlSession sqlSession) {//代理回調(diào)類為MapperProxyfinalMapperProxy mapperProxy =newMapperProxy(sqlSession, mapperInterface, methodCache);returnnewInstance(mapperProxy);? }
在上一個實際執(zhí)行sql時候調(diào)用代理類的序列圖:
screenshot.png
所以當調(diào)用實際的數(shù)據(jù)庫操作時候會調(diào)用MapperProxy的invoke方法:
publicObjectinvoke(Object proxy, Method method, Object[] args)throwsThrowable{if(Object.class.equals(method.getDeclaringClass())) {try{returnmethod.invoke(this, args);? ? ? }catch(Throwable t) {throwExceptionUtil.unwrapThrowable(t);? ? ? }? ? }finalMapperMethod mapperMethod = cachedMapperMethod(method);returnmapperMethod.execute(sqlSession, args);? }
mapperMethod.execute(sqlSession, args);里面實際是調(diào)用當前mapper對應的SqlSessionTemplate的數(shù)據(jù)庫操作,而它有委托給了代理類sqlSessionProxy绿淋,sqlSessionProxy是在SqlSessionTemplate的構造函數(shù)里面創(chuàng)建的:
publicSqlSessionTemplate(SqlSessionFactory sqlSessionFactory, ExecutorType executorType,
? ? ? PersistenceExceptionTranslator exceptionTranslator){? ? notNull(sqlSessionFactory,"Property 'sqlSessionFactory' is required");? ? notNull(executorType,"Property 'executorType' is required");this.sqlSessionFactory = sqlSessionFactory;this.executorType = executorType;this.exceptionTranslator = exceptionTranslator;this.sqlSessionProxy = (SqlSession) newProxyInstance(? ? ? ? SqlSessionFactory.class.getClassLoader(),newClass[] { SqlSession.class },newSqlSessionInterceptor());? }
所以最終數(shù)據(jù)庫操作有被代理SqlSessionInterceptor執(zhí)行:
publicObjectinvoke(Object proxy, Method method, Object[] args)throwsThrowable{//有TransactionSynchronizationManager管理SqlSession sqlSession = getSqlSession(? ? ? ? ? SqlSessionTemplate.this.sqlSessionFactory,? ? ? ? ? SqlSessionTemplate.this.executorType,? ? ? ? ? SqlSessionTemplate.this.exceptionTranslator);try{? ? ? ? Object result = method.invoke(sqlSession, args);if(!isSqlSessionTransactional(sqlSession, SqlSessionTemplate.this.sqlSessionFactory)) {// force commit even on non-dirty sessions because some databases require// a commit/rollback before calling close()sqlSession.commit(true);? ? ? ? }returnresult;? ? ? }catch(Throwable t) {? ? ? ? ? .....? ? ? }? ? }publicstaticSqlSessiongetSqlSession(SqlSessionFactory sessionFactory, ExecutorType executorType, PersistenceExceptionTranslator exceptionTranslator){? ? notNull(sessionFactory, NO_SQL_SESSION_FACTORY_SPECIFIED);? ? notNull(executorType, NO_EXECUTOR_TYPE_SPECIFIED);? ? SqlSessionHolder holder = (SqlSessionHolder) TransactionSynchronizationManager.getResource(sessionFactory);? ? SqlSession session = sessionHolder(executorType, holder);if(session !=null) {returnsession;? ? }if(LOGGER.isDebugEnabled()) {? ? ? LOGGER.debug("Creating a new SqlSession");? ? }//這里看到了使用sessionfactory熟悉的打開了一個sessionsession = sessionFactory.openSession(executorType);? ? registerSessionHolder(sessionFactory, executorType, exceptionTranslator, session);returnsession;? }
關于事務配置可移步:http://www.reibang.com/p/1d882343c036
三、Spring中Mybatis的配置方案二
2.1 多數(shù)據(jù)源配置案例
(1)數(shù)據(jù)源配置(2)創(chuàng)建sqlSessionFactory(3)配置掃描器尝盼,掃描指定路徑的mapper生成數(shù)據(jù)庫操作代理類(4)數(shù)據(jù)源配置(5)創(chuàng)建sqlSessionFactory(6)配置掃描器吞滞,掃描指定路徑的mapper生成數(shù)據(jù)庫操作代理類
與上節(jié)不同在在于(3)(6)
3.2 原理簡單介紹
這里只看??標簽解析,按照慣例看jar包的spring.handler找標簽解析
image.png
image.png
image.png
MapperScannerBeanDefinitionParser的代碼如下:
粘貼圖片.png
可知MapperScannerBeanDefinitionParser所做的事情和MapperScannerConfigurer類似都是內(nèi)部搞了個ClassPathMapperScanner盾沫。
四裁赠、SpringBoot中Mybatis的配置方案
4.1 SpringBoot中多數(shù)據(jù)源使用
數(shù)據(jù)源一配置:
//三、設置掃描器@MapperScan(basePackages ="com.alibaba.zlx.web.speech.mapper",sqlSessionFactoryRef="sqlSessionFactory1")publicclassTddlAutoConfiguration{@AutowiredprivateTddlProperties properties;//一赴精、創(chuàng)建數(shù)據(jù)源@Primary@Bean(name ="dataSource1")publicDataSourcedataSource1()throwsTddlException{? ? ? ? TDataSource dataSource =newTDataSource();? ? ? ? dataSource.setAppName(properties.getAppName());? ? ? ? dataSource.setSharding(properties.getSharding());? ? ? ? dataSource.setDynamicRule(properties.getDynamicRule());? ? ? ? dataSource.init();returndataSource;? ? }//二佩捞、創(chuàng)建SqlSessionFactory@Bean(name ="sqlSessionFactory1")@PrimarypublicSqlSessionFactorysqlSessionFactoryBean1()throwsException{? ? ? ? SqlSessionFactoryBean sqlSessionFactoryBean =newSqlSessionFactoryBean();? ? ? ? sqlSessionFactoryBean.setDataSource(dataSource1());? ? ? ? sqlSessionFactoryBean.setMapperLocations(resolveMapperLocations(newString[]{"classpath:mapper/*.xml"}));returnsqlSessionFactoryBean.getObject();? ? }//四、 創(chuàng)建事務管理器@Bean(name ="txManager1")@PrimarypublicPlatformTransactionManagertxManager1(@Qualifier("dataSource1")DataSource dataSource){? ? ? ? ? ? ? ? ? ? ? ? System.out.println("-----------dataource-----"+ dataSource.toString());returnnewDataSourceTransactionManager(dataSource);? ? }@Bean("sqlSessionTemplate1")@PrimarypublicSqlSessionTemplatesqlSessionTemplate(@Qualifier("sqlSessionFactory1")SqlSessionFactory sqlSessionFactory){? ? ? ? System.out.println("-----------sqlSessionFactory-----"+ sqlSessionFactory.toString());returnnewSqlSessionTemplate(sqlSessionFactory);? ? }}
數(shù)據(jù)源二配置:
//三蕾哟、設置掃描器@MapperScan(basePackages ="com.alibaba.gh.web.speech.mapper", sqlSessionFactoryRef ="sqlSessionFactory2")publicclassTddlAutoConfiguration2{@AutowiredprivateTddlProperties properties;//一一忱、創(chuàng)建數(shù)據(jù)源@Bean(name ="dataSource2")publicDataSourcedataSource2()throwsTddlException{? ? ? ? TDataSource dataSource =newTDataSource();? ? ? ? dataSource.setAppName(properties.getAppName());? ? ? ? dataSource.setSharding(properties.getSharding());? ? ? ? dataSource.setDynamicRule(properties.getDynamicRule());? ? ? ? dataSource.init();returndataSource;? ? }//二、創(chuàng)建SqlSessionFactory@Bean(name ="sqlSessionFactory2")publicSqlSessionFactorysqlSessionFactoryBean1()throwsException{? ? ? ? SqlSessionFactoryBean sqlSessionFactoryBean =newSqlSessionFactoryBean();? ? ? ? sqlSessionFactoryBean.setDataSource(dataSource2());? ? ? ? sqlSessionFactoryBean.setMapperLocations(resolveMapperLocations(newString[] {"classpath:mapper2/*.xml"}));returnsqlSessionFactoryBean.getObject();? ? }// 四谭确、創(chuàng)建事務管理器@Bean(name ="txManager2")publicPlatformTransactionManagertxManager1(@Qualifier("dataSource2")DataSource dataSource){? ? ? ? System.out.println("-----------dataource-----"+ dataSource.toString());returnnewDataSourceTransactionManager(dataSource);? ? }@Bean("sqlSessionTemplate2")publicSqlSessionTemplatesqlSessionTemplate(@Qualifier("sqlSessionFactory2")SqlSessionFactory sqlSessionFactory){? ? ? ? System.out.println("-----------sqlSessionFactory-----"+ sqlSessionFactory.toString());returnnewSqlSessionTemplate(sqlSessionFactory);? ? }}
另外SqlSessionTemplate是對SqlSessionFactory的一個包裝帘营,這里每個數(shù)據(jù)源也配置了一個,如果想使用它的話逐哈,只需要修改@mapperscan芬迄,設置sqlSessionTemplateRef替換sqlSessionFactoryRef