spring myBatis
MyBatis 独立使用
- mybatis 配置文件结构
- configuration 根元素
- properties 定义配置外在化
- settings 全局性的配置
- typeAliases 定义的类的别名
- typeHandles 自定义的类型处理,即数据库类型与java类型之间的数据转换
- ObjectFactory 用于指定结果集的对象的实例是如何创建的
- plugins myBatis 的插件,可以修改mybatis 内部的运行规则
- environments 运行环境
- transactionManager 事物管理器
- dataResource 数据源
- mappers 指定映射文件或者映射类
配置文件
1 | <configuration> |
2 | <settings> |
3 | < 1-- changes from the defaults for testing --> |
4 | <setting name="cacheEnabled" value="false"/> |
5 | <setting name="useGeneratedKeys " value="true "/> |
6 | <setting name="defaultExecutorType" value="REUSE"/> |
7 | </settings> |
8 | <typeAliases> |
9 | <typeAlias alias = "User" type= "beanUser"/> |
10 | </typeAliases> |
11 | <environments default="development"> |
12 | <environment id="development "> |
13 | <transactioηManager type="jdbc"/> |
14 | <dataSource type="POOLED"> |
15 | <property name="driver " value="com.mysq.jdbc.Driver"/> |
16 | <property name="url" value="jddbc:mysql//localhost/lexueba"/> |
17 | <property name= "username" value="root" /> |
18 | <property name = "password" value = "123456"/> |
19 | </dataSource> |
20 | </environment> |
21 | </environments> |
22 | <mappers> |
23 | <mapper resource= "resource/UserMapper.xml "/> |
24 | </mappers> |
25 | </configuration> |
映射文件 UserMapper
1 |
|
2 |
|
3 |
|
4 |
|
5 | |
6 | <mapper namespace=" mapper.UserMapper" > |
7 | <!-- 这里namespace 必须是UserMapper 接口的路径,不然要运行的时候要报销“ is not known to the MapperRegistry" --> |
8 | <insert id= "insertUser" parameterType= "User " > |
9 | insert into user(name , age) values(#{name) , #{age)) |
10 | <!--这里sql 结尾不能力II分号, 否则报“ORA-00911 "的错误--> |
11 | </insert> |
12 | <!--这里的id 必须和IUserMapper 接口中的接口方法名相同,不然运行的时候也要报销--> |
13 | <select id= "getUser" resultType="User" parameterType="java.lang.Integer"> |
14 | select * from user where id=#{id} |
15 | </select> |
16 | </mapper> |
spring 中使用Mybatis
- 通过 org.mybatis.Spring.SqlSessionFactoryBean引入了 Mybatis 的配置和数据源
- 通过 org.mybatis.Spring.mapper.MapperFactoryBean 集成了 sqlSessionFaction 和 MapperInterface 接口类之间的关系
- environments 中设置的dataSource 被转移到了Spring 的核心配置文件中管理。
源码分析
SqlSessionFactory 的创建
SqlSessionFactory 的创建是依赖于 SqlSessionFactoryBean
SqlSessionFactoryBean 继承关系
实现 InitalizingBean 接口的bean 会在 初始化时调用 afterPropertiesSet 方法来进行bean 的初始化逻辑。
实现 FactoryBean 接口,获取 bean 时,其实是该bean 的getObject 方法获取的实例。
sqlSessionFactory 的初始化
1 | //SqlSessionFactoryBean implements InitalizingBean |
2 | |
3 | public void afterPropertiesSet() throws Exception { |
4 | notNull(dataSource, "Property 'dataSource' is required"); |
5 | notNull(sqlSessionFactoryBuilder, "Property 'sqlSessionFactoryBuilder' is required"); |
6 | state((configuration == null && configLocation == null) || !(configuration != null && configLocation != null), |
7 | "Property 'configuration' and 'configLocation' can not specified with together"); |
8 | |
9 | this.sqlSessionFactory = buildSqlSessionFactory(); |
10 | } |
- sqlSessionFacotry 是myBatis 所有功能的基础从以上方法看出,我们也可以不使用mybatis 的配置文件,而将具体的属性直接配置在
1
//SqlSessionFactoryBean
2
protected SqlSessionFactory buildSqlSessionFactory() throws Exception {
3
4
final Configuration targetConfiguration;
5
6
XMLConfigBuilder xmlConfigBuilder = null;
7
if (this.configuration != null) {
8
targetConfiguration = this.configuration;
9
if (targetConfiguration.getVariables() == null) {
10
targetConfiguration.setVariables(this.configurationProperties);
11
} else if (this.configurationProperties != null) {
12
targetConfiguration.getVariables().putAll(this.configurationProperties);
13
}
14
} else if (this.configLocation != null) {
15
xmlConfigBuilder = new XMLConfigBuilder(this.configLocation.getInputStream(), null, this.configurationProperties);
16
targetConfiguration = xmlConfigBuilder.getConfiguration();
17
} else {
18
LOGGER.debug(
19
() -> "Property 'configuration' or 'configLocation' not specified, using default MyBatis Configuration");
20
targetConfiguration = new Configuration();
21
Optional.ofNullable(this.configurationProperties).ifPresent(targetConfiguration::setVariables);
22
}
23
24
Optional.ofNullable(this.objectFactory).ifPresent(targetConfiguration::setObjectFactory);
25
Optional.ofNullable(this.objectWrapperFactory).ifPresent(targetConfiguration::setObjectWrapperFactory);
26
Optional.ofNullable(this.vfs).ifPresent(targetConfiguration::setVfsImpl);
27
28
if (hasLength(this.typeAliasesPackage)) {
29
scanClasses(this.typeAliasesPackage, this.typeAliasesSuperType).stream()
30
.filter(clazz -> !clazz.isAnonymousClass()).filter(clazz -> !clazz.isInterface())
31
.filter(clazz -> !clazz.isMemberClass()).forEach(targetConfiguration.getTypeAliasRegistry()::registerAlias);
32
}
33
34
if (!isEmpty(this.typeAliases)) {
35
Stream.of(this.typeAliases).forEach(typeAlias -> {
36
targetConfiguration.getTypeAliasRegistry().registerAlias(typeAlias);
37
LOGGER.debug(() -> "Registered type alias: '" + typeAlias + "'");
38
});
39
}
40
41
if (!isEmpty(this.plugins)) {
42
Stream.of(this.plugins).forEach(plugin -> {
43
targetConfiguration.addInterceptor(plugin);
44
LOGGER.debug(() -> "Registered plugin: '" + plugin + "'");
45
});
46
}
47
48
if (hasLength(this.typeHandlersPackage)) {
49
scanClasses(this.typeHandlersPackage, TypeHandler.class).stream().filter(clazz -> !clazz.isAnonymousClass())
50
.filter(clazz -> !clazz.isInterface()).filter(clazz -> !Modifier.isAbstract(clazz.getModifiers()))
51
.forEach(targetConfiguration.getTypeHandlerRegistry()::register);
52
}
53
54
if (!isEmpty(this.typeHandlers)) {
55
Stream.of(this.typeHandlers).forEach(typeHandler -> {
56
targetConfiguration.getTypeHandlerRegistry().register(typeHandler);
57
LOGGER.debug(() -> "Registered type handler: '" + typeHandler + "'");
58
});
59
}
60
61
if (!isEmpty(this.scriptingLanguageDrivers)) {
62
Stream.of(this.scriptingLanguageDrivers).forEach(languageDriver -> {
63
targetConfiguration.getLanguageRegistry().register(languageDriver);
64
LOGGER.debug(() -> "Registered scripting language driver: '" + languageDriver + "'");
65
});
66
}
67
Optional.ofNullable(this.defaultScriptingLanguageDriver)
68
.ifPresent(targetConfiguration::setDefaultScriptingLanguage);
69
70
if (this.databaseIdProvider != null) {// fix #64 set databaseId before parse mapper xmls
71
try {
72
targetConfiguration.setDatabaseId(this.databaseIdProvider.getDatabaseId(this.dataSource));
73
} catch (SQLException e) {
74
throw new NestedIOException("Failed getting a databaseId", e);
75
}
76
}
77
78
Optional.ofNullable(this.cache).ifPresent(targetConfiguration::addCache);
79
80
if (xmlConfigBuilder != null) {
81
try {
82
xmlConfigBuilder.parse();
83
LOGGER.debug(() -> "Parsed configuration file: '" + this.configLocation + "'");
84
} catch (Exception ex) {
85
throw new NestedIOException("Failed to parse config resource: " + this.configLocation, ex);
86
} finally {
87
ErrorContext.instance().reset();
88
}
89
}
90
91
targetConfiguration.setEnvironment(new Environment(this.environment,
92
this.transactionFactory == null ? new SpringManagedTransactionFactory() : this.transactionFactory,
93
this.dataSource));
94
95
if (this.mapperLocations != null) {
96
if (this.mapperLocations.length == 0) {
97
LOGGER.warn(() -> "Property 'mapperLocations' was specified but matching resources are not found.");
98
} else {
99
for (Resource mapperLocation : this.mapperLocations) {
100
if (mapperLocation == null) {
101
continue;
102
}
103
try {
104
XMLMapperBuilder xmlMapperBuilder = new XMLMapperBuilder(mapperLocation.getInputStream(),
105
targetConfiguration, mapperLocation.toString(), targetConfiguration.getSqlFragments());
106
xmlMapperBuilder.parse();
107
} catch (Exception e) {
108
throw new NestedIOException("Failed to parse mapping resource: '" + mapperLocation + "'", e);
109
} finally {
110
ErrorContext.instance().reset();
111
}
112
LOGGER.debug(() -> "Parsed mapper file: '" + mapperLocation + "'");
113
}
114
}
115
} else {
116
LOGGER.debug(() -> "Property 'mapperLocations' was not specified.");
117
}
118
119
return this.sqlSessionFactoryBuilder.build(targetConfiguration);
120
}
SqlSessionFactoryBean
中。其属性有:configLocation 、
objectFactory 、objectWrapperFactory 、typeAliasesPackage 、typeAliases 、typeHandlersPackage 、plugins 、typeHandlers 、transactionFactory 、databaseldProvider 、mapperLocations 。
获取 SqlSessionFactoryBean 实例
由于 SqlSessionFactoryBean 实现了FactoryBean 接口,所以使用getBean 方法时,获取的是该类的getObject() 方法返回的对象。也就是获取初始化后的sqlSessionFactory 属性。
1 | //SqlSessionFactoryBean implements FactoryBean |
2 | public SqlSessionFactory getObject() throws Exception { |
3 | if (this.sqlSessionFactory == null) { |
4 | afterPropertiesSet(); |
5 | } |
6 | |
7 | return this.sqlSessionFactory; |
8 | } |
MapperFactoryBean 的创建
mybatis 和spring 获取映射对象实例:
1 | //mybatis |
2 | UserMapper userMapper = sqlSession.getMapper(UserMapper.class ); |
3 | //spring |
4 | UserMapper userMapper = (UserMapper)context.getBean("userMapper"); |
spring 在使用映射类(UserMapper.class)生成映射bean时,一定是使用了Mybatis 原生的方式.
- 查看mapperFactoryBean 的层次结构:
MapperFactoryBean 实现了 InitialingBean 和FactoryBean 接口.
MapperFactoryBean 的初始化
- 初始化逻辑在其父类DaoSupport 中实现的
1
//DaoSupport
2
3
public final void afterPropertiesSet() throws IllegalArgumentException, BeanInitializationException {
4
// Let abstract subclasses check their configuration.
5
checkDaoConfig();
6
7
// Let concrete implementations initialize themselves.
8
try {
9
//模板方法,留给子类去实现
10
initDao();
11
}
12
catch (Exception ex) {
13
throw new BeanInitializationException("Initialization of DAO failed", ex);
14
}
15
}
- checkDaoConfig()
1
//MapperFactoryBean
2
3
protected void checkDaoConfig() {
4
super.checkDaoConfig();
5
6
notNull(this.mapperInterface, "Property 'mapperInterface' is required");
7
8
Configuration configuration = getSqlSession().getConfiguration();
9
if (this.addToConfig && !configuration.hasMapper(this.mapperInterface)) {
10
try {
11
configuration.addMapper(this.mapperInterface);
12
} catch (Exception e) {
13
logger.error("Error while adding the mapper '" + this.mapperInterface + "' to configuration.", e);
14
throw new IllegalArgumentException(e);
15
} finally {
16
ErrorContext.instance().reset();
17
}
18
}
19
}
- sqlSessionTemplate 依据接口创建创建映射器代理的接触类一般不可能为空. 而sqlSessionTemplate的初始化是:
super.checkDaoConfig() 验证 sqlSessionTemplate 不能为空 1
//SqlSessionDaoSupport
2
3
protected void checkDaoConfig() {
4
notNull(this.sqlSessionTemplate, "Property 'sqlSessionFactory' or 'sqlSessionTemplate' are required");
5
}
从以上代码可以看出,如果sqlSessionFactory 的配置有问题,就可以在此处体现出来.1
//SqlSessionDaoSupport
2
public void setSqlSessionFactory(SqlSessionFactory sqlSessionFactory) {
3
if (this.sqlSessionTemplate == null || sqlSessionFactory != this.sqlSessionTemplate.getSqlSessionFactory()) {
4
this.sqlSessionTemplate = createSqlSessionTemplate(sqlSessionFactory);
5
}
6
}
7
protected SqlSessionTemplate createSqlSessionTemplate(SqlSessionFactory sqlSessionFactory) {
8
return new SqlSessionTemplate(sqlSessionFactory);
9
}
1
<bean id="userMapper" class="org.mybatis.Spring.mapper.MapperFactoryBean ">
2
<property name="mapperinterface" value="test.mybatis.dao.UserMapper"></property>
3
<property name = "sqlSessionFactory" ref = "sqlSessionFactory" ></property>
4
</bean>
- 映射接口的验证
接口时映射的基础,sqlSession会依据接口创建代理类.映射接口必不可少. - 验证文件的存在性
在MyBatis 实现过程中并没有于动调用configuration.addMapper 方法,而是在映射文件读取过程中一旦解析到如
<mapper namespace="mapper.UserMapper">
,便会自动进行类型映射的注册。在上面的函数中, configuration.addMapper(this.mapperInterface)其实就是将UserMapper 注册到映射类型中.,如果你可以保证这个接口一定存在对应的映射文件,那么其实这个验证并没有必要。但是,由于这个是我们自行决定的配置,无法保证这里配置的接口一定存在对应的映射文件,所以这里非常有必要进行验证。在执行此代码的时候, MyBatis 会检查嵌入的映射接口是否存在对应的映射文件,如果没有回抛出异常, Spring 正是在用这种方式来完成接口对应的映射文件存在性验证。
获取 MapperFactoryBean
1 | //MapperFactoryBean implements BeanFactory |
2 |
|
3 | public T getObject() throws Exception { |
4 | return getSqlSession().getMapper(this.mapperInterface); |
5 | } |
spring 通过封装 myBatis 获取 映射代理类的方式获取映射bean
MapperScannerConfigurer
1 | <bean class= "org.mybatis.Spring.mapper.MapperScannerConfigurer"> |
2 | <property name= "basePackage" value= " test.mybatis.dao"/> |
3 | </bean> |
我们可以使用 MapperScannerConfigurer 来扫描指定包下的映射器接口,代替配置文件的方式,如下:
1 | <beans xmlns="http://www.springframework.org/schema/beans" |
2 | xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" |
3 | xmlns:context="http://www.springframework.org/schema/context" xmlns:tx="http://www.springframework.org/schema/tx" |
4 | xmlns:mybatis="http://mybatis.org/schema/mybatis-spring" |
5 | xsi:schemaLocation="http://www.springframework.org/schema/beans |
6 | http://www.springframework.org/schema/beans/spring-beans.xsd |
7 | http://www.springframework.org/schema/context |
8 | http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd http://mybatis.org/schema/mybatis-spring http://mybatis.org/schema/mybatis-spring.xsd"> |
9 | </beans> |
10 | <bean id="dataSource" class = "org.apache.comrnons.dbcp.BasicDataSource"> |
11 | <property name= "driverClassNarne" value="com.mysql.jdbc.Driver"><property> |
12 | <property name= "url" value="jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=UTF8&zeroDateTimeBehavior=convertToNull"></property> |
13 | <property name="username " value="root "></property> |
14 | <property name= "password" value="hao] ia042lxixi "></property> |
15 | <property name="maxActive" value = "100"></property> |
16 | <property name= "maxldle" value= "30"></property> |
17 | <property name="maxWait" value="500"></property> |
18 | <property name="defaultAutoCommit" value= "true"></property> |
19 | </bean> |
20 | <bean id="sqlSessionFactory" class="org mybatis.Spring.SqlSessionFactoryBean"> |
21 | <property narne="configLocation" value="classpath : test/rnybatis/MyBatis-Configuration.xml"></property> |
22 | <property name= "dataSource" ref= "dataSource"/> |
23 | <property name="typeAliasesPackage" value ="aaaaa" /> |
24 | </bean> |
25 | |
26 | <!--注释掉原有代码 |
27 | <bean id="userMapper" class = "org.mybatis.Spring.mapper.MapperFactoryBean"> |
28 | <property name="mapperInterface " value="test.mybatis.dao.UserMapper"></property> |
29 | <property name= "sqlSessionFactory" ref="sqlSessionFactory"></property> |
30 | </bean> |
31 | --> |
32 | <bean class= "org.mybatis.Spring.mapper.MapperScannerConfigurer"> |
33 | <property name= "basePackage" value= " test.mybatis.dao"/> |
34 | </bean> |
- 屏蔽掉了原始的代码( userMapper 的创建)而增加了MapperScannerConfigurer 的配置, basePackage 属性是让你为映射器接口文件设置基本的包路径。可以使用分号或逗号作为分隔符设置多于一个的包路径。每个映射器将会在指定的包路径中递归地被搜索到。
- 被发现的映射器将会使用Spring 对自动侦测组件默认的命名策略来命名。也就是说,如果没有发现注解,它就会使用映射器的非大写的非完全限定类名。
- 但是如果发现了@Component或JSR-330 @Named 注解,它会获取名称
MapperScannerConfigurer的类结构
- InitalizingBean#afterPropettiesSet 初始化逻辑未作任何处理,只做了basePackage简单的非空校验
- BeanFactoryPostProcessor#postProcessBeanFactory 没有任何逻辑
- BeanDefinitionRegistryPostProcessor#postProcessBeanDefinitionRegistry 核心逻辑所在地
1
//MapperScannerConfigurer implements BeanDefinitionRegistryPostProcessor
2
3
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) {
4
if (this.processPropertyPlaceHolders) {
5
//配置属性的处理
6
processPropertyPlaceHolders();
7
}
8
9
ClassPathMapperScanner scanner = new ClassPathMapperScanner(registry);
10
scanner.setAddToConfig(this.addToConfig);
11
scanner.setAnnotationClass(this.annotationClass);
12
scanner.setMarkerInterface(this.markerInterface);
13
scanner.setSqlSessionFactory(this.sqlSessionFactory);
14
scanner.setSqlSessionTemplate(this.sqlSessionTemplate);
15
scanner.setSqlSessionFactoryBeanName(this.sqlSessionFactoryBeanName);
16
scanner.setSqlSessionTemplateBeanName(this.sqlSessionTemplateBeanName);
17
scanner.setResourceLoader(this.applicationContext);
18
scanner.setBeanNameGenerator(this.nameGenerator);
19
scanner.setMapperFactoryBeanClass(this.mapperFactoryBeanClass);
20
if (StringUtils.hasText(lazyInitialization)) {
21
scanner.setLazyInitialization(Boolean.valueOf(lazyInitialization));
22
}
23
//注册过滤器
24
scanner.registerFilters();
25
//java 文件扫描
26
scanner.scan(
27
StringUtils.tokenizeToStringArray(this.basePackage, ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS));
28
}
processPropertyPlaceHolders(); 属性处理
- BeanDefinitionRegistries 会在应用启动的时候调用,并且会早于BeanFactoryPostProcessors 的调用,这就意味着PropertyResourceConfigurers 还没有被加载所有对于属性文件的引用将会失效.为避免此种情况发生,此方法手动地找出定义的PropertyResourceConfigurers 并进行提前调用以保证对于属性的引用可以正常工作。
1
//BeanDefinitionRegistryPostProcessor
2
private void processPropertyPlaceHolders() {
3
Map<String, PropertyResourceConfigurer> prcs = applicationContext.getBeansOfType(PropertyResourceConfigurer.class);
4
5
if (!prcs.isEmpty() && applicationContext instanceof ConfigurableApplicationContext) {
6
BeanDefinition mapperScannerBean = ((ConfigurableApplicationContext) applicationContext).getBeanFactory()
7
.getBeanDefinition(beanName);
8
9
// PropertyResourceConfigurer does not expose any methods to explicitly perform
10
// property placeholder substitution. Instead, create a BeanFactory that just
11
// contains this mapper scanner and post process the factory.
12
DefaultListableBeanFactory factory = new DefaultListableBeanFactory();
13
factory.registerBeanDefinition(beanName, mapperScannerBean);
14
15
for (PropertyResourceConfigurer prc : prcs.values()) {
16
prc.postProcessBeanFactory(factory);
17
}
18
19
PropertyValues values = mapperScannerBean.getPropertyValues();
20
21
this.basePackage = updatePropertyValue("basePackage", values);
22
this.sqlSessionFactoryBeanName = updatePropertyValue("sqlSessionFactoryBeanName", values);
23
this.sqlSessionTemplateBeanName = updatePropertyValue("sqlSessionTemplateBeanName", values);
24
this.lazyInitialization = updatePropertyValue("lazyInitialization", values);
25
}
26
this.basePackage = Optional.ofNullable(this.basePackage).map(getEnvironment()::resolvePlaceholders).orElse(null);
27
this.sqlSessionFactoryBeanName = Optional.ofNullable(this.sqlSessionFactoryBeanName)
28
.map(getEnvironment()::resolvePlaceholders).orElse(null);
29
this.sqlSessionTemplateBeanName = Optional.ofNullable(this.sqlSessionTemplateBeanName)
30
.map(getEnvironment()::resolvePlaceholders).orElse(null);
31
this.lazyInitialization = Optional.ofNullable(this.lazyInitialization).map(getEnvironment()::resolvePlaceholders)
32
.orElse(null);
33
}
- 找到所有已经注册的PropertyResourceConfigurer 类型的bean
- 模拟Spring 中的环境来用处理器。这里通过使用new DefaultListableBeanFactory()来模拟Spring 中的环境(完成处理器的调用后便失效),将映射的bean,也就是MapperScannerConfigurer 类型bean 注册到环境中来进行后理器的调用,处理器PropertyPlaceholderConfigurer调用完成的功能,再将模拟bean 中相关的属性提取出来应用在真实的bean 中。
根据配置属性生成过滤器 scanner.registerFilters();
1 | //ClassPathMapperScanner |
2 | public void registerFilters() { |
3 | boolean acceptAllInterfaces = true; |
4 | |
5 | // if specified, use the given annotation and / or marker interface |
6 | if (this.annotationClass != null) { |
7 | addIncludeFilter(new AnnotationTypeFilter(this.annotationClass)); |
8 | acceptAllInterfaces = false; |
9 | } |
10 | |
11 | // override AssignableTypeFilter to ignore matches on the actual marker interface |
12 | if (this.markerInterface != null) { |
13 | addIncludeFilter(new AssignableTypeFilter(this.markerInterface) { |
14 | |
15 | protected boolean matchClassName(String className) { |
16 | return false; |
17 | } |
18 | }); |
19 | acceptAllInterfaces = false; |
20 | } |
21 | |
22 | if (acceptAllInterfaces) { |
23 | // default include filter that accepts all classes |
24 | addIncludeFilter((metadataReader, metadataReaderFactory) -> true); |
25 | } |
26 | |
27 | // exclude package-info.java |
28 | addExcludeFilter((metadataReader, metadataReaderFactory) -> { |
29 | String className = metadataReader.getClassMetadata().getClassName(); |
30 | return className.endsWith("package-info"); |
31 | }); |
32 | } |
annotationClass 属性处理
如果annotationClass 不为空,表示用户设置了此属性,那么就要根据此属性生成过滤器以保证达到用户想要的效果,而封装此属性的过滤器就是AnnotationTypeFilter. AnnotationTypeFilter 保证在扫描对应Java 文件时只接受标记有注解为annotationClass 的接口.
markerlnterface 属性处理
如果markerInterface 不为空,表示用户设置了此属性,那么就要根据此属性生成过滤器以保证达到用户想要的效果,而封装此属性的过滤器就是实现AssignableTypeFilter 接口的局部类。表示扫描过程中只有实现markerInterface 接口的接口才会被接受。
全局默认处理。
在上面两个属性中如果存在其中任何属性, acceptAllinterfaces 的值将会变改变,但是如果用户没有设定以上两个属性,那么, Spring 会为我们增加一个默认的过滤器实现TypeFilter 接口的局部类,旨在接受所有接口文件。
package-info.java 处理。
对于命名以package-info 结尾的Java 文件,默认不作为逻辑实现接口,将其排除掉,使用TypeFilter 接口的局部类实现match 方法。
扫描java 文件
1 | //ClassPathBeanDefinitionScanner |
2 | public int scan(String... basePackages) { |
3 | int beanCountAtScanStart = this.registry.getBeanDefinitionCount(); |
4 | |
5 | doScan(basePackages); |
6 | |
7 | // Register annotation config processors, if necessary. |
8 | if (this.includeAnnotationConfig) { |
9 | AnnotationConfigUtils.registerAnnotationConfigProcessors(this.registry); |
10 | } |
11 | |
12 | return (this.registry.getBeanDefinitionCount() - beanCountAtScanStart); |
13 | } |
14 | |
15 | //ClassPathMapperScanner |
16 | public Set<BeanDefinitionHolder> doScan(String... basePackages) { |
17 | Set<BeanDefinitionHolder> beanDefinitions = super.doScan(basePackages); |
18 | |
19 | if (beanDefinitions.isEmpty()) { |
20 | LOGGER.warn(() -> "No MyBatis mapper was found in '" + Arrays.toString(basePackages) |
21 | + "' package. Please check your configuration."); |
22 | } else { |
23 | processBeanDefinitions(beanDefinitions); |
24 | } |
25 | |
26 | return beanDefinitions; |
27 | } |
28 | //ClassPathBeanDefinitionScanner |
29 | protected Set<BeanDefinitionHolder> doScan(String... basePackages) { |
30 | Assert.notEmpty(basePackages, "At least one base package must be specified"); |
31 | Set<BeanDefinitionHolder> beanDefinitions = new LinkedHashSet<>(); |
32 | for (String basePackage : basePackages) { |
33 | //扫描basePackage 路径下面的Java文件 |
34 | Set<BeanDefinition> candidates = findCandidateComponents(basePackage); |
35 | for (BeanDefinition candidate : candidates) { |
36 | ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(candidate); |
37 | candidate.setScope(scopeMetadata.getScopeName()); |
38 | String beanName = this.beanNameGenerator.generateBeanName(candidate, this.registry); |
39 | if (candidate instanceof AbstractBeanDefinition) { |
40 | postProcessBeanDefinition((AbstractBeanDefinition) candidate, beanName); |
41 | } |
42 | if (candidate instanceof AnnotatedBeanDefinition) { |
43 | AnnotationConfigUtils.processCommonDefinitionAnnotations((AnnotatedBeanDefinition) candidate); |
44 | } |
45 | if (checkCandidate(beanName, candidate)) { |
46 | BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(candidate, beanName); |
47 | definitionHolder = |
48 | AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry); |
49 | beanDefinitions.add(definitionHolder); |
50 | registerBeanDefinition(definitionHolder, this.registry); |
51 | } |
52 | } |
53 | } |
54 | return beanDefinitions; |
55 | } |
56 | public Set<BeanDefinition> findCandidateComponents(String basePackage) { |
57 | if (this.componentsIndex != null && indexSupportsIncludeFilters()) { |
58 | return addCandidateComponentsFromIndex(this.componentsIndex, basePackage); |
59 | } |
60 | else { |
61 | return scanCandidateComponents(basePackage); |
62 | } |
63 | } |
64 | |
65 | private Set<BeanDefinition> scanCandidateComponents(String basePackage) { |
66 | Set<BeanDefinition> candidates = new LinkedHashSet<>(); |
67 | try { |
68 | String packageSearchPath = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX + |
69 | resolveBasePackage(basePackage) + '/' + this.resourcePattern; |
70 | Resource[] resources = getResourcePatternResolver().getResources(packageSearchPath); |
71 | boolean traceEnabled = logger.isTraceEnabled(); |
72 | boolean debugEnabled = logger.isDebugEnabled(); |
73 | for (Resource resource : resources) { |
74 | if (traceEnabled) { |
75 | logger.trace("Scanning " + resource); |
76 | } |
77 | if (resource.isReadable()) { |
78 | try { |
79 | MetadataReader metadataReader = getMetadataReaderFactory().getMetadataReader(resource); |
80 | if (isCandidateComponent(metadataReader)) { |
81 | ScannedGenericBeanDefinition sbd = new ScannedGenericBeanDefinition(metadataReader); |
82 | sbd.setResource(resource); |
83 | sbd.setSource(resource); |
84 | if (isCandidateComponent(sbd)) { |
85 | |
86 | candidates.add(sbd); |
87 | } |
88 | } |
89 | |
90 | }catch (Throwable ex) { |
91 | throw new BeanDefinitionStoreException( |
92 | "Failed to read candidate component class: " + resource, ex); |
93 | } |
94 | } |
95 | } |
96 | } |
97 | catch (IOException ex) { |
98 | throw new BeanDefinitionStoreException("I/O failure during classpath scanning", ex); |
99 | } |
100 | return candidates; |
101 | } |
102 | //ClassPathScanningCandidateComponentProvider |
103 | // 使用到了之前注册的过滤器 |
104 | protected boolean isCandidateComponent(MetadataReader metadataReader) throws IOException { |
105 | for (TypeFilter tf : this.excludeFilters) { |
106 | if (tf.match(metadataReader, getMetadataReaderFactory())) { |
107 | return false; |
108 | } |
109 | } |
110 | for (TypeFilter tf : this.includeFilters) { |
111 | if (tf.match(metadataReader, getMetadataReaderFactory())) { |
112 | return isConditionMatch(metadataReader); |
113 | } |
114 | } |
115 | return false; |
116 | } |