SpringBoot底层原理解读

目录

自动配置原理

@SpringBootApplication

描述注解

SpringApplicatin初始化方法

springBoot启动流程

启动器:starter

自定义starter

SpringBoot配置详解

配置文件分类

从配置文件中获取值

@ConfigurationProperties

简单类型、对象类型、对象集合、数组类型、map等分别用yml和properties两种写法赋值

SpringBoot配置切换

@Conditional条件注解


自动配置原理

@SpringBootApplication

作用:启动类上加的注解 ,这个类的同级别或者子包下面的组件才会被扫描到,是一个复合注解,等价于@Configuration(标明类为配置类),

@EnableAutoConfiguration(启动自动配置功能),

@ComponentScan(扫描注解)三个注解

描述注解

@Target

作用:用于修饰注解,该注解可以声明在哪些目标元素之前,也可以理解为注释类型的程序元素的种类

@Retention

作用:说明注解的生命周期以及作用域,有些注解是可以改变你代码运行结果的,有些注解不能(例如:@Override)

RetentionPolicy.SOURCE:只是在代码中有,编译后的class文件没有,所以不会造成任何影响(元注解)

RetentionPolicy.RUNTIME:对代码有影响(运行时注解)

@Documented:注解标注文档

@SpringBootConfiSguration:表示springboot启动类是一个配置类

@EnableAutoConfiguration:自动配置开关

@Import(AutoConfigurationImportSelector.class):自动导入SpringBoot自动配置类{

127个(最基本的SpringBoot的web项目+自己引入的jar)自动配置类(一个自动配置类等于一个application.xml文件,也就是说一个自动配置类中可能有多个bean),根据我们的需要保留自动自动配置类(工根据pom.xml中写的依赖),

}

流程:

1.它会读取所有的spring.factories文件,读取里面的自动配置类

2.它会继续读取POM依赖里面所包含有的自动配置类,一样是去读该对应包下的spring.factories文件找到里面EnableAutoConfiguration的key,读取所有的自动配置

3.一共127个自动配置类,但是当前程序没有那么多

4.继续筛选,先筛选Exclud排除的自动配置类

5.再根据pom.xml文件里的依赖,对比留下项目所需要的自动配置类

@ComponentScan:扫包,默认是扫描当前其同类同级别的包以及子包

SpringApplicatin初始化方法

SpringBoot启动的时候,会构造一个SpringApplication的实例,构造SpringApplication的时候会进行初始化,初始化的时候会进行以下几件事:

  1. 把参数sources设置到SpringApplication属性中,这个sources可以是任何类型的参数
  2. 判断是否是web程序,并设置到webEnvironment的boolean属性中
  3. 创建并初始化ApplicationInitializer中,设置到Initalizer属性中
  4. 创建并初始化ApplicationListener,设置到listeners属性中
  5. 初始化主类mainApplicationClas

总结:将以前的web.xml拉起框架,改为类方法拉起

springBoot启动流程

  1. 构建一个StopWatch计时器,用来记录SpringBoot的启动时间
  2. 初始化监听器,获取SpringApplicationRunListeners并启动监听,用于监听run方法的执行
  3. 创建并初始化ApplicationArguments,获取run方法传递的args参数
  4. 创建并初始化ConfigurableEnvironment(环境配置).封装main方法的参数,初始化参数,写入到Environment中,发布ApplicationEnvironmentPreparedEvent(环境事件),做一些绑定后返回Environment
  5. 打印banner版本
  6. 构造Spring容器(ApplicationContext)上下文.先填充Environment环境和设置的参数,如果application有设置beanNameGenerator(bean),resourceLoader(加载器)就将其注入到上下文中,调用初始化的切面,发布ApplicationContextInitalizedEvent(上下文初始化)时间.
  7. SpringApplicationRunListeners发布finish事件
  8. StopWatch计时器停止计时,日志打印总共启动的时间
  9. 发布SpringBoot程序已启动事件(started())
  10. 调用ApplicationRunner和CommandLineRunner
  11. 最后发布就绪事件ApplicationReadEvent,标志着SpringBoot可以处理就收的请求了(running())

注:先进run方法,随后进自动配置

启动器:starter

Starter是什么:

Starter可以理解为启动器,它包含了一系列可以集成到应用里面的依赖包,你可以一站式集成Spring及其他技术,而不需要到处找示例代码和依赖包,如你想使用SpringJPA访问数据库,只要加入spring-boot-starter-data-jpa启动器依赖就能使用了,Starters包含了许多项目中需要用到的依赖,它们能快速持续的运行,都是一系列得到支持的管理传递性依赖.

Starters命名:

Spring Boot官方的启动器都是以spring-boot-starter命名的,代表了一个特定的应用类型.第三方的启动器不能以spring-boot开头命名,它们都被Spring boot官方保留,一般一个第三方的应该这样命名,像mvbatis的mvbatis-spring-boot-starter.

自定义starter

以Web应用中常用的验证码为例,一般使用kaptcha类库

  1. 创建kaptcha-spring-boot-autoconfigure模块,修改pom.xml文件
<properties>
        <!--指定项目的源代码版本,此处代码版本为Java8-->
        <maven.compiler.source>8</maven.compiler.source>
          <!--指定项目的字节码版本,此处代码版本为Java8-->
        <maven.compiler.target>8</maven.compiler.target>
    </properties>
<dependencies>
  <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
  </dependency>
  <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-autoconfigure</artifactId>
  </dependency>
  <!--        方便IDE能够检测到该依赖中用到的配置属性,能够自动补全,
  其实就是在编译的时候在META-INF下面生成了一个spring-configuration-metadata.json文件-->
  <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-configuration-processor</artifactId>
    <optional>true</optional>
  </dependency>
  
  <!--        引入@Data等注解,可省略get,set方法-->
  <dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
  </dependency>
  <!--        验证码依赖-->
  <dependency>
    <groupId>com.github.penggle</groupId>
    <artifactId>kaptcha</artifactId>
    <version>2.3.2</version>
  </dependency>
</dependencies>
  1. 新建属性类,属性类接收配置文件中配置的属性,可以给属性提供默认值,如下代码
@Data  //自动生成getter,setter方法
@Component //实例化为一个bean,并启用spring的自动装配机制
 /*
 绑定配置属性到到一个类,
 (通常与application.properties,application.yml文件中的属性配合使用)
 prefix属性用来指定配置属性的前缀
 */
@ConfigurationProperties(prefix='kaptcha') 
public class KaptchaProperties{
    private int imageWidth = 100;  //宽度
    private int imageHeight = 40;  //高度
    private int fontSize = 32; //字体大小
    private String fontColor = "0,0,0"; //字符颜色
    private String charString = "0123456789";  //显示的字符
    private int charLength = 4;  //字符数
    private String noiseImpl = "com.google.code.kaptcha.impl.NoNoise"; //添加噪声的
    
}
  1. 新建配置类,只有Kaptcha类库添加后才需要配置,因而使用注解@ConditionalOnClass
@Configuration  //表明这是一个Spring配置类
@Import(KaptchaProperties.class) //导入类,该类包含了Katcha库的配置属性
/*
这个注解指定条件,在类路径中存在类的情况下,这个配置类才会生效
通常用于保证在某些类库或依赖存在才启用相关配置
*/
@ConditionalOnClass(KaptchaServlet.class)

public class KaptchaConfiguration {
    //属性类
    @Autowired
    private KaptchaProperties k;

    @Bean
    public Producer producer(){
        Properties props = new Properties();  //java中用于处理属性的类,通常用于管理配置信息
        //使用键值对的形式存储kaptcha库的配置信息
        props.setProperty(Constants.KAPTCHA_TEXTPRODUCER_CHAR_STRING, k.getCharString());
        props.setProperty(Constants.KAPTCHA_TEXTPRODUCER_FONT_COLOR, k.getFontColor());
        props.setProperty(Constants.KAPTCHA_NOISE_IMPL, k.getNoiseImpl());
        props.setProperty(Constants.KAPTCHA_TEXTPRODUCER_CHAR_LENGTH, "" + k.getCharLength());
        props.setProperty(Constants.KAPTCHA_TEXTPRODUCER_FONT_SIZE, "" + k.getFontSize());
        props.setProperty(Constants.KAPTCHA_IMAGE_HEIGHT, "" + k.getImageHeight());
        props.setProperty(Constants.KAPTCHA_IMAGE_WIDTH, "" + k.getImageWidth());
        //DefaultKaptcha为Kaptcha库的默认实现,用于生成验证码
        DefaultKaptcha kaptcha = new DefaultKaptcha(); 
        //在Kaptcha库中,Config类用于配置验证码的生成方式和外观等属性
        Config config = new Config(props);
        //使用setConfig方法将之前创建的config设置给kaptcha对象
        kaptcha.setConfig(config);
        //最后,返回配置好了的kaptcha对象
        return kaptcha;
    }

}
  1. 新建spring.factories
    1. 在resource目录新建META-INF目录,然后新建spring.factories
org.springframework.boot.autoconfigure.EnableAutoConfiguration=配置类在项目中的完全限定名
  1. 新建模块kaptcha-spring-boot-starter
    1. 新建一个Maven然后删除src,对应的starter项目不需要有src内容,修改pom.xml
 <properties>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
    </properties>
    <dependencies>
      <!--引入配置的kaptcha-spring-boot-autoconfigure-->
        <dependency>
            <groupId>org.bdqn</groupId>
            <artifactId>kaptcha-spring-boot-autoconfigure</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>
      <!--生成验证码的库-->
        <dependency>
            <groupId>com.github.penggle</groupId>
            <artifactId>kaptcha</artifactId>
            <version>2.3.2</version>
        </dependency>
    </dependencies>
  1. 安装两个模块到本地库

  1. 新建测试Maven项目,修改pom.xml文件导入配置的kaptcha-spring-boot-starter
<dependency>
	<groupId>org.bdqn</groupId>
	<artifactId>kaptcha-spring-boot-starter</artifactId>
	<version>1.0-SNAPSHOT</version>
</dependency>
  1. 新建启动类,配置Controller
@Autowired
private Producer producer;
@GetMapping("/getKaptcha")
public void getKaptcha(HttpSession session, HttpServletResponse response){
    //生成验证码文本
    String text = producer.createText();
    //使用验证码文本生成验证码图像
    BufferedImage image = producer.createImage(text);
    //将图片存入session作用域中
    session.setAttribute("kap",image);
    //设置响应内容为"image/png"
    response.setContentType("image/png");
    try{
        //将验证码写入响应的输出流
        OutputStream os = response.getOutputStream();
        ImageIO.write(image,"png",os);
    } catch (IOException e) {
        System.out.println("失败");
    }
}
  1. 测试

与传统的引入验证码依赖相比,自定义starter不需要去装配第三方Servlet,减少了配置类代码,也更便于修改Kaptcha的各项属性

SpringBoot配置详解

application.properties配置文件一共可以出现在如下四个位置

  1. 项目根目录下的config文件夹中(注意是工程的根目录下,不是模块的根目录下)
  2. 项目根目录下(注意是工程的根目录下,不是模块的根目录下)
  3. classpath下的config文件夹中
  4. classpath下

如果4个地方都存在配置文件,会取四个文件的并集,

如果配置出现冲突(出现相同的key),则优先级高的会覆盖优先级低的。

加载的优先级从1到4依次降低

配置文件分类

  • properties和yml的优先级(properties的优先级高)
  • bootstrap.properties

bootstrap.properties的优先级更高,但在单纯的springboot项目里是不起效果的sprngcloud分布式项目的时候才有用。

从配置文件中获取值

@ConfigurationProperties

  • 可以批量的将配置文件中的内容注入到对象中,支持松散绑定
@Component  //注册到IOC容器中
@Data //自动生成getter,setter方法
@ConfigurationProperties(prefix = "book")
public class Book {
    private String name;
    private String author;
}
book.name=三国演义
book.author=罗贯中
@SpringBootTest
public class BookTest {
    @Autowired
    Book book;

    @Test
    void testBook(){
        System.out.println(book);
    }
}
  • 使用@ConfigurationProperties和@Bean注解在配置类的Bean定义方法上
QianMo.spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
QianMo.spring.datasource.jdbcUrl=jdbc:mysql:///assay?serverTimezone=Asia/Shanghai
QianMo.spring.datasource.username=root
QianMo.spring.datasource.password=123456
@Configuration
public class DataSourceConfig {

    @Bean
    @ConfigurationProperties(prefix = "QianM.spring.datasource")
    DataSource dataSource(){
        return DataSourceBuilder.create().build();
    }

}

这便是将前缀"QianMo.spring.datasource"的属性,赋值给DataSource对应的属性值

@Configuration注解的配置类中通过@Bean注解在某个方法上将方法返回的对象定义为一个Bean,并使用配置文件中相应的属性初始化该Bean的属性

  • 使用@ConfigurationProperties注解普通类,然后通过在启动类中使用@EnableConfigurationProperties定义为bean
@Data
@ConfigurationProperties(prefix = "book")
public class Book {

    private String name;
    private String author;
}
@SpringBootApplication
@EnableConfigurationProperties(Book.class)
public class Ch501Application {

    public static void main(String[] args) {
        SpringApplication.run(Ch501Application.class, args);
    }

}

使用的时候直接依赖注入就ok @Autowired Book book;

简单类型、对象类型、对象集合、数组类型、map等分别用yml和properties两种写法赋值

@Data
@Component
@ConfigurationProperties("QianMO")
public class TestYml {
    private Book book;
    private List<Book> books;
    private String[] strs;
    private Map<String,Object> maps;
}
QianMO:
#对象
  book:
    name: 水浒传
    author: 施耐庵
    #list集合
  books:
    - name: 西游记
      author: xxx
    - name: 红楼梦
      author: yyyy
#  strs: ['aaa','bbb']
#数组
  strs:
    - 'aaa'
    - 'bbb'
#  maps: {'account': z3, 'realname': 张三}
#map集合
  maps:
    'account': ls
    'realname': 李四

yaml和properties写法自动转换网址

在线yaml转properties-在线properties转yaml-ToYaml.com

SpringBoot配置切换

application.properties一般分为三种:application.properties,application-dev.properties(开发者版本),applicaiton-pro.properties(生产阶段)

使用spring.profiles.active进行版本切换

#切换到生产阶段
spring.profiles.active=pro 

@Conditional条件注解

用于条件启用或者禁用@Configuration或则@Bean方法,例如:把默认数据源HikariDataSource换成Druid,那么默认的数据源HikariDataSource对应的bean就不能再配置,否则会出现两个数据源,因而某些bean是否需要注册到spring容器是有 条件的。

@ConditionalOnBean: 系统的ioc容器里如果存在某个bean的情况下才会实例化这个bean

@ConditionalOnClass: 系统中如果存在某个类的情况下,才会实例化一个Bean.

@ConditionalOnExpression: 当表达式为true的时候, 才会实例化一个Bean.

@ConditionalOnMissingBean: 系统的ioc容器里如果不存在某个bean的情况下才会实例化这个bean

@ConditionalOnMissingClass: 系统中如果不存在某个类的情况下,才会实例化一个Bean.

@ConditionalOnNotWebApplication: 不是web应用,才会实例化一个Bean。

@ConditionalOnBean: 当容器中有指定Bean的条件下进行实例化。

@ConditionalohMissingBean: 当容器里没有指定Bean的条件下进行实例化。

@ConditionalOnClass: 当classpath类路径下有指定类的条件下进行实例化。

@ConditionalOnMissingClass: 当类路径下没有指定类的条件下进行实例化。

@ConditionalOnWebApplication: 当项目是一个Web项目时进行实例化。

@ConditionalOnNotWebApplication: 当项目不是一个Web项目时进行实例化。

@ConditionalOnProperty:当指定的属性有指定的值时进行实例化。

@ConditionalOnExpression:基于SpEL表达式的条件判断。

@Condit ionalOnJava:当JVM版本为指定的版本范围时触发实例化。

@ConditionalOnResource:当类路径下有指定的资源时触发实例化。

@ConditionalOnJndi:在JNDI存在的条件下触发实例化。

  • 25
    点赞
  • 31
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值