加深了解springboot(一)

本文深入探讨SpringBoot的自动配置机制,包括版本依赖管理、场景启动器、配置文件注入及解析、Profile文件应用、自动配置原理等内容,为开发者提供全面的技术指南。

最近在利用工作之余自学点东西,主要想长点见识。学习的方向主要是基础的springboot,以及springboot与各种数据库交互、队列的关联使用;spring基于注解开发;jvm的底层分析与代码优化的方向,高并发高可用场景模拟等。后续可能会接触一点机器学习与自然语言相关知识,不过这个后面再说吧。学习过程中自认为比较重要的东西会记录下来,以供日后参考使用。
还是想说一句,其实一切在springboot的官方说明文档上已经说明的很清楚了,多看看文档会有很大的帮助(中文文档总感觉是机翻)。另外此文章不太适合0基础的小伙伴。

springboot

我认为springboot的核心是自动装配,且在满足自动装配的同时也支持高度定制化,定制化的过程也不复杂。后面就学到哪说到哪吧

  • springboot jar版本管理中心
    在pom.xml中可以发现,有个父项目是
 <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.3.2.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>

而点进这个父项目

<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-dependencies</artifactId>
    <version>2.3.2.RELEASE</version>
  </parent>
<properties>
    <activemq.version>5.15.13</activemq.version>
    <antlr2.version>2.7.7</antlr2.version>
    <appengine-sdk.version>1.9.81</appengine-sdk.version>
    <artemis.version>2.12.0</artemis.version>
    <aspectj.version>1.9.6</aspectj.version>
    <assertj.version>3.16.1</assertj.version>
    <atomikos.version>4.0.6</atomikos.version>
    <awaitility.version>4.0.3</awaitility.version>
    <bitronix.version>2.1.4</bitronix.version>
    <build-helper-maven-plugin.version>3.1.0</build-helper-maven-plugin.version>
    <byte-buddy.version>1.10.13</byte-buddy.version>
    <caffeine.version>2.8.5</caffeine.version>
    <cassandra-driver.version>4.6.1</cassandra-driver.version>
    <classmate.version>1.5.1</classmate.version>
    <commons-codec.version>1.14</commons-codec.version>
    <commons-dbcp2.version>2.7.0</commons-dbcp2.version>
    <commons-lang3.version>3.10</commons-lang3.version>
    <commons-pool.version>1.6</commons-pool.version>
    <commons-pool2.version>2.8.0</commons-pool2.version>
    <couchbase-client.version>3.0.6</couchbase-client.version>
    <db2-jdbc.version>11.5.4.0</db2-jdbc.version>
    <dependency-management-plugin.version>1.0.9.RELEASE</dependency-management-plugin.version>
    <derby.version>10.14.2.0</derby.version>
    ...多余的我就不一一列举了

可以发现真正管理springboot的各种版本依赖的其实是 spring-boot-dependencies,这里是springboot的版本仲裁中心,以后导入依赖默认是不需要写版本的,没有在dependencies里面管理的依赖还是需要声明版本号。这已经可以避免掉很多的版本冲突情况了。

  • springboot 场景启动器
 <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-data-elasticsearch</artifactId>
  </dependency>
  <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-data-jdbc</artifactId>
  </dependency>
  <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-data-redis</artifactId>
  </dependency>
  <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-jdbc</artifactId>
  </dependency>
  <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-web</artifactId>
  </dependency>

Spring Boot将大部分常用的功能场景都抽取出来,做成一个个的starters(启动器),只需要在项目里面引入这些starter相关场景的所有相关依赖都会导入进来。要用什么功能就导入什么场景的启动器。再加以简单配置,就可以直接使用。

  • springboot 配置文件
    配置文件名称固定 例如application-*.propertiesapplication-*.ymlapplication-*.yaml.需要注意的是,配置文件只有放在指定位置才生效,且高优先级同配置会覆盖低优先级,不同配置会互相组合。
    说到这里,我们来说个配置文件的注入吧。yml配置文件如下:
#这是配置文件
person: 
    lastName: hello
    age: 18
    boss: false
    birth: 2017/12/12
    maps: {k1: v1,k2: 12}
    lists:
      - lisi
      - zhaoliu
    dog:
      name: 小狗
      age: 12
/**
 * 将配置文件中配置的每一个属性的值,映射到这个组件中
 * @ConfigurationProperties:告诉SpringBoot将本类中的所有属性和配置文件中相关的配置进行绑定;
 *      prefix = "person":配置文件中哪个下面的所有属性进行一一映射
 * @Component:只有这个组件是容器中的组件,才能容器提供的@ConfigurationProperties功能;
 */
@Component
@ConfigurationProperties(prefix = "person")
public class Person {

    private String lastName;
    private Integer age;
    private Boolean boss;
    private Date birth;

    private Map<String,Object> maps;
    private List<Object> lists;
    private Dog dog;
<!--导入配置文件处理器,配置文件进行绑定就会有提示-->        
<dependency>            
	<groupId>org.springframework.boot</groupId>            
	<artifactId>spring-boot-configuration-processor</artifactId>            
	<optional>true</optional>        
</dependency>

也可通过@Value来进行配置文件的内容注入(@value使用方式不做赘述),我们可以来比较两者的特性:

@ConfigurationProperties@Value
功能批量注入配置文件中的属性一个个指定
松散绑定(松散语法)支持不支持
SpEL不支持支持
JSR303数据校验支持不支持
复杂类型封装(对象包对象)支持不支持

也可通过@PropertySource形式加载指定的配置文件使配置文件内容注入

//新建person.properties文件,在里面对实体类属性进行赋值
@PropertySource(value = {"classpath:person.properties"})
@Component
public class Person {

也可以通过使用spring配置文件的形式,使用@ImportResource使配置文件生效,不在此说明。

@ImportResource(locations = {"classpath:beans.xml"})

还可以配合@Configuration@Bean形式来给容器添加组件

/**
 * @Configuration:指明当前类是一个配置类;就是来替代之前的Spring配置文件,在配置文件中用<bean><bean/>标签添加组件
 */
@Configuration
public class MyAppConfig {

    //将方法的返回值添加到容器中;容器中这个组件默认的id就是方法名
    @Bean
    public HelloService helloService02(){
        System.out.println("配置类@Bean给容器中添加组件了...");
        return new HelloService();
    }
}
  • springboot Profile文件
    我们在主配置文件编写的时候,文件名可以是 application-{profile}.properties/yml。默认使用application.properties的配置;

yml支持多文档块方式,使用 — 分割

server:
  port: 8081
spring:
  profiles:
    active: prod
---
server:
  port: 8083
spring:
  profiles: dev
---
server:
  port: 8084
spring:
  profiles: prod  #指定属于哪个环境

激活指定profile(好像使用最多的是idea打包时选择哪个环境):
1、在配置文件中指定 spring.profiles.active=dev
2、命令行:java -jar xx.jar --spring.profiles.active=dev
​3、虚拟机参数:-Dspring.profiles.active=dev

  • springboot 配置文件加载位置与顺序

springboot 启动会扫描以下位置的application.properties或者application.yml文件作为Spring boot的默认配置文件

–file:./config/

–file:./

–classpath:/config/

–classpath:/

优先级由高到底,高优先级的配置会覆盖低优先级的配置;SpringBoot会从这四个位置全部加载主配置文件;互补配置
项目打包好以后,我们可以使用命令行参数的形式,启动项目的时候来指定配置文件的新位置;指定配置文件和默认加载的这些配置文件共同起作用形成互补配置;
java -jar xx.jar --spring.config.location=G:/application.properties
java -jar xx.jar --server.port=8087 --server.context-path=/abc
还有其它的方式来更改配置文件内容,详细的可以见官方文档

  • springboot 自动配置原理
    SpringBoot启动的时候加载主配置类,开启了自动配置功能@EnableAutoConfiguration
@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 {

@EnableAutoConfiguration作用:
org.springframework.boot:spring-boot-autoconfigure:2.3.2.RELEASEjar中有个EnableAutoConfiguration接口,其导入了AutoConfigurationImportSelector自动配置选择器

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {

这个类里面有个这个方法:

/**
	 * Return the auto-configuration class names that should be considered. By default
	 * this method will load candidates using {@link SpringFactoriesLoader} with
	 * {@link #getSpringFactoriesLoaderFactoryClass()}.
	 * @param metadata the source metadata
	 * @param attributes the {@link #getAttributes(AnnotationMetadata) annotation
	 * attributes}
	 * @return a list of candidate configurations
	 */
	protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
		List<String> configurations = SpringFactoriesLoader.loadFactoryNames(getSpringFactoriesLoaderFactoryClass(),
				getBeanClassLoader());
		Assert.notEmpty(configurations, "No auto configuration classes found in META-INF/spring.factories. If you "
				+ "are using a custom packaging, make sure that file is correct.");
		return configurations;
	}

SpringFactoriesLoader.loadFactoryNames()这个方法是扫描META-INF/spring.factories路径下所有的内容包装成properties对象,从properties中获取到EnableAutoConfiguration.class类(类名)对应的值,然后把他们添加在容器中。随意从spring.factories中看一个自动装配类(以RedisAutoConfiguration举例)

@Configuration(proxyBeanMethods = false)
//表示这是一个配置类,与编写的配置文件一样,也可以给容器中添加组件
@ConditionalOnClass(RedisOperations.class)
//Spring底层@Conditional注解(Spring注解版),根据不同的条件,如果满足指定的条件,整个配置类里面的配置就会生效;    
//判断当前是否有RedisOperations组件,如果有,当前配置类生效
@EnableConfigurationProperties(RedisProperties.class)
//启动指定类的ConfigurationProperties功能;将配置文件中对应的值和RedisProperties绑定起来;并把RedisProperties加入到ioc容器中
@Import({ LettuceConnectionConfiguration.class, JedisConnectionConfiguration.class })
public class RedisAutoConfiguration {

	@Bean
	//注册组件到容易,注册完成后,可以自动注入。
	@ConditionalOnMissingBean(name = "redisTemplate")
	//同为Conditional派生注解,详细规则下方有说明
	public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory)
			throws UnknownHostException {
		RedisTemplate<Object, Object> template = new RedisTemplate<>();
		template.setConnectionFactory(redisConnectionFactory);
		return template;
	}

	@Bean
	@ConditionalOnMissingBean
	public StringRedisTemplate stringRedisTemplate(RedisConnectionFactory redisConnectionFactory)
			throws UnknownHostException {
		StringRedisTemplate template = new StringRedisTemplate();
		template.setConnectionFactory(redisConnectionFactory);
		return template;
	}

}

再来看RedisProperties.class

@ConfigurationProperties(prefix = "spring.redis")
//从配置文件中获取指定的值和bean的属性进行绑定,一般情况下,这个类有哪些值,那么在配置文件里就能配置哪些值。额外的值除非手动注册,不然不生效
public class RedisProperties {

	/**
	 * Database index used by the connection factory.
	 */
	private int database = 0;

	/**
	 * Connection URL. Overrides host, port, and password. User is ignored. Example:
	 * redis://user:password@example.com:6379
	 */
	private String url;

	/**
	 * Redis server host.
	 */
	private String host = "localhost";

	/**
	 * Login password of the redis server.
	 */
	private String password;

	/**
	 * Redis server port.
	 */
	private int port = 6379;

	/**
	 * Whether to enable SSL support.
	 */
	private boolean ssl;

总结来说:
1)、SpringBoot启动会加载大量的自动配置类

2)、我们看我们需要的功能有没有SpringBoot默认写好的自动配置类;

3)、我们再来看这个自动配置类中到底配置了哪些组件;(只要我们要用的组件有,我们就不需要再来配置了)

4)、给容器中自动配置类添加组件的时候,会从properties类中获取某些属性。我们就可以在配置文件中指定这些属性的值;

再来看看Conditional派生注解

作用:必须是@Conditional指定的条件成立,才给容器中添加组件,配置配里面的所有内容才生效;

@Conditional扩展注解作用(判断是否满足当前指定条件)
@ConditionalOnJava系统的java版本是否符合要求
@ConditionalOnBean容器中存在指定Bean;
@ConditionalOnMissingBean容器中不存在指定Bean;
@ConditionalOnExpression满足SpEL表达式指定
@ConditionalOnClass系统中有指定的类
@ConditionalOnMissingClass系统中没有指定的类
@ConditionalOnSingleCandidate容器中只有一个指定的Bean,或者这个Bean是首选Bean
@ConditionalOnProperty系统中指定的属性是否有指定的值
@ConditionalOnResource类路径下是否存在指定资源文件
@ConditionalOnWebApplication当前是web环境
@ConditionalOnNotWebApplication当前不是web环境
@ConditionalOnJndiJNDI存在指定项

我们可以通过启用 debug=true属性;来让控制台打印自动配置报告,这样我们就可以很方便的知道哪些自动配置类生效

=========================
AUTO-CONFIGURATION REPORT
=========================


Positive matches:(自动配置类启用的)
-----------------

   DispatcherServletAutoConfiguration matched:
      - @ConditionalOnClass found required class 'org.springframework.web.servlet.DispatcherServlet'; @ConditionalOnMissingClass did not find unwanted class (OnClassCondition)
      - @ConditionalOnWebApplication (required) found StandardServletEnvironment (OnWebApplicationCondition)
        
    
Negative matches:(没有启动,没有匹配成功的自动配置类)
-----------------

   ActiveMQAutoConfiguration:
      Did not match:
         - @ConditionalOnClass did not find required classes 'javax.jms.ConnectionFactory', 'org.apache.activemq.ActiveMQConnectionFactory' (OnClassCondition)

   AopAutoConfiguration:
      Did not match:
         - @ConditionalOnClass did not find required classes 'org.aspectj.lang.annotation.Aspect', 'org.aspectj.lang.reflect.Advice' (OnClassCondition)
        

今天就写到这里吧,下次更新springboot的底层日志框架
在此声明下,我不是大神,我是按照自己的理解来写的,想到哪写到哪,目的是为了以后自己能回顾一些东西。大家仅供参考

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值