mybatis源碼學習(二)--mybatis+spring源碼學習

這篇筆記主要來就,mybatis是如何利用spring的擴展點來實現和spring的整合spring

1.mybatis和spring整合以後,咱們就不須要使用sqlSession.selectOne()這種方式了,能夠直接從spring容器中獲取到接口的代理對象,而後調用對應的目標方法,那麼,mybatis在將接口交給spring管理的時候,用到了三個擴展點:sql

  1.1 factoryBean  mapperFactoryBean就是實現了factoryBean,而後,經過getObject方法來返回一個代理對象mybatis

  1.2 mapperFactoryBean同時繼承了SqlSessionDao,SqlSessionDao繼承了DaoSupport,DaoSuppor有實現了InitializingBean,在初始化mybatis接口的時候,會調用DaoSupport的afterPropertiesSet()方法,也就是spring的初始化方法app

  1.3 mapperScannerRegistrar實現了ImportBeanDefinitionRegistrar接口,在registerBeanDefinitions方法中,會對mapperScan註解聲明的包,進行掃描,class,掃描到beanDefinitionMap中,而後完成bean的初始化ui

 

2.mybatis在和spring整合的時候,須要用到一個註解 @MapperScan,這個註解使用了@Import,引入了mapperScannerRegistrar,關於import註解的做用,在spring源碼解析(一)中有介紹,不詳細說了,在將類put到beanDefinitionMap中的時候,會執行mapperScannerRegistrar的registryBeanDefintions方法,這時候,會調用doScan方法,將mapperScan對應包下的class掃描出來,放到beanDefinitionMap中,這裏要提的一個點是:this

 @ComponentScan和@MapperScan,兩個註解的包可能會同樣,掃描出來的全部.class文件都是同樣的,可是注入到beanDefinitionMap中的時候,ComponentScan註解注入的是@Component、@Controller、@Repository、@Service註解對應的class,mapperScan注入的是接口對應的類,關於這個點,我debug看過源碼,在後面會把代碼貼出來spa

 1 public Set<BeanDefinitionHolder> doScan(String... basePackages) {
 2         Set<BeanDefinitionHolder> beanDefinitions = super.doScan(basePackages);
 3         if (beanDefinitions.isEmpty()) {
 4             this.logger.warn("No MyBatis mapper was found in '" + Arrays.toString(basePackages) + "' package. Please check your configuration.");
 5         } else {
 6             this.processBeanDefinitions(beanDefinitions);
 7         }
 8 
 9         return beanDefinitions;
10     }

這個方法就是mapperScannerRegistrar中要調用的掃描方法,supper.doScan()會把包下全部的接口注入到beanDefinitionMap中,在注入完以後,會對mybatis的beanDefinition進行一些處理,在第六行的這個方法中,debug

 1.把當前beanDefinition的beanClass設置爲mapperFactoryBean.calss,這樣設置,是爲了spring在對bean進行初始化的時候,會執行bean的初始化方法(就是上面說到的afterPropertiesSet())方法,在執行mybatis接口的初始化方法的時候,會根據beanClass,來調用mapperFactoryBean的getObject()方法來返回一個代理對象;設計

 2.把beanDefinition的注入模型(autowiredMode)設置爲2(byType);這裏會設計到spring的注入模型,簡單而言,若是是byType,那麼,就不須要在bean中添加@Autowired和@Resource,只須要提供set方法便可(注意:這裏的byType和@Resource不同)代理


因爲mapperFatoryBean繼承了daoSupport,daoSupport又實現了InitializingBean,因此,spring容器在對mybatis的接口進行初始化以後,進行屬性注入,而後會進行初始化,調用DaoSuppro的afterPropertySet方法,其中,會調用子類的checkDaoConfig()方法,在checkDaoConfig中,調用了configuration.addMapper(this.mapperInterface);這個方法就是mybatis中,將class放到knownMappers這個map的操做

 1 protected void checkDaoConfig() {
 2         super.checkDaoConfig();
 3         Assert.notNull(this.mapperInterface, "Property 'mapperInterface' is required");
 4         Configuration configuration = this.getSqlSession().getConfiguration();
 5         if (this.addToConfig && !configuration.hasMapper(this.mapperInterface)) {
 6             try {
 7                 configuration.addMapper(this.mapperInterface);
 8             } catch (Exception var6) {
 9                 this.logger.error("Error while adding the mapper '" + this.mapperInterface + "' to configuration.", var6);
10                 throw new IllegalArgumentException(var6);
11             } finally {
12                 ErrorContext.instance().reset();
13             }
14         }
15 
16     }

 


 

那mapperFactoryBean的getObject()方法是在何時執行呢?假如說咱們在service中注入了mybatis的dao接口,在實例化service的時候,會進行屬性注入,屬性注入的時候,會發現,service中須要注入dao,這時候,會調用getBean()從spring容器中獲取,若是dao,沒有實例化,就會去實例化,在實例化完成以後,放到單實例池中,而後,會調用getObject()

 

 

在調用getObject的時候,會經過jdk動態代理來生成一個代理對象

 1 public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
 2         MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory)this.knownMappers.get(type);
 3         if (mapperProxyFactory == null) {
 4             throw new BindingException("Type " + type + " is not known to the MapperRegistry.");
 5         } else {
 6             try {
 7                 return mapperProxyFactory.newInstance(sqlSession);
 8             } catch (Exception var5) {
 9                 throw new BindingException("Error getting mapper instance. Cause: " + var5, var5);
10             }
11         }
12     }

這裏生成代理對象和原生mybatis生成代理對象有一個區別,就是這裏的sqlSession,原生的,用的是DefaultSqlSession,mybatis和spring整合以後,這裏用的是sqlSessionTemplate,那爲何會是sqlSessionTemplate呢?

在mapperFactoryBean的父類,SqlSessionDaoSupport中,會對sqlSession賦值,這時候,是直接new SqlSessionTemplate(SqlSessionFactory),因此,在和spring整合以後,mybatis用的sqlSession是sqlSessionTemplate

 

在實際調用目標方法的時候,會被mapperProxy的invoke方法攔截,和原生mybatis不一樣的時候,在判斷是select仍是update,以後,原生的mybatis會調用sqlSession.selectOne();和spring整合以後,會調用SqlSessionTemplate.selectOne(),而後再調用sqlSessionInterceptor.invoke()方法

相關文章
相關標籤/搜索