【Mybatis】【二】源码分析-Mapper 接口都是怎么注入到 Spring容器中的?
1 前言
续上节主要用调试的方式,看了一下 Mapper 接口的注入过程,本节就从源码的角度,一步步分析 Mapper 接口都是怎么注入到 Spring容器中的。
2 源码分析
2.1 入口分析
我们上节看到扫描 @Mapper 的方式有两种,一种是基于 @MapperScan 的主动扫描,另一种就是基于默认包扫描 @Mapper 的方式,但两种方式最后都是落在 MapperScannerConfigurer 这个类来处理。两种方式还是有一点不一样的,我们从源码看下。
2.1.1 主动方式(@MapperScan)
我们首先来看下主动方式的:
@Retention(RetentionPolicy.RUNTIME) @Target({ElementType.TYPE}) @Documented // 可以看到这里引入了 MapperScannerRegistrar @Import({MapperScannerRegistrar.class}) @Repeatable(MapperScans.class) public @interface MapperScan { String[] value() default {}; }
关于 @MapperScan 的默认定义信息:
@Import({MapperScannerRegistrar.class}) @Repeatable(MapperScans.class) public @interface MapperScan { String[] value() default {}; String[] basePackages() default {}; Class<?>[] basePackageClasses() default {}; Class<? extends BeanNameGenerator> nameGenerator() default BeanNameGenerator.class; Class<? extends Annotation> annotationClass() default Annotation.class; Class<?> markerInterface() default Class.class; String sqlSessionTemplateRef() default ""; String sqlSessionFactoryRef() default ""; // 默认的 FactoryBean Class<? extends MapperFactoryBean> factoryBean() default MapperFactoryBean.class; String lazyInitialization() default ""; String defaultScope() default ""; }
我们继续跟进去看 MapperScannerRegistrar:
// 可以看到 MapperScannerRegistrar 实现了 ImportBeanDefinitionRegistrar // 也就是说它是 BeanDefinition 的一个注册器 // registerBeanDefinitions 方法来注入一些 BeanDefinition public class MapperScannerRegistrar implements ImportBeanDefinitionRegistrar, ResourceLoaderAware { public MapperScannerRegistrar() { } /** @deprecated */ @Deprecated public void setResourceLoader(ResourceLoader resourceLoader) { } // 扫描注入 public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) { AnnotationAttributes mapperScanAttrs = AnnotationAttributes.fromMap(importingClassMetadata.getAnnotationAttributes(MapperScan.class.getName())); if (mapperScanAttrs != null) { this.registerBeanDefinitions(importingClassMetadata, mapperScanAttrs, registry, generateBaseBeanName(importingClassMetadata, 0)); } } }
我们继续看看内部的 registerBeanDefinitions 方法:
void registerBeanDefinitions(AnnotationMetadata annoMeta, AnnotationAttributes annoAttrs, BeanDefinitionRegistry registry, String beanName) { BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(MapperScannerConfigurer.class); builder.addPropertyValue("processPropertyPlaceHolders", true); Class<? extends Annotation> annotationClass = annoAttrs.getClass("annotationClass"); if (!Annotation.class.equals(annotationClass)) { builder.addPropertyValue("annotationClass", annotationClass); } Class<?> markerInterface = annoAttrs.getClass("markerInterface"); if (!Class.class.equals(markerInterface)) { builder.addPropertyValue("markerInterface", markerInterface); } Class<? extends BeanNameGenerator> generatorClass = annoAttrs.getClass("nameGenerator"); if (!BeanNameGenerator.class.equals(generatorClass)) { builder.addPropertyValue("nameGenerator", BeanUtils.instantiateClass(generatorClass)); } Class<? extends MapperFactoryBean> mapperFactoryBeanClass = annoAttrs.getClass("factoryBean"); if (!MapperFactoryBean.class.equals(mapperFactoryBeanClass)) { builder.addPropertyValue("mapperFactoryBeanClass", mapperFactoryBeanClass); } String sqlSessionTemplateRef = annoAttrs.getString("sqlSessionTemplateRef"); if (StringUtils.hasText(sqlSessionTemplateRef)) { builder.addPropertyValue("sqlSessionTemplateBeanName", annoAttrs.getString("sqlSessionTemplateRef")); } String sqlSessionFactoryRef = annoAttrs.getString("sqlSessionFactoryRef"); if (StringUtils.hasText(sqlSessionFactoryRef)) { builder.addPropertyValue("sqlSessionFactoryBeanName", annoAttrs.getString("sqlSessionFactoryRef")); } // 这里就是获取我们 @MapperScan 上提供的包名 List<String> basePackages = new ArrayList(); basePackages.addAll((Collection)Arrays.stream(annoAttrs.getStringArray("value")).filter(StringUtils::hasText).collect(Collectors.toList())); basePackages.addAll((Collection)Arrays.stream(annoAttrs.getStringArray("basePackages")).filter(StringUtils::hasText).collect(Collectors.toList())); basePackages.addAll((Collection)Arrays.stream(annoAttrs.getClassArray("basePackageClasses")).map(ClassUtils::getPackageName).collect(Collectors.toList())); if (basePackages.isEmpty()) { basePackages.add(getDefaultBasePackage(annoMeta)); } String lazyInitialization = annoAttrs.getString("lazyInitialization"); if (StringUtils.hasText(lazyInitialization)) { builder.addPropertyValue("lazyInitialization", lazyInitialization); } String defaultScope = annoAttrs.getString("defaultScope"); if (!"".equals(defaultScope)) { builder.addPropertyValue("defaultScope", defaultScope); } builder.addPropertyValue("basePackage", StringUtils.collectionToCommaDelimitedString(basePackages)); registry.registerBeanDefinition(beanName, builder.getBeanDefinition()); }
可以看到注入了一个 MapperScannerConfigurer 类型的 BeanDefinition。MapperScannerConfigurer 又是一个 Bean 定义信息的后置处理器,来注入一些 BeanDefinition。
public class MapperScannerConfigurer implements BeanDefinitionRegistryPostProcessor, InitializingBean, ApplicationContextAware, BeanNameAware { private String basePackage; private boolean addToConfig = true; ... }
那我们看它的 postProcessBeanDefinitionRegistry 方法:
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) { if (this.processPropertyPlaceHolders) { this.processPropertyPlaceHolders(); } // 构造 ClassPathMapperScanner 来进行扫描 ClassPathMapperScanner scanner = new ClassPathMapperScanner(registry); 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.setMapperFactoryBeanClass(this.mapperFactoryBeanClass); if (StringUtils.hasText(this.lazyInitialization)) { scanner.setLazyInitialization(Boolean.valueOf(this.lazyInitialization)); } if (StringUtils.hasText(this.defaultScope)) { scanner.setDefaultScope(this.defaultScope); } // 注入扫描的过滤器 scanner.registerFilters(); // 扫描 @MapperScan 提供的包名 scanner.scan(StringUtils.tokenizeToStringArray(this.basePackage, ",; \t\n")); }
我们看一下它注入的过滤器,看看它的筛选规则:
public void registerFilters() { boolean acceptAllInterfaces = true; // @MapperScan 方式引入的,this.annotationClass 这里是空 if (this.annotationClass != null) { this.addIncludeFilter(new AnnotationTypeFilter(this.annotationClass)); acceptAllInterfaces = false; } // 这个也是空 if (this.markerInterface != null) { this.addIncludeFilter(new AssignableTypeFilter(this.markerInterface) { protected boolean matchClassName(String className) { return false; } }); acceptAllInterfaces = false; } // true if (acceptAllInterfaces) { // 白名单直接全部返回 true this.addIncludeFilter((metadataReader, metadataReaderFactory) -> { return true; }); } // 黑名单 过滤掉不是 package-info 结尾的 this.addExcludeFilter((metadataReader, metadataReaderFactory) -> { String className = metadataReader.getClassMetadata().getClassName(); return className.endsWith("package-info"); }); }
从过滤器的规则看下来,@MapperScan 方式引入的会扫描注入包下的除了 package-info 外的所有类。
ClassPathMapperScanner 本身又是继承 Spring 的 ClassPathBeanDefinitionScanner :
public class ClassPathMapperScanner extends ClassPathBeanDefinitionScanner {...}
我们继续跟进去,看看 ClassPathMapperScanner 的扫描方法:
public int scan(String... basePackages) { int beanCountAtScanStart = this.registry.getBeanDefinitionCount(); // 开始进行扫描 doScan(basePackages); // Register annotation config processors, if necessary. if (this.includeAnnotationConfig) { AnnotationConfigUtils.registerAnnotationConfigProcessors(this.registry); } return (this.registry.getBeanDefinitionCount() - beanCountAtScanStart); }
doScan 就开始进行扫描注入了,我们跟进去:
public Set<BeanDefinitionHolder> doScan(String... basePackages) { // 直接调用父类的包扫描 Set<BeanDefinitionHolder> beanDefinitions = super.doScan(basePackages); if (beanDefinitions.isEmpty()) { LOGGER.warn(() -> { return "No MyBatis mapper was found in '" + Arrays.toString(basePackages) + "' package. Please check your configuration."; }); } else { this.processBeanDefinitions(beanDefinitions); } return beanDefinitions; }
protected Set<BeanDefinitionHolder> doScan(String... basePackages) { Assert.notEmpty(basePackages, "At least one base package must be specified"); Set<BeanDefinitionHolder> beanDefinitions = new LinkedHashSet<>(); // 多个包的话就逐个进行扫描 for (String basePackage : basePackages) { // 筛选包下符合条件的 BeanDefinition 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); if (candidate instanceof AbstractBeanDefinition) { postProcessBeanDefinition((AbstractBeanDefinition) candidate, beanName); } if (candidate instanceof AnnotatedBeanDefinition) { AnnotationConfigUtils.processCommonDefinitionAnnotations((AnnotatedBeanDefinition) candidate); } 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; }
注意这里的 findCandidateComponents 筛选,就会根据刚才的过滤器规则,先进行黑名单校验(主要不是package-info的类),再进行白名单(默认都是true)的校验,然后得出符合条件的 BeanDefinition.
得到符合条件的 BeanDefinition,这时候对 BeanDefinition 进行修改,改变为 MapperFactoryBean 的方式。
private void processBeanDefinitions(Set<BeanDefinitionHolder> beanDefinitions) { BeanDefinitionRegistry registry = this.getRegistry(); Iterator var4 = beanDefinitions.iterator(); while(var4.hasNext()) { BeanDefinitionHolder holder = (BeanDefinitionHolder)var4.next(); AbstractBeanDefinition definition = (AbstractBeanDefinition)holder.getBeanDefinition(); boolean scopedProxy = false; if (ScopedProxyFactoryBean.class.getName().equals(definition.getBeanClassName())) { definition = (AbstractBeanDefinition)Optional.ofNullable(((RootBeanDefinition)definition).getDecoratedDefinition()).map(BeanDefinitionHolder::getBeanDefinition).orElseThrow(() -> { return new IllegalStateException("The target bean definition of scoped proxy bean not found. Root bean definition[" + holder + "]"); }); scopedProxy = true; } String beanClassName = definition.getBeanClassName(); LOGGER.debug(() -> { return "Creating MapperFactoryBean with name '" + holder.getBeanName() + "' and '" + beanClassName + "' mapperInterface"; }); definition.getConstructorArgumentValues().addGenericArgumentValue(beanClassName); definition.setBeanClass(this.mapperFactoryBeanClass); definition.getPropertyValues().add("addToConfig", this.addToConfig); definition.setAttribute("factoryBeanObjectType", beanClassName); 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(() -> { return "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(() -> { return "Cannot use both: sqlSessionTemplate and sqlSessionFactory together. sqlSessionFactory is ignored."; }); } definition.getPropertyValues().add("sqlSessionTemplate", this.sqlSessionTemplate); explicitFactoryUsed = true; } if (!explicitFactoryUsed) { LOGGER.debug(() -> { return "Enabling autowire by type for MapperFactoryBean with name '" + holder.getBeanName() + "'."; }); definition.setAutowireMode(2); } definition.setLazyInit(this.lazyInitialization); if (!scopedProxy) { if ("singleton".equals(definition.getScope()) && this.defaultScope != null) { definition.setScope(this.defaultScope); } if (!definition.isSingleton()) { BeanDefinitionHolder proxyHolder = ScopedProxyUtils.createScopedProxy(holder, registry, true); if (registry.containsBeanDefinition(proxyHolder.getBeanName())) { registry.removeBeanDefinition(proxyHolder.getBeanName()); } registry.registerBeanDefinition(proxyHolder.getBeanName(), proxyHolder.getBeanDefinition()); } } } }
是不是有点乱,我们画个图捋一下:
2.1.2 被动默认方式
看完主动扫描方式的,我们继续看第二种方式,也就是默认的被动方式,它的入口是在 Mybatis 的自动装配的类MybatisAutoConfiguration 里:
@Configuration // 引入 @Import({MybatisAutoConfiguration.AutoConfiguredMapperScannerRegistrar.class}) // 条件机制 在没有 MapperScannerConfigurer 的情况下,也就是没有@MapperScan的情况下 @ConditionalOnMissingBean({MapperFactoryBean.class, MapperScannerConfigurer.class}) public static class MapperScannerRegistrarNotFoundConfiguration implements InitializingBean { public MapperScannerRegistrarNotFoundConfiguration() { } public void afterPropertiesSet() { MybatisAutoConfiguration.logger.debug("Not found configuration for registering mapper bean using @MapperScan, MapperFactoryBean and MapperScannerConfigurer."); } }
那我们继续看引入的AutoConfiguredMapperScannerRegistrar:
// Bean 定义的注册器 public static class AutoConfiguredMapperScannerRegistrar implements BeanFactoryAware, ImportBeanDefinitionRegistrar { private BeanFactory beanFactory; public AutoConfiguredMapperScannerRegistrar() { } // 注册 BeanDefinition public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) { if (!AutoConfigurationPackages.has(this.beanFactory)) { MybatisAutoConfiguration.logger.debug("Could not determine auto-configuration package, automatic mapper scanning disabled."); } else { MybatisAutoConfiguration.logger.debug("Searching for mappers annotated with @Mapper"); List<String> packages = AutoConfigurationPackages.get(this.beanFactory); if (MybatisAutoConfiguration.logger.isDebugEnabled()) { packages.forEach((pkg) -> { MybatisAutoConfiguration.logger.debug("Using auto-configuration base package '{}'", pkg); }); } // 可以看到注入的类型就是 MapperScannerConfigurer BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(MapperScannerConfigurer.class); builder.addPropertyValue("processPropertyPlaceHolders", true); // 看这里就是跟上边的 MapperScannerConfigurer 不一样的地方,这里设置属性,会影响到注册过滤器的地方 会根据 @Mapper 注解进行过滤了,而不是像上边扫描包下的除了 package-info外的所有接口了,这就是两者的一个区别哈 builder.addPropertyValue("annotationClass", Mapper.class); // 扫描默认包 默认情况下是启动类所在的包 builder.addPropertyValue("basePackage", StringUtils.collectionToCommaDelimitedString(packages)); BeanWrapper beanWrapper = new BeanWrapperImpl(MapperScannerConfigurer.class); Set<String> propertyNames = (Set)Stream.of(beanWrapper.getPropertyDescriptors()).map(FeatureDescriptor::getName).collect(Collectors.toSet()); if (propertyNames.contains("lazyInitialization")) { builder.addPropertyValue("lazyInitialization", "${mybatis.lazy-initialization:false}"); } if (propertyNames.contains("defaultScope")) { builder.addPropertyValue("defaultScope", "${mybatis.mapper-default-scope:}"); } // 注入 registry.registerBeanDefinition(MapperScannerConfigurer.class.getName(), builder.getBeanDefinition()); } } public void setBeanFactory(BeanFactory beanFactory) { this.beanFactory = beanFactory; } }
看我们调试一下:
源码我们就不一一看了哈,执行过程差不多,我这里就再把上边的图改一下:
2.1.3 方式差异
看完两种方式,可以看到主要有两个地方不一样,
(1)ClassPathMapperScan 的 mapperFactoryBean 来源不一样,第一种方式来源于注解,第二种方式来源于自身默认的,第二种默认的扫描也会设置 MapperScannerConfigurer 的 mapperFactoryBeanClass 但它为空,这时 ClassPathMapperScan 方法设置的时候会判断,设置的是空,就会塞默认的 MapperFactoryBean 哈:
public void setMapperFactoryBeanClass(Class<? extends MapperFactoryBean> mapperFactoryBeanClass) { this.mapperFactoryBeanClass = mapperFactoryBeanClass == null ? MapperFactoryBean.class : mapperFactoryBeanClass; }
(2)过滤机制不一样,不一样的源点在 annotationClass 哈,第一种方式 annotationClass 为空,就扫描指定包下的所有不是 package-info 的接口,第二种方式指定了 annotationClass = Mapper.class 所以过滤只有 @Mapper 注解的:
public void registerFilters() { boolean acceptAllInterfaces = true; if (this.annotationClass != null) { this.addIncludeFilter(new AnnotationTypeFilter(this.annotationClass)); acceptAllInterfaces = false; } if (this.markerInterface != null) { this.addIncludeFilter(new AssignableTypeFilter(this.markerInterface) { protected boolean matchClassName(String className) { return false; } }); acceptAllInterfaces = false; } if (acceptAllInterfaces) { this.addIncludeFilter((metadataReader, metadataReaderFactory) -> { return true; }); } this.addExcludeFilter((metadataReader, metadataReaderFactory) -> { String className = metadataReader.getClassMetadata().getClassName(); return className.endsWith("package-info"); }); }
3 小结
好啦,本节有点长了,防止你们看困了,我把 MapperFactoryBean 生成的过程放到下一节了哈,我们下节见,本文有理解不对的地方欢迎指正哈。