springboot 自动装配、集成 mybatis 分析、自定义 springboot-start

参考 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());
	}
}

能输出结果,说明自动配置成功。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值