spring整合mybatis的底層原理分析
spring整合mybatis的底層原理
原理:
- FactoryBean的自定義對(duì)象
- jdk動(dòng)態(tài)代理Mapper接口對(duì)象
一、手寫一個(gè)spring集成mybatis
目錄結(jié)構(gòu):
1.1 入口類
public class Test { public static void main(String[] args) { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(); context.register(AppConfig.class); context.refresh(); UserService userService = (UserService)context.getBean("userService"); userService.test(); } }
1.2 配置類
@CondorHeroMapperScan("com.athome.tulin.springmybatis.mapper") @ComponentScan("com.athome.tulin.springmybatis") public class AppConfig { @Bean public SqlSessionFactory sqlSessionFactory() throws IOException { System.out.println("4.依賴注入MemberMapper需要先創(chuàng)建對(duì)象………AppConfig…………SqlSessionFactory………"); InputStream inputStream = Resources.getResourceAsStream("mybatis.xml"); SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream); return sqlSessionFactory; } }
1.3 業(yè)務(wù)類
@Component public class UserService { public UserService() { System.out.println("3.…………創(chuàng)建UserService…………"); } //如何把mybatis生成的UserMapper的代理對(duì)象賦值給UserMapper @Autowired private UserMapper userMapper; @Autowired private OrderMapper orderMapper; @Autowired private MemberMapper memberMapper; public void test(){ System.out.println("7.……UserService…test…"); System.out.println(userMapper.selectById()); System.out.println(orderMapper.selectById()); System.out.println(memberMapper.selectById()); } }
1.4 創(chuàng)建3個(gè)Mapper接口
public interface MemberMapper { @Select("select 'member' ") String selectById(); }
public interface OrderMapper { @Select("select 'order' ") String selectById(); }
public interface UserMapper { @Select("select 'user' ") String selectById(); }
1.5 自定義注解
@Retention(RetentionPolicy.RUNTIME) @Target(ElementType.TYPE) @Import(CondorHeroImportBeanDefinitionRegistrar.class) public @interface CondorHeroMapperScan { String value(); }
1.6 自定義fanctoryBean
public class CondorHeroFactoryBean implements FactoryBean { private Class mapperInterface; private SqlSession sqlSession; public CondorHeroFactoryBean(Class mapperInterface) { this.mapperInterface = mapperInterface; } /** * 從容器查找SqlSessionFactory 并獲取sqlSession 賦值于sqlSession * 掃描的時(shí)候有 beanDefinition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE); * 那他就會(huì)自動(dòng)找set方法 * @param sqlSessionFactory */ public void setSqlSession(SqlSessionFactory sqlSessionFactory) { System.out.println("5.……setSqlSession……"); sqlSessionFactory.getConfiguration().addMapper(mapperInterface); this.sqlSession = sqlSessionFactory.openSession(); } @Override public Object getObject() throws Exception { //動(dòng)態(tài)代理獲取UserMapper接口對(duì)象 System.out.println("6.……getObject……"); return sqlSession.getMapper(mapperInterface); } @Override public Class<?> getObjectType() { return mapperInterface; } }
1.7 自定義Bean注冊(cè)類
public class CondorHeroImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar { @Override public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry, BeanNameGenerator importBeanNameGenerator) { System.out.println("1.……registerBeanDefinitions……"); //1.獲取注解上的指定路徑 Map<String, Object> annotationAttributes = importingClassMetadata.getAnnotationAttributes(CondorHeroMapperScan.class.getName()); String path = (String)annotationAttributes.get("value"); //2.掃描 CondorHeroBeanDefinitionScanner scanner = new CondorHeroBeanDefinitionScanner(registry); scanner.addIncludeFilter(new TypeFilter() { @Override public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) throws IOException { return true; } }); scanner.scan(path); } }
1.8 自定義掃描類
public class CondorHeroBeanDefinitionScanner extends ClassPathBeanDefinitionScanner { public CondorHeroBeanDefinitionScanner(BeanDefinitionRegistry registry) { super(registry); } @Override protected boolean isCandidateComponent(AnnotatedBeanDefinition beanDefinition) { //只關(guān)心接口(判斷是否是接口) return beanDefinition.getMetadata().isInterface(); } /** * 掃描路徑并得到beanDefinition * @param basePackages * @return */ @Override protected Set<BeanDefinitionHolder> doScan(String... basePackages) { System.out.println("2.……doScan……"); Set<BeanDefinitionHolder> beanDefinitionHolders = super.doScan(basePackages); for (BeanDefinitionHolder beanDefinitionHolder: beanDefinitionHolders) { GenericBeanDefinition beanDefinition = (GenericBeanDefinition)beanDefinitionHolder.getBeanDefinition(); //設(shè)置值 beanDefinition.getConstructorArgumentValues().addGenericArgumentValue(beanDefinition.getBeanClassName()); //設(shè)置名稱 beanDefinition.setBeanClassName(CondorHeroFactoryBean.class.getName()); //將MapperFactoryBean的注入模型設(shè)置為By-Type。也就是說,MapperFactoryBean中的setXxx中的屬性會(huì)從容器中來進(jìn)行查找 beanDefinition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE); } return beanDefinitionHolders; } }
運(yùn)行結(jié)果是:
1.……registerBeanDefinitions……
2.……doScan……
3.…………創(chuàng)建UserService…………
4.依賴注入MemberMapper需要先創(chuàng)建對(duì)象………AppConfig…………SqlSessionFactory………
5.……setSqlSession……
6.……getObject……
5.……setSqlSession……
6.……getObject……
5.……setSqlSession……
6.……getObject……
7.……UserService…test… user order member
二、原理解析
2.1 通過@MapperScan導(dǎo)入了MapperScannerRegistrar類
2.2 MapperScannerRegistrar類實(shí)現(xiàn)了ImportBeanDefinitionRegistrar接口,所以Spring在啟動(dòng)時(shí)會(huì)調(diào)用MapperScannerRegistrar類中的registerBeanDefinitions方法
2.3 在registerBeanDefinitions方法中注冊(cè)一個(gè)MapperScannerConfigurer
類型的BeanDefinition
2.4 而MapperScannerConfigurer實(shí)現(xiàn)了BeanDefinitionRegistryPostProcessor接口,所以Spring在啟動(dòng)過程中時(shí)會(huì)調(diào)用它的postProcessBeanDefinitionRegistry()方法
2.5 在postProcessBeanDefinitionRegistry方法中會(huì)生成一個(gè)ClassPathMapperScanner對(duì)象,然后進(jìn)行掃描(scanner.scan)2.6 通過利用Spring的掃描后,會(huì)把接口掃描出來并且得到對(duì)應(yīng)的BeanDefinition
2.7 接下來把掃描得到的BeanDefinition進(jìn)行修改,把BeanClass修改為MapperFactoryBean,把AutowireMode修改為byType(在類ClassPathMapperScanner的方法processBeanDefinitions中)
private void processBeanDefinitions(Set<BeanDefinitionHolder> beanDefinitions) { GenericBeanDefinition definition; for (BeanDefinitionHolder holder : beanDefinitions) { definition = (GenericBeanDefinition) holder.getBeanDefinition(); String beanClassName = definition.getBeanClassName(); LOGGER.debug(() -> "Creating MapperFactoryBean with name '" + holder.getBeanName() + "' and '" + beanClassName + "' mapperInterface"); // the mapper interface is the original class of the bean // but, the actual class of the bean is MapperFactoryBean //設(shè)置值 definition.getConstructorArgumentValues().addGenericArgumentValue(beanClassName); // issue #59 //設(shè)置名稱 definition.setBeanClass(this.mapperFactoryBeanClass); definition.getPropertyValues().add("addToConfig", this.addToConfig); boolean explicitFactoryUsed = false; if (StringUtils.hasText(this.sqlSessionFactoryBeanName)) { definition.getPropertyValues().add("sqlSessionFactory", new RuntimeBeanReference(this.sqlSessionFactoryBeanName)); explicitFactoryUsed = true; } else if (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", new RuntimeBeanReference(this.sqlSessionTemplateBeanName)); explicitFactoryUsed = true; } else if (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) { LOGGER.debug(() -> "Enabling autowire by type for MapperFactoryBean with name '" + holder.getBeanName() + "'."); //將MapperFactoryBean的注入模型設(shè)置為By-Type。也就是說,MapperFactoryBean中的setXxx中的屬性會(huì)從容器中來進(jìn)行查找 definition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE); } definition.setLazyInit(lazyInitialization); } }
2.8 掃描完成后,Spring就會(huì)基于BeanDefinition去創(chuàng)建Bean了,相當(dāng)于每個(gè)Mapper對(duì)應(yīng)一個(gè)FactoryBean
2.9 在MapperFactoryBean中的getObject方法中,調(diào)用了getSqlSession()去得到一個(gè)sqlSession對(duì)象,然后根據(jù)對(duì)應(yīng)的Mapper接口生成一個(gè)Mapper接口代理對(duì)象,這個(gè)代理對(duì)象就成為Spring容器中的Bean
public class MapperFactoryBean<T> extends SqlSessionDaoSupport implements FactoryBean<T> {}
@Override public T getObject() throws Exception { return getSqlSession().getMapper(this.mapperInterface); } @Override public Class<T> getObjectType() { return this.mapperInterface; }
注意:這里的getObject調(diào)用時(shí)機(jī)是,在創(chuàng)建的對(duì)象依賴了Mapper對(duì)象就會(huì)去創(chuàng)建該Mapper對(duì)象,此時(shí)通過MapperFactoryBean去獲取
2.10 sqlSession對(duì)象是Mybatis中的,一個(gè)sqlSession對(duì)象需要SqlSessionFactory來產(chǎn)生
上面的getSqlSession()對(duì)應(yīng)源碼是:
2.11 MapperFactoryBean的AutowireMode為byType,所以Spring會(huì)自動(dòng)調(diào)用set方法,有兩個(gè)set方法,一個(gè)setSqlSessionFactory,一個(gè)setSqlSessionTemplate,而這兩個(gè)方法執(zhí)行的前提是根據(jù)方法參數(shù)類型能找到對(duì)應(yīng)的bean,所以Spring容器中要存在SqlSessionFactory類型的bean或者SqlSessionTemplate類型的bean
2.12 如果你定義的是一個(gè)SqlSessionFactory類型的bean,那么最終也會(huì)被包裝為一個(gè)SqlSessionTemplate對(duì)象,并且賦值給sqlSession屬性
這一步是程序員自己定義一個(gè)SqlSessionFactory,例如:
@CondorHeroMapperScan("com.athome.tulin.springmybatis.mapper") @ComponentScan("com.athome.tulin.springmybatis") public class AppConfig { @Bean public SqlSessionFactory sqlSessionFactory() throws IOException { System.out.println("4.依賴注入MemberMapper需要先創(chuàng)建對(duì)象………AppConfig…………SqlSessionFactory………"); InputStream inputStream = Resources.getResourceAsStream("mybatis.xml"); SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream); return sqlSessionFactory; } }
這里定義的SqlSessionFactory 會(huì)賦值于2.10的sqlSessionTemplate
2.13 而在SqlSessionTemplate類中就存在一個(gè)getMapper方法,這個(gè)方法中就產(chǎn)生一個(gè)Mapper接口代理對(duì)象
2.14 當(dāng)執(zhí)行該代理對(duì)象的某個(gè)方法時(shí),就會(huì)進(jìn)入到Mybatis框架的底層執(zhí)行流程
至此:業(yè)務(wù)類中的引入Mapper對(duì)象就復(fù)制成功。
@Autowired private OrderMapper orderMapper;
即:這時(shí)候的orderMapper就 是賦值了代理對(duì)象的對(duì)象是有值 的。
總結(jié)
以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
- Spring與Mybatis整合方式(mybatis-spring整合jar包功能)
- SpringBoot整合MyBatis和SpringBoot整合MyBatis-Plus教程
- 淺析SpringBoot整合Mybatis如何實(shí)現(xiàn)二級(jí)緩存
- SpringBoot整合mybatisPlus實(shí)現(xiàn)批量插入并獲取ID詳解
- SpringBoot整合Mybatis-plus關(guān)鍵詞模糊查詢結(jié)果為空
- Spring Boot整合MyBatis-Plus實(shí)現(xiàn)CRUD操作的示例代碼
- Spring Boot 整合 MyBatis 連接數(shù)據(jù)庫(kù)及常見問題
相關(guān)文章
springboot攔截器過濾token,并返回結(jié)果及異常處理操作
這篇文章主要介紹了springboot攔截器過濾token,并返回結(jié)果及異常處理操作,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧2020-09-09java基于jdbc實(shí)現(xiàn)簡(jiǎn)單學(xué)生管理系統(tǒng)
本文主要主要介紹了java連接mysql數(shù)據(jù)庫(kù)的一個(gè)簡(jiǎn)單學(xué)生系統(tǒng),通過jdbc連接數(shù)據(jù)庫(kù)。文中通過示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2021-10-10PowerJob的WorkerHealthReporter工作流程源碼解讀
這篇文章主要為大家介紹了PowerJob的WorkerHealthReporter工作流程源碼解讀,2023-12-12Java?Unsafe創(chuàng)建對(duì)象的方法實(shí)現(xiàn)
Java中使用Unsafe實(shí)例化對(duì)象是一項(xiàng)十分有趣而且強(qiáng)大的功能,本文主要介紹了Java?Unsafe創(chuàng)建對(duì)象的方法實(shí)現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2023-07-07spring-@Autowired注入與構(gòu)造函數(shù)注入使用方式
這篇文章主要介紹了spring-@Autowired注入與構(gòu)造函數(shù)注入使用方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-12-12SpringCloud使用Feign實(shí)現(xiàn)服務(wù)調(diào)用
這篇文章主要為大家詳細(xì)介紹了SpringCloud使用Feign實(shí)現(xiàn)服務(wù)調(diào)用,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2019-04-04java中public class與class的區(qū)別詳解
以下是對(duì)java中public class與class的區(qū)別進(jìn)行了分析介紹,需要的朋友可以過來參考下2013-07-07