【SpringBoot(一)】Spring的认知、容器功能讲解与自动装配原理的入门,带你熟悉Springboot中基本的注解使用

本文涉及的产品
应用实时监控服务-可观测链路OpenTelemetry版,每月50GB免费额度
容器镜像服务 ACR,镜像仓库100个 不限时长
MSE Nacos/ZooKeeper 企业版试用,1600元额度,限量50份
简介: SpringBoot专栏开篇第一章,讲述认识SpringBoot、Bean容器功能的讲解、自动装配原理的入门,还有其他常用的Springboot注解!如果想要了解SpringBoot,那么就进来看看吧!

建议先学习Spring,再来查阅本Springboot专栏

1. Spring 与 SpringBoot

1.1 Spring 能做什么?

1.1.1 Spring 的生态圈

Spring 的生态圈覆盖了:

  • web 开发
  • 数据访问
  • 安全控制
  • 分布式
  • 消息服务
  • 移动开发
  • 批处理

Spring 生态几乎覆盖了 java 的全部领域

1.1.2 Spring5 重大升级

1.1.2.1 响应式编程

1.1.2.2 内部源码设计

基于 java8 的一些新特性,如:接口默认实现、重新设计源码架构

1.2 为什么要用 SpringBoot?

SpringBoot 最明显的优点:能快速创建出生产量级的 Spring 应用

1.2.1 SpringBoot 的优点:

  • 创建独立 Spring 应用
  • 内嵌 Web 服务器
  • 自动 starter 依赖,简化构建配置
  • 自动配置 Spring 以及第三方框架
  • 提供生产级别的监控,健康检查以及外部化配置
  • 无代码生成、无需编写 XML

SpringBoot 是整合 Spring 技术栈的一站式框架

SpringBoot 是简化 Spring 技术栈的快速开发脚手架

1.2.2 SpringBoot 的缺点:

  • 人称版本帝,迭代快,需要时刻关注变化
  • 封装太深,内部原理复杂,不容易精通

2. 时代背景

2.1 微服务

  • 微服务是一种架构风格
  • 一个应用拆分为一组小型服务
  • 每个服务运行在自己的进程内,也就是可独立部署和升级
  • 服务之间使用轻量级 Http 交互
  • 服务围绕业务功能拆分
  • 可以由全自动部署机制独立部署
  • 去中心化、服务自治。服务可以使用不同的语言、不同的存储技术

2.2 分布式

2.2.1 分布式的困难

  • 远程调用
  • 服务发现
  • 负载均衡
  • 服务容错
  • 配置管理
  • 服务监控
  • 链路追踪
  • 日志管理
  • 任务调度

2.2.2 分布式的解决

  • SpringBoot + SpringCloud+SpringCloud Data

2.3 云原生

原生应用如何上云?Cloud Native

2.3.1 上云的困难

  • 服务自慰
  • 弹性伸缩
  • 服务隔离
  • 自动化部署
  • 灰度发布
  • 流量治理

2.3.2 上云的解决

3. SpringBoot入门

3.1 系统要求:

  • Java8 && 兼容 Java14
  • Maven3.3+
  • idea2019.1.2

3.2 Maven 设置

<!--  SpringBoot 的父工程  -->
<parent>
    <artifactId>spring-boot-starter-parent</artifactId>
    <groupId>org.springframework.boot</groupId>
    <version>2.5.1</version>
</parent>
<dependencies>
    <!-- 导入 SpringBootWeb 的 jar 包,
    那么及所需的子包都会自动配置 -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
</dependencies>

3.3 主程序

固定写法

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

3.4 业务处理

直接使用以前的方式也可以进行业务处理

@RestController
public class HelloController {
    @RequestMapping("/hello")
    public String handle(){
        return "Hello SpringBoot!";
    }
}

3.5 测试

直接运行 main 方法即可

3.6 简化配置

在项目中创建 appliction.properties 文件,在该文件中配置即可

server.port = 8080

3.7 简化部署

我们只需要在 pom.xml 中引入插件,把项目打成 jar 包即可

<!--    插件-->
<build>
    <plugins>
        <!-- 加载 SpringBoot 的 maven 插件, -->
        <plugin>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-maven-plugin</artifactId>
        </plugin>
    </plugins>
</build>
  • 注意 Maven 是否将 SpringBoot 所需的 jar 包全部导入?
  • 如果没有导入可能会出现启动异常
  • 在 cmd 中使用 java -jar 的方式来启动 jar 包,请取消掉 cmd 的快速编辑模式
  • 如果没有取消,那么可能出现意外时会中断运行

4 SpringBoot 特点

4.1 依赖管理

  • 依赖管理
<parent>
    <artifactId>spring-boot-starter-parent</artifactId>
    <groupId>org.springframework.boot</groupId>
    <version>2.5.1</version>
</parent>
<!-- SpringBoot2 的父工程 -->
<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-dependencies</artifactId>
    <version>2.5.1</version>
</parent>
<!--
  在 spring-boot-dependencies 父工程中,配置了 SpringBoot 几乎所有的jar包
  所以我们配置 SpringBoot 就是继承这个父工程,使用的这个父工程
-->
  • 开发导入 starter 场景启动器
  • 见到 spring-boot-starter-*,就可以说是某种场景
  • 只要引入 starter,这个场景的所有依赖我们就都引入了
  • SpringBoot 所有支持的场景链接:
  • 见到的 *-spring-boot-starter,都是第三方为我们提供的简化开发的场景启动器
  • 所有场景启动器最底层的依赖:
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter</artifactId>
    <version>2.5.1</version>
    <scope>compile</scope>
</dependency>
  • 无需关注版本号,自动版本仲裁
  • 引入依赖默认都可以不写版本
  • 引入非仲裁的 jar,要写版本号
  • 可以修改版本号
  1. 查看 spring-boot-dependencies 里面规定当前依赖版本用的 key
  2. 在当前项目重写配置
<properties>
    <mysql.version>8.0.25</mysql.version>
</properties>

4.2 自动配置

  • 自动配好 Tomcat
  • 引入 Tomcat 依赖
  • 配置 Tomcat
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-tomcat</artifactId>
    <version>2.5.1</version>
    <scope>compile</scope>
</dependency>
  • 自动配好 SpringMVC
  • 引入 SpringMVC 全套组件
  • 自动配好 SpringMVC 常用组件(功能)
  • 自动配好 Web 常见功能,如:字符编码问题
  • SpringBoot 帮我们配置好了所有 web 开发的常见场景
  • 默认的包装结构
  • 主程序所在包及其下面的所有子包里面的组件都会被默认扫描
  • 无需以前的包扫描配置
  • 如果想要修改扫描路径怎么办?
  • 在主程序注解中配置即可 @SpringBootApplication(scanBasePackages = "包路径")
  • 在主程序中使用@ComponentScan("包名路径") 也可以
  • 一个@SpringBootApplication注解,就等于下面三个注解
  • @SpringBootConfiguration (Spring配置加载)
  • @EnableAutoConfiguration(自动开启配置加载)
  • @ComponentScan("包名路径") (组件、资源包扫描)
  • 各种配置拥有默认值
  • 默认配置最终都是映射,例如:MultipartProperties
  • 配置文件的值,最终会绑定到某个类上,这个类会在容器中创建对象
  • 按需加载所有自动配置项
  • 非常多的 starter
  • 引入了哪些场景,那么这个场景才会开启
  • SpringBoot 所有的自动配置功能都在spring-boot-autoconfigure场景中
  • 包括 aop、batch、cache…等功能都被集成在这个场景中

5. 容器功能

5.2 组件添加

5.2.1 @Configuration

  • 基本使用
  • Full 模式与 Lite 模式
  • 示例
  • 最佳实战(proxyBeanMethods 的 false 和 true)
  • 配置 类组件之间无依赖关系用 Lite 模式加速容器启动过程,减少判断
  • 配置 类组件之间有依赖关系
public static void main(String[] args) {
    ConfigurableApplicationContext ioc = SpringApplication.run(MainContorller.class, args);
    
    Object zhangsan1 = ioc.getBean("zhangsan");
    Object zhangsan2 = ioc.getBean("zhangsan");
    System.out.println(zhangsan1.equals(zhangsan2));// true
    // 如果被 @Configuration(proxyBeanMethods = true) 那么该类就是代理对象调用方法
    // SpringBoot 总会检查最高组件是否在容器中。保持组件单实例
    MyConfig config = ioc.getBean(MyConfig.class);
    Pat tomcat1 = config.tomcat();
    Pat tomcat2 = config.tomcat();
    // 配置类中调用方法地址是否一致
    System.out.println(tomcat1.equals(tomcat2));// true
    // 单实例对象中的组件注入
    User user = ioc.getBean("zhangsan", User.class);
    Pat pat = ioc.getBean("tomcat", Pat.class);
    System.out.println("默认方法下,单实例对象中的组件内容是否指向同一个:"+user.getPat().equals(pat));// 默认 true
}
//############################# Config ##################################
/**
 * SpringBoot 的配置类
 *  - 配置类里面使用 @Bean 注解在方法上给容器注册组件,默认也是单实例的
 *  - 配置类本身也是组件
 *  - proxyBeanMethods:代理 Bean 的方法
 *      - Full(proxyBeanMethods = true):单实例对象
 *      - Lite(proxyBeanMethods = false):非单实例
 *      - 为了解决组件依赖问题
 */
@Configuration(proxyBeanMethods = true)
public class MyConfig {
    /**
     * 外部无论对配置类中的最高组件注册方法调用多少次获取的都是之前注册容器中的单实例对象
     * @return
     */
    @Bean("zhangsan")
    public User user01(){
        User user = new User("zhangsan", 18);
        user.setPat(tomcat());
        return user;
    }
    @Bean
    public Pat tomcat(){
        return new Pat(this.getClass().getName());
    }
}

5.2.2 @Bean、@component、@controller、@Service、@Repository

  • 跟 Spring 的用法一致

5.2.3 @ComponentScan、@Import

@ComponentScan:包扫描,跟 Spring 的用法一致

@Import:引入类(组件)

@Import({User.class, DBHelper.class})
  • 给容器中自动创建出指定类型的组件,默认组件的名字就是全类名
---------------------------------------------------------------
com.renexdemo.po.User
zhangsan
ch.qos.logback.core.db.DBHelper

5.2.4 @Conditional

条件装配:满足 Condittional 指定的条件,则进行组件注入

例如:@ConditionalOnBean - 该注解可以声明在方法或类上,当条件满足那么类中或者声明方法的下面的组件才会被注入

// @ConditionalOnMissingBean :当容器中不存在某个 Bean 时,其下面的组件才会被注入
@ConditionalOnMissingBean(name = "tomcat")//- ----------从这开始判断-----------
@Bean("zhangsan")
public User user01(){
    User user = new User("zhangsan", 18);
    user.setPat(tomcat());
    return user;
}
//    @Bean
public Pat tomcat(){
    return new Pat(this.getClass().getName());
}

5.3 原生配置文件引入

5.3.1 @ImportResource

某些公司使用的方法,可能还在使用老旧的 *.xml 的配置方式。我们要想将 bean.xml 文件中的配置文件引入到项目中莫过于太复杂,因为一个 xml 中可能存在几百行配置代码,看都看不过来。

那么这时候 @ImportResource 就可以很好的解决这个问题。声明该注解引入 bean.xml 文件即可。它会重新加载 bean.xml 文件,将文件中的内容注入到容器中

@ImportResource("classpath:beans.xml")
// ------示例--------
@ImportResource("beans.xml 路径")
public class 类 {}

5.4 配置绑定

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

5.4.1 @ConfiguraionProperties

@ConfigurationProperties(prefix = "前缀名") 该注解的属性有两个

// 指定前缀名:在 properties 中前缀名也就是 **.属性 = ? 中的 ** 这个值
@AliasFor("prefix")
String value() default "";
// 指定内容:在 properties 中内容也就是 **.属性 =? 中的属性这个值(大概)
@AliasFor("value")
String prefix() default "";

5.4.2 @EnableConfiguraionProperties + @ConfiguraionProperties

// 开启 Car 类的属性配置功能,并将该类注册到容器中
@EnableConfigurationProperties(Car.class)
// --------------- pojo 类 ------------
@ConfigurationProperties(prefix = "mycar") // 指定该类的前缀名
public class Car {}
// --------------- properties --------------
mycar.brand=BC
mycar.price=1000000

5.4.3 @Component + @ConfiguraionProperties

/**
 * 只有在容器中,才可以使用 SpringBoot 提供的功能
 */
@Component// 注入组件
@ConfigurationProperties(prefix = "mycar")
public class Car {}
// --------------- properties --------------
mycar.brand=BC
mycar.price=1000000

6. 自动配置原理入门

6.1 引导加载自动配置类

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

6.1.1 @SpringBootConfiguration

@Configuration:代表被修饰的类为一个配置类

6.1.2 @ComponentScan

扫描指定 包路径 中的所有 Spring 注解;

6.1.3 @EnableAutoConfiguration

// 自动配置包
@AutoConfigurationPackage
@Import({AutoConfigurationImportSelector.class})
public @interface EnableAutoConfiguration {
    String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";
    Class<?>[] exclude() default {};
    String[] excludeName() default {};
}

1.1.3.1 @AutoConfigurationPackage

  • 自动配置包
@Import({Registrar.class})// 给容器中导入组件
public @interface AutoConfigurationPackage {
    String[] basePackages() default {};
    Class<?>[] basePackageClasses() default {};
}

利用 Registrar 类,给容器中导入一系列组件

及那个指定的一个包下的所有组件导入进 MainApplication 所在包下

6.1.3.2 @@Import({AutoConfigurationImportSelector.class})

  1. 利用 getAutoConfigurationEntry(AnnotationMetadata) 方法;给容器中批量导入一些组件
  2. 调用 List<String> configurations = this.getCandidateConfigurations(annotationMetadata, attributes); 获取到所有需要导入到指定路径的类
  3. 利用工厂加载 loadSpringFactories(@NUllable ClassLoader classLoader),得到所有组件
  4. META-INF/spring.factories 位置来加载一个文件
    默认扫描我们当前系统里面 META-INF/spring.factories 位置的文件
  5. 每一个 jar 包中的 spring.factories 文件,其内部都已经写死了 spring-boot 一启动就要给容器中加载的所有配置类

6.2 按需开启自动配置项

虽然我们 127 个场景的所有自动配置启动的时候默认全部加载

但是所有的场景都会按照条件装配规则来判断是否进行配置,如果装配规则正确,最终会按需配置

6.3 修改默认配置

@Bean // 重新注入组件
@ConditionalOnBean({MultipartResolver.class})// 容器中有这个类型的组件
@ConditionalOnMissingBean( // 容器中没有 multipartResolver 这个名字的组件
    name = {"multipartResolver"}
)
public MultipartResolver multipartResolver(MultipartResolver resolver) {
    /*
      给 @Bean 标注的方法传入了对象参数,这个参数的值就会从容器中找 SpringMVC multipartResolver,
      
      该方法其根本目的就是为了防止有些用户配置的文件上传解析器不符合规范
    */
    return resolver;
}

6.3.1 设计模式

  • SpringBoot 默认会在底层配好所有的组件。
  • 但是如果用户自己配置了,那么以用户的配置优先

6.4 总结:

  • SpringBoot 先加载所有的自动配置类;xxxxAutoConfiguration.java
  • 每次自动,配置类按照条件进行生效。默认都会绑定配置文件指定的值;xxxxProperties,而properties 和配置文件进行了绑定(或yaml)
  • 生效的配置类就会给容器中装配很多组件
  • 只要容器中有这些组件,相当于这些功能就有了
  • 只要用户自己配置了,那么以用户配置的优先
  • 定制化配置:
  • 用户直接自己 @Bean 替换掉底层的组件
  • 用户去看这个组件是获取的配置文件是什么值就去修改

整体流程:xxxxAutoConfiguration --> 组件 --> xxxxProperties 拿值 --> application.properties 设置

7. 最佳实践

7.1 步骤

  1. 引入场景依赖
  2. 查看自动配置(选做)
  • 自己分析,引入场景对应的自动配置一般都生效
  • 在配置文件中,写入 debug = true 开启自动配置报告,
  • Negative:列举配置不生效
  • Proitive:列举配置生效
  1. 是否需要修改
  • 参考文档修改配置项
  • Spring 官网
  • 自己分析,xxxProperties 绑定了配置文件的哪些
  • 自定义加入或者替换掉组件
  • @Bean、@Component…
  • 自定义其:xxxxCustomizer

8. 开发小技巧

8.1 Lombok

简化 JavaBean 开发

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

8.1.1 自动生成 pojo 的 get、set、构造方法

@ToString // lombok:插件,自动生成 toString 方法
@Data // lombok:插件,自动生成 get、set方法
@AllArgsConstructor // lombok:插件,自动生成 全部参数的有参构造器
@NoArgsConstructor // lombok:插件,自动生成 无参构造器
@EqualsAndHashCode // lombok:插件,自动比对成员变量的哈希 code
@ConfigurationProperties(prefix = "mycar")
public class Car {
    private String brand;
    private Integer price;
}

8.1.2 简化日志开发

import com.renexdemo.po.Car;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import lombok.extern.java.Log;
import lombok.extern.slf4j.Slf4j;
@Slf4j // lombok:插件的日志模块
@RestController
public class CarWebNode {
    @Autowired
    Car car;
    @RequestMapping("/car")
    public Car car(){
        Log.info("日志输出");
        return car;
    }
}

8.2 dev-tools

开启代码的热更新;当需要更新按:ctrl+F9

<dependency>
     <groupId>org.springframework.boot</groupId>
     <artifactId>spring-boot-devtools</artifactId>
     <optional>true</optional>
</dependency>
  • 但这不是最纯正的热加载,只是 重启而已

8.2 Spring Initailizr

  • 项目初始化向导

https://start.aliyun.com/

9. 💕👉 其他好文推荐

目录
相关文章
|
1月前
|
安全 Java 决策智能
Spring Boot自动装配
Spring Boot自动装配基于“约定优于配置”理念,通过条件化配置与Starters机制,智能推断并加载所需组件,大幅简化开发流程。它实现配置自动化,提升效率,降低维护成本,支持自定义扩展,推动微服务快速构建,是Java生态中开发范式的革新之作。(238字)
|
1月前
|
XML Java 测试技术
《深入理解Spring》:IoC容器核心原理与实战
Spring IoC通过控制反转与依赖注入实现对象间的解耦,由容器统一管理Bean的生命周期与依赖关系。支持XML、注解和Java配置三种方式,结合作用域、条件化配置与循环依赖处理等机制,提升应用的可维护性与可测试性,是现代Java开发的核心基石。
|
2月前
|
人工智能 Java 机器人
基于Spring AI Alibaba + Spring Boot + Ollama搭建本地AI对话机器人API
Spring AI Alibaba集成Ollama,基于Java构建本地大模型应用,支持流式对话、knife4j接口可视化,实现高隐私、免API密钥的离线AI服务。
1701 1
基于Spring AI Alibaba + Spring Boot + Ollama搭建本地AI对话机器人API
存储 JSON Java
467 0
|
2月前
|
人工智能 Java 开发者
【Spring】原理解析:Spring Boot 自动配置
Spring Boot通过“约定优于配置”的设计理念,自动检测项目依赖并根据这些依赖自动装配相应的Bean,从而解放开发者从繁琐的配置工作中解脱出来,专注于业务逻辑实现。
|
2月前
|
缓存 Java 开发者
【Spring】原理:Bean的作用域与生命周期
本文将围绕 Spring Bean 的作用域与生命周期展开深度剖析,系统梳理作用域的类型与应用场景、生命周期的关键阶段与扩展点,并结合实际案例揭示其底层实现原理,为开发者提供从理论到实践的完整指导。
|
1月前
|
NoSQL 算法 Redis
【Docker】(3)学习Docker中 镜像与容器数据卷、映射关系!手把手带你安装 MySql主从同步 和 Redis三主三从集群!并且进行主从切换与扩容操作,还有分析 哈希分区 等知识点!
Union文件系统(UnionFS)是一种**分层、轻量级并且高性能的文件系统**,它支持对文件系统的修改作为一次提交来一层层的叠加,同时可以将不同目录挂载到同一个虚拟文件系统下(unite several directories into a single virtual filesystem) Union 文件系统是 Docker 镜像的基础。 镜像可以通过分层来进行继承,基于基础镜像(没有父镜像),可以制作各种具体的应用镜像。
320 5
|
1月前
|
监控 Linux 调度
【赵渝强老师】Docker容器的资源管理机制
本文介绍了Linux CGroup技术及其在Docker资源管理中的应用。通过实例演示了如何利用CGroup限制应用程序的CPU、内存和I/O带宽使用,实现系统资源的精细化控制,帮助理解Docker底层资源限制机制。
156 6