Mybaits3源码分析(二):扫描Mapper关联到spring IOC容器

首先讲讲mapper是怎么从配置到对象的。

<!– 采用自动扫描方式创建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有关的属性,例如sqlSessionFactoryBeanNameprocessPropertyPlaceHolders();}//创建类路径扫描器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容器中注册它的BeanDefinitionif (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加载要求返回falseif (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() + "]");}

家!甜蜜的家!天下最美好的莫过於家

Mybaits3源码分析(二):扫描Mapper关联到spring IOC容器

相关文章:

你感兴趣的文章:

标签云: