spring整合mybatis的底層原理分析
spring整合mybatis的底層原理
原理:
- FactoryBean的自定義對象
- jdk動態(tài)代理Mapper接口對象
一、手寫一個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)建對象………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的代理對象賦值給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個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
* 掃描的時候有 beanDefinition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE);
* 那他就會自動找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 {
//動態(tài)代理獲取UserMapper接口對象
System.out.println("6.……getObject……");
return sqlSession.getMapper(mapperInterface);
}
@Override
public Class<?> getObjectType() {
return mapperInterface;
}
}1.7 自定義Bean注冊類
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中的屬性會從容器中來進(jìn)行查找
beanDefinition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE);
}
return beanDefinitionHolders;
}
}運行結(jié)果是:
1.……registerBeanDefinitions……
2.……doScan……
3.…………創(chuàng)建UserService…………
4.依賴注入MemberMapper需要先創(chuàng)建對象………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類實現(xiàn)了ImportBeanDefinitionRegistrar接口,所以Spring在啟動時會調(diào)用MapperScannerRegistrar類中的registerBeanDefinitions方法

2.3 在registerBeanDefinitions方法中注冊一個MapperScannerConfigurer類型的BeanDefinition

2.4 而MapperScannerConfigurer實現(xiàn)了BeanDefinitionRegistryPostProcessor接口,所以Spring在啟動過程中時會調(diào)用它的postProcessBeanDefinitionRegistry()方法

2.5 在postProcessBeanDefinitionRegistry方法中會生成一個ClassPathMapperScanner對象,然后進(jìn)行掃描(scanner.scan)2.6 通過利用Spring的掃描后,會把接口掃描出來并且得到對應(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中的屬性會從容器中來進(jìn)行查找
definition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE);
}
definition.setLazyInit(lazyInitialization);
}
}2.8 掃描完成后,Spring就會基于BeanDefinition去創(chuàng)建Bean了,相當(dāng)于每個Mapper對應(yīng)一個FactoryBean
2.9 在MapperFactoryBean中的getObject方法中,調(diào)用了getSqlSession()去得到一個sqlSession對象,然后根據(jù)對應(yīng)的Mapper接口生成一個Mapper接口代理對象,這個代理對象就成為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)用時機(jī)是,在創(chuàng)建的對象依賴了Mapper對象就會去創(chuàng)建該Mapper對象,此時通過MapperFactoryBean去獲取
2.10 sqlSession對象是Mybatis中的,一個sqlSession對象需要SqlSessionFactory來產(chǎn)生
上面的getSqlSession()對應(yīng)源碼是:

2.11 MapperFactoryBean的AutowireMode為byType,所以Spring會自動調(diào)用set方法,有兩個set方法,一個setSqlSessionFactory,一個setSqlSessionTemplate,而這兩個方法執(zhí)行的前提是根據(jù)方法參數(shù)類型能找到對應(yīng)的bean,所以Spring容器中要存在SqlSessionFactory類型的bean或者SqlSessionTemplate類型的bean
2.12 如果你定義的是一個SqlSessionFactory類型的bean,那么最終也會被包裝為一個SqlSessionTemplate對象,并且賦值給sqlSession屬性
這一步是程序員自己定義一個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)建對象………AppConfig…………SqlSessionFactory………");
InputStream inputStream = Resources.getResourceAsStream("mybatis.xml");
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
return sqlSessionFactory;
}
}這里定義的SqlSessionFactory 會賦值于2.10的sqlSessionTemplate
2.13 而在SqlSessionTemplate類中就存在一個getMapper方法,這個方法中就產(chǎn)生一個Mapper接口代理對象

2.14 當(dāng)執(zhí)行該代理對象的某個方法時,就會進(jìn)入到Mybatis框架的底層執(zhí)行流程
至此:業(yè)務(wù)類中的引入Mapper對象就復(fù)制成功。
@Autowired
private OrderMapper orderMapper;即:這時候的orderMapper就 是賦值了代理對象的對象是有值 的。
總結(jié)
以上為個人經(jīng)驗,希望能給大家一個參考,也希望大家多多支持腳本之家。
相關(guān)文章
springboot攔截器過濾token,并返回結(jié)果及異常處理操作
這篇文章主要介紹了springboot攔截器過濾token,并返回結(jié)果及異常處理操作,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2020-09-09
java基于jdbc實現(xiàn)簡單學(xué)生管理系統(tǒng)
本文主要主要介紹了java連接mysql數(shù)據(jù)庫的一個簡單學(xué)生系統(tǒng),通過jdbc連接數(shù)據(jù)庫。文中通過示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下2021-10-10
PowerJob的WorkerHealthReporter工作流程源碼解讀
這篇文章主要為大家介紹了PowerJob的WorkerHealthReporter工作流程源碼解讀,2023-12-12
Java?Unsafe創(chuàng)建對象的方法實現(xiàn)
Java中使用Unsafe實例化對象是一項十分有趣而且強(qiáng)大的功能,本文主要介紹了Java?Unsafe創(chuàng)建對象的方法實現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2023-07-07
spring-@Autowired注入與構(gòu)造函數(shù)注入使用方式
這篇文章主要介紹了spring-@Autowired注入與構(gòu)造函數(shù)注入使用方式,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教2023-12-12
SpringCloud使用Feign實現(xiàn)服務(wù)調(diào)用
這篇文章主要為大家詳細(xì)介紹了SpringCloud使用Feign實現(xiàn)服務(wù)調(diào)用,具有一定的參考價值,感興趣的小伙伴們可以參考一下2019-04-04
java中public class與class的區(qū)別詳解
以下是對java中public class與class的區(qū)別進(jìn)行了分析介紹,需要的朋友可以過來參考下2013-07-07

