锋盈数科-知识库 Logo
首页
软件开发
计算机基础
Hello Halo
新手必读
关于本知识库
登录 →
锋盈数科-知识库 Logo
首页 软件开发 计算机基础 Hello Halo 新手必读 关于本知识库
登录
  1. 首页
  2. 软件开发
  3. 【SpringBoot】| Spring Boot 常见的底层注解剖析

【SpringBoot】| Spring Boot 常见的底层注解剖析

0
  • 软件开发
  • 发布于 2024-08-17
  • 0 次阅读
黄健
黄健

本文由 简悦 SimpRead 转码, 原文地址 blog.csdn.net

目录

一:Spring Boot 常见的底层注解

1. 容器功能

1.1 组件添加

方法一:使用 @Configuration 注解 +@Bean 注解

方法二:使用 @Configuration 注解 +@Import 注解

方法三:使用 @Configuration 注解 +@Conditional 注解

1.2 原生 xml 配置文件引入

@ImportResource 注解

1.3 配置绑定

方法一:@Component 注解 + @ConfigurationProperties 注解

方法二:@EnableConfigurationProperties 注解 + @ConfigurationProperties 注解

2. 自动配置原理入门

2.1 引导加载自动配置类 @SpringBootApplication

@EnableAutoConfiguration 注解

2.2 修改默认配置

2.3 最佳实践

3. 开发技巧

3.1 Lombok

3.2 dev-tools

3.3 Spring Initailizr(项目初始化向导)

图书推荐:《深入浅出 Java 虚拟机:JVM 原理与实战》

一:Spring Boot 常见的底层注解

为了后面能够深入的掌握 SpringBoot 的自动配置原理,这里就专门总结一下 SpringBoot 的一些底层注解是怎样完成相关的功能!

1. 容器功能

User 类

package com.zl.bean;
 
public class User {
    private String name;
    private int age;
 
    public User() {
    }
 
    public User(String name, int age) {
        this.name = name;
        this.age = age;
    }
 
    @Override
    public String toString() {
        return "User{" +
                " + name + '\'' +
                ", age=" + age +
                '}';
    }
 
    public String getName() {
        return name;
    }
 
    public void setName(String name) {
        this.name = name;
    }
 
    public int getAge() {
        return age;
    }
 
    public void setAge(int age) {
        this.age = age;
    }
}

Pet 类

package com.zl.bean;
 
public class Pet {
    private String name;
 
    public Pet() {
    }
    public Pet(String name) {
        this.name = name;
    }
 
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
 
    @Override
    public String toString() {
        return "Pet{" +
                " + name + '\'' +
                '}';
    }
}

1.1 组件添加

回顾 Spring:Spring 如何把上面的两个类纳入 Spring 容器管理!

在 resources 下创建一个 spring.xml,使用 Bean 标签

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
 
    <bean id="userBean" class="com.zl.bean.User">
        <property />
        <property  />
    </bean>
    <bean id="petBean" class="com.zl.bean.Pet">
        <property />
    </bean>
</beans>

使用 SpringBoot:使用 SpringBoot 中的注解来完成

方法一:使用 @Configuration 注解 +@Bean 注解

第一步:编写一个配置类 MyConfig

package com.zl.config;
 
import com.zl.bean.Pet;
import com.zl.bean.User;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
 
 
@Configuration // 告诉SpringBoot这是一个配置类
public class MyConfig {
    // @Bean注解:给容器添加组件,以方法名作为组件的id,返回的值就是组件容器的实例
    // @Bean("u"),表示不再以方法为名,自己定义名字
    @Bean
    public User user(){
        return new User("张三",18);
    }
    @Bean
    public Pet pet(){
        return new Pet("Tom");
    }
}

第二步:从容器中获取

package com.zl;
 
import com.zl.bean.User;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;
 
@SpringBootApplication
public class App {
    public static void main( String[] args ) {
        // 返回的就是IOC容器
        ConfigurableApplicationContext run = SpringApplication.run(App.class, args);
        // 从容器中获取(获取的是单列的)
        User user = run.getBean("user", User.class);
        System.out.println(user);
    }
}

剖析:

(1)使用 @Configuration 注解表示配置类,配置类里面使用 @Bean 标注在方法上给容器注册组件,默认也是单例的。

(2)配置类本身也是一个组件,也可以调用 getBean 方法获取到;从输出格式可以看出这个对象实际上是 CJLIB 代理对象。

(3)@Configuration 注解有一个 proxyBeanMethods 属性:代理 bean 的方法,默认的值是 true;表示外部无论对配置类中的这个组件注册方法调用多少次获取的都是之前注册容器中的单实例。

package com.zl;
 
import com.zl.bean.User;
import com.zl.config.MyConfig;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;
 
 
@SpringBootApplication
public class App {
    public static void main( String[] args ) {
        // 返回的就是IOC容器
        ConfigurableApplicationContext run = SpringApplication.run(App.class, args);
        // 从容器中获取组件
        User user = run.getBean("user", User.class);
        System.out.println(user);// User{name='张三', age=18}
        // 配置类本身也是一个组件,可以获取到(实际上获取到的是一个代理代理对象)
        MyConfig bean = run.getBean(MyConfig.class);
        System.out.println(bean); // com.zl.config.MyConfig$$EnhancerBySpringCGLIB$$8f51ff9f@5488b5c5
        // 获取到配置类就可以通过外部的方式调用里面的组件方法
        User user1 = bean.user();
        User user2 = bean.user();
        // 外部无论调用多少次,调用的都是容器中的,而不是重新创建的
        // 本质上相等的原因就是@Configuration注解的proxyBeanMethods属性,结果为true
        // 就是代理对象创建方法,SpringBoot会检查这个组件是否在容器当中
        System.out.println(user1==user2); // true
    }
}

Full 模式与 Lite 模式

(1)**Full 模式:**proxyBeanMethods = true,保证每个 @Bean 方法被调用多少次返回的组件都是单实例的;代理对象去调用。
(2)**Lite 模式:**proxyBeanMethods = false,每个 @Bean 方法被调用多少次返回的组件都是新创建的。
**注:**有组件依赖必须使用 Full 模式;其他默认是否 Lite 模式!

例:假如现在 User 有一个宠物 Pet,有组件依赖关系只能使用 Full 模式!

(1)重写 User 类:类中含有 Pet 属性,添加 set 和 get 方法,重写 toString 方法

// 添加Pet属性
    private Pet pet;
    // 添加set和get方法
    public Pet getPet() {
        return pet;
    }
    public void setPet(Pet pet) {
        this.pet = pet;
    }
    // 重写toString
    @Override
    public String toString() {
        return "User{" +
                " + name + '\'' +
                ", age=" + age +
                ", pet=" + pet +
                '}';
    }

(2)给 User 的 pet 属性赋值

package com.zl.config;
 
import com.zl.bean.Pet;
import com.zl.bean.User;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
 
@Configuration // 告诉SpringBoot这是一个配置类
public class MyConfig {
    // @Bean注解:给容器添加组件,以方法名作为组件的id,返回的值就是组件容器的实例
    // @Bean("u"),表示不再以方法为名,自己定义名字
    @Bean
    public User user(){
        User user = new User("张三", 18);
        // 给User中的pet属性赋值(通过下面的方法名)
        user.setPet(pet());
        return user;
    }
    @Bean
    public Pet pet(){
        return new Pet("Tom");
    }
}

(3)验证 User 中的 pet 属性和原来的 Pet 是否是同一个

package com.zl;
 
import com.zl.bean.Pet;
import com.zl.bean.User;
import com.zl.config.MyConfig;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;
 
@SpringBootApplication
public class App {
    public static void main( String[] args ) {
        // 返回的就是IOC容器
        ConfigurableApplicationContext run = SpringApplication.run(App.class, args);
        // 获取User中的pet
        User user = run.getBean("user", User.class);
        Pet userPet = user.getPet();
        // 获取Pet
        Pet pet = run.getBean("pet", Pet.class);
        // 判断两者是否是同一个
        System.out.println(userPet == pet); 
 
    }
}

Full 模式下(有依赖关系):是一个;Lite 模式下(无依赖关系):不是同一个!

配置类组件之间无依赖关系用 Lite 模式加速容器启动过程,减少判断,启动速度快!

配置类组件之间有依赖关系,方法会被调用得到之前单实例组件,用 Full 模式!

方法二:使用 @Configuration 注解 +@Import 注解 

**回顾:**前面我们已经学习了 @Componet、@Controller、@Service、@Repository、@Bean 注解结合组件扫描 @ComponentScan 注解也可以完成纳入 Spring 容器管理!

在配置类上使用 @Import 注解,通过源码分析发现它的参数是一个 Class 类型的数据

package org.springframework.context.annotation;
 
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
 
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Import {
    Class<?>[] value();
}

例如:把 User 类配置进去

此时已经把 User 纳入 Spring 容器管理,实际上此时已经加入两个 User 类,我们打印输出就可以发现!

package com.zl.config;
 
import com.zl.bean.Pet;
import com.zl.bean.User;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
 
@Configuration(proxyBeanMethods = true) // 告诉SpringBoot这是一个配置类
// 使用Import注解
@Import({User.class})
public class MyConfig {
    // @Bean注解:给容器添加组件,以方法名作为组件的id,返回的值就是组件容器的实例
    // @Bean("u"),表示不再以方法为名,自己定义名字
    @Bean
    public User user(){
        User user = new User("张三", 18);
        // 给User中的pet属性赋值
        user.setPet(pet());
        return user;
    }
    @Bean
    public Pet pet(){
        return new Pet("Tom");
    }
}

进行打印

实际上 @Bean 注解和 @Import 注解的作用相同:

第一个 User 类:@Bean 注解,事实上 key 是方法的方法名 ----》user;

第二个 User 类:@Import 注解,事实上 key 是本类的完整类名 ----》com.zl.bean.User;

package com.zl;
 
import com.zl.bean.Pet;
import com.zl.bean.User;
import com.zl.config.MyConfig;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;
 
@SpringBootApplication
public class App {
    public static void main( String[] args ) {
        // 返回的就是IOC容器
        ConfigurableApplicationContext run = SpringApplication.run(App.class, args);
        // 获取User组件Bean的名字
        String[] beanNamesForType = run.getBeanNamesForType(User.class);
        for (String s : beanNamesForType) {
            System.out.println(s); // com.zl.bean.User(完整类名) 和 user(方法名)
        }
    }
}
方法三:使用 @Configuration 注解 +@Conditional 注解 

按照条件装配:满足 Conditional 指定的条件,则进行组件注入!

Ctrl+h 打开 @Conditional 注解的继承树:

案例:现在要 User 中有 tom 组件,才让 User 注入

 此时配置类中并没有注入 tom

package com.zl.config;
 
import com.zl.bean.Pet;
import com.zl.bean.User;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
 
 
@Configuration(proxyBeanMethods = true) // 告诉SpringBoot这是一个配置类
 
public class MyConfig {
    @Bean
    public User user(){
        User user = new User("张三", 18);
        // 给User中的pet属性赋值
        user.setPet(pet());
        return user;
    }
    // @Bean("tom")没有注入tom
    public Pet pet(){
        return new Pet("Tom");
    }
}

容器中含有 User 但是没有 Pet

package com.zl;
 
import com.zl.bean.Pet;
import com.zl.bean.User;
import com.zl.config.MyConfig;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.Conditional;
 
@SpringBootApplication
 
public class App {
    public static void main( String[] args ) {
        // 返回的就是IOC容器
        ConfigurableApplicationContext run = SpringApplication.run(App.class, args);
        System.out.println(run.containsBean("user")); // true
        System.out.println(run.containsBean("tom")); // false
    }
}

需求:因为 User 是包含 Pet 的,所以如果 Pet 没有,User 也无法注入,此时就可以使用

注:当然这个注解也可以标注在方法上!

此时没有注入 tom,所以两个结果都是 false!

1.2 原生 xml 配置文件引入

@ImportResource 注解

这个注解是用来引入外部的 xml 文件的,例如:导入 Spring 的配置文件!

spring.xml

注:此时只是一个 xml,SpringBoot 是无法识别的,没有注入成功

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
 
    <bean id="userBean" class="com.zl.bean.User">
        <property />
        <property  />
    </bean>
    <bean id="petBean" class="com.zl.bean.Pet">
        <property />
    </bean>
</beans>

在配置类上使用 @ImportResource 注解进行导入

这个配置文件是在类文件下,所以格式是:classpath:xxx.xml!

从容器中取出这两个 Bean

package com.zl;
 
import com.zl.bean.Pet;
import com.zl.bean.User;
import com.zl.config.MyConfig;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.Conditional;
 
 
@SpringBootApplication
public class App {
    public static void main( String[] args ) {
        // 返回的就是IOC容器
        ConfigurableApplicationContext run = SpringApplication.run(App.class, args);
        // 从容器中取出引入的外部xml的Bean
        User userBean = run.getBean("userBean", User.class);
        System.out.println(userBean);
        Pet petBean = run.getBean("petBean", Pet.class);
        System.out.println(petBean);
    }
}

1.3 配置绑定

如何使用 Java 读取到 properties 文件中的内容,并且把它封装到 JavaBean 中,以供随时使用?

方法一:@Component 注解 + @ConfigurationProperties 注解

注:@Component 注解和 @ConfigurationProperties 注解都是写在当前要注入的类当中的!

@Component 注解是把 JavaBean 纳入 Spring 容器管理,@ConfigurationPropertues 注解是让 JavaBean 的属性与配置文件 applicatio.properties 中的属性建立关系,进行赋值!

application.properties 属性配置

// 端口号
server.port=9999
// 属性
mycar.brand=比亚迪
mycar.price=24

在 Car 类中引入属性

(1)首先在类中引入 @Component 注解,只有纳入容器中管理的组件,才可以使用 SpringBoot 提供的强大功能!

(2)@ConfigurationProperties 注解的 pefix 属性(也就是 value 属性别名)指定 application.properties 属性配置中的前缀 mycar 即可。

package com.zl.bean;
 
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
 
 
 
@Component
@ConfigurationProperties(prefix = "mycar")
public class Car {
    private String brand;
    private Integer price;
 
    @Override
    public String toString() {
        return "Car{" +
                "brand='" + brand + '\'' +
                ", price=" + price +
                '}';
    }
 
    public String getBrand() {
        return brand;
    }
 
    public void setBrand(String brand) {
        this.brand = brand;
    }
 
    public Integer getPrice() {
        return price;
    }
 
    public void setPrice(Integer price) {
        this.price = price;
    }
}

编写 Controller 进行访问,看属性是否已经赋值上

package com.zl.controler;
 
import com.zl.bean.Car;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
 
// @Controller
@RestController
public class AppController {
    // @ResponseBody
    // 前面已经自动纳入Spring容器管理了,这里自动注入
    @Autowired
    Car car;
    @RequestMapping("/car")
    public Car car(){
        return car;
    }
}

进行访问

方法二:@EnableConfigurationProperties 注解 + @ConfigurationProperties 注解

注:@EnableConfigurationProperties 注解是写在配置类当中的,@ConfigurationProperties 注解是写在当前要注入的类当中的!

@EnableConfigurationProperties 注解有两个作用:

①开启某个类的配置绑定功能;

②把某个类这个组件自动注入到容器中去;(相当于 @Component 注解的功能)

要注入的 Car 类

package com.zl.bean;
 
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
 
 
// @Component
@ConfigurationPropertie s(prefix = "mycar")
public class Car {
    private String brand;
    private Integer price;
 
    @Override
    public String toString() {
        return "Car{" +
                "brand='" + brand + '\'' +
                ", price=" + price +
                '}';
    }
 
    public String getBrand() {
        return brand;
    }
 
    public void setBrand(String brand) {
        this.brand = brand;
    }
 
    public Integer getPrice() {
        return price;
    }
 
    public void setPrice(Integer price) {
        this.price = price;
    }
}

配置类上的 @EnableConfigurationProperties 注解

package com.zl.config;
 
import com.zl.bean.Car;
import com.zl.bean.Pet;
import com.zl.bean.User;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.context.annotation.ImportResource;
 
 
@Configuration(proxyBeanMethods = true) 
// 参数是一个Class类型
@EnableConfigurationProperties(Car.class)
public class MyConfig {
  
    @Bean
    public User user(){
        User user = new User("张三", 18);
        // 给User中的pet属性赋值
        user.setPet(pet());
        return user;
    }
    @Bean("tom")
    public Pet pet(){
        return new Pet("Tom");
    }
}

进行访问

2. 自动配置原理入门

2.1 引导加载自动配置类 @SpringBootApplication

在启动类中有一个核心注解 @SpringBootApplication,这个注解是一个复合注解!

①@SpringBootConfiguration 注解本质上就是 @Configuration 注解,表明这是一个配置类;

②@ComponentScan 注解就是 Spring 中学习的组件扫描;

③@EnableAutoConfiguration 注解是最核心的注解,下面详细进行分析。

@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
		@Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
public @interface SpringBootApplication{
}
@EnableAutoConfiguration 注解

@EnableAutoConfiguration 注解也是一个复合注解,由 @AutoConfigurationPackage 和 @Import 注解组成!

注:@AutoConfigurationPackage 是导入当前配置类的包及其子包下的所有类到容器;@Import 是导入组件到容器!

@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {}

(1) @AutoConfigurationPackage 注解

翻译为自动配置包,本质上是一个 @Import 注解:给容器导入一个组件,这个组件是 Register

package org.springframework.boot.autoconfigure;
 
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.springframework.context.annotation.Import;
 
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@Import({AutoConfigurationPackages.Registrar.class})
public @interface AutoConfigurationPackage {
    String[] basePackages() default {};
 
    Class<?>[] basePackageClasses() default {};
}

通过源码发现这个 Register 中有两个方法,通过方法区批量导入组件(一个个导太麻烦了)

static class Registrar implements ImportBeanDefinitionRegistrar, DeterminableImports {
        Registrar() {
        }
        // 批量注册
        // 
        public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
            AutoConfigurationPackages.register(registry, (String[])(new PackageImports(metadata)).getPackageNames().toArray(new String[0]));
        }
 
        public Set<Object> determineImports(AnnotationMetadata metadata) {
            return Collections.singleton(new PackageImports(metadata));
        }
    }

①AnnotationMetadata metadata 是注解的原信息,表示注解是标注在哪里;通过打断点就可以得到是在:com.zl.App 类上

②new PackageImports 后面表示通过注解的原信息拿到包名去导入整个包,在这里的包名就是 com.zl。通过 (new PackageImports(metadata)).getPackageNames() 这段代码计算出来。

**总结 @AutoConfigurationPackage 注解:**就是把一个包下的所有内容批量注入到容器;这也就解释了在启动类的包或者子包下的类都会被自动注入进去!

(2)@Import 注解

@Import 注解前面已经学习过,这是导入某个组件,这里就是把 AutoConfigurationImportSelector 这个类纳入容器的管理;所以这里需要分析这个类的作用!

通过分析 AutoConfigurationImportSelector 这个类的源码发现:

是利用 getAutoConfigurationEntry(annotationMetadata); 给容器中批量导入一些组件

public String[] selectImports(AnnotationMetadata annotationMetadata) {
        if (!this.isEnabled(annotationMetadata)) {
            return NO_IMPORTS;
        } else {
            AutoConfigurationEntry autoConfigurationEntry = this.getAutoConfigurationEntry(annotationMetadata);
            return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
        }
    }

在 getAutoConfigurationEntry 实现上打断点,进行调试

①最终返回的是封装后的 configurations,当调试走到 configurations 时发现里面有 127 个组件,这些都是需要导入容器当中的;实际上就是调用 List configurations = getCandidateConfigurations(annotationMetadata, attributes) 获取到所有需要导入到容器中的配置类!

②再次查看 getCandidateConfigurations 方法(获取候选配置)的源码发现是通过这个方法得到所有的组件。更详细点是从 META-INF/spring.factories 位置来加载一个文件,默认扫描我们当前系统里面所有 META-INF/spring.factories 位置的文件!

总结 @Import 注解:SpringBoot 一启动,就给容器加载这 127 个配置类(这 127 个配置类实际上是在配置文件写死的);虽然 127 个场景的所有自动配置启动的时候默认全部加载,但是具体生效还是没生效,需要按需开启!

**注:**这里的按需开启,实际上使用的就是使用 @Conditional 注解按照条件装配:满足 Conditional 指定的条件,则进行组件注入!

2.2 修改默认配置

SpringBoot 默认会在底层配好所有的组件;但是如果用户自己配置了以用户的优先!

实际上是底层是通过 @ConditionalOnMissingBean 这个注解来实现的:只有你没有配置某个 Bean,SpringBoot 才会帮你配置。

// 底层大量的这样代码
@Bean
@ConditionalOnMissingBean
public CharacterEncodingFilter characterEncodingFilter() {
}

总结:

(1)SpringBoot 先加载所有的自动配置类 ;绑定 xxxxxAutoConfiguration 到容器当中。

(2)每个自动配置类按照条件进行生效;默认都会绑定配置文件指定的值,xxxxProperties 里面拿,xxxProperties 和配置文件进行了绑定。

(3)生效的配置类就会给容器中装配很多组件,只要容器中有这些组件,相当于这些功能就有了。

(4)只要用户有自己配置的组件,就以用户的优先。

以 HttpEncodingAutoConfiguration 字符编码为例:

对于经常需要改变的值,例如字符编码从 UTF-8 改成 GBK:会利用 @EnableConfigurationProperties(ServerProperties.class) 注解把 ServerProperties 类绑定到容器,然后对于 properties 的值就是从 ServerProperties 类中取出来的,后面在进行修改

ServerProperties 类与配置文件 application 绑定

所以对于定制化配置实际上有两种方法:

 (1)用户直接自己 @Bean 替换底层的组件;

// 在配置类中去自己写,替换
@Bean
public CharacterEncodingFilter characterEncodingFilter() {
  return null;
}

(2)用户去看这个组件是获取的配置文件什么值就去修改。

server.port=9999
mycar.brand=比亚迪
mycar.price=24
// 通过属性配置文件application.properties进行更改
// server.servlet.encoding就是上述注解中prefix指定的前缀
server.servlet.encoding.charset=GBK

自动配置的核心原理:从 xxxAutoConfiguration 中导进来很多的组件 -----》组件从 xxxProperties 中获取值 ----》xxxProperties 又从 application.properties 配置文件中获取!

小试牛刀:改缓存里面的配置

第一步:找到缓存的类 xxxAutoConfiguration(CacheAutoConfiguration),然后找到 xxxProperties(CacheProperties)

第二步:点进去 CacheProperties 类,与配置文件的 spring.cache 前缀进行绑定 

第三步:在配置文件 application.properties 中使用 spring.cache 进行修改缓存的信息

2.3 最佳实践

第一步:引入场景依赖

通过官方文档可以查看:Developing with Spring Boot

第二步:查看自动配置了哪些(选做),实际上是底层的原理,看配置是否生效

第一种方法:自己分析,引入场景对应的自动配置一般都生效了

导入 spring-boot-starter-web 依赖后,关于 Web 的场景都会被导入!

 ①找到 spring-boot-autoconfigure 的 jar 包

②找到对应的 web 包小的类进行分析

 第二种方法:配置文件 application.properties 中写 debug=true 开启自动配置报告

// 默认值是false
debug=true

启动:Negative(不生效),Positive(生效)

第三步:是否需要修改

例如:连接数据库的信息

方法一:参照文档修改配置项:Common Application Properties

方法二: 自己分析,xxxxProperties 绑定了配置文件的哪些前缀

再例如:自己定义或者替换组件

使用 @Bean 注解和 @Component 注解进行替换 ---- 用户配置生效优先原则!

3. 开发技巧

3.1 Lombok

Lombok 插件是可以简化 JavaBean 开发的,例如:setter 和 getter 方法、构造方法、toString 方法等;在程序编译时自动生成这些!

第一步:引入 Lombok 的依赖

<dependency>
   <groupId>org.projectlombok</groupId>
   <artifactId>lombok</artifactId>
</dependency>

第二步:在软件市场进行安装

注:对于高版本的 IDEA 已经集成了这个插件,就不需要安装了! 

此时的 JavaBean 形式

package com.zl.bean;
 
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.ToString;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
 
 
@Component
@ConfigurationProperties(prefix = "myfriend")
// 生成setter and getter
@Data
// 生成toString
@ToString
// 生成有参和无参构造
@AllArgsConstructor
@NoArgsConstructor
// 生成equals和hashcode
@EqualsAndHashCode
public class Person {
    private String name;
    private int age;
}

此时进行访问

注:对于 Lombok 有一个 @Slf4j 注解,表示引入日志!

package com.zl.controler;
 
import com.zl.bean.Car;
import com.zl.bean.Person;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
 
@RestController
@Slf4j // 引入日志信息
public class AppController {
  
    @Autowired
    Person person;
    @RequestMapping("/person")
    public Person person(){
        log.info("请求进来了....................");
        return person;
    }
 
}

发出请求访问时,控制器会打印日志信息

3.2 dev-tools

打开文档,找到 Developer Tools,按照步骤完成:Developing with Spring Boot

第一步:引入依赖

<dependency>
   <groupId>org.springframework.boot</groupId>
   <artifactId>spring-boot-devtools</artifactId>
   <optional>true</optional>
</dependency>

第二步:以后项目或者页面更改,按 Ctrl+F9 就可以生效

3.3 Spring Initailizr(项目初始化向导)

****https://start.spring.io,使用国外的地址,必须联网

①新建项目

②设置基本信息

③选择依赖的列表

④项目的结构

⑤生成的 pom.xml 配置

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <!--SpringBoot项目的父项目-->
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>3.0.3</version>
        <relativePath/>
    </parent>
    <!--当前项目的gav坐标-->
    <groupId>com.zl</groupId>
    <artifactId>study-springboot-002</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>study-springboot-002</name>
    <description>study-springboot-002</description>
    <!--JDK的版本-->
    <properties>
        <java.version>1.8</java.version>
    </properties>
    <dependencies>
        <!--web依赖,版本号就是父的版本号-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <!--单元测试-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>
 
    <build>
        <plugins>
            <!--maven的插件,打包用的-->
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>
 
</project>

图书推荐:《深入浅出 Java **虚拟机:**JVM 原理与实战》

参与方式:

本次送书 2 本! 
活动时间:截止到 2023-07-20 00:00:00。

抽奖方式:利用程序进行抽奖。

参与方式:关注博主(只限粉丝福利哦)、点赞、收藏,评论区随机抽取,最多三条评论!

Java 虚拟机核心技术一本通:通过实战案例 + 执行效果图 + 核心代码,剖析探索 JVM 核心底层原理,强化推动 JVM 优化落地,手把手教你吃透 Java 虚拟机深层原理!

推荐理由

**系统:**全书内容层层递进,深入浅出,手把手教你吃透 JVM 虚拟机核心技术

**深入:**剖析探索 JVM 核心底层原理,强化推动 JVM 优化落地

**实战:**原理与实践相结合,懂理论,能落地,实战化案例精准定位技术细节

**资源:**附赠全书案例源代码,知其然更知其所以然,快速上手不用愁

内容简介

        本书主要以 Java 虚拟机的基本特性及运行原理为中心,深入浅出地分析 JVM 的组成结构和底层实现,介绍了很多性能调优的方案和工具的使用方法。最后还扩展介绍了 JMM 内存模型的实现原理和 Java 编译器的优化机制,让读者不仅可以学习 JVM 的核心技术知识,还能夯实 JVM 调优及代码优化的技术功底。

        本书适合已具有一定 Java 编程基础的开发人员、项目经理、架构师及性能调优工程师参考阅读,同时,本书还可以作为广大职业院校、计算机培训班相关专业的教学参考用书。

京东购买链接:《深入浅出 Java 虚拟机:JVM 原理与实战》(李博)【摘要 书评 试读】- 京东图书

标签: #软件开发 1171 #JAVA 991 #Spring Boot 173
相关文章

万字:支付“核心系统”详解 2024-11-02 15:33

专栏作者:隐墨星辰 \| 主编:陈天宇宙 这篇文章也尝试化繁为简,探寻支付系统的本质,讲清楚在线支付系统最核心的一些概念和设计理念。 虽然支付行业已经过了风头最劲的时光,但跨境支付仍然在蓬勃发展,每年依然有很多新人进入这个行业,这篇文章尝试为这些刚入行的新人提供一点帮助。 文章只介绍一些支付行业十几

资深支付架构师视角:实战从问题定义到代码落地的完整套路 2024-11-02 15:33

前言 今天从一个实际案例入手,介绍站在架构师的角度,如何识别并定义问题,提炼需求,技术方案选型,再到详细设计,最后利用AI的能力协助写出核心的代码,验证与调优。 解决问题存在一定的模式,也可以称之为框架,总结出自己的思考和解题框架,以后再碰到同类型的问题就可以如庖丁解牛一样容易。 很多年前,我写代码

Spring 实现 3 种异步接口 2024-10-18 09:07

大家好,我是苏三~ 如何处理比较耗时的接口? 这题我熟,直接上异步接口,使用 Callable、WebAsyncTask 和 DeferredResult、CompletableFuture等均可实现。 但这些方法有局限性,处理结果仅返回单个值。在某些场景下,如果需要接口异步处理的同时,还持续不断地

重学SpringBoot3-集成Redis(五)之布隆过滤器 2024-10-08 11:24

更多SpringBoot3内容请关注我的专栏:《SpringBoot3》 期待您的点赞👍收藏⭐评论✍ 重学SpringBoot3-集成Redis(五)之布隆过滤器 1. 什么是布隆过滤器? * 基本概念 适用场景 2. 使用 Redis 实现布隆过滤器 * 项目依赖 Redis 配置

设计模式第16讲——迭代器模式(Iterator) 2024-10-08 11:24

一、什么是迭代器模式 迭代器模式是一种行为型设计模式,它提供了一种统一的方式来访问集合对象中的元素,而不是暴露集合内部的表示方式。简单地说,就是将遍历集合的责任封装到一个单独的对象中,我们可以按照特定的方式访问集合中的元素。 二、角色组成 抽象迭代器(Iterator):定义了遍历聚合对象所需的方法

vue2路由和vue3路由区别及原理 2024-10-08 11:24

一、Vue2 与 Vue3 路由的区别 1. 创建路由实例方式的不同 Vue 2 中,通过 Vue.use() 注册路由插件,并通过 new VueRouter() 来创建路由实例。 import Vue from 'vue';import VueRouter from 'vue-router';i

目录

IT 外包服务商

  • 意见投递
  • zyf6619

软件开发应用

主菜单

  • 首页
  • 软件开发
  • 计算机基础
  • Hello Halo
  • 新手必读
  • 关于本知识库
Copyright © 2024 your company All Rights Reserved. Powered by Halo.