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

这篇具有很好参考价值的文章主要介绍了【SpringBoot】| Spring Boot 常见的底层注解剖析。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

目录

一: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='" + 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='" + 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 name="name" value="张三"/>
        <property name="age" value="18" />
    </bean>
    <bean id="petBean" class="com.zl.bean.Pet">
        <property name="name" value="Tom"/>
    </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='" + 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注解的继承树:

【SpringBoot】| Spring Boot 常见的底层注解剖析,第五步:互联网分布式,spring boot,java,后端

案例:现在要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也无法注入,此时就可以使用

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

【SpringBoot】| Spring Boot 常见的底层注解剖析,第五步:互联网分布式,spring boot,java,后端

此时没有注入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 name="name" value="张三"/>
        <property name="age" value="18" />
    </bean>
    <bean id="petBean" class="com.zl.bean.Pet">
        <property name="name" value="Tom"/>
    </bean>
</beans>

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

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

【SpringBoot】| Spring Boot 常见的底层注解剖析,第五步:互联网分布式,spring boot,java,后端

从容器中取出这两个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;
    }
}

进行访问

【SpringBoot】| Spring Boot 常见的底层注解剖析,第五步:互联网分布式,spring boot,java,后端

方法二:@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");
    }
}

进行访问

【SpringBoot】| Spring Boot 常见的底层注解剖析,第五步:互联网分布式,spring boot,java,后端

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类上

【SpringBoot】| Spring Boot 常见的底层注解剖析,第五步:互联网分布式,spring boot,java,后端

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

【SpringBoot】| Spring Boot 常见的底层注解剖析,第五步:互联网分布式,spring boot,java,后端

总结@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<String> configurations = getCandidateConfigurations(annotationMetadata, attributes)获取到所有需要导入到容器中的配置类!

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

【SpringBoot】| Spring Boot 常见的底层注解剖析,第五步:互联网分布式,spring boot,java,后端

总结@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类中取出来的,后面在进行修改

【SpringBoot】| Spring Boot 常见的底层注解剖析,第五步:互联网分布式,spring boot,java,后端

ServerProperties类与配置文件application绑定

【SpringBoot】| Spring Boot 常见的底层注解剖析,第五步:互联网分布式,spring boot,java,后端

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

 (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)

【SpringBoot】| Spring Boot 常见的底层注解剖析,第五步:互联网分布式,spring boot,java,后端

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

【SpringBoot】| Spring Boot 常见的底层注解剖析,第五步:互联网分布式,spring boot,java,后端

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

【SpringBoot】| Spring Boot 常见的底层注解剖析,第五步:互联网分布式,spring boot,java,后端

2.3 最佳实践

第一步:引入场景依赖

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

【SpringBoot】| Spring Boot 常见的底层注解剖析,第五步:互联网分布式,spring boot,java,后端

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

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

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

 ①找到spring-boot-autoconfigure的jar包

【SpringBoot】| Spring Boot 常见的底层注解剖析,第五步:互联网分布式,spring boot,java,后端

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

【SpringBoot】| Spring Boot 常见的底层注解剖析,第五步:互联网分布式,spring boot,java,后端

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

// 默认值是false
debug=true

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

【SpringBoot】| Spring Boot 常见的底层注解剖析,第五步:互联网分布式,spring boot,java,后端

第三步:是否需要修改

例如:连接数据库的信息

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

【SpringBoot】| Spring Boot 常见的底层注解剖析,第五步:互联网分布式,spring boot,java,后端

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

【SpringBoot】| Spring Boot 常见的底层注解剖析,第五步:互联网分布式,spring boot,java,后端

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

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

3. 开发技巧

3.1 Lombok

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

第一步:引入Lombok的依赖

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

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

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

【SpringBoot】| Spring Boot 常见的底层注解剖析,第五步:互联网分布式,spring boot,java,后端

此时的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;
    }

}

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

【SpringBoot】| Spring Boot 常见的底层注解剖析,第五步:互联网分布式,spring boot,java,后端

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,使用国外的地址,必须联网

①新建项目

【SpringBoot】| Spring Boot 常见的底层注解剖析,第五步:互联网分布式,spring boot,java,后端

②设置基本信息

【SpringBoot】| Spring Boot 常见的底层注解剖析,第五步:互联网分布式,spring boot,java,后端

③选择依赖的列表

【SpringBoot】| Spring Boot 常见的底层注解剖析,第五步:互联网分布式,spring boot,java,后端

④项目的结构

【SpringBoot】| Spring Boot 常见的底层注解剖析,第五步:互联网分布式,spring boot,java,后端

⑤生成的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 编程基础的开发人员、项目经理、架构师及性能调优工程师参考阅读,同时,本书还可以作为广大职业院校、计算机培训班相关专业的教学参考用书。

【SpringBoot】| Spring Boot 常见的底层注解剖析,第五步:互联网分布式,spring boot,java,后端

京东购买链接:《深入浅出Java虚拟机:JVM原理与实战》(李博)【摘要 书评 试读】- 京东图书文章来源地址https://www.toymoban.com/news/detail-581289.html

到了这里,关于【SpringBoot】| Spring Boot 常见的底层注解剖析的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • Spring框架常见注解(Spring、Springboot、Springmvc)

    注解 说明 @Component、@Controller、@Service、@Repository 使用在类上用于实例化Bean @Autowired 使用在字段上用于根据类型依赖注入 @Qualifier 结合@Autowired一起使用用于根据名称进行依赖注入 @Scope 标注Bean的作用范围 @Configuration 指定当前类是一个 Spring 配置类,当创建容器时会从该类上

    2024年02月15日
    浏览(52)
  • 拆解Spring boot:Springboot为什么如此丝滑而简单?源码剖析解读自动装配

    🎉🎉欢迎光临,终于等到你啦🎉🎉 🏅我是苏泽,一位对技术充满热情的探索者和分享者。🚀🚀 🌟持续更新的专栏 《Spring 狂野之旅:从入门到入魔》 🚀 本专栏带你从Spring入门到入魔   这是苏泽的个人主页可以看到我其他的内容哦👇👇 努力的苏泽 http://suzee.blog.csdn

    2024年03月23日
    浏览(44)
  • SpringBoot底层注解

    本文主要讲诉@Configuration、@Import、@Conditional、@ImportResource、@ConfigurationProperties注解。 先将实体类给大家,方便下面测试: get、set、构造器大家自己添加,这里我用的是ToString等注解(以后会出文章讲诉)所以没写这些,就不列出来了。 1、配置类里面使用@Bean标注在方法上给

    2024年02月09日
    浏览(28)
  • Spring(18) @Order注解介绍、使用、底层原理

    @Order :是 spring-core 包下的一个注解。@Order 作用是 定义 Spring IOC 容器中 Bean 的执行顺序 。 注意: Spring 的 @Order 注解或者 Ordered 接口,不决定 Bean 的加载顺序和实例化顺序,只决定 Bean 注入到 List 中的顺序。 @Order 注解接受一个整数值作为参数, 数值越小表示优先级越高 。

    2024年02月20日
    浏览(39)
  • 第五步:STM32F4端口复用

    STM32 有很多的内置外设,这些外设的外部引脚都是与 GPIO 复用的。也就是说,一个 GPIO 如果可以复用为内置外设的功能引脚,那么当这个 GPIO 作为内置外设使用的时候,就叫做复用。 例如串口 1 的发送接收引脚是 PA9,PA10 ,当我们把 PA9,PA10 不用作 GPIO ,而用做复用功能串口

    2024年02月12日
    浏览(54)
  • 【Spring Boot】Spring Boot源码解读与原理剖析

    承载着作者的厚望,掘金爆火小册同名读物《Spring Boot源码解读与原理剖析》正式出书! 本书前身是掘金社区销量TOP的小册——《Spring Boot源码解读与原理剖析》,整个社区中有3600+开发者都不约而同地选择了这本小册,也使它成为掘金社区首屈一指的王牌Spring教程,非常能打

    2024年02月08日
    浏览(40)
  • Spring Boot Starter 剖析与实践

    对于 Java 开发人员来说,Spring 框架几乎是必不可少的。它是一个广泛用于开发企业应用程序的开源轻量级框架。近几年,Spring Boot 在传统 Spring 框架的基础上应运而生,不仅提供了 Spring 的全部功能,还使开发人员更加便捷地使用。在使用 Spring Boot 时,我们经常会接触到各种

    2024年02月14日
    浏览(44)
  • Spring boot注解讲解

    人不走空                                                                            目录         🌈个人主页:人不走空       💖系列专栏:算法专题 ⏰诗词歌赋:斯是陋室,惟吾德馨   注解 注解列表如下 JPA注解 作者其他作品:   @SpringBootApplication :申

    2024年02月19日
    浏览(50)
  • Spring Boot常用注解

    在 Spring Boot 中,有许多注解用于简化和标识应用程序的不同方面。以下是一些常用的 Spring Boot 注解: @SpringBootApplication : 用于标识主应用程序类。通常与 @EnableAutoConfiguration 、 @ComponentScan 和 @Configuration 一起使用,它是一个复合注解,用于简化配置。 @Controller : 用于标识控

    2024年01月19日
    浏览(43)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包