SpringBoot 入门

文章目录

  • [1. 简介](#1. 简介)
    • [1.1 SpringFramework 解决了什么问题?没有解决什么问题?](#1.1 SpringFramework 解决了什么问题?没有解决什么问题?)
      • [SpringFramework 解决了什么问题?](#SpringFramework 解决了什么问题?)
      • [SpringFramework 没有解决什么问题?](#SpringFramework 没有解决什么问题?)
    • [1.2 SpringBoot 的概述](#1.2 SpringBoot 的概述)
      • [SpringBoot 解决上述 Spring 的缺点](#SpringBoot 解决上述 Spring 的缺点)
      • [SpringBoot 的特点](#SpringBoot 的特点)
      • [SpringBoot 的核心功能](#SpringBoot 的核心功能)
  • [2. HelloWorld](#2. HelloWorld)
    • [2.1 创建 SpringBoot Web 应用](#2.1 创建 SpringBoot Web 应用)
    • [2.2 初始化后内容](#2.2 初始化后内容)
    • [2.3 添加一个控制层](#2.3 添加一个控制层)
    • [2.4 运行你的第一个程序](#2.4 运行你的第一个程序)
    • [2.5 一些思考](#2.5 一些思考)
      • [为什么我们添加一个 starter-web 模块便可以了呢?](#为什么我们添加一个 starter-web 模块便可以了呢?)
      • [我们如何更改更多 Server 的配置呢?比如 Tomcat Server](#我们如何更改更多 Server 的配置呢?比如 Tomcat Server)
      • [SpringBoot 还提供了哪些 starter 模块呢?](#SpringBoot 还提供了哪些 starter 模块呢?)
  • [3. 对 Hello World 进行 MVC 分层](#3. 对 Hello World 进行 MVC 分层)
    • [3.1 经典的 MVC 三层架构](#3.1 经典的 MVC 三层架构)
    • [3.2 用 Package 解耦三层结构](#3.2 用 Package 解耦三层结构)
    • [3.3 运行测试](#3.3 运行测试)
  • [4. 定制自己的 Banner](#4. 定制自己的 Banner)
    • [4.1 什么是 Banner](#4.1 什么是 Banner)
    • [4.2 如何更改 Banner](#4.2 如何更改 Banner)
    • [4.3 文字 Banner 的设计](#4.3 文字 Banner 的设计)
      • [一些设计 Banner 的网站](#一些设计 Banner 的网站)
      • [IDEA 中 Banner 的插件](#IDEA 中 Banner 的插件)
      • 其它工具
    • [4.4 Banner 中其它配置信息](#4.4 Banner 中其它配置信息)
    • [4.5 动画 Banner 的设计](#4.5 动画 Banner 的设计)
    • [4.6 进一步思考](#4.6 进一步思考)
      • [图片 Banner 是如何起作用的?](#图片 Banner 是如何起作用的?)
  • [5. 添加 Logback 日志](#5. 添加 Logback 日志)
  • [6. 配置热部署 devtools 工具](#6. 配置热部署 devtools 工具)
    • [6.1 概述](#6.1 概述)
    • [6.2 配置 devtools 实现热部署](#6.2 配置 devtools 实现热部署)
      • [POM 配置](#POM 配置)
      • [IDEA 配置](#IDEA 配置)
      • [application.yml 配置](#application.yml 配置)
      • [使用 LiveLoad](#使用 LiveLoad)
    • [6.3 进一步理解](#6.3 进一步理解)
      • [devtool 的原理?为何会自动重启?](#devtool 的原理?为何会自动重启?)
      • [devtool 是否会被打包进 jar?](#devtool 是否会被打包进 jar?)
      • [devtool 为何会默认禁用缓存选项?](#devtool 为何会默认禁用缓存选项?)
      • [devtool 是否可以给所有 Springboot 应用做全局的配置?](#devtool 是否可以给所有 Springboot 应用做全局的配置?)
      • [如果不用 devtool,还有什么选择?](#如果不用 devtool,还有什么选择?)
  • [7. 常用注解](#7. 常用注解)

1. 简介

前面我们已经了解了 SpringFramework,那为什么有了 SpringFramework 还会诞生 SpringBoot?简单而言,因为虽然 Spring 的组件代码是轻量级的,但它的配置却是重量级的;所以 SpringBoot 的设计策略是通过开箱即用约定大于配置来解决配置重的问题。

1.1 SpringFramework 解决了什么问题?没有解决什么问题?

需要概括性的理解 SpringFramework 解决了什么问题,没有解决什么问题?

SpringFramework 解决了什么问题?

​ Spring 是 Java 企业版(Java Enterprise Edition,JEE,也称 J2EE)的轻量级代替品。无需开发重量级的 EnterpriseJavaBean(EJB),Spring 为企业级 Java 开发提供了一种相对简单的方法,通过依赖注入和面向切面编程,用简单的 Java 对象(Plain Old Java Object,POJO)实现了 EJB 的功能。

  1. 使用 Spring 的 IOC 容器,将对象之间的依赖关系交给 Spring,降低组件之间的耦合性,让我们更专注于应用逻辑。
  2. 可以提供众多服务,事务管理,WS 等。
  3. AOP 的很好支持,方便面向切面编程。
  4. 对主流的框架提供了很好的集成支持,如 Hibernate,Struts2,JPA 等。
  5. Spring DI 机制降低了业务对象替换的复杂性。
  6. Spring 属于低侵入,代码污染极低。
  7. Spring 的高度开放性,并不强制依赖于 Spring,开发者可以自由选择 SPring 部分或全部。

SpringFramework 没有解决什么问题?

​ 虽然 Spring 的组件代码是轻量级的,但它的配置却是重量级的。一开始,Spring 用 XML 配置,而且是很多 XML 配置。Spring 2.5 引入了基于注解的组件扫描,这消除了大量针对应用程序自身组件的显示 XML 配置。Spring 3.0 引入了基于 Java 的配置,这是一种类型安全的可重构配置方式,可以代替 XML。

​ 所有这些配置都代表了开发时的损耗。因为在思考 Spring 特性配置和解决业务问题之间需要进行思维切换,所以编写配置挤占了编写应用程序逻辑的时间。所有框架一样,Spring 实用,但与此同时它要求的回报也不少。

​ 除此之外,项目的依赖管理也是一件耗时耗力的事情。在环境搭建时,需要分析要导入哪些库的坐标,而且还需要分析导入与之有依赖关系的其它库的坐标,一旦选错了依赖的版本,随之而来的不兼容问题就会严重阻碍项目的开发进度。

  1. JSP 中要写很多代码、控制器过于灵活,缺少一个公用控制器。
  2. Spring 不支持分布式,这也是 EJB 仍然在用的原因之一。

1.2 SpringBoot 的概述

SpringBoot 解决上述 Spring 的缺点

​ SpringBoot 对上述 Spring 的缺点进行了改善和优化,基于约定优于配置的思想,可以让开发人员不必在配置与逻辑业务之间进行思维的切换,全身心地投入到逻辑业务的代码编写中,从而大大提高了开发的效率,一定程度上缩短了项目周期。

SpringBoot 的特点

  1. 为基于 Spring 的开发提供更快的入门体验;
  2. 开箱即用,没有代码生成,也无需 XML 配置。同时也可以修改默认值来满足特定的需求;
  3. 提供了一些大型项目中常见的非功能性特性,如嵌入式服务器、安全、指标、监控检测、外部配置等。

​ SpringBoot 不是对 Spring 功能上的增强,而是提供了一种快速使用 Spring 的方式。

SpringBoot 的核心功能

  • 起步依赖:起步依赖本质上是一个 Maven 项目对象模型(Project Object Model,POM),定义了对其它库的依赖传递,这些东西加在一起即支持某项功能;

​ 简单来说,起步依赖就是将具备某种功能的坐标打包到一起,并提供一些默认的功能。

  • 自动配置

​ SpringBoot 的自动配置是一个运行时(更准确地说,是应用程序启动时)的过程,考虑了众多因素,才决定 Spring 配置应该用哪个,不该用哪个。该过程是 Spring 自动完成的。

2. HelloWorld

我们了解了 SpringBoot 和 SpringFramework 的关系之后,我们可以开始创建一个 Hello World 级别的项目了。

2.1 创建 SpringBoot Web 应用

为快速进行开发,推荐使用 IDEA 这类开发工具,它将大大提升你学习和开发的效率。

  • 选择 Spring Boot,填写 GAV 三元组

Group:是公司或组织的名称,是一个命名空间的概念。

Artifat:当前项目的唯一标识。

Version:项目的版本号,一般 xx-SNAPSHOT 表示非稳定版。

​ Spring 提供的初始化项目的工具。

​ 当然也可以在 Spring Initializr 中初始化你项目工程(但官网已不支持 SpringBoot 的 2.X 版本初始化)。

如果想进行 SpringBoot 2.X 版本的初始化,可在 https://start.aliyun.com/ 进行初始化项目工程。

  • 选择初始化模板

​ Spring Initialize 可以帮助你选择常见的功能模块的 starter 包,就可以初始化项目了。

2.2 初始化后内容

pom.xml

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>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.6.13</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>

    <groupId>io.zhanbo</groupId>
    <artifactId>springboot-helloworld</artifactId>
    <version>0.0.1-SNAPSHOT</version>

    <properties>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
        <java.version>1.8</java.version>
        <springboot.version>2.6.13</springboot.version>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
    </dependencies>
    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>
</project>

2.3 添加一个控制层

​ 我们添加如下代码,启动即可启动一个 WEB 服务,通过浏览器访问 /hello,并返回 Hello World。

java 复制代码
@RestController
public class Controller {
    @GetMapping("/hello")
    public ResponseEntity<String> hello() {
        return new ResponseEntity<>("Hello World", HttpStatus.OK);
    }
}

2.4 运行你的第一个程序

​ 点击 SpringBootHelloWorldApplication 入口的绿色按钮,运行程序。

​ 运行后,你将看到如下信息:表明我们启动程序成功(启动了一个内嵌的 Tomcat 容器,服务端口在 8080)

​ 这时候我们便可以通过浏览器访问服务。

2.5 一些思考

到此,你会发现一个简单的 web 程序居然完成了。这里你需要一些思考:

为什么我们添加一个 starter-web 模块便可以了呢?

​ 我们安装 Maven Helper 的插件,用来查看 spring-boot-starter-web 模块的依赖。

​ 我们看下这个模块的依赖,你便能初步窥探出模块支撑。

我们如何更改更多 Server 的配置呢?比如 Tomcat Server

​ 为什么 Tomcat 默认端口是 8080 呢?如前文所述,SpringBoot 最强大的地方在于约定大于配置,只要你引入某个模块的 xx-start 包,它将自动注入配置,提供了这个模块的功能;比如这里我们在 POM 中添加了如下的包。

xml 复制代码
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>

​ 它内嵌了 Tomcat 并且提供了默认的配置,比如默认端口是 8080

​ 我们可以在 Application.properties 或者 application.yml 中配置

​ 特别的,如果你添加了如下包:

xml 复制代码
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-configuration-processor</artifactId>
    <optional>true</optional>
</dependency>

​ 并且你的 IDE 支持,可以自动给你配置提示。

​ 你也可以(cmd + 点击)进入具体的配置文件。

SpringBoot 还提供了哪些 starter 模块呢?

​ Spring Boot 推荐的基础 POM 文件:

名称 说明
spring-boot-starter 核心 POM,包含自动配置支持、日志库和对 YAML 配置文件的支持。
spring-boot-starter-amqp 通过 spring-rabbit 支持 AMQP。
spring-boot-starter-aop 包含 spring-aop 和 AspectJ 来支持面向切面编程(AOP)。
spring-boot-starter-batch 支持 Spring Batch,包含 HSQLDB。
spring-boot-starter-data-jpa 包含 spring-data-jpa、spring-orm 和 Hibernate 来支持 JPA。
spring-boot-starter-data-mongodb 包含 spring-data-mongodb 来至此 MongoDB。
spring-boot-starter-data-rest 通过 spring-data-rest-webmvc 支持以 REST 方式暴露 SpringData 仓库。
spring-boot-starter-jdbc 支持使用 JDBC 访问数据库。
spring-boot-starter-security 包含 Spring-security。
spring-boot-starter-test 包含常见的测试所需的依赖,如 JUnit、Hamcrest、Mockito 和 spring-test 等。
spring-boot-starter-velocity 支持使用 Velocity 作为模板引擎。
spring-boot-starter-web 支持 Web 应用开发,包含 Tomcat 和 spring-mvc。
spring-boot-starter-websocket 支持使用 Tomcat 开发 WebSocket 应用。
spring-boot-starter-ws 支持 Spring Web Services。
spring-boot-starter-actuator 添加适用于生成环境的功能,如性能指标和检测等功能。
spring-boot-starter-remote-shell 添加远程 SSH 支持。
spring-boot-starter-jetty 使用 Jetty 而不是默认的 Tomcat 作为应用服务。
spring-boot-starter-log4j 添加 Log4j 的支持。
spring-boot-starter-logging 使用 Spring Boot 默认的日志框架 Logback。
spring-boot-starter-tomcat 使用 Spring Boot 默认的 Tomcat 作为应用服务器。

​ 所有这些 POM 依赖的好处在于为开发 Spring 应用提供了一个良好的基础。Spring Boot 所选择的是第三方库是经过考虑的,是比较适合产品开发的选择。但是 Spring Boot 也通过了不同的选项,比如日志框架可以用 Logback 或 Log4j,应用服务器可以用 Tomcat 或 Jetty。

3. 对 Hello World 进行 MVC 分层

本节结合常见的 MVC 分层思路学习常见的包结构划分。

3.1 经典的 MVC 三层架构

​ 三层架构(3-tier application)通常意义上的三层架构就是将整个业务应用划分为:表现层(UI)、业务逻辑层(BLL)、数据访问层(DAL)。区别层次的目的即为了 "高内聚、低耦合" 的思想。

  1. 表现层(UI):通俗讲就是展现给用户的界面,即用户在使用一个系统的时候他的所见所得。
  2. 业务逻辑层(BLL):针对具体问题的操作,也可以锁是对数据层的操作,对数据业务逻辑处理。
  3. 数据访问层(DAL):该层所做事务直接操作数据库,针对数据的增删改查等。

3.2 用 Package 解耦三层结构

​ 我们这里设计一个常见的用户增删改查项目,通常来说对应的包结构如下:

controller

​ 表示层

java 复制代码
@RestController
@RequestMapping("user")
public class UserController {
    @Autowired
    private UserService userService;

    @RequestMapping("add")
    public User add(User user) {
        userService.addUser(user);
        return user;
    }

    @GetMapping("list")
    public List<User> list() {
        return userService.list();
    }
}

service

​ 业务逻辑层

java 复制代码
@Service
public class IUserServiceImpl implements UserService {
    @Autowired
    private UserRepository userDao;

    @Override
    public void addUser(User user) {
        userDao.save(user);
    }

    @Override
    public List<User> list() {
        return userDao.findAll();
    }
}

dao

​ 数据访问层,数据放在内存中。

java 复制代码
@Repository
public class UserRepository {
    private List<User> userDemoList = new ArrayList<>();

    public void save(User user) {
        userDemoList.add(user);
    }

    public List<User> findAll() {
        return userDemoList;
    }
}

entity

​ model 实体层

java 复制代码
public class User {
    private int userId;

    private String userName;

    public int getUserId() {
        return userId;
    }

    public void setUserId(int userId) {
        this.userId = userId;
    }

    public String getUserName() {
        return userName;
    }

    public void setUserName(String userName) {
        this.userName = userName;
    }
}

3.3 运行测试

​ 添加用户

​ 查询用户列表

我们在启动 Spring Boot 程序时,有 SpringBoot 的 Banner 信息,那么如何自定义成自己项目的信息呢?

​ 我们在启动 Spring Boot 程序时,有如下 Banner 信息:

​ 那么如何自定义成自己项目的名称呢?

更改 Banner 有如下几种方式:

  • banner.txt 配置(最常用)

​ 在 application.yml 中添加配置

yml 复制代码
spring:
  banner:
    charset: UTF-8
    location: classpath:banner.txt

​ 在 resource 下创建 banner.txt,内容自定义:

txt 复制代码
----- Hello World -----
    This is Test !
-----------------------

​ 修改后,重启的 app 的效果

  • SpringApplication 启动时设置参数
java 复制代码
        SpringApplication application = new SpringApplication();
        application.setBannerMode(Banner.Mode.OFF);
        SpringApplication.run(SpringbootHelloWorldApplication.class, args);

​ SpringApplication 还可以设置自定义的 Banner 的接口类

如何设计上面的文字呢?

​ 可以通过这个网站进行设计:https://patorjk.com/software/taag

​ 例如:

​ 修改 banner.txt 运行效果如下:

其它工具

https://www.degraeve.com/img2txt.php

https://www.bootschool.net/ascii

除了文件信息,还有哪些信息可以配置呢?比如 Spring 默认还带有 SpringBoot 当前的版本号信息。

​ 在 banner.txt 中,还可以进行一些设置:

java 复制代码
# SpringBoot 的版本号
${spring-boot.version}

# SpringBoot 的版本号前面加 v,前后带括号
${spring-boot.formatted-version}

# MANIFEST.MF 文件中的版本号
${application.version}

# MANIFEST.MF 文件的版本号前面加 v,前后带括号
${application.formatted-version}

# MANIFEST.MF 文件中的程序名称
${application.title}

# ANSI 样色或样式等
${Ansi.NAME} (or ${AnsiColor.NAME}, ${AnsiBackground.NAME}, ${AnsiStyle.NAME})

​ 简单测试如下:(注意必须打包出Jar, 才会生成resources/META-INF/MANIFEST.MF)

那能不能设置动态的 Banner 呢?比如一个图片?

​ SpringBoot2 是支持图片形式的 Banner,

yml 复制代码
spring:
  main:
    banner-mode: console
    show-banner: true
  banner:
    charset: UTF-8
    image:
      margin: 0
      height: 10
      invert: false
      location: classpath:banner.png

​ 效果如下:(需要选择合适的照片,不然效果不好,所以这种方式很少使用)

​ 注意:格式不能太大,不然会报错。

java 复制代码
org.springframework.boot.ImageBanner     : Image banner not printable: class path resource [banner.gif] (class java.lang.ArrayIndexOutOfBoundsException: '4096')

4.6 进一步思考

发现 SpringBoot 可以把图片转换成 ASCII 图案,那么它是怎么做的呢?以此为例,我们看下 Spring 的 Banner 是如何生成的呢?

  • 获取 Banner
    • 优先级是环境变量中的 image 优先,格式在 IMAGE_EXTENSION 中
    • 然后才是 banner.txt
    • 没有的话就用 SpringBootBanner
  • 如果是图片
    • 获取图片 Banner(属性配置等)
    • 转换成 ascii

获取 banner

java 复制代码
class SpringApplicationBannerPrinter {
    static final String BANNER_LOCATION_PROPERTY = "spring.banner.location";
    static final String BANNER_IMAGE_LOCATION_PROPERTY = "spring.banner.image.location";
    static final String DEFAULT_BANNER_LOCATION = "banner.txt";
    static final String[] IMAGE_EXTENSION = new String[]{"gif", "jpg", "png"};
    private static final Banner DEFAULT_BANNER = new SpringBootBanner();
    private final ResourceLoader resourceLoader;
    private final Banner fallbackBanner;
    
    ...
        
    // 获取 Banner,优先级是环境变量中的 Image' 优先,格式在 IMAGE_EXTENSION 中,然后才是 banner.txt
    private Banner getBanner(Environment environment) {
        Banners banners = new Banners();
        banners.addIfNotNull(this.getImageBanner(environment));
        banners.addIfNotNull(this.getTextBanner(environment));
        if (banners.hasAtLeastOneBanner()) {
            return banners;
        } else {
            return this.fallbackBanner != null ? this.fallbackBanner : DEFAULT_BANNER;
        }
    }
    
    ...
}

获取图片 Banner

java 复制代码
private Banner getImageBanner(Environment environment) {
    String location = environment.getProperty("spring.banner.image.location");
    if (StringUtils.hasLength(location)) {
        Resource resource = this.resourceLoader.getResource(location);
        return resource.exists() ? new ImageBanner(resource) : null;
    } else {
        for(String ext : IMAGE_EXTENSION) {
            Resource resource = this.resourceLoader.getResource("banner." + ext);
            if (resource.exists()) {
                return new ImageBanner(resource);
            }
        }

        return null;
    }
}

获取图片的配置等

java 复制代码
private void printBanner(Environment environment, PrintStream out) throws IOException {
    int width = (Integer)this.getProperty(environment, "width", Integer.class, 76);
    int height = (Integer)this.getProperty(environment, "height", Integer.class, 0);
    int margin = (Integer)this.getProperty(environment, "margin", Integer.class, 2);
    boolean invert = (Boolean)this.getProperty(environment, "invert", Boolean.class, false);
    AnsiColors.BitDepth bitDepth = this.getBitDepthProperty(environment);
    PixelMode pixelMode = this.getPixelModeProperty(environment);
    Frame[] frames = this.readFrames(width, height);// 读取图片的帧

    for(int i = 0; i < frames.length; ++i) {
        if (i > 0) {
            this.resetCursor(frames[i - 1].getImage(), out);
        }

        this.printBanner(frames[i].getImage(), margin, invert, bitDepth, pixelMode, out);
        this.sleep(frames[i].getDelayTime());
    }

}

转换成 ascii

java 复制代码
private void printBanner(BufferedImage image, int margin, boolean invert, AnsiColors.BitDepth bitDepth, PixelMode pixelMode, PrintStream out) {
    AnsiElement background = invert ? AnsiBackground.BLACK : AnsiBackground.DEFAULT;
    out.print(AnsiOutput.encode(AnsiColor.DEFAULT));
    out.print(AnsiOutput.encode(background));
    out.println();
    out.println();
    AnsiElement lastColor = AnsiColor.DEFAULT;
    AnsiColors colors = new AnsiColors(bitDepth);

    for(int y = 0; y < image.getHeight(); ++y) {
        for(int i = 0; i < margin; ++i) {
            out.print(" ");
        }

        for(int x = 0; x < image.getWidth(); ++x) {
            Color color = new Color(image.getRGB(x, y), false);
            AnsiElement ansiColor = colors.findClosest(color);
            if (ansiColor != lastColor) {
                out.print(AnsiOutput.encode(ansiColor));
                lastColor = ansiColor;
            }

            out.print(this.getAsciiPixel(color, invert, pixelMode)); // 像素点转换成字符输出
        }

        out.println();
    }

    out.print(AnsiOutput.encode(AnsiColor.DEFAULT));
    out.print(AnsiOutput.encode(AnsiBackground.DEFAULT));
    out.println();
}

5. 添加 Logback 日志

SpringBoot 开发中如何选用日志框架呢?出于性能等原因,Logback 目前是 SpringBoot 应用日志的标配;当然有时候在生产环境中也会考虑和三方中间件采用统一处理方式。

5.1 日志框架的基础

​ 在学习这一部分时需要了解一些日志框架的发展和基础,同时了解日志配置时考虑的因素。

关于日志框架(日志门面)

Java 日志库是最能体现 Java 库在进化中的渊源关系的,在理解时重点理解日志框架本身日志门面,以及比较好的实践等。要关注其历史渊源和设计(比如桥接),而具体在使用时查询接口即可,否者会陷入 JUL(Java Util Log),JCL(Commons Loggings),Log4j,SLF4J,Logback,Log4j2 傻傻分不清楚的境地。

​ 关于日志框架(日志门面)可以看:日志类库详解

配置时考虑点

在配置日志时需要考虑哪些因素?

  • 支持日志路径,日志 level 等配置
  • 日志控制配置通过 application.yml 下发
  • 按天生成日志,当前的日志 > 50 MB 回滚
  • 最多保存 10 天日志
  • 生成的日志中 Pattern 自定义
  • Pattern 中添加用户自定义的 MDC 字段,比如用户信息(当前日志是由哪个用户的请求产生),request 信息。此种方式可以通过 AOP 切面控制,在 MDC 中添加 requestID,在 spring-logback.xml 中配置 Pattern
  • 根据不同的运行环境设置 Profile - dev,test,product
  • 对控制台,Err 和全量日志分别配置
  • 对第三方包路径日志控制

5.2 实现范例

application.yml

yml 复制代码
server:
  port: 8080

logging:
  level:
    root: debug
  file:
    path: classpath:/logs/log
    
spring:
  application:
    name: springboot-logback
    
debug: false

Spring-logback.xml

xml 复制代码
<?xml version="1.0" encoding="UTF-8" ?>
<configuration>
    <!-- 日志根目录 -->
    <springProperty scope="context" name="LOG_HOME" source="logging.path" defaultValue="/data/logs/springboot-logback"/>

    <!-- 日志级别 -->
    <springProperty scope="context" name="LOG_ROOT_LEVEL" source="logging.level.root" defaultValue="DEBUG"/>

    <!-- 标识这个 "STDOUT" 将会添加到这个 logger -->
    <springProperty scope="context" name="STDOUT" source="log.stdout" defaultValue="STDOUT"/>

    <!-- 日志文件名称 -->
    <property name="LOG_PREFIX" value="spring-boot-logback"/>

    <!-- 日志文件编码 -->
    <property name="LOG_CHARSET" value="UTF-8"/>

    <!-- 日志文件路径 + 日期 -->
    <property name="LOG_DIR" value="${LOG_HOME}/%d{yyyyMMdd}"/>

    <!-- 对日志进行格式化 -->
    <property name="LOG_MSG" value="- | [%X{requestUUID}] | [%d{yyyyMMdd HH:mm:ss.SSS}] | [%level] | [${HOSTNAME}] | [%thread] | [%logger{36}] | --> %msg|%n "/>

    <!-- 文件大小,默认 10 MB -->
    <property name="MAX_FILE_SIZE" value="50MB"/>

    <!-- 配置日志的滚动时间,表示只保留最近 10 天的日志 -->
    <property name="MAX_HISTORY" value="10"/>

    <!-- 输出到控制台 -->
    <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
        <!-- 输出的日志内容格式化 -->
        <layout class="ch.qos.logback.classic.PatternLayout">
            <pattern>${LOG_MSG}</pattern>
        </layout>
    </appender>

    <!-- 输出到文件 -->
    <appender name="0" class="ch.qos.logback.core.rolling.RollingFileAppender"/>

    <!-- 定义 ALL 日志的输出方式 -->
    <appender name="FILE_ALL" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <!-- 日志文件路径,日志文件名称 -->
        <File>${LOG_HOME}/all_${LOG_PREFIX}.log</File>
        <!-- 设置滚动策略,当天的日志大小超过 ${MAX_FILE_SIZE} 文件大小时,新的内容写入新的文件,默认大小为 10MB -->
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">

            <!-- 日志文件路径,新的 ALL 日志文件名称,"i" 是个变量 -->
            <FileNamePattern>${LOG_DIR}/all_${LOG_PREFIX}%i.log</FileNamePattern>

            <!-- 配置日志的滚动时间,表示只保留最近 10 天的日志 -->
            <MaxHistory>${MAX_HISTORY}</MaxHistory>

            <!-- 当天的日志大小超过 ${MAX_FILE_SIZE} 文件大小时候,新的内容写入新的文件,默认大小为 10MB -->
            <timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
                <maxFileSize>${MAX_FILE_SIZE}</maxFileSize>
            </timeBasedFileNamingAndTriggeringPolicy>
        </rollingPolicy>

        <!-- 输出的日志内容格式化 -->
        <layout class="ch.qos.logback.classic.PatternLayout">
            <pattern>${LOG_MSG}</pattern>
        </layout>
    </appender>

    <!-- 定义 ERROR 日志的输出方式 -->
    <appender name="FILE_ERROR" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <!-- 下面为配置只输出 error 级别的日志 -->
        <filter class="ch.qos.logback.classic.filter.LevelFilter">
            <level>ERROR</level>
            <onMismatch>DENY</onMismatch>
            <onMatch>ACCEPT</onMatch>
        </filter>

        <!-- 日志文件路径,日志文件名称 -->
        <File>${LOG_HOME}/err_${LOG_PREFIX}.log</File>

        <!-- 设置滚动策略,当天的日志大小超过 ${MAX_FILE_SIZE} 文件大小时候,新的内容写入新的文件,默认大小为 10MB -->
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <!-- 日志文件路径,新的 ERR 日志文件名称,"i" 是个变量 -->
            <FileNamePattern>${LOG_DIR}/err_${LOG_PREFIX}%i.log</FileNamePattern>

            <!-- 配置日志的滚动时间,表示只保留最近 10 天的日志 -->
            <MaxHistory>${MAX_HISTORY}</MaxHistory>

            <!-- 当天的日志大小超过 ${MAX_FILE_SIZE} 文件大小时候,新的内容写入新的文件,默认大小为 10MB -->
            <timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
                <maxFileSize>${MAX_FILE_SIZE}</maxFileSize>
            </timeBasedFileNamingAndTriggeringPolicy>
        </rollingPolicy>

        <!-- 输出的日志内容格式化 -->
        <layout class="ch.qos.logback.classic.PatternLayout">
            <pattern>${LOG_MSG}</pattern>
        </layout>
    </appender>

    <!-- additivity 设为 false,则 logger 内容不附加至 root,配置以配置包下的所有类的日志的打印,级别是 ERROR -->
    <logger name="org.springframework" level="ERROR"/>
    <logger name="org.apache.commons" level="ERROR"/>

    <!-- ${LOG_ROOT_LEVEL} 日志级别-->
    <root level="${LOG_ROOT_LEVEL}">
        <!-- 标识这个 "${STDOUT}" 将会添加到这个 logger -->
        <appender-ref ref="${STDOUT}"/>
        <!-- FILE_ALL 日志输出添加到 logger -->
        <appender-ref ref="FILE_ALL"/>
        <!-- FILE_ERROR 日志输出添加到 logger -->
        <appender-ref ref="FILE_ERROR"/>
    </root>
</configuration>

​ Profile 相关的配置:

xml 复制代码
<?xml version="1.0" encoding="UTF-8" ?>
<configuration>
	<include resource="org/springframework/boot/logging/logback/base.xml"/>
    <!-- roll by day -->
    <appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <fileNamePattern>logs/springboot-logback.%d{yyyy-MM-dd}.log</fileNamePattern>
            <maxHistory>30</maxHistory>
        </rollingPolicy>
        <encoder>
            <pattern>%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} - %msg%n</pattern>
        </encoder>
    </appender>
    
    <!-- dev -->
    <logger name="org.springframework.web" level="INFO"/>
    <root level="INFO">
        <appender-ref ref="FILE"/>
    </root>
    
    <!-- test or production -->
    <springProfile name="test,prod">
        <logger name="org.springframework.web" level="INFO"/>
        <logger name="io.zhanbo.springboot" level="INFO"/>
        <root level="INFO">
            <appender-ref ref="FILE"/>
        </root>
    </springProfile>
    
</configuration>

5.3 参考文档

  • Logback 官网

Chapter 6: Layouts

  • Logback 官网文档

Logback Manual

  • Logback 中 Encoder Pattern
xml 复制代码
    <encoder>
        <pattern>%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} - %msg%n</pattern>
    </encoder>

6. 配置热部署 devtools 工具

在 SpringBoot 开发调试中,如果每行代码的修改都需要重新启动再调试,可能比较费时间;SpringBoot团队针对此问题提供了 spring-boot-devtools(简称 devtools)插件,它视图提升开发调试的效率。

6.1 概述

什么是热部署和热加载?

热部署和热加载是在应用正常运行的时候,自动更新(重新加载或者替换 class 等)应用的一种能力。(PS:spring-boot-devtools 提供的方案也是要重启的,只是无需手动重启能实现加载而已。)

​ 严格意义上,我们需要区分下热部署和热加载。对于 Java 项目而言:

  • 热部署
    • 在服务器运行时重新部署项目;
    • 它是直接重新加载整个应用,这种方式会释放内存,比热加载更加干净彻底,但同时也更费时间。
  • 热加载
    • 在运行时重新加载 class,从而升级应用;
    • 热加载的实现原理主要依赖 Java 类加载机制,在实现方式可以概括为在容器启动的时候起一条后台线程,定时的检测类文件的时间戳变化,如果类的时间戳变掉了,则将类重新载入;
    • 对比反射机制,反射是在运行时获取类信息,通过动态的调用来改变程序行为;热加载则是在运行时通过重新加载改变类信息,直接改变程序行为。

什么是 LiveLoad?

​ LiveLoad 是提供浏览器客户端自动加载更新的工具,分为 LiveLoad 服务器和 LiveLoad 浏览器插件两部分;devtools 中已经集成了 LiveLoad 服务器,所以如果我们开发的是 web 应用,并且期望浏览器自动刷新,这时候可以考虑 LiveLoad。

​ 同一时间只能运行一个 LiveReload 服务器。开始应用程序之前,请确保没有其它 LiveReload 服务器正在运行。如果从 IDE 启动多个应用程序,则只有第一个应用程序将支持 LiveReload。

6.2 配置 devtools 实现热部署

我们通过如下配置来实现自动重启方式的热部署

POM 配置

​ 添加依赖:

xml 复制代码
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-devtools</artifactId>
    <optional>true</optional> <!-- 可以防止将 devtools 依赖传递到其它模块中 -->
</dependency>

IDEA 配置

如果你使用 IDEA 开发工具,通常有如下两种方式:

  • 方式一:无任何配置时,手动触发重启更新(Ctrl + F9)

(也可以用 mvn compile 编译触发重启更新)

  • 方式二:IDEA 需开启运行时编译,自动重启更新

    1. 设置 1:File -> Build,Execution,Deployment -> Compile

      勾选:Mark project automatically

    2. 设置 2:

      1. 旧版 IDEA:ctrl + alt + shift + /,选择:Registry,勾选:compiler.automake.allow.when.app.running
      2. 新版 IDEA:File -> setting -> Advanced Settings

application.yml 配置

yml 复制代码
spring:
  devtools:
    restart:
      enabled: true # 开启热部署
      additional-paths: src/main/java # 重启目录
      exclude: WEB-INF/** # 排除目录
  thymeleaf:
    cache: false # 关闭 thymeleaf 缓存

使用 LiveLoad

​ spring-boot-devtools 模块包含嵌入式 LiveReload 服务器,可以在资源更改时用于触发浏览器刷新。LiveReload 浏览器拓展程序支持 Chrome,Firefox 和 Safari。

​ 如果你不想在应用程序运行时启动 LiveReload 服务器,则可以将 spring.devtools.livereload.enabled 属性设置为 false。

​ 同一时间只能运行一个 LiveReload 服务器。开始应用程序之前,请确保没有其它 LiveReload 服务器正在运行。如果从 IDE 启动多个应用程序,则只有第一个应用程序将支持 LiveReload。

6.3 进一步理解

虽然一些开发者会使用 devtool 工具,但是很少有人能够深入理解的;让我们理解如下几个问题,帮助你进一步理解。

devtool 的原理?为何会自动重启?

为什么同样是重启应用,为什么不手动重启,而是建议使用 spring-boot-devtools 进行热部署重启?

​ spring-boot-devtools 使用了两个类加载器 ClassLoader,一个 ClassLoader 加载不会发生更改的类(第三方 jar 包),另一个 ClassLoader(restart ClassLoader)加载会更改的类(自定义的类)。

后台启动一个**文件监听线程(Filter Watcher),监测的目录中的文件发生变动时,原来的 restart ClassLoader 被丢弃,将会重新加载新的 restart ClassLoader**。

​ 因为文件变动后,第三方 jar 包不再重新加载,只加载自定义的类,加载的类比较少,所以重启比较快。

这也是为什么,同样是重启应用,为什么不手动重启,建议使用 spring-boot-devtools 进行热部署重启。

​ 在自动重启中有几点需要注意:

  • 自动重启会记录日志的:记录在什么情况下重启的日志

​ 可以通过如下关闭:

yml 复制代码
spring:
  devtools:
    restart:
      log-condition-evaluation-delta: false
  • 排除一些不需要自动重启的资源

​ 某些资源在更改时不一定需要触发重新启动。默认情况下,改变资源 /META-INF/maven/META-INF/resources/resources/static/public/templates 不触发重新启动,但却会触发现场重装。如果要自定义这些排除项,可以使用该 spring.devtools.restart.exclude 属性。例如,要仅排除 /static/public 可以设置以下属性:

yml 复制代码
spring:
  devtools:
    restart:
      exclude: "static/**,public/**"

​ 如果要保留这些默认值并添加其它排除项,请改用该 spring.devtools.restart.additional-exclude 属性。

  • 自定义重启类加载器

​ 重启功能是通过使用两个类加载器来实现的。对于大多数应用程序,这种方法效果很好。但是,它有时会导致类加载问题。

​ 默认情况下,IDE 中的任何打开项目都使用 "重启" 类加载器加载,任何常规 .jar 文件都使用 "基本" 类加载器加载。如果你处理一个多模块项目,并且不是每个模块都导入到你的 IDE 中,你可能需要自定义一些东西。为此,你可以创建一个 META-INF/spring-devtools.properties 文件。

​ 该 spring-devtools.properties 文件可以包含以 restart.excluderestart.include 为前缀的属性。该 include 元素是应该被拉高到 "重启" 的类加载器的项目,以及 exclude 要素是应该向下推入 "Base" 类加载器的项目。该属性的值是应用于类路径的正则表达式模板,如以下示例所示:

yml 复制代码
restart:
  exclude:
    companycommonlibs: "/mycorp-common-[\\w\\d-\\.]/(build|bin|out|target)/"
  include:
    projectcommon: "/mycorp-myproj-[\\w\\d-\\.]+\\.jar"

更多信息查看:Developer Tools :: Spring Boot

devtool 是否会被打包进 jar?

devtool 原则上来说应该是只在开发调试的时候使用,而在生产环境运行 jar 包时是不需要的,所以 Spring 打包会不会把它打进 JAR 呢?

  • 默认情况下,不会被打包进 JAR

​ 运行打包的应用程序时,开发人员工具会自动禁用。如果通过 java -jar` 或者其它特殊的类加载器进行启动时,都会被认为是 "生产环境的应用"。

  • 如果我们期望远程调试应用

生产环境勿用,只有在受信任的网络上运行或使用 SSL 进行保护时,才应启用它。

​ 在这种情况下,devtool 也具备远程调试的能力:远程客户端应用程序旨在从你的 IDE 中运行。你需要 org.springframework.boot.devtools.RemoteSpringApplication 使用与你连接的远程项目相同的类路径运行,应用程序的唯一必须参数是它连接到的远程 URL。

​ 例如,如果使用 IDEA 执行以下操作:

  1. 选择 Run Configurations ... 从 Run 菜单;
  2. 创建一个新的 Java Application "启动配置";
  3. 浏览 springboot-helloworld 项目;
  4. 使用 org.springframework.boot.devtools.RemoteSpringApplication 作为主类;
  5. 添加 http://localhost:8080Program arguments(或任何你的远程 URL)。

​ 正在运行的远程客户端可能类似于以下列表:

  .   ____          _                                              __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _          ___               _      \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` |        | _ \___ _ __  ___| |_ ___ \ \ \ \
 \\/  ___)| |_)| | | | | || (_| []::::::[]   / -_) '  \/ _ \  _/ -_) ) ) ) )
  '  |____| .__|_| |_|_| |_\__, |        |_|_\___|_|_|_\___/\__\___|/ / / /
 =========|_|==============|___/===================================/_/_/_/
 :: Spring Boot Remote ::  (v2.6.13)

2024-12-26 15:08:31.173  INFO 25848 --- [           main] o.s.b.devtools.RemoteSpringApplication   : Starting RemoteSpringApplication v2.6.13 using Java 1.8.0_351 on LAPTOP-T8OAQR71 with PID 25848 (D:\Maven\apache-maven-3.6.1\mvn_resp\org\springframework\boot\spring-boot-devtools\2.6.13\spring-boot-devtools-2.6.13.jar started by user in F:\Code\springboot-helloworld)
2024-12-26 15:08:31.176  INFO 25848 --- [           main] o.s.b.devtools.RemoteSpringApplication   : No active profile set, falling back to 1 default profile: "default"
2024-12-26 15:08:31.324  WARN 25848 --- [           main] o.s.b.d.r.c.RemoteClientConfiguration    : The connection to http://localhost:8080 is insecure. You should use a URL starting with 'https://'.
2024-12-26 15:08:31.366  WARN 25848 --- [           main] o.s.b.d.a.OptionalLiveReloadServer       : Unable to start LiveReload server
2024-12-26 15:08:31.379  INFO 25848 --- [           main] o.s.b.devtools.RemoteSpringApplication   : Started RemoteSpringApplication in 0.52 seconds (JVM running for 0.853)

devtool 为何会默认禁用缓存选项?

SpringBoot 支持的一些库使用缓存来提高性能。例如,模板引擎缓存已编译的模板以避免重复解析模板文件。此外,Spring MVC 可以在提供静态资源时向响应添加 HTTP 缓存标头。

​ 虽然缓存在生产中非常有益,但在开发过程中可能会适得其反,使用 spring-boot-devtools 模块时是不需要手动设置这些属性的,因为 spring-boot-devtools 会自动进行设置。

​ 那么会自动设置哪些配置呢?可以在 devtools-property-defaults.properties 找到对应的默认配置。

properties 复制代码
server.error.include-binding-errors=always
server.error.include-message=always
server.error.include-stacktrace=always
server.servlet.jsp.init-parameters.development=true
server.servlet.session.persistent=true
spring.freemarker.cache=false
spring.groovy.template.cache=false
spring.h2.console.enabled=true
spring.mustache.cache=false
spring.mvc.log-resolved-exception=true
spring.reactor.debug=true
spring.template.provider.cache=false
spring.thymeleaf.cache=false
spring.web.resources.cache.period=0
spring.web.resources.chain.cache=false

​ 当然如果你不想让应用属性被 spring-boot-devtools 默认设置,可以通过 spring:devtools:add-properties: falseapplication.yml 中。

devtool 是否可以给所有 Springboot 应用做全局的配置?

可以通过将 spring-boot-devtools.yml 文件添加到 $HOME/.config/spring-boot 目录来配置全局 devtools 设置。

​ 添加到这些文件的任何属性都适用于你机器上使用 devtools 的所有 Spring Boot 应用程序。例如,要将重新启动配置为始终使用触发器文件,你需要将以下属性添加到你的 spring-boot-devtools 文件中:

yml 复制代码
spring:
	devtools:
		restart:
			trigger-file: ".reloadtrigger"

如果不用 devtool,还有什么选择?

在实际的开发过程中,通常不会用 devtool 工具,因为:

  • devtool 本身基于重启方式,这种仍然不是真正的热替换方案,JRebel 才是(它是收费的);
  • 开发调试最重要的还是一种权衡
    • 自动重启的开销如果和手动重启没有什么太大差别,那么还不如手动重启(按需重启);
    • 多数情况下,如果是 方法内部的修改或者静态资源的修改,在 IDEA 中是可以通过 Rebuild (Ctrl +Shift + F9)进行热更新的。
  • 此外还有一个工具 spring loaded,可实现修改类文件的热部署,具体可看:spring-projects/spring-loaded: Java agent that enables class reloading in a running JVM

7. 常用注解

@SpringBootApplication

java 复制代码
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@Configuration
@EnableAutoConfiguration
@ComponentScan
public @interface SpringBootApplication {
    Class<?>[] exclude() default {};
}

​ 定义在 main 方法入口类处,用于启动 springboot 应用项目。

@EnableAutoConfiguration

​ 让 springboot 根据类路径中的 jar 包依赖当前项目进行自动配置。

​ 在 src/main/resourcesMETA-INF/spring.factories

java 复制代码
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.boot.autoconfigure.admin.SpringApplicationAdminJmxAutoConfiguration,\
org.springframework.boot.autoconfigure.aop.AopAutoConfiguration
若有多个自动配置,用 "," 隔开

@ImportResource

​ 加载 xml 配置,一般是放在启动 main 类上。

java 复制代码
@ImportResource("classpath*:/spring/*.xml")  单个

@ImportResource({"classpath*:/spring/1.xml","classpath*:/spring/2.xml"})   多个

@Value

application.properties 定义属性,直接使用 @Value 注入即可。

java 复制代码
public class A{
	 @Value("${push.start:0}")    如果缺失,默认值为0
     private Long  id;
}

@ConfigurationProperties(prefix="person")

​ 可以新建一个 properties 文件,ConfigurationProperties 的属性 prefix 指定 properties 的配置的前缀,通过 location 指定 properties 文件的位置。

java 复制代码
@ConfigurationProperties(prefix="person")
public class PersonProperties {
	/* name 和 age 属性必须保持与application.properties中的属性一致*/
	private String name ;
	private int age;
}

@EnableConfigurationProperties

​ 用 @EnableConfigurationProperties 注解使得 @ConfigurationProperties 生效,并从 IOC 容器中获取 bean。

@RestController

​ 组合 @Controller@ResponseBody,当开发一个和页面交互数据的控制时,比如 bbs-web 的 api 接口需要此注解。

@RequestMappling("/api/test")

​ 用来映射 web 请求(访问路径和参数)、处理类和方法,可以注解在类或方法上。注解在方法上的路径会继承注解在类上的路径。

​ produces 属性:定制返回的 response 的媒体类型和字符集,或需返回值是 json 对象。

java 复制代码
@RequestMapping(value="/api2/copper",produces="application/json;charset=UTF-8",method = RequestMethod.POST)

@RequestParam

​ 获取 request 请求的参数值。

java 复制代码
public List<User> getAllUser(@RequestParam(value = "pageIndex", required = false) Integer pageIndex,
                            @RequestParam(value = "pageSize", required = false) Integer pageSize)

@ResponseBody

​ 支持将返回值放在 response 体内,而不是返回一个页面。比如 Ajax 接口,可以用此注解返回数据而不是页面。此注解可以放置在返回值前或方法前。

java 复制代码
另一个玩法,可以不用@ResponseBody。
继承FastJsonHttpMessageConverter类并对writeInternal方法扩展,在spring响应结果时,再次拦截、加工结果
// stringResult: json返回结果
//HttpOutputMessage outputMessage

 byte[] payload = stringResult.getBytes();
 outputMessage.getHeaders().setContentType(META_TYPE);
 outputMessage.getHeaders().setContentLength(payload.length);
 outputMessage.getBody().write(payload);
 outputMessage.getBody().flush();

@Bean

@Bean(name = "bean 的名字", initMethod = "初始化时调用方法的名字", destoryMethod = "close")

​ 定义在方法上,在容器内初始化一个 bean 实例类。

java 复制代码
@Bean(destroyMethod="close")
@ConditionalOnMissingBean
public PersonService registryService() {
    return new PersonService();
}

@Service

​ 用于标注业务层组件。

@Controller

​ 用于标注控制层组件(如 struts 中的 action)。

@Repository

​ 用于标注数据访问组件,即 DAO 组件。

@Component

​ 泛指组件,当组件不好归类时,我们可以使用这个注解进行标注。

@PostConstruct

​ spring 容器初始化时,要执行该方法。

java 复制代码
@PostConstruct  
public void init() {   
}   

@PathVariable

​ 用来获取请求 url 中的动态参数。

java 复制代码
@Controller  
public class TestController {  

     @RequestMapping(value="/user/{userId}/roles/{roleId}",method = RequestMethod.GET)  
     public String getLogin(@PathVariable("userId") String userId,  
         @PathVariable("roleId") String roleId){
           
         System.out.println("User Id : " + userId);  
         System.out.println("Role Id : " + roleId);  
         return "hello";  
     
     }  
}

@ComponentScan

​ 注解会告知 Spring 扫描指定的包来初始化 Spring。

java 复制代码
@ComponentScan(basePackages = "io.zhanbo.xx")

@EnableZuulProxy

​ 路由网关的主要目的是为了让所有的微服务对外只有一个接口,我们只需访问一个网关地址,即可由网关将所有的请求代理到不同的服务中。Spring Cloud 通过 Zuul 来实现的,支持自动路由映射到在 Eureka Server 上注册的服务。Spring Cloud 提供了注解 @EnableZuulProxy 来启动路由代理。

@Autowired

​ 在默认情况下使用 @Autowired 注解进行自动注入时,Spring 容器中匹配的候选 Bean 数目必须由且仅有一个。当找不到一个匹配的 Bean 时,Spring 容器将抛出 BeanCreationException 异常,并指出必须至少拥有一个匹配的 Bean。

​ 当不能确定 SPring 容器中一定拥有某个类的 Bean 时,可以在需要自动注入该类 Bean 的地方可以使用 @Autowired(required = false),这等于告诉 Spring:在找不到匹配 Bean 时不报错。

@Configuration

​ 注解作用在类上,表明该类是一个配置信息类,可以给这个配置类也起一个名称。

java 复制代码
@Configuration("name")//表示这是一个配置信息类,可以给这个配置类也起一个名称
@ComponentScan("spring4")//类似于xml中的<context:component-scan base-package="spring4"/>
public class Config {

    @Autowired//自动注入,如果容器中有多个符合的bean时,需要进一步明确
    @Qualifier("compent")//进一步指明注入bean名称为compent的bean
    private Compent compent;

    @Bean//类似于xml中的<bean id="newbean" class="spring4.Compent"/>
    public Compent newbean(){
        return new Compent();
    }   
}

@Import(Config.class)

​ 导入 Config 配置类里实例化的 bean。

java 复制代码
@Configuration
public class CDConfig {

    @Bean   // 将SgtPeppers注册为 SpringContext中的bean
    public CompactDisc compactDisc() {
        return new CompactDisc();  // CompactDisc类型的
    }
}

@Configuration
@Import(CDConfig.class)  //导入CDConfig的配置
public class CDPlayerConfig {

    @Bean(name = "cDPlayer")
    public CDPlayer cdPlayer(CompactDisc compactDisc) {  
         // 这里会注入CompactDisc类型的bean
         // 这里注入的这个bean是CDConfig.class中的CompactDisc类型的那个bean
        return new CDPlayer(compactDisc);
    }
}

@Order

​ @Order(1),值越小,优先级越高,越先运行。

@ConditionalOnExpression

​ 开关为 true 的时候才实例化 bean。

java 复制代码
@Configuration
@ConditionalOnExpression("${enabled:false}")
public class BigpipeConfiguration {
    @Bean
    public OrderMessageMonitor orderMessageMonitor(ConfigContext configContext) {
        return new OrderMessageMonitor(configContext);
    }
}

@ConditionalOnProperty

​ 这个注解能够控制某个 @Configuration 是否生效。具体操作是通过其两个属性 name 以及 havingValue 来实现的,其中 name 用来从 application.properties 中读取某个属性值,如果该值为空,则返回 false;如果值不为空,则将该值与 havingValue 指定的值进行比较,如果一样则返回 true;否则返回 false。如果返回值为 false,则该 configuration 不生效;为 true 则生效。

@ConditionalOnClass

​ 该注解的参数对应的类必须存在,否则不解析该注解修饰的配置类。

java 复制代码
@Configuration
@ConditionalOnClass({Gson.class})
public class GsonAutoConfiguration {
    public GsonAutoConfiguration() {
    }

    @Bean
    @ConditionalOnMissingBean
    public Gson gson() {
        return new Gson();
    }
}

@ConditionalOnMissingClass({ApplicationManager.class})

​ 如果存在它修饰的类的 bean,则不需要再创建这个 bean。

@ConditionOnMissingBean(name = "example")

​ 表示如果 name 为 "example" 的 bean 存在,该注解修饰的代码块不执行。

相关推荐
utmhikari6 分钟前
【架构艺术】Go语言微服务monorepo的代码架构设计
后端·微服务·架构·golang·monorepo
蜡笔小新星8 分钟前
Flask项目框架
开发语言·前端·经验分享·后端·python·学习·flask
计算机学姐12 分钟前
基于Asp.net的驾校管理系统
vue.js·后端·mysql·sqlserver·c#·asp.net·.netcore
欢乐少年19042 小时前
SpringBoot集成Sentry日志收集-3 (Spring Boot集成)
spring boot·后端·sentry
夏天的味道٥3 小时前
使用 Java 执行 SQL 语句和存储过程
java·开发语言·sql
冰糖码奇朵5 小时前
大数据表高效导入导出解决方案,mysql数据库LOAD DATA命令和INTO OUTFILE命令详解
java·数据库·sql·mysql
好教员好5 小时前
【Spring】整合【SpringMVC】
java·spring
浪九天6 小时前
Java直通车系列13【Spring MVC】(Spring MVC常用注解)
java·后端·spring
堕落年代6 小时前
Maven匹配机制和仓库库设置
java·maven
功德+n6 小时前
Maven 使用指南:基础 + 进阶 + 高级用法
java·开发语言·maven