参考 https://blog.csdn.net/weixin_39854681/article/details/110859474
一、springboot 集成 mybatis
springboot 配置文件有两种 yml 和 properties,且 yml 的优先级高于 properties,yml 为树形
yml 如下
properties 如下
SpringBoot 默认会从 Resources 目录下加载 application.properties 或 application.yml,会先读取配置文件的 spring.profiles.active 属性,再确定加载什么环境的配置文件
1、配置 maven 依赖
<!-- Mybatis SpringBoot组件 -->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
</dependency>
2、application.yml配置文件
###服务器配置
server:
port: 9001
spring:
application:
name: user
###druid数据源配置
datasource:
name: dataSource
type: com.alibaba.druid.pool.DruidDataSource
driver-class-name: com.mysql.jdbc.Driver
url: jdbc:mysql://127.0.0.1:3306/imalvisc?useSSL=false
username: root
password: imalvisc
druid:
initial-size: 5
.................
###mybatis数据源配置
mybatis:
type-aliases-package: com.imalvisc.spring.mybatis.model
configuration:
map-underscore-to-camel-case: true
3、启动类
package com.imalvisc.spring.mybatis;
import com.imalvisc.spring.mybatis.mapper.UserInfoMapper;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;
@SpringBootApplication
@MapperScan(basePackages = {"com.imalvisc.spring.mybatis.mapper"})
public class Application {
public static void main(String[] args) {
ConfigurableApplicationContext applicationContext = SpringApplication.run(Application.class, args);
UserInfoMapper userInfoMapper = applicationContext.getBean(UserInfoMapper.class);
System.out.println(userInfoMapper.selectAll());
}
}
以上就已经将 springboot 集成 mybatis 环境搭建完成
二、原理分析
参考 E:\BaiduNetdiskDownload\SpringBoot基础.pdf
springboot 自动装配
@SpringBootApplication 主要由如下三个注解构成:
@SpringBootConfiguration:等同与@Configuration,既标注该类是Spring的一个配置类
@EnableAutoConfiguration:启用 SpringBoot 的自动配置机制
@ComponentScan:扫描被 @Component、@Service、@Controller 等注解的 bean,默认会扫描启动类所在的包下所有的类
@EnableAutoConfiguration 注意:spring boot 启动时查找中类路径的 META-INF/spring.factories 文件中找到 EnableAutoConfiguration 对应的所有的自动配置类,通过反射实例化为对象加载到 spring 容器中
【spring.factories 文件在 spring-boot-auto-configuration.jar 包内也有,以 key-value 键值对的形式存储,其中有一个 key 为 EnableAutoConfiguration ,它对应的 value 值都是一个个以 AutoConfiguration 结尾来命名的 xxxAutoConfiguration 自动配置类】
前面加载的所有自动配置类并不是都生效的,每一个 xxxAutoConfiguration 自动配置类都是在某些特定的条件之下才会生效。通过@ConditionOnxxx 注解实现的
常见的 @ConditionOnxxx 注解有以下几种:
@ConditionalOnBean:当容器里存在指定bean的条件下。
@ConditionalOnMissingBean:当容器里不存在指定bean的条件下。
@ConditionalOnClass:当类路径下有指定类的条件下。
@ConditionalOnMissingClass:当类路径下不存在指定类的条件下。
@ConditionalOnProperty:指定的属性是否有指定的值,比如
@EnableAutoConfiguration (SpringBoot 自动配置功能开启)
@Import(AutoConfigurationImportSelector.class) 导入了 AutoConfigurationImportSelector 类,
其中,该类下的 getCandidateConfigurations 方法下的 SpringFactoriesLoader.loadFactoryNames 方法的作用就是从 META-INF/spring.factories 文件中读取指定类对应的类名称列表
mybatis 自动装配
项目中引入了 mybatis-spring-boot-starter 依赖,所以间接引入了 mybatis-spring-boot-autoconfigure ,在 mybatis-spring-boot-autoconfigure 的类路径下,看到 META-INF 目录下有一个 spring.factories 文件,文件内容是个类似键值对的配置,key 是 EnableAutoConfiguration ,值是 mybatis 的配置类如下
注意: springboot 项目启动时,会扫描项目本身及依赖的【所有 jar 包】类路径下的 META-INF 目录的 spring.factories 文件,再根据文件内容中的【全限定类名】找到并加载执行该类,这就是 springboot 自动配置的真谛。
上述键值对的值中一个类是 MybatisAutoConfiguration
注解含义
// 相当于<beans>
@org.springframework.context.annotation.Configuration
// 当前classpath路径下面是否存在 SqlSessionFactory.class, SqlSessionFactoryBean.class,存在则
// 进行将当前配置装载到spring容器中
@ConditionalOnClass({ SqlSessionFactory.class, SqlSessionFactoryBean.class })
// 相当于 beanFactory.getBeanByType(javax.sql.DataSource.class)
@ConditionalOnBean(DataSource.class)
// 让用了 @ConfigurationProperties 注解的类生效并将该类注入到 IOC 容器中,交由 IOC 容器进行管理
// MybatisProperties 是 yml 中 mybatis 配置属性的实体映射,该类用了 @ConfigurationProperties
@EnableConfigurationProperties(MybatisProperties.class)
// 顺序在 DataSourceAutoConfiguration.class之后
@AutoConfigureAfter(DataSourceAutoConfiguration.class)
其他 Conditional 注解
@Conditional(TestCondition.class)
这句代码可以标注在类上面,表示该类下面的所有 @Bean 都会启用配置,也可以标注在方法上面,只是对该方法启用配置。
@ConditionalOnBean(仅仅在当前上下文中存在某个对象时,才会实例化一个 Bean)
@ConditionalOnClass(某个 class 位于类路径上,才会实例化一个 Bean)
@ConditionalOnSingleCandidate(DataSource.class) IOC 容器中只有一个指定的候选对象才起作用
@ConditionalOnExpression(当表达式为true的时候,才会实例化一个 Bean)
@ConditionalOnMissingBean(仅仅在当前上下文中不存在某个对象时,才会实例化一个 Bean)
@ConditionalOnMissingClass(某个 class 类路径上不存在的时候,才会实例化一个 Bean)
@ConditionalOnNotWebApplication(不是web应用)
这是一个 Configuration 类,@AutoConfigureAfter(DataSourceAutoConfiguration.class) 代表在DataSourceAutoConfiguration 类加载完后再加载,因为 mybatis 要依赖数据源配置,要在数据源配置完成后才进行 mybatis 配置。在 MybatisAutoConfiguration 类中,配置了 SqlSessionFactory类,所在到这里 mybatis 的环境就已经生效了。
@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()));
}
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());
}
SqlSessionFactory 配置完成后,剩下的就是扫描 mapper 接口并生成代理类存放到 IOC 容器中,这样就可以依赖注入 Mapper 了。
在 Application 启动类中,加上了 @MapperScan(basePackages = {“xxx”}) 注解, MapperScan 注解类上加上另一个注解 @Import(MapperScannerRegistrar.class),spring 监测到 @Import 注解时会加载其指定的配置类 MapperScannerRegistrar
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Import(MapperScannerRegistrar.class)
@Repeatable(MapperScans.class)
public @interface MapperScan {
}
MapperScannerRegistrar 实现了 ImportBeanDefinitionRegistrar 接口,该接口的作用是 spring 会调用实现该接口的 registerBeanDefinitions 方法,传入 AnnotationMetadata 和 BeanDefinitionRegistry 两个参数,AnnotationMetadata 的作用是封装了加上 @Import 注解的注解的属性(这里就是MapperScan的basePackages = {“xxx”}),
总结说明:就是 @MapperScan 注解加上了 @Import 注解, AnnotationMetadata 封装了@MapperScan 注解的 basePackages 属性的值。
MapperScannerRegistrar 的 registerBeanDefinitions 方法中,获取了 @MapperScan 注解的属性后,调用了自身的重载 registerBeanDefinitions 方法,重载 registerBeanDefinitions 方法通过调用ClassPathMapperScanner 的 doScan 方法完成了 Mapper 的扫描并生产动态代理加入到 spring 容器中。
三、自定义 springboot start
参考 https://blog.csdn.net/yiwuxia23/article/details/84584269
自定义 lijin-springboot-start-redis 替代官方的 spring-boot-data-redis 的功能(粗糙替代),能让程序读取我们自定义开头的配置文件。并且自动注册 bean 到 spring 的上下文中
完整的项目结构如下
1、新建一个 maven 项目
2、在 resources 目录下新建 META-INF 文件夹,建立 spring.factories ,并在里面输入
org.springframework.boot.autoconfigure.EnableAutoConfiguration=com.jin.lijin_springboot_start_redis.MyRedisAutoConfiguration
目的是让 spring 发现并加载你自己写的东西,你想让 spring 帮你做事情,总得打个招呼,让别人知道你在哪里吧。这是只定义了一个 AutoConfiguration 的情形,定义以 ,分隔
第一行是固定的,第二行起是自定义的,每行以斜杠结尾,表示后面还有,最后一行不需要末尾加斜杠。
3、由于需要读取自定义前缀的配置文件,所以定义一个实体类来接收。这里我们只定义两个参数,分别是redis的ip和port.能够连接到redis就行。
//接受默认配置文件中以 lijin 开头的数据
@ConfigurationProperties("lijin")
public class MyRedisProperties {
private String myIp;
private int myPort;
public String getMyIp() {
return myIp;
}
public void setMyIp(String myIp) {
this.myIp = myIp;
}
public int getMyPort() {
return myPort;
}
public void setMyPort(int myPort) {
this.myPort = myPort;
}
}
4、自定义 MyRedisAutoConfiguration,这个类完成组装bean的功能。
@Configuration
/*
* 当classpath中出现了Jedis这个类时,才会进行相应的配置。
* conditional 是有条件的 意思
*/
@ConditionalOnClass({ Jedis.class })
/*
* @ConfigurationProperties注解主要用来把properties配置文件转化为bean来使用的,
* 而@EnableConfigurationProperties注解的作用是@ConfigurationProperties注解生效。
* 如果只配置@ConfigurationProperties注解,在IOC容器中是获取不到properties配置文件转化的bean的。
*/
@EnableConfigurationProperties(MyRedisProperties.class)
public class MyRedisAutoConfiguration {
@Autowired
private MyRedisProperties properties;
@Bean
public Jedis myRedis() {
Jedis jedis = new Jedis(properties.getMyIp(), properties.getMyPort());
return jedis;
}
}
5、执行 mvn install 命令进行打包
检测是否能够成功应用在springboot中
在一个能正常运行的springboot项目中,pom.xml中添加依赖
<dependency>
<groupId>com.jin</groupId>
<artifactId>lijin-springboot-start-redis</artifactId>
<version>0.0.1-SNAPSHOT</version>
</dependency>
在application.properties文件中写入:
lijin.myIp=localhost
lijin.myPort=6379
注意这是我们自定义的前缀的配置项,除了我们自定的模块外,其他模块是不认识的。测试类如下:
@SpringBootApplication
public class DemoApplication implements CommandLineRunner {
//注入jedis
@Autowired
Jedis jedis;
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
@Override
public void run(String... args) throws Exception {
System.out.println(jedis.getClient().getHost());
System.out.println(jedis.getClient().getPort());
}
}
能输出结果,说明自动配置成功。