Mybatis3源码分析(二):扫描Mapper关联到spring IOC容器
2015-02-04 01:36
656 查看
首先讲讲mapper是怎么从配置到对象的。
实现了basePackage下所有实现了markerInterface指明的类的接口都会被扫描解析到。MapperScannerConfigurer实现了BeanDefinitionRegistryPostProcessor(BeanDefinitionRegistry后置处理器,想想Spring AOP)接口,使其能在Spring ApplicationContext容器注册了所有BeanDefinition后调用它的postProcessBeanDefinitionRegistry方法。下面我们看看MapperScannerConfigurer是怎么实现这个方法的。
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
if (this.processPropertyPlaceHolders) {
//处理一些.properties中配置的mybaits有关的属性,例如sqlSessionFactoryBeanName
processPropertyPlaceHolders();
}
//创建类路径扫描器
ClassPathMapperScanner scanner = new ClassPathMapperScanner(registry);
//根据配置为scanner定义扫描规则
scanner.setAddToConfig(this.addToConfig);
scanner.setAnnotationClass(this.annotationClass);
scanner.setMarkerInterface(this.markerInterface);
scanner.setSqlSessionFactory(this.sqlSessionFactory);
scanner.setSqlSessionTemplate(this.sqlSessionTemplate);
scanner.setSqlSessionFactoryBeanName(this.sqlSessionFactoryBeanName);
scanner.setSqlSessionTemplateBeanName(this.sqlSessionTemplateBeanName);
scanner.setResourceLoader(this.applicationContext);
scanner.setBeanNameGenerator(this.nameGenerator);
scanner.registerFilters();
//开始扫描,参数中使用了tokenizeToStringArray函数来处理多basePackage的情况一般是“;”分隔
scanner.scan(StringUtils.tokenizeToStringArray(this.basePackage, ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS));
} ClassPathMapperScanner类的父类ClassPathBeanDefinitionScanner的doScan函数:
我们再来看看MapperFactoryBean的checkDaoConfig函数,看它是怎么将DAO关联到对应的mapper.xml的:protected void checkDaoConfig() {
//调用父类同名方法
super.checkDaoConfig();
//获取sqlSessionFactoryBean中配置文件在java中的对象Configuration
//如果没有mapperInterface的定义则加入此mapper的定义
//此configuration对象是mybaits下的不是spring的哦!
Configuration configuration = getSqlSession().getConfiguration();
if (this.addToConfig && !configuration.hasMapper(this.mapperInterface)) {
try {
configuration.addMapper(this.mapperInterface);
} catch (Throwable t) {
logger.error("Error while adding the mapper '" + this.mapperInterface + "' to configuration.", t);
throw new IllegalArgumentException(t);
} finally {
ErrorContext.instance().reset();
}
}
}
这样Mapper接口就被注册到sqlSession中,每当操作sqlSession时就会根据这个mapperInterface去查找对应的mapper.xml构建mapper从而完成数据库操作。
经过以上过程mybaits找到了所有mapper并将其加载到了spring ioc容器里,本章也就完结了。下一章我们再讲mybaits是怎么解析mapper.xml的。
<!-- 采用自动扫描方式创建mapper bean(单个更新模式) --> <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer"> <property name="basePackage" value="com.xxx.dao" /> <property name="sqlSessionTemplateBeanName" value="sqlSessionTemplateSimple" /> <property name="markerInterface" value="com.xxx.dao.SimpleDao" /> </bean>
实现了basePackage下所有实现了markerInterface指明的类的接口都会被扫描解析到。MapperScannerConfigurer实现了BeanDefinitionRegistryPostProcessor(BeanDefinitionRegistry后置处理器,想想Spring AOP)接口,使其能在Spring ApplicationContext容器注册了所有BeanDefinition后调用它的postProcessBeanDefinitionRegistry方法。下面我们看看MapperScannerConfigurer是怎么实现这个方法的。
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
if (this.processPropertyPlaceHolders) {
//处理一些.properties中配置的mybaits有关的属性,例如sqlSessionFactoryBeanName
processPropertyPlaceHolders();
}
//创建类路径扫描器
ClassPathMapperScanner scanner = new ClassPathMapperScanner(registry);
//根据配置为scanner定义扫描规则
scanner.setAddToConfig(this.addToConfig);
scanner.setAnnotationClass(this.annotationClass);
scanner.setMarkerInterface(this.markerInterface);
scanner.setSqlSessionFactory(this.sqlSessionFactory);
scanner.setSqlSessionTemplate(this.sqlSessionTemplate);
scanner.setSqlSessionFactoryBeanName(this.sqlSessionFactoryBeanName);
scanner.setSqlSessionTemplateBeanName(this.sqlSessionTemplateBeanName);
scanner.setResourceLoader(this.applicationContext);
scanner.setBeanNameGenerator(this.nameGenerator);
scanner.registerFilters();
//开始扫描,参数中使用了tokenizeToStringArray函数来处理多basePackage的情况一般是“;”分隔
scanner.scan(StringUtils.tokenizeToStringArray(this.basePackage, ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS));
} ClassPathMapperScanner类的父类ClassPathBeanDefinitionScanner的doScan函数:
protected Set<BeanDefinitionHolder> doScan(String... basePackages) { Assert.notEmpty(basePackages, "At least one base package must be specified"); //BeanDefinitionHolder的容器 Set<BeanDefinitionHolder> beanDefinitions = new LinkedHashSet<BeanDefinitionHolder>(); for (String basePackage : basePackages) { //选择候选者 Set<BeanDefinition> candidates = findCandidateComponents(basePackage); for (BeanDefinition candidate : candidates) { ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(candidate); candidate.setScope(scopeMetadata.getScopeName()); String beanName = this.beanNameGenerator.generateBeanName(candidate, this.registry); //如果是基包下的一个抽象类,我们配置的mapper接口如果不是注解方式也会走这处理 if (candidate instanceof AbstractBeanDefinition) { postProcessBeanDefinition((AbstractBeanDefinition) candidate, beanName); } //如果基包下的类是有注解的 if (candidate instanceof AnnotatedBeanDefinition) { AnnotationConfigUtils.processCommonDefinitionAnnotations((AnnotatedBeanDefinition) candidate); } //如果dao包(一般配置的basePakage是这个)下的类是符合mybaits要求的则向spring IOC容器中注册它的BeanDefinition if (checkCandidate(beanName, candidate)) { BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(candidate, beanName); definitionHolder = AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry); beanDefinitions.add(definitionHolder); registerBeanDefinition(definitionHolder, this.registry); } } } return beanDefinitions; }ClassPathMapperScanner的doScan函数:
public Set<BeanDefinitionHolder> doScan(String... basePackages) { //调用父类扫描函数 Set<BeanDefinitionHolder> beanDefinitions = super.doScan(basePackages); if (beanDefinitions.isEmpty()) { logger.warn("No MyBatis mapper was found in '" + Arrays.toString(basePackages) + "' package. Please check your configuration."); } else { for (BeanDefinitionHolder holder : beanDefinitions) { GenericBeanDefinition definition = (GenericBeanDefinition) holder.getBeanDefinition(); //此处的mapperInterface就是我们写的mapper接口一般是个空接口,实际上关联到它 //的BeanDefinition中的Bean是MapperFactoryBean,MapperFactoryBean很重要 definition.getPropertyValues().add("mapperInterface", definition.getBeanClassName()); definition.setBeanClass(MapperFactoryBean.class); 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) { if (logger.isDebugEnabled()) { logger.debug("Enabling autowire by type for MapperFactoryBean with name '" + holder.getBeanName() + "'."); } definition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE); } } } return beanDefinitions; }
protected boolean checkCandidate(String beanName, BeanDefinition beanDefinition) throws IllegalStateException { //如果spring中没注册这个beanName对应的bean那么就是mybaits需要的类,一般mybaits的mapper类是一个接口不会被spring实例化加载到IOC容器中 if (!this.registry.containsBeanDefinition(beanName)) { return true; } //如果spring中有这个类的bean定义 BeanDefinition existingDef = this.registry.getBeanDefinition(beanName); BeanDefinition originatingDef = existingDef.getOriginatingBeanDefinition(); if (originatingDef != null) { existingDef = originatingDef; } //如果重复扫描此类或者或者重复获取同一个类的源文件或者是其它类的子类则是不兼容的,这样的类是不符合mybaits加载要求返回false if (isCompatible(beanDefinition, existingDef)) { return false; } throw new ConflictingBeanDefinitionException("Annotation-specified bean name '" + beanName + "' for bean class [" + beanDefinition.getBeanClassName() + "] conflicts with existing, " + "non-compatible bean definition of same name and class [" + existingDef.getBeanClassName() + "]"); }
我们再来看看MapperFactoryBean的checkDaoConfig函数,看它是怎么将DAO关联到对应的mapper.xml的:protected void checkDaoConfig() {
//调用父类同名方法
super.checkDaoConfig();
//获取sqlSessionFactoryBean中配置文件在java中的对象Configuration
//如果没有mapperInterface的定义则加入此mapper的定义
//此configuration对象是mybaits下的不是spring的哦!
Configuration configuration = getSqlSession().getConfiguration();
if (this.addToConfig && !configuration.hasMapper(this.mapperInterface)) {
try {
configuration.addMapper(this.mapperInterface);
} catch (Throwable t) {
logger.error("Error while adding the mapper '" + this.mapperInterface + "' to configuration.", t);
throw new IllegalArgumentException(t);
} finally {
ErrorContext.instance().reset();
}
}
}
这样Mapper接口就被注册到sqlSession中,每当操作sqlSession时就会根据这个mapperInterface去查找对应的mapper.xml构建mapper从而完成数据库操作。
经过以上过程mybaits找到了所有mapper并将其加载到了spring ioc容器里,本章也就完结了。下一章我们再讲mybaits是怎么解析mapper.xml的。
相关文章推荐
- Mybatis结合Spring注解自动扫描源码分析
- Mybatis3源码分析(21)-Mapper实现-动态代理
- Mybatis3源码分析(21)-Mapper实现-动态代理
- Mybatis工作机制源码分析—初始化—mapper配置文件解析
- Mybatis源码分析--关联表查询及延迟加载原理(二)
- Mybatis3源码分析(三):解析mapper的xml配置文件
- Mybatis扫描全包(包含Service、Dao的接口)导致抛出BindingException异常,引发的一系列思考(含源码分析)
- Tomcat源码分析(三)--连接器是如何与容器关联的?
- Mybatis3源码分析(19)-Mapper生成过程-示例
- Mybatis源码分析--MapperScannerConfigurer
- Mybatis3源码分析(04)-加载Configuration-XMLMapperBuilder加载ResultMap
- mybatis源码分析之MapperMethod
- Mybatis3源码分析(20)-Mapper实现-配置加载
- 修改MyBatis源码实现扫描注册枚举-分析源码
- Tomcat源码分析(三)--连接器是如何与容器关联的?
- mybatis-spring 源码分析MapperScannerConfigurer