共计 5974 个字符,预计需要花费 15 分钟才能阅读完成。
前言
用过 springboot 的肯定很熟悉,它其中有个重要的特性,就是自动配置 (平时习惯的一些设置的配置作为默认配置)。springboot 提倡无 XML 配置文件的理念,使用 springboot 生成的应用完全不会生成任何配置代码与 XML 配置文件。下面先看一个 springboot 集成 mybatis 的例子。
第一步: 引入 pom 文件
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.0.1</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.47</version>
</dependency>
第二步: 因为我使用的 xml 配置文件去使用 mybatis,在 application.properties 文件加入如下配置:
# 指定 mapper 文件位置
mybatis.mapper-locations=classpath:mapper/*.xml
#数据源信息
spring.datasource.url=jdbc:mysql://127.0.0.1:3306/zplxjj?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=GMT%2B8
spring.datasource.username=root
spring.datasource.password=123456
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
第三步: 加入实体类、dao、mapper 文件
第四步:启动类上面加入注解
@SpringBootApplication
@MapperScan("com.stone.zplxjj.dao")
public class Application {public static void main(String[] args) {SpringApplication.run(Application.class, args);
}
}
第五步:至此,配置完成,只需要写个单侧,springboot 已经完美集成 mybatis
@RunWith(SpringRunner.class)
@SpringBootTest
public class ApplicationTests {
@Autowired
UserMapper userMapper;
@Test
public void testMybatis() {System.out.println(userMapper.selectByPrimaryKey(1L));
}
}
@EnableAutoConfiguration
通过上面的例子,我们发现集成 mybatis 特别简单,那些繁琐的类的注入都没有写,只需要加入数据库的一些配置即可,那这其中 @EnableAutoConfiguration 功不可没。@EnableAutoConfiguration 注解已经在 @SpringBootApplication 里面了
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(
excludeFilters = {@Filter(
type = FilterType.CUSTOM,
classes = {TypeExcludeFilter.class}
), @Filter(
type = FilterType.CUSTOM,
classes = {AutoConfigurationExcludeFilter.class}
)}
)
public @interface SpringBootApplication {
@AliasFor(annotation = EnableAutoConfiguration.class)
Class<?>[] exclude() default {};
@AliasFor(annotation = EnableAutoConfiguration.class)
String[] excludeName() default {};
@AliasFor(
annotation = ComponentScan.class,
attribute = "basePackages"
)
String[] scanBasePackages() default {};
@AliasFor(
annotation = ComponentScan.class,
attribute = "basePackageClasses"
)
Class<?>[] scanBasePackageClasses() default {};}
我们看到 @EnableAutoConfiguration 结构如下:
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import({AutoConfigurationImportSelector.class})
public @interface EnableAutoConfiguration {
String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";
Class<?>[] exclude() default {};
String[] excludeName() default {};}
这其中起作用的一个重要注解 @Import,这个 Spring 提供的一个注解,可以导入配置类或者 Bean 到当前类中,我们进入到 AutoConfigurationImportSelector 类中查看,方法太长,截取核心的两个方法:
public String[] selectImports(AnnotationMetadata annotationMetadata) {if (!this.isEnabled(annotationMetadata)) {return NO_IMPORTS;} else {AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader.loadMetadata(this.beanClassLoader);
AutoConfigurationImportSelector.AutoConfigurationEntry autoConfigurationEntry = this.getAutoConfigurationEntry(autoConfigurationMetadata, annotationMetadata);
return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
}
}
protected AutoConfigurationImportSelector.AutoConfigurationEntry getAutoConfigurationEntry(AutoConfigurationMetadata autoConfigurationMetadata, AnnotationMetadata annotationMetadata) {if (!this.isEnabled(annotationMetadata)) {return EMPTY_ENTRY;} else {AnnotationAttributes attributes = this.getAttributes(annotationMetadata);
List<String> configurations = this.getCandidateConfigurations(annotationMetadata, attributes);
configurations = this.removeDuplicates(configurations);
Set<String> exclusions = this.getExclusions(annotationMetadata, attributes);
this.checkExcludedClasses(configurations, exclusions);
configurations.removeAll(exclusions);
configurations = this.filter(configurations, autoConfigurationMetadata);
this.fireAutoConfigurationImportEvents(configurations, exclusions);
return new AutoConfigurationImportSelector.AutoConfigurationEntry(configurations, exclusions);
}
}
通过项目启动后,打上注解,可以看到 MybatisAutoConfiguration 引入了进来
而 MybatisAutoConfiguration 能引入进来,其实是在 mybatis-spring-boot-autoconfigure-2.0.1.jar 包里面的 spring.factories 指定的, 通过调用 SpringFactoriesLoader.loadFactoryNames()来扫描加载含有 META-INF/spring.factories 文件的 jar 包,从而标识哪些自动配置的类
# Auto Configure
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.mybatis.spring.boot.autoconfigure.MybatisAutoConfiguration
@Conditional
@Conditional 的作用,可以根据条件去加载特定的 bean,原理这边不做探讨,springboot 基于此实现了几个注解,比较方便的实现条件加载类
@ConditionalOnBean:Spring 容器中是否存在对应的实例
@ConditionalOnMissingBean:Spring 容器中是否缺少对应的实例
通过查看 MybatisAutoConfiguration 中的 SqlSessionFactory 的写法
@Bean
@ConditionalOnMissingBean
public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception {SqlSessionFactoryBean factory = new SqlSessionFactoryBean();
factory.setDataSource(dataSource);
factory.setVfs(SpringBootVFS.class);
if (StringUtils.hasText(this.properties.getConfigLocation())) {factory.setConfigLocation(this.resourceLoader.getResource(this.properties.getConfigLocation()));
}
this.applyConfiguration(factory);
if (this.properties.getConfigurationProperties() != null) {factory.setConfigurationProperties(this.properties.getConfigurationProperties());
}
if (!ObjectUtils.isEmpty(this.interceptors)) {factory.setPlugins(this.interceptors);
}
if (this.databaseIdProvider != null) {factory.setDatabaseIdProvider(this.databaseIdProvider);
}
if (StringUtils.hasLength(this.properties.getTypeAliasesPackage())) {factory.setTypeAliasesPackage(this.properties.getTypeAliasesPackage());
}
if (this.properties.getTypeAliasesSuperType() != null) {factory.setTypeAliasesSuperType(this.properties.getTypeAliasesSuperType());
}
if (StringUtils.hasLength(this.properties.getTypeHandlersPackage())) {factory.setTypeHandlersPackage(this.properties.getTypeHandlersPackage());
}
if (!ObjectUtils.isEmpty(this.properties.resolveMapperLocations())) {factory.setMapperLocations(this.properties.resolveMapperLocations());
}
return factory.getObject();}
结语
通过上面分析 mybatis 如何集成 springboot,知道了 springboot 入口在哪里以及如何实现的自动配置,这里只是简单的做了介绍,其中的一些源码和细节就没有分析了,我相信,入口知道了,接下来就好抠细节了。
本人也开通了微信公众号:stonezplxjj,更多文章欢迎关注公众号: