【SpringBoot(一)】Spring的认知、容器功能讲解与自动装配原理的入门,带你熟悉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 设置

xml 复制代码
<!--  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 主程序

固定写法

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

3.4 业务处理

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

java 复制代码
@RestController
public class HelloController {

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

3.5 测试

直接运行 main 方法即可

3.6 简化配置

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

ruby 复制代码
server.port = 8080

3.7 简化部署

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

xml 复制代码
<!--    插件-->
<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 依赖管理

  • 依赖管理

    xml 复制代码
    <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,都是第三方为我们提供的简化开发的场景启动器

    • 所有场景启动器最底层的依赖:

      xml 复制代码
      <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. 在当前项目重写配置

      xml 复制代码
      <properties>
          <mysql.version>8.0.25</mysql.version>
      </properties>

4.2 自动配置

  • 自动配好 Tomcat

    • 引入 Tomcat 依赖

    • 配置 Tomcat

      xml 复制代码
      <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 模式加速容器启动过程,减少判断
      • 配置 类组件之间有依赖关系
java 复制代码
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:引入类(组件)

java 复制代码
@Import({User.class, DBHelper.class})
  • 给容器中自动创建出指定类型的组件,默认组件的名字就是全类名

    java 复制代码
    ---------------------------------------------------------------
    com.renexdemo.po.User
    zhangsan
    ch.qos.logback.core.db.DBHelper

5.2.4 @Conditional

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

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

java 复制代码
// @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 文件,将文件中的内容注入到容器中

java 复制代码
@ImportResource("classpath:beans.xml")


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

5.4 配置绑定

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

5.4.1 @ConfiguraionProperties

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

java 复制代码
// 指定前缀名:在 properties 中前缀名也就是 **.属性 = ? 中的 ** 这个值
@AliasFor("prefix")
String value() default "";

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

5.4.2 @EnableConfiguraionProperties + @ConfiguraionProperties

java 复制代码
// 开启 Car 类的属性配置功能,并将该类注册到容器中
@EnableConfigurationProperties(Car.class)


// --------------- pojo 类 ------------
@ConfigurationProperties(prefix = "mycar") // 指定该类的前缀名
public class Car {}

// --------------- properties --------------
mycar.brand=BC
mycar.price=1000000

5.4.3 @Component + @ConfiguraionProperties

java 复制代码
/**
 * 只有在容器中,才可以使用 SpringBoot 提供的功能
 */
@Component// 注入组件
@ConfigurationProperties(prefix = "mycar")
public class Car {}

// --------------- properties --------------
mycar.brand=BC
mycar.price=1000000

6. 自动配置原理入门

6.1 引导加载自动配置类

java 复制代码
@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

java 复制代码
// 自动配置包
@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
  • 自动配置包
java 复制代码
@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 修改默认配置

java 复制代码
@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:列举配置生效
  3. 是否需要修改
    • 参考文档修改配置项
      • Spring 官网
      • 自己分析,xxxProperties 绑定了配置文件的哪些
    • 自定义加入或者替换掉组件
      • @Bean、@Component...
    • 自定义其:xxxxCustomizer
    • ...

8. 开发小技巧

8.1 Lombok

简化 JavaBean 开发

xml 复制代码
<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
</dependency>

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

java 复制代码
@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 简化日志开发

java 复制代码
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

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

8.2 Spring Initailizr

  • 项目初始化向导
ruby 复制代码
https://start.aliyun.com/

9. 💕👉 其他好文推荐

相关推荐
啦啦右一几秒前
Spring Boot | (一)Spring开发环境构建
spring boot·后端·spring
森屿Serien2 分钟前
Spring Boot常用注解
java·spring boot·后端
苹果醋31 小时前
React源码02 - 基础知识 React API 一览
java·运维·spring boot·mysql·nginx
Hello.Reader1 小时前
深入解析 Apache APISIX
java·apache
盛派网络小助手2 小时前
微信 SDK 更新 Sample,NCF 文档和模板更新,更多更新日志,欢迎解锁
开发语言·人工智能·后端·架构·c#
菠萝蚊鸭2 小时前
Dhatim FastExcel 读写 Excel 文件
java·excel·fastexcel
旭东怪2 小时前
EasyPoi 使用$fe:模板语法生成Word动态行
java·前端·word
007php0072 小时前
Go语言zero项目部署后启动失败问题分析与解决
java·服务器·网络·python·golang·php·ai编程
∝请叫*我简单先生2 小时前
java如何使用poi-tl在word模板里渲染多张图片
java·后端·poi-tl
ssr——ssss2 小时前
SSM-期末项目 - 基于SSM的宠物信息管理系统
java·ssm