看现象
本文基于mybatis-spring 2.0以上版本,低版本的源码与本文所展示的不同。
整合Spring
依赖:
implementation group: 'org.mybatis', name: 'mybatis', version: '3.5.5'
implementation group: 'org.mybatis', name: 'mybatis-spring', version: '2.0.5'
implementation group: 'mysql', name: 'mysql-connector-java', version: '8.0.23'
api(project(":spring-jdbc"))
主配置类:
@Configuration
@MapperScan("com.wj.scan.bean")
public class ScanConfig {
//配置数据源
@Bean
public DataSource dataSource(){
DriverManagerDataSource driverManagerDataSource
= new DriverManagerDataSource();
driverManagerDataSource.setDriverClassName("com.mysql.cj.jdbc.Driver");
driverManagerDataSource.setPassword("1234");
driverManagerDataSource.setUsername("root");
driverManagerDataSource.setUrl("jdbc:mysql://localhost:3306/study?useSSL=false&allowPublicKeyRetrieval=true&serverTimezone=UTC");
return driverManagerDataSource;
}
//配置SqlSessionFactory
@Bean
public SqlSessionFactory sqlSessionFactory() throws Exception {
SqlSessionFactoryBean factoryBean = new SqlSessionFactoryBean();
factoryBean.setDataSource(dataSource());
return factoryBean.getObject();
}
}
mapper类:在com.wj.scan.bean
包下
public interface IM {
@Select("select * from tb_resume")
List<Map<String, Object>> select();
}
数据库和数据表:
main方法:
public class ScanMain {
public static void main(String[] args) {
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext();
applicationContext.register(ScanConfig.class);
applicationContext.refresh();
IM im = applicationContext.getBean(IM.class);
System.out.println(im.select());
}
}
可以看到,Mybatis
整合Spring
时,通过在配置类上添加@MapperScan
扫描指定包,就可以扫描到Mapper
接口。
整合Spring Boot
依赖:
<dependencies>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.2.2</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
mapper:
@Mapper
public interface IM {
@Select("select * from tb_resume")
public List<Map<String, Object>> select();
}
配置文件application.yaml:
spring:
datasource:
driver-class-name: com.mysql.jdbc.Driver
url: jdbc:mysql://localhost:3306/study?serverTimezone=GMT%2B8&useSSL=true
username: root
password: 1234
springboot主启动类:
@SpringBootApplication
public class BootMybatisApplication {
public static void main(String[] args) {
SpringApplication.run(BootMybatisApplication.class, args);
}
}
测试方法:
@SpringBootTest
class BootMybatisApplicationTests {
@Autowired
IM im;
@Test
void contextLoads() {
System.out.println(im.select());
}
}
运行结果:
可以看到,当Mybatis
整合Spring boot
时候,是不需要加上@MapperScan
的,但是必须要在Mapper
接口上标注@Mapper
注解。
Mybatis整合Spring和Springboot是不一样的,所需要的配置和需要添加的注解也是不同的,那么造成这种现象必须要深究源码。
前置知识
FactoryBean
FactoryBean是spring提供的一个接口,通过该接口我可以高度定制bean的创建过程。
下面通过示例说明FactoryBean的使用:
public class Book {
}
@Component
public class BookFactoryBean implements FactoryBean<Book> {
//用来创建Bean。当IoC容器通过getBean方法来FactoryBean创建的实例时实际获取的不是FactoryBean 本身而是具体创建的T泛型实例
@Override
public Book getObject() throws Exception {
return new Book();
}
//getObjectType() 获取 T getObject()中的返回值 T 的具体类型
@Override
public Class<?> getObjectType() {
return Book.class;
}
}
@ComponentScan("com.wj.factorybean")
public class MainConfig {
}
public class Main {
public static void main(String[] args) {
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig.class);
System.out.println(applicationContext.getBean(Book.class));
}
}
运行效果:
ConfigurationClassPostProcessor和BeanDefinitionRegistryPostProcessor
ConfigurationClassPostProcessor
实现了BeanDefinitionRegistryPostProcessor
接口,该接口回调于AbstractApplicationContext#refresh()
方法中的invokeBeanFactoryPostProcessors(beanFactory)
,通过此接口可以向BeanDefinitionRegistry
中批量注册BeanDefinition
。
public class ConfigurationClassPostProcessor implements BeanDefinitionRegistryPostProcessor,
PriorityOrdered, ResourceLoaderAware, ApplicationStartupAware, BeanClassLoaderAware, EnvironmentAware {
ConfigurationClassPostProcessor的注册:
而ConfigurationClassPostProcessor
接口的注册早在new AnnotationConfigApplicationContext()
的时候已经添加到ioc容器中:
源码如下:这里内部会创建出一个AnnotatedBeanDefinitionReader
对象,该对象可将一个Class渲染成BeanDefinition
并注册到BeanDefinitionRegistry
中:
public AnnotationConfigApplicationContext() {
StartupStep createAnnotatedBeanDefReader = this.getApplicationStartup().start("spring.context.annotated-bean-reader.create");
this.reader = new AnnotatedBeanDefinitionReader(this);
createAnnotatedBeanDefReader.end();
this.scanner = new ClassPathBeanDefinitionScanner(this);
}
AnnotatedBeanDefinitionReader
构造方法如下:
public AnnotatedBeanDefinitionReader(BeanDefinitionRegistry registry) {
this(registry, getOrCreateEnvironment(registry));
}
public AnnotatedBeanDefinitionReader(BeanDefinitionRegistry registry, Environment environment) {
Assert.notNull(registry, "BeanDefinitionRegistry must not be null");
Assert.notNull(environment, "Environment must not be null");
this.registry = registry;
this.conditionEvaluator = new ConditionEvaluator(registry, environment, null);
AnnotationConfigUtils.registerAnnotationConfigProcessors(this.registry);
}
最后调用AnnotationConfigUtils
类的registerAnnotationConfigProcessors
方法:在这里ConfigurationClassPostProcessor
就已经注册到ioc容器中的,这时候尚未实例化。
public static Set<BeanDefinitionHolder> registerAnnotationConfigProcessors(
BeanDefinitionRegistry registry, @Nullable Object source) {
//获取到DefaultListableBeanFactory,便于注册bean定义信息
DefaultListableBeanFactory beanFactory = unwrapDefaultListableBeanFactory(registry);
if (beanFactory != null) {
if (!(beanFactory.getDependencyComparator() instanceof AnnotationAwareOrderComparator)) {
//设置比较器(@Order @Priority Ordered)
beanFactory.setDependencyComparator(AnnotationAwareOrderComparator.INSTANCE);
}
if (!(beanFactory.getAutowireCandidateResolver() instanceof ContextAnnotationAutowireCandidateResolver)) {
beanFactory.setAutowireCandidateResolver(new ContextAnnotationAutowireCandidateResolver());
}
}
//创建BeanDefinitionHolder集合
Set<BeanDefinitionHolder> beanDefs = new LinkedHashSet<>(8);
//注册ConfigurationClassPostProcessor ==> BeanDefinitionRegistryPostProcessor ==> BeanFactoryPostProcessor
if (!registry.containsBeanDefinition(CONFIGURATION_ANNOTATION_PROCESSOR_BEAN_NAME)) {
RootBeanDefinition def = new RootBeanDefinition(ConfigurationClassPostProcessor.class);
def.setSource(source);
beanDefs.add(registerPostProcessor(registry, def, CONFIGURATION_ANNOTATION_PROCESSOR_BEAN_NAME));
}
//注册AutowiredAnnotationBeanPostProcessor
if (!registry.containsBeanDefinition(AUTOWIRED_ANNOTATION_PROCESSOR_BEAN_NAME)) {
RootBeanDefinition def = new RootBeanDefinition(AutowiredAnnotationBeanPostProcessor.class);
def.setSource(source);
beanDefs.add(registerPostProcessor(registry, def, AUTOWIRED_ANNOTATION_PROCESSOR_BEAN_NAME));
}
// Check for JSR-250 support, and if present add the CommonAnnotationBeanPostProcessor.
//注册CommonAnnotationBeanPostProcessor,用于解析@PostConstruct等jsr250注解
if (jsr250Present && !registry.containsBeanDefinition(COMMON_ANNOTATION_PROCESSOR_BEAN_NAME)) {
RootBeanDefinition def = new RootBeanDefinition(CommonAnnotationBeanPostProcessor.class);
def.setSource(source);
beanDefs.add(registerPostProcessor(registry, def, COMMON_ANNOTATION_PROCESSOR_BEAN_NAME));
}
// Check for JPA support, and if present add the PersistenceAnnotationBeanPostProcessor.
if (jpaPresent && !registry.containsBeanDefinition(PERSISTENCE_ANNOTATION_PROCESSOR_BEAN_NAME)) {
RootBeanDefinition def = new RootBeanDefinition();
try {
def.setBeanClass(ClassUtils.forName(PERSISTENCE_ANNOTATION_PROCESSOR_CLASS_NAME,
AnnotationConfigUtils.class.getClassLoader()));
}
catch (ClassNotFoundException ex) {
throw new IllegalStateException(
"Cannot load optional framework class: " + PERSISTENCE_ANNOTATION_PROCESSOR_CLASS_NAME, ex);
}
def.setSource(source);
beanDefs.add(registerPostProcessor(registry, def, PERSISTENCE_ANNOTATION_PROCESSOR_BEAN_NAME));
}
//支持EventListener方法
if (!registry.containsBeanDefinition(EVENT_LISTENER_PROCESSOR_BEAN_NAME)) {
RootBeanDefinition def = new RootBeanDefinition(EventListenerMethodProcessor.class);
def.setSource(source);
beanDefs.add(registerPostProcessor(registry, def, EVENT_LISTENER_PROCESSOR_BEAN_NAME));
}
if (!registry.containsBeanDefinition(EVENT_LISTENER_FACTORY_BEAN_NAME)) {
RootBeanDefinition def = new RootBeanDefinition(DefaultEventListenerFactory.class);
def.setSource(source);
beanDefs.add(registerPostProcessor(registry, def, EVENT_LISTENER_FACTORY_BEAN_NAME));
}
return beanDefs;
}
ConfigurationClassPostProcessor
的执行:
前面说到该接口回调与AbstractApplicationContext#refresh()
方法中的invokeBeanFactoryPostProcessors(beanFactory)
,代码如下:
protected void invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory beanFactory) {
PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(beanFactory, getBeanFactoryPostProcessors());
// Detect a LoadTimeWeaver and prepare for weaving, if found in the meantime
// (e.g. through an @Bean method registered by ConfigurationClassPostProcessor)
if (!NativeDetector.inNativeImage() && beanFactory.getTempClassLoader() == null && beanFactory.containsBean(LOAD_TIME_WEAVER_BEAN_NAME)) {
beanFactory.addBeanPostProcessor(new LoadTimeWeaverAwareProcessor(beanFactory));
beanFactory.setTempClassLoader(new ContextTypeMatchClassLoader(beanFactory.getBeanClassLoader()));
}
}
PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(beanFactory, getBeanFactoryPostProcessors())
代码如下:
//beanFactoryPostProcessors:通过API注册的BeanFactoryPostProcessor
public static void invokeBeanFactoryPostProcessors(
ConfigurableListableBeanFactory beanFactory, List<BeanFactoryPostProcessor> beanFactoryPostProcessors) {
// WARNING: Although it may appear that the body of this method can be easily
// refactored to avoid the use of multiple loops and multiple lists, the use
// of multiple lists and multiple passes over the names of processors is
// intentional. We must ensure that we honor the contracts for PriorityOrdered
// and Ordered processors. Specifically, we must NOT cause processors to be
// instantiated (via getBean() invocations) or registered in the ApplicationContext
// in the wrong order.
//
// Before submitting a pull request (PR) to change this method, please review the
// list of all declined PRs involving changes to PostProcessorRegistrationDelegate
// to ensure that your proposal does not result in a breaking change:
// https://github.com/spring-projects/spring-framework/issues?q=PostProcessorRegistrationDelegate+is%3Aclosed+label%3A%22status%3A+declined%22
// Invoke BeanDefinitionRegistryPostProcessors first, if any.
//存放已经处理完了的BeanFactoryPostProcessor和BeanDefinitionRegistryPostProcessor
//防止重复执行
Set<String> processedBeans = new HashSet<>();
if (beanFactory instanceof BeanDefinitionRegistry) {
BeanDefinitionRegistry registry = (BeanDefinitionRegistry) beanFactory;
//存放所有实现了BeanFactoryPostProcessor的
List<BeanFactoryPostProcessor> regularPostProcessors = new ArrayList<>();
//存放所有实现了BeanDefinitionRegistryPostProcessor的
List<BeanDefinitionRegistryPostProcessor> registryProcessors = new ArrayList<>();
for (BeanFactoryPostProcessor postProcessor : beanFactoryPostProcessors) {
if (postProcessor instanceof BeanDefinitionRegistryPostProcessor) {
BeanDefinitionRegistryPostProcessor registryProcessor =
(BeanDefinitionRegistryPostProcessor) postProcessor;
//执行postProcessBeanDefinitionRegistry
registryProcessor.postProcessBeanDefinitionRegistry(registry);
registryProcessors.add(registryProcessor);
}
else {
regularPostProcessors.add(postProcessor);
}
}
// Do not initialize FactoryBeans here: We need to leave all regular beans
// uninitialized to let the bean factory post-processors apply to them!
// Separate between BeanDefinitionRegistryPostProcessors that implement
// PriorityOrdered, Ordered, and the rest.
//存放当前需要执行的BeanDefinitionRegistryPostProcessor
List<BeanDefinitionRegistryPostProcessor> currentRegistryProcessors = new ArrayList<>();
// First, invoke the BeanDefinitionRegistryPostProcessors that implement PriorityOrdered.
//根据类型查询bean的名字 (默认情况找到ConfigurationClassPostProcessor)
String[] postProcessorNames =
beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, true, false);
for (String ppName : postProcessorNames) {
//实现了PriorityOrdered接口的
if (beanFactory.isTypeMatch(ppName, PriorityOrdered.class)) {
//这里就会创建BeanDefinitionRegistryPostProcessor,存放到单例池中
currentRegistryProcessors.add(beanFactory.getBean(ppName, BeanDefinitionRegistryPostProcessor.class));
processedBeans.add(ppName);
}
}
sortPostProcessors(currentRegistryProcessors, beanFactory);
registryProcessors.addAll(currentRegistryProcessors);
//默认情况下在此执行ConfigurationClassPostProcessor
invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry, beanFactory.getApplicationStartup());
currentRegistryProcessors.clear();
// Next, invoke the BeanDefinitionRegistryPostProcessors that implement Ordered.
//实现了Ordered接口的
postProcessorNames = beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, true, false);
for (String ppName : postProcessorNames) {
if (!processedBeans.contains(ppName) && beanFactory.isTypeMatch(ppName, Ordered.class)) {
currentRegistryProcessors.add(beanFactory.getBean(ppName, BeanDefinitionRegistryPostProcessor.class));
processedBeans.add(ppName);
}
}
sortPostProcessors(currentRegistryProcessors, beanFactory);
registryProcessors.addAll(currentRegistryProcessors);
invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry, beanFactory.getApplicationStartup());
currentRegistryProcessors.clear();
//调用所有其他 BeanDefinitionRegistryPostProcessor 直到不再出现。
// Finally, invoke all other BeanDefinitionRegistryPostProcessors until no further ones appear.
boolean reiterate = true;
while (reiterate) {
//终止循环
reiterate = false;
postProcessorNames = beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, true, false);
for (String ppName : postProcessorNames) {
if (!processedBeans.contains(ppName)) {
currentRegistryProcessors.add(beanFactory.getBean(ppName, BeanDefinitionRegistryPostProcessor.class));
processedBeans.add(ppName);
//如果找到了,后面还需要继续循环 继续找
//找出来必然是子类 有可能会动态添加新的BeanDefinitionRegistryPostProcessor
reiterate = true;
}
}
sortPostProcessors(currentRegistryProcessors, beanFactory);
registryProcessors.addAll(currentRegistryProcessors);
invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry, beanFactory.getApplicationStartup());
currentRegistryProcessors.clear();
}
// Now, invoke the postProcessBeanFactory callback of all processors handled so far.
//调用到目前为止处理的所有处理器的 postProcessBeanFactory 回调
invokeBeanFactoryPostProcessors(registryProcessors, beanFactory);
invokeBeanFactoryPostProcessors(regularPostProcessors, beanFactory);
}
else {
// Invoke factory processors registered with the context instance.
invokeBeanFactoryPostProcessors(beanFactoryPostProcessors, beanFactory);
}
// Do not initialize FactoryBeans here: We need to leave all regular beans
// uninitialized to let the bean factory post-processors apply to them!
String[] postProcessorNames =
beanFactory.getBeanNamesForType(BeanFactoryPostProcessor.class, true, false);
// Separate between BeanFactoryPostProcessors that implement PriorityOrdered,
// Ordered, and the rest.
//实现了priorityOrdered的父类集合
List<BeanFactoryPostProcessor> priorityOrderedPostProcessors = new ArrayList<>();
//实现了ordered的父类集合
List<String> orderedPostProcessorNames = new ArrayList<>();
//没有实现排序
List<String> nonOrderedPostProcessorNames = new ArrayList<>();
for (String ppName : postProcessorNames) {
if (processedBeans.contains(ppName)) {
// skip - already processed in first phase above
}
else if (beanFactory.isTypeMatch(ppName, PriorityOrdered.class)) {
//为什么这里先实例化
//因为此时修改bean定义信息,在后面会生效
priorityOrderedPostProcessors.add(beanFactory.getBean(ppName, BeanFactoryPostProcessor.class));
}
else if (beanFactory.isTypeMatch(ppName, Ordered.class)) {
//这里不实例化
orderedPostProcessorNames.add(ppName);
}
else {
nonOrderedPostProcessorNames.add(ppName);
}
}
// First, invoke the BeanFactoryPostProcessors that implement PriorityOrdered.
//执行实现PriorityOrdered接口的BeanFactoryPostProcessor
sortPostProcessors(priorityOrderedPostProcessors, beanFactory);
invokeBeanFactoryPostProcessors(priorityOrderedPostProcessors, beanFactory);
// Next, invoke the BeanFactoryPostProcessors that implement Ordered.
//执行实现Ordered接口的BeanFactoryPostProcessor
List<BeanFactoryPostProcessor> orderedPostProcessors = new ArrayList<>(orderedPostProcessorNames.size());
for (String postProcessorName : orderedPostProcessorNames) {
orderedPostProcessors.add(beanFactory.getBean(postProcessorName, BeanFactoryPostProcessor.class));
}
sortPostProcessors(orderedPostProcessors, beanFactory);
invokeBeanFactoryPostProcessors(orderedPostProcessors, beanFactory);
// Finally, invoke all other BeanFactoryPostProcessors.
//执行剩下的BeanFactoryPostProcessor
List<BeanFactoryPostProcessor> nonOrderedPostProcessors = new ArrayList<>(nonOrderedPostProcessorNames.size());
for (String postProcessorName : nonOrderedPostProcessorNames) {
nonOrderedPostProcessors.add(beanFactory.getBean(postProcessorName, BeanFactoryPostProcessor.class));
}
invokeBeanFactoryPostProcessors(nonOrderedPostProcessors, beanFactory);
// Clear cached merged bean definitions since the post-processors might have
// modified the original metadata, e.g. replacing placeholders in values...
beanFactory.clearMetadataCache();
}
那么ConfigurationClassPostProcessor
类做了什么事情?
该类会解析所有当前ioc容器中已经解析的配置类,对各种注解的解析:@Bean,@ComponentScan,@Import,@Primary等等
限于篇幅原因,这里不对ConfigurationClassPostProcessor
类中的方法做解释,可看我之前写的一篇文章:Spring源码分析:@Configuration原理,该文章中对ConfigurationClassPostProcessor
源码做了详细解释。
ImportBeanDefinitionRegistrar
该接口中有两个方法:通过该接口可以实现批量注册BeanDefinition
到BeanDefinitionRegistry
中
public interface ImportBeanDefinitionRegistrar {
default void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry,
BeanNameGenerator importBeanNameGenerator) {
registerBeanDefinitions(importingClassMetadata, registry);
}
default void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
}
}
关于该接口方法的回调在ConfigurationClassParser#processImports()
方法中。
@ComponentScan原理
在ComponentScanAnnotationParser
类中的parse
方法:内部创建一个ClassPathBeanDefinitionScanner
执行doScan
方法进行扫描。
题外话:
AnnotationConfigApplicationContext
类中也有个scan
方法,如果传入包名进行扫描。默认情况下,@ComponentScan
和AnnotationConfigApplicationContext
调用scan
进行扫描最后的效果都是一样的。
public Set<BeanDefinitionHolder> parse(AnnotationAttributes componentScan, final String declaringClass) {
ClassPathBeanDefinitionScanner scanner = new ClassPathBeanDefinitionScanner(this.registry,
componentScan.getBoolean("useDefaultFilters"), this.environment, this.resourceLoader);
//中间部分代码省略...
for (AnnotationAttributes filter : componentScan.getAnnotationArray("includeFilters")) {
for (TypeFilter typeFilter : typeFiltersFor(filter)) {
scanner.addIncludeFilter(typeFilter);
}
}
for (AnnotationAttributes filter : componentScan.getAnnotationArray("excludeFilters")) {
for (TypeFilter typeFilter : typeFiltersFor(filter)) {
scanner.addExcludeFilter(typeFilter);
}
}
boolean lazyInit = componentScan.getBoolean("lazyInit");
if (lazyInit) {
scanner.getBeanDefinitionDefaults().setLazyInit(true);
}
Set<String> basePackages = new LinkedHashSet<>();
String[] basePackagesArray = componentScan.getStringArray("basePackages");
for (String pkg : basePackagesArray) {
String[] tokenized = StringUtils.tokenizeToStringArray(this.environment.resolvePlaceholders(pkg),
ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS);
Collections.addAll(basePackages, tokenized);
}
for (Class<?> clazz : componentScan.getClassArray("basePackageClasses")) {
basePackages.add(ClassUtils.getPackageName(clazz));
}
if (basePackages.isEmpty()) {
basePackages.add(ClassUtils.getPackageName(declaringClass));
}
//排除自己,不需要扫描
scanner.addExcludeFilter(new AbstractTypeHierarchyTraversingFilter(false, false) {
@Override
protected boolean matchClassName(String className) {
return declaringClass.equals(className);
}
});
//开始扫描
return scanner.doScan(StringUtils.toStringArray(basePackages));
}
先看ClassPathBeanDefinitionScanner
的构造方法(比较重要):
这里如果我们ComponentScan
中没有配置useDefaultFilters
属性,默认为true,就会默认注册三个includeFilters
提供了对@Component
、@ManagedBean
、@Named
这三个注解的扫描支持(如果添加了jsr250和jsr303 jar包的情况下)。
ExcludeFilter
默认会有一个排除自己不用扫描的AbstractTypeHierarchyTraversingFilter
匿名内部类.
public ClassPathBeanDefinitionScanner(BeanDefinitionRegistry registry, boolean useDefaultFilters,
Environment environment, @Nullable ResourceLoader resourceLoader) {
Assert.notNull(registry, "BeanDefinitionRegistry must not be null");
this.registry = registry;
if (useDefaultFilters) {
//注册默认的过滤器
registerDefaultFilters();
}
//设置Environment和ResourceLoader
setEnvironment(environment);
setResourceLoader(resourceLoader);
}
protected void registerDefaultFilters() {
//提供对@Component注解的支持
this.includeFilters.add(new AnnotationTypeFilter(Component.class));
ClassLoader cl = ClassPathScanningCandidateComponentProvider.class.getClassLoader();
try {
//提供对@ManagedBean注解的支持
this.includeFilters.add(new AnnotationTypeFilter(
((Class<? extends Annotation>) ClassUtils.forName("javax.annotation.ManagedBean", cl)), false));
logger.trace("JSR-250 'javax.annotation.ManagedBean' found and supported for component scanning");
}
catch (ClassNotFoundException ex) {
// JSR-250 1.1 API (as included in Java EE 6) not available - simply skip.
}
try {
//提供对@Named注解的支持
this.includeFilters.add(new AnnotationTypeFilter(
((Class<? extends Annotation>) ClassUtils.forName("javax.inject.Named", cl)), false));
logger.trace("JSR-330 'javax.inject.Named' annotation found and supported for component scanning");
}
catch (ClassNotFoundException ex) {
// JSR-330 API not available - simply skip.
}
}
ClassPathBeanDefinitionScanner
类的scan
方法如下:主要调用doScan
:
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
方法如下:
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) {
//查找所有候选的组件
Set<BeanDefinition> candidates = findCandidateComponents(basePackage);
for (BeanDefinition candidate : candidates) {
//解析出ScopeMetadata
ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(candidate);
candidate.setScope(scopeMetadata.getScopeName());
//通过名字生成器生成bean的名字
String beanName = this.beanNameGenerator.generateBeanName(candidate, this.registry);
if (candidate instanceof AbstractBeanDefinition) {
postProcessBeanDefinition((AbstractBeanDefinition) candidate, beanName);
}
if (candidate instanceof AnnotatedBeanDefinition) {
//解析配置类上的@Lazy、@Primary、@DependsOn、@Role、@Description的注解信息
AnnotationConfigUtils.processCommonDefinitionAnnotations((AnnotatedBeanDefinition) candidate);
}
//判断是否注册了,以及需要注册的BeanDefinition和现有的BeanDefinition是否有冲突
if (checkCandidate(beanName, candidate)) {
//注册bean定义信息
BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(candidate, beanName);
definitionHolder =
AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);
beanDefinitions.add(definitionHolder);
registerBeanDefinition(definitionHolder, this.registry);
}
}
}
return beanDefinitions;
}
不难观察到,查找BeanDefinition
的核心代码在findCandidateComponents
方法中:
public Set<BeanDefinition> findCandidateComponents(String basePackage) {
if (this.componentsIndex != null && indexSupportsIncludeFilters()) {
//这里是用来处理META-INF/spring.components配置的component,很少用到
return addCandidateComponentsFromIndex(this.componentsIndex, basePackage);
}
else {
//默认进入这里
return scanCandidateComponents(basePackage);
}
}
scanCandidateComponents
方法如下:
- 这里先将需要扫描包下面的所有文件扫描成Resource,内部就是一个InputStream
- 然后通过ClassReader(ASM知识)去读取类的信息(这里不用Class.forName读取类的信息是因为可以避免类加载到JVM中)
- 扫描到类的信息后,通过调用
isCandidateComponent
方法判断时候是spring识别的组件,如果是,则添加到candidates
集合中并返回。
private Set<BeanDefinition> scanCandidateComponents(String basePackage) {
Set<BeanDefinition> candidates = new LinkedHashSet<>();
//通过包名获取包所在的路径得到一个文件 流
try {
String packageSearchPath = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX +
resolveBasePackage(basePackage) + '/' + this.resourcePattern;
Resource[] resources = getResourcePatternResolver().getResources(packageSearchPath);
boolean traceEnabled = logger.isTraceEnabled();
boolean debugEnabled = logger.isDebugEnabled();
for (Resource resource : resources) {
if (traceEnabled) {
logger.trace("Scanning " + resource);
}
if (resource.isReadable()) {
try {
//通过ASM读取类信息
MetadataReader metadataReader = getMetadataReaderFactory().getMetadataReader(resource);
//判断是否是候选的组件
if (isCandidateComponent(metadataReader)) {
ScannedGenericBeanDefinition sbd = new ScannedGenericBeanDefinition(metadataReader);
sbd.setSource(resource);
if (isCandidateComponent(sbd)) {
if (debugEnabled) {
logger.debug("Identified candidate component class: " + resource);
}
candidates.add(sbd);
}
else {
if (debugEnabled) {
logger.debug("Ignored because not a concrete top-level class: " + resource);
}
}
}
else {
if (traceEnabled) {
logger.trace("Ignored because not matching any filter: " + resource);
}
}
}
catch (Throwable ex) {
throw new BeanDefinitionStoreException(
"Failed to read candidate component class: " + resource, ex);
}
}
else {
if (traceEnabled) {
logger.trace("Ignored because not readable: " + resource);
}
}
}
}
catch (IOException ex) {
throw new BeanDefinitionStoreException("I/O failure during classpath scanning", ex);
}
return candidates;
}
再看isCandidateComponent
方法(重点),这里就是辨别一个类是否是ioc容器中的bean:
默认情况下:excludeFilters中只有一个
AbstractTypeHierarchyTraversingFilter
排除自己,includeFilters
会判断是否标注了@Component
protected boolean isCandidateComponent(MetadataReader metadataReader) throws IOException {
//首先判断这个类有没有被排除
for (TypeFilter tf : this.excludeFilters) {
if (tf.match(metadataReader, getMetadataReaderFactory())) {
return false;
}
}
for (TypeFilter tf : this.includeFilters) {
if (tf.match(metadataReader, getMetadataReaderFactory())) {
//判断@Condition注解,默认没加@Condition注解会返回true
return isConditionMatch(metadataReader);
}
}
return false;
}
SpringBoot自动配置原理
springboot启动时候,会扫描类路径下的所有META-INF/spring.factories
文件,文件中的配置的所有配置类,会根据条件自动扫描到ioc容器。
详情可见:Spring Boot:自动配置原理
Mybatis整合Spring原理
Mapper接口的扫描
Mybatis整合Spring,主要通过添加@MapperScan
,批量扫描Mapper
接口,与@ComponentScan
类似,这就前面我花大量篇幅写@ComponentScan
原理的原因。
所以我们从@MapperScan开始着手:这里@Import
了一个MapperScannerRegistrar
类
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Import(MapperScannerRegistrar.class)
@Repeatable(MapperScans.class)
public @interface MapperScan {
再看MapperScannerRegistrar
类:实现了ImportBeanDefinitionRegistrar
,前面提到通过该接口可以向ioc容器注册bean定义信息
public class MapperScannerRegistrar implements ImportBeanDefinitionRegistrar, ResourceLoaderAware {
//其他不重要代码省略
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
//拿到MapperScan注解中的所有元信息
AnnotationAttributes mapperScanAttrs = AnnotationAttributes
.fromMap(importingClassMetadata.getAnnotationAttributes(MapperScan.class.getName()));
if (mapperScanAttrs != null) {
//调用registerBeanDefinitions注册bean定义信息
registerBeanDefinitions(importingClassMetadata, mapperScanAttrs, registry,
generateBaseBeanName(importingClassMetadata, 0));
}
}
void registerBeanDefinitions(AnnotationMetadata annoMeta, AnnotationAttributes annoAttrs,
BeanDefinitionRegistry registry, String beanName) {
//创建BeanDefinitionBuilder
BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(MapperScannerConfigurer.class);
//通过builder.addPropertyValue向bean定义信息中设置属性值,这样通过BeanDefinition创建bean的时候,就会把属性值设置到bean的字段中。
builder.addPropertyValue("processPropertyPlaceHolders", true);
Class<? extends Annotation> annotationClass = annoAttrs.getClass("annotationClass");
//判断annotationClass是否是Annotation.class
if (!Annotation.class.equals(annotationClass)) {
//只有不为Annotation.class,才会把annotationClass的值设置进去
//因为@MapperScan默认annotationClass的值就是@Annotation,所以Spring创建出来的MapperScannerConfigurer类中annotationClass就为null
builder.addPropertyValue("annotationClass", annotationClass);
}
//中间一些属性设置,这里省略
List<String> basePackages = new ArrayList<>();
basePackages.addAll(
Arrays.stream(annoAttrs.getStringArray("value")).filter(StringUtils::hasText).collect(Collectors.toList()));
basePackages.addAll(Arrays.stream(annoAttrs.getStringArray("basePackages")).filter(StringUtils::hasText)
.collect(Collectors.toList()));
basePackages.addAll(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);
}
builder.addPropertyValue("basePackage", StringUtils.collectionToCommaDelimitedString(basePackages));
//注册bean定义信息
registry.registerBeanDefinition(beanName, builder.getBeanDefinition());
}
因为MapperScannerRegistrar
向容器中添加了MapperScannerConfigurer
组件,再看MapperScannerConfigurer
类:
实现了BeanDefinitionRegistryPostProcessor
,根据前文所说,该类会被执行在PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(beanFactory, getBeanFactoryPostProcessors())
方法中,并且回调postProcessBeanDefinitionRegistry
方法,用于注册bean定义信息,所以mybatis极大可能就是通过该类注册Mapper接口的。
public class MapperScannerConfigurer
implements BeanDefinitionRegistryPostProcessor, InitializingBean, ApplicationContextAware, BeanNameAware {
再看MapperScannerConfigurer
实现的方法:
postProcessBeanFactory
方法主要用于修改bean的定义信息,或者修改BeanFactory的配置,这里只是一个空方法。
postProcessBeanDefinitionRegistry
中创建了一个ClassPathMapperScanner
,然后设置了一些属性,调用其scan
方法。
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {
// left intentionally blank
}
@Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) {
if (this.processPropertyPlaceHolders) {
processPropertyPlaceHolders();
}
ClassPathMapperScanner scanner = new ClassPathMapperScanner(registry);
scanner.setAddToConfig(this.addToConfig);
//这里annotationClass默认是null
scanner.setAnnotationClass(this.annotationClass默认是null);
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(lazyInitialization)) {
scanner.setLazyInitialization(Boolean.valueOf(lazyInitialization));
}
scanner.registerFilters();
scanner.scan(
StringUtils.tokenizeToStringArray(this.basePackage, ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS));
}
那么Mapper扫描的核心代码就在ClassPathMapperScanner
中,先看该类的类图:
ClassPathMapperScanner
是mybatis-spring包中的,它继承了spring的ClassPathBeanDefinitionScanner
,先看ClassPathMapperScanner
类的构造方法:
useDefaultFilters传的false,这样就不会创建默认的Filter
public ClassPathMapperScanner(BeanDefinitionRegistry registry) {
super(registry, false);
}
然后创建ClassPathMapperScanner
的时候调用了registerFilters
方法:
因为this.annotationClass
是null,所以addIncludeFilter(new AnnotationTypeFilter(this.annotationClass))
这行代码走不到。
public void registerFilters() {
boolean acceptAllInterfaces = true;
// if specified, use the given annotation and / or marker interface
//annotationClass默认为null
if (this.annotationClass != null) {
addIncludeFilter(new AnnotationTypeFilter(this.annotationClass));
acceptAllInterfaces = false;
}
// override AssignableTypeFilter to ignore matches on the actual marker interface
if (this.markerInterface != null) {
addIncludeFilter(new AssignableTypeFilter(this.markerInterface) {
@Override
protected boolean matchClassName(String className) {
return false;
}
});
acceptAllInterfaces = false;
}
//acceptAllInterfaces默认为true,会进入到这个if
//而这里这个filter内部match方法直接返回true,默认不过滤,即扫描所有的类
if (acceptAllInterfaces) {
// default include filter that accepts all classes
addIncludeFilter((metadataReader, metadataReaderFactory) -> true);
}
// exclude package-info.java
addExcludeFilter((metadataReader, metadataReaderFactory) -> {
String className = metadataReader.getClassMetadata().getClassName();
return className.endsWith("package-info");
});
}
之后调用ClassPathMapperScanner
的scan
方法,仍然是父类的scan
方法:
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
方法:
@Override
public Set<BeanDefinitionHolder> doScan(String... basePackages) {
//调用的仍然是父类ClassPathBeanDefinitionScanner的doScan方法
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 {
processBeanDefinitions(beanDefinitions);
}
return beanDefinitions;
}
尽管说ClassPathMapperScanner
的doScan
方法先调用了父类ClassPathBeanDefinitionScanner
的doScan
方法,扫描逻辑与父类一样,但是要注意ClassPathMapperScanner
重写了isCandidateComponent
方法,它这里跟父类的isCandidateComponent
判断逻辑不一样。
第一个判断条件是判断扫描出来的类是否是接口:
@Override
protected boolean isCandidateComponent(AnnotatedBeanDefinition beanDefinition) {
return beanDefinition.getMetadata().isInterface() && beanDefinition.getMetadata().isIndependent();
}
第二个判断条件isIndependent
可能不好理解,我们可以看该方法上作者给的注释:判断该类是否是top-level class或者是nested class (static inner class)
/**
* Determine whether the underlying class is independent, i.e. whether
* it is a top-level class or a nested class (static inner class) that
* can be constructed independently from an enclosing class.
* top level class 或者 nested class,任意成立返回true
*/
boolean isIndependent();
那么是什么意思?
其实在jdk中的Class类的getEnclosingClass方法中有一段注释:总共有五种类
@CallerSensitive
public Class<?> getEnclosingClass() throws SecurityException {
// There are five kinds of classes (or interfaces):
// a) Top level classes 顶级类
// b) Nested classes (static member classes) 静态内部类
// c) Inner classes (non-static member classes) 非静态内部类
// d) Local classes (named classes declared within a method) 在方法内定义的类
// e) Anonymous classes 匿名类
所以说@MapperScan
扫描的是接口,并且是顶级类或者是静态内部类。(静态内部类也可以作为Mapper接口扫描进入ioc容器,我私下测试过了)
至此就解释了Spring整合Mybatis只需要加上@MapperScan就可以扫描到Mapper接口。
Bean定义信息的注册
那么Mapper接口对应的bean定义信息是怎么创建成ioc容器的单例bean的?当执行doScan方法扫描到Mapper接口后,会执行processBeanDefinitions
对当前扫描到的bean定义信息进行处理:
private void processBeanDefinitions(Set<BeanDefinitionHolder> beanDefinitions) {
GenericBeanDefinition definition;
//遍历所有的beanDefinition
for (BeanDefinitionHolder holder : beanDefinitions) {
definition = (GenericBeanDefinition) holder.getBeanDefinition();
//获取到bean的全类名
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
//设置创建bean的构造器使用参数就是bean的全类名
definition.getConstructorArgumentValues().addGenericArgumentValue(beanClassName); // issue #59
//mapperFactoryBeanClass对应的Class就是MapperFactoryBean.class
definition.setBeanClass(this.mapperFactoryBeanClass);
definition.getPropertyValues().add("addToConfig", this.addToConfig);
//设置后面bean属性注入的sqlSessionFactory属性的值
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;
}
//设置后面bean属性注入的sqlSessionTemplate属性的值
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() + "'.");
definition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE);
}
definition.setLazyInit(lazyInitialization);
}
}
通过这里,可见Mapper
接口创建成ioc
容器单例bean
的端倪:当容器创建bean的时候,进入bean的生命周期,首先spring会推断该类的构造方法,是MapperFactoryBean
有参的构造方法,并且是Mapper接口的全类名字符串,
再看MapperFactoryBean
类(这里我只摘取了部分重要的代码在这里):
- 首先该类实现了spring中的FactoryBean接口,然而这里只有一个有参的构造方法,并且参数是Class类型,与上面提到的String类型有差异。这是因为spring在调用构造方法时候,会进行校验判断是否需要类型转换。
- (Spring类型转换不知道的可以阅读Spring源码分析:类型转换(一)之PropertyEditor和ConversionService)和 Spring源码分析:类型转换(二)之TypeConverter
- spring在创建MapperFactoryBean时会将String类型的全类名转换成Class类型。
- FactoryBean也是Spring的重要内容,这里
getObject
内部调用getSqlSession().getMapper(this.mapperInterface)
方法生成指定接口的代理类,而这个方法就是Mybatis
生成代理类的原理,比较简单,可参考:Mybatis源码:getMapper获取接口代理对象
public class MapperFactoryBean<T> extends SqlSessionDaoSupport implements FactoryBean<T> {
private Class<T> mapperInterface;
private boolean addToConfig = true;
public MapperFactoryBean() {
// intentionally empty
}
public MapperFactoryBean(Class<T> mapperInterface) {
this.mapperInterface = mapperInterface;
}
@Override
public T getObject() throws Exception {
return getSqlSession().getMapper(this.mapperInterface);
}
@Override
public Class<T> getObjectType() {
return this.mapperInterface;
}
@Override
public boolean isSingleton() {
return true;
}
}
至此,Spring
与mybatis
是整合结束,后面就是FactoryBean
的生命周期,属于Spring
的内容,这里不再赘述。
Mybatis整合Spring Boot原理
先看mybatis-spring-boot-starter中添加了哪些依赖,然后再做具体分析:
根据依赖图,不难得出springboot
整合mybatis的核心应该在mybatis-spring-boot-autoconfigure
中,而自己开发过springboot的场景启动器的同学们,也知道自定义场景启动器约定俗成的名字规范就是xxx-spring-boot-mybatis
。
直接去该jar中看对应的spring.factories
文件,springboot会扫描其中配置的两个自动配置类。而核心就在MybatisAutoConfiguration
中。
该类大致结构如下,部分代码省略:
@org.springframework.context.annotation.Configuration
@ConditionalOnClass({ SqlSessionFactory.class, SqlSessionFactoryBean.class })
@ConditionalOnSingleCandidate(DataSource.class)
@EnableConfigurationProperties(MybatisProperties.class)
@AutoConfigureAfter({ DataSourceAutoConfiguration.class, MybatisLanguageDriverAutoConfiguration.class })
public class MybatisAutoConfiguration implements InitializingBean {
@Bean
@ConditionalOnMissingBean
public SqlSessionFactory sqlSessionFactory(DataSource dataSource){....}
@Bean
@ConditionalOnMissingBean
public SqlSessionTemplate sqlSessionTemplate(SqlSessionFactory sqlSessionFactory) {....}
@org.springframework.context.annotation.Configuration
@Import(AutoConfiguredMapperScannerRegistrar.class)
@ConditionalOnMissingBean({ MapperFactoryBean.class, MapperScannerConfigurer.class })
public static class MapperScannerRegistrarNotFoundConfiguration implements InitializingBean {
}
- 注册了
SqlSessionFactory
和SqlSessionTemplate
- 添加了
MapperScannerRegistrarNotFoundConfiguration
配置类,而在这个类上判断IOC容器中没有MapperFactoryBean
类和MapperScannerConfigurer
类则会生效,默认是生效的,因为我没有手动注册或者手动扫描到ioc容器MapperScannerRegistrarNotFoundConfiguration
类上@Import
了一个类AutoConfiguredMapperScannerRegistrar
再看AutoConfiguredMapperScannerRegistrar
类:
- 该类通过实现
ImportBeanDefinitionRegistrar
接口向容器中注册MapperScannerConfigurer
组件,这就是前面MapperScannerRegistrarNotFoundConfiguration
生效的条件,只有容器中没有MapperScannerConfigurer
组件时,才会向容器中注册默认的MapperScannerConfigurer
- 这里需要注意的是
MapperScannerConfigurer
中的annotationClass
属性会被赋值,默认是Mapper.class
,然后导致ClassPathMapperScanner
中会创建一个关于@Mapper
注解的AnnotationTypeFilter
,而不是所有扫描的接口TypeFilter.match
都返回true。- 这与前面Spring整合Mybatis是不同的,前面注册到ioc容器中
MapperScannerConfigurer
的annotationClass
属性默认是null,最终导致Mapper接口不需要添加@Mapper注解也能扫描到。但是对于SpringBoot来说,因为MapperScannerConfigurer
中的annotationClass
默认是Mapper.class
,导致Mybtis在扫描包的时候会看接口上是否存在@Mapper
注解。
//实现了ImportBeanDefinitionRegistrar接口,通过该接口可以注册bean的定义信息
public static class AutoConfiguredMapperScannerRegistrar
implements BeanFactoryAware, EnvironmentAware, ImportBeanDefinitionRegistrar {
private BeanFactory beanFactory;
private Environment environment;
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
if (!AutoConfigurationPackages.has(this.beanFactory)) {
logger.debug("Could not determine auto-configuration package, automatic mapper scanning disabled.");
return;
}
logger.debug("Searching for mappers annotated with @Mapper");
//获取扫描包的路径,默认是SpringBoot主启动类所在包的路径
List<String> packages = AutoConfigurationPackages.get(this.beanFactory);
if (logger.isDebugEnabled()) {
packages.forEach(pkg -> logger.debug("Using auto-configuration base package '{}'", pkg));
}
//以下就是创建MapperScannerConfigurer类的bean定义信息过程
BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(MapperScannerConfigurer.class);
builder.addPropertyValue("processPropertyPlaceHolders", true);
//设置annotationClass为@Mapper注解
//当MapperScannerConfigurer创建时,走到populateBean方法时,会把属性值设置到属性中
builder.addPropertyValue("annotationClass", Mapper.class);
builder.addPropertyValue("basePackage", StringUtils.collectionToCommaDelimitedString(packages));
BeanWrapper beanWrapper = new BeanWrapperImpl(MapperScannerConfigurer.class);
Set<String> propertyNames = Stream.of(beanWrapper.getPropertyDescriptors()).map(PropertyDescriptor::getName)
.collect(Collectors.toSet());
if (propertyNames.contains("lazyInitialization")) {
// Need to mybatis-spring 2.0.2+
builder.addPropertyValue("lazyInitialization", "${mybatis.lazy-initialization:false}");
}
if (propertyNames.contains("defaultScope")) {
// Need to mybatis-spring 2.0.6+
builder.addPropertyValue("defaultScope", "${mybatis.mapper-default-scope:}");
}
// for spring-native
boolean injectSqlSession = environment.getProperty("mybatis.inject-sql-session-on-mapper-scan", Boolean.class,
Boolean.TRUE);
if (injectSqlSession && this.beanFactory instanceof ListableBeanFactory) {
ListableBeanFactory listableBeanFactory = (ListableBeanFactory) this.beanFactory;
Optional<String> sqlSessionTemplateBeanName = Optional
.ofNullable(getBeanNameForType(SqlSessionTemplate.class, listableBeanFactory));
Optional<String> sqlSessionFactoryBeanName = Optional
.ofNullable(getBeanNameForType(SqlSessionFactory.class, listableBeanFactory));
if (sqlSessionTemplateBeanName.isPresent() || !sqlSessionFactoryBeanName.isPresent()) {
builder.addPropertyValue("sqlSessionTemplateBeanName",
sqlSessionTemplateBeanName.orElse("sqlSessionTemplate"));
} else {
builder.addPropertyValue("sqlSessionFactoryBeanName", sqlSessionFactoryBeanName.get());
}
}
builder.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
registry.registerBeanDefinition(MapperScannerConfigurer.class.getName(), builder.getBeanDefinition());
}
至此:mybatis整合Spring和整合SpringBoot的区别和原理讲完。