Spring翻译为中文是“春天”,的确,在某段时间内,它给Java开发人员带来过春天,但是随着项目规模的扩大,Spring需要配置的地方就越来越多,夸张点说,“配置两小时,Coding五分钟”。这种纷繁复杂的xml配置随着软件行业一步步地发展,必将逐步退出历史舞台。此时,springboot的出现改变了java的开发,从此变得便利。

简介

Spring Boot是由Pivotal团队提供的全新框架,其设计目的是用来简化新Spring应用的初始搭建以及开发过程。该框架使用了特定的方式来进行配置,从而使开发人员不再需要定义样板化的配置。通过这种方式,Spring Boot致力于在蓬勃发展的快速应用开发领域(rapid application development)成为领导者。

SpringBoot所具备的特征有:

  • 可以创建独立的Spring应用程序,并且基于其Maven或Gradle插件,可以创建可执行的JARs和WARs;

  • 内嵌Tomcat或Jetty等Servlet容器;

  • 提供自动配置的“starter”项目对象模型(POMS)以简化Maven配置;

  • 尽可能自动配置Spring容器;

  • 提供准备好的特性,如指标、健康检查和外部化配置;

  • 绝对没有代码生成,不需要XML配置。

在过去,开发者要让一个Spring项目启动,往往需要配置很多的xml配置文件,但是在使用SpringBoot之后,开发人员无需写一行xml,就可以直接将整个项目启动,这种“零配置”的做法减轻了开发人员很多的工作量,可以让开发人员一心扑在业务逻辑的设计上,使项目的逻辑更加完善。

除此之外,其采用了JavaConfig的配置风格,导入组件的方式也由原来的直接配置改为@EnableXXXX,这种纯Java代码的配置和导入组件的方式,使代码看上去更加的优雅,所以SpringBoot如今受到大小公司和大多数程序员的青睐,不是没有原因的。

SpringBoot之所以可以做到简化配置文件直接启动,无外乎是其内部的两种设计策略:开箱即用和约定大于配置

  • 开箱即用:在开发过程中,通过maven项目的pom文件中添加相关依赖包,然后通过相应的注解来代替繁琐的XML配置以管理对象的生命周期。

  • 约定大于配置:由SpringBoot本身来配置目标结构,由开发者在结构中添加信息的软件设计范式。这一特点虽降低了部分灵活性,增加了BUG定位的复杂性,但减少了开发人员需要做出决定的数量,同时减少了大量的XML配置,并且可以将代码编译、测试和打包等工作自动化。

HelloWorld

前面介绍了springboot的两种内部设计策略,想要真正体验到springboot的开箱即用,还在springboot的项目搭建上。

首先说明Springboot的各种版本:

  • CURRENT:最新版本(但不一定稳定)

  • GA:General Availability,正式发布的版本,官方推荐使用该版本,国外很多项目都是使用GA来表示正式发布版本的

  • SNAPSHOT:快照版,可以稳定使用,且该版本会一直进行小量的优化和改进

  • PRE:预览版,主要是用来内部开发人员和测试人员测试使用,因此不建议使用

  • RC:Release,该版本已经相当成熟了,基本上不存在导致错误的BUG,与即将发行的正式版相差无几。

日常使用中通常选择GA版 ,版本稳定性排序为:PRE < SNAPSHOT < RC < GA

start.spring.io

进入快速创建SpringBoot项目的网址Spring Initializr,选择项目的各大选项:

  • Project:说明是什么类型的项目

    • Maven Project:Maven项目(默认)

    • Gradle Project:Gradle项目

  • Language:使用什么计算机语言开发

    • Java(默认)

    • Kotlin

    • Groovy

  • Spring Boot:选择Springboot版本(版本不一一列举出来)

    • 2.6.7(SNAPSHOT)(笔者选择这个)

  • Project Metadata:项目的大致情况

    • Group:开发团队,一般为com.xxxx

    • Artifact:项目名称

    • Name:项目名

    • Description:项目描述

    • Package name:项目的包名

    • Packaging:打包的类型

      • Jar:普通的Jar包(默认)

      • War:网络War包

    • Java:java版本(版本不一一列举出来)

      • 8(笔者选择)

在完成项目填写后,点击GENERATE即可生成项目,浏览器会自动下载保存到本地

Intellij IDEA Spring Initializr

日常使用idea的时候,使用Spring Initializr去创建项目会十分麻烦,这个时候可以在idea里面的Spring Initializr去创建springboot项目,idea会通过调用start.spring.io创建springboot的项目,十分方便。

打开File -> New -> Project,在New Project窗口中点击Spring Initializr,之后点击Next(若没有设置SDK,请设置IDEA的SDK,Choose starter service URL中选择Default即可)。

在Spring Initializr Project Settings的窗口的设置和在Spring Initializr网页中的设置是一模一样的,参数详情查看上一节即可。输入参数完毕后,点击Next,之后选择Web -> springboot web即可。输入项目名称后,即可开始使用springboot项目。

文件结构

在进行项目搭建后,文件大致结构如下:

  • src:项目的主要文件

    • main:放置java包的文件夹

      • com.xxx:java包

      • xxxxApplication.java:springboot启动类

    • resources:资源文件

      • static:存放静态文件(css、js等)

      • templates:存放html文件等

      • application.properties:springboot的配置文件,可修改为yml

  • .gitignore:上传git需要忽略的文件

  • pom.xml:maven的pom配置文件,用于加入新的第三方依赖

值得注意的是,在html引入css、js文件夹时,只需要按照同级目录的方式引入即可,以bootstrap为例:

<link rel="stylesheet" href="css/bootstrap.min.css"/>

spring-boot-starter

Spring Boot Starter是Spring Boot框架的一种预配置方案,用于快速构建常见功能的Spring Boot应用程序。它提供了一组相关的依赖项,可以一次性安装

每个Spring Boot Starter都是一个独立的模块,提供了一组特定的功能。例如:

  1. spring-boot-starter-web - 提供基本的Web功能,包括Spring MVC和Tomcat。

  2. spring-boot-starter-data-jpa - 提供数据持久化功能,包括JPA和Hibernate。

  3. spring-boot-starter-test - 提供测试功能,包括JUnit,Mockito和Spring Test。

通过使用Spring Boot Starter,可以轻松构建复杂的Spring Boot应用程序,并节省大量的配置时间。

现在拆解spring-boot-starter-web,详细的pom.xml配置如下:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>

点进去,可以看到:

spring-boot-starter,里面封装了:

  • springboot(spring容器)

  • spring-boot-autoconfigure(springboot的自动装配)

  • spring-boot-starter-logging(springboot的日志)

  • jakarta.annotation-api

  • spring-core

  • snakeyaml(yml配置)

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter</artifactId>
    <version>2.6.6</version>
    <scope>compile</scope>
</dependency>

spring-boot-starter-json,封装了json相关的包

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-json</artifactId>
    <version>2.6.6</version>
    <scope>compile</scope>
</dependency>

spring-boot-starter-tomcat,封装了tomcat相关的包

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-tomcat</artifactId>
    <version>2.6.6</version>
    <scope>compile</scope>
</dependency>

spring-web,封装了spring的bean相关内容

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-web</artifactId>
    <version>5.3.18</version>
    <scope>compile</scope>
</dependency>

spring-webmvc,封装了springmvc的内容

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-webmvc</artifactId>
    <version>5.3.18</version>
    <scope>compile</scope>
</dependency>

application.yml

修改application.properties为application.yml,理由如下:

  • 如果工程中同时存在application.properties文件和application.yml文件,yml文件会先加载

  • yml文件通过来分层,结构上有比较明显的层次感

  • yml配置文件上比properties简洁

存放位置

resources文件夹

配置细节

yml采用树状结构,结构如下

server:
  port: 8081

改配置将整个项目的端口改为了8081,以下是springboot的jdbc的yml配置样式

spring:
  datasource:
    # UTC: Coordinated Universal Time, 国际协调时间,也称世界标准时间。
    # 设置国际标准时间能够保证mysql连接不会出现时区错误,并且保证编号为UTF-8
    url: jdbc:mysql://localhost:3306/test?characterEncoding=utf-8&serverTimezone=UTC
    username: ****
    password: ****
    driver-class-name: com.mysql.jdbc.Driver
  security: # 简单配置springboot-Security的验证登录密码和账号
    user:
      name: admin
    password: admin
mybatis:
  mapper-locations: classpath:/mapper/*Mapper.xml # mapper文件存放位置
  type-aliases-package: smg.****.dao # mapper配置类存放包

项目运行

首先删除一些无关紧要的文件,分别为.mvn、HELP.md、mvnw、mvnw.cmd,使项目保持整洁。该操作为可选操作。

打开src -> main -> java -> com -> 包名 -> xxxApplication.java,可看到:

package com.example.demo;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class DemoApplication {

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

}

这就是springboot的运行程序,点击run,可以看到整个项目开始运行,若看到Started DemoApplication in 1.488 seconds (JVM running for 2.522)为项目运行成功。查看Tomcat started on port(s): 8080 (http) wih context path ''为项目运行的端口号。至此,springboot搭建完成。

名词解释

以下是一些在Spring Boot中经常使用的名词及其简要解释:

名词

解释

Auto-configuration

自动配置是Spring Boot框架的一种特性,用于配置应用程序所需的bean和配置信息。

Spring Application Context

Spring应用程序上下文是一个容器,用于管理应用程序中的bean。

Bean

Bean是一个对象,它是由Spring框架创建和管理的。

Component Scanning

组件扫描是查找并注册应用程序中的组件的过程。

Dependency Injection

依赖注入是一种设计模式,用于将对象的依赖关系注入到该对象中。

Restful Web Services

RESTful Web服务是一种创建Web服务的架构风格,它遵循REST架构原则。

JPA

JPA是Java持久化API的缩写,用于持久化Java对象。

Hibernate

Hibernate是一个ORM(对象关系映射)框架,用于在数据库和Java对象之间映射数据。

Spring MVC

Spring MVC是一个Web框架,用于创建Web应用程序。

Tomcat

Tomcat是一个Web服务器,用于运行Java Web应用程序。

Spring Boot CLI

Spring Boot命令行界面,用于快速创建和运行Spring Boot应用程序。

Actuator

Actuator是一组功能,用于监视和管理Spring Boot应用程序。

Spring Boot Initializer

Spring Boot Initializer是一个Web界面,用于创建基于Spring Boot的应用程序。

Spring Boot DevTools

Spring Boot DevTools是一组开发工具,用于提高开发人员的生产力。

Spring Boot Admin

Spring Boot Admin是一个Web界面,用于管理和监视Spring Boot应用程序。

Spring Boot Config Server

Spring Boot Config Server是一个配置服务器,用于存储应用程序的配置信息。

Spring Boot Actuator Endpoints

Spring Boot Actuator端点是提供给Actuator功能使用的URL路径。

Spring Boot Properties

Spring Boot属性是配置Spring Boot应用程序的配置文件。

Spring Boot Profile

Spring Boot配置文件可以定义多个配置文件,称为配置文件

层次结构

在Spring Boot应用程序中,常见的分层结构如下:

  1. 视图层/控制器(Controller)层:处理用户的请求,并将请求转发给服务层。

  2. 服务(Service)层:执行业务逻辑,调用数据访问层处理数据。

  3. 数据访问层(DAO层):与数据库交互,负责存储和检索数据。

  4. 实体(entity)层:表示数据库中的实体,通常使用JPA或Hibernate映射到数据库表。

服务(Service)层和数据访问层(DAO层)虽然都是处理数据的一层,但是却是两个独立的层,它们有以下区别:

  1. 职责不同:服务层负责执行业务逻辑,而数据访问层负责与数据库交互。

  2. 技术不同:服务层可以使用任何技术实现,而数据访问层通常使用ORM(对象关系映射)技术,如JPA或Hibernate。

  3. 交互方式不同:服务层通过接口与数据访问层进行交互,而数据访问层通过SQL语句与数据库进行交互。

  4. 可测试性不同:服务层的业务逻辑易于测试,而数据访问层的测试困难,需要使用模拟数据。

注解

Spring Boot提供了许多注解来帮助编写应用程序。一些最常用的注解包括:

  1. @SpringBootApplication:标识一个Spring Boot应用,包含了自动配置、扫描包以及其他特性。

  2. @RestController:标识一个RESTful风格的控制器,返回的对象将作为HTTP响应体返回。

  3. @Controller:标识一个控制器,用于处理HTTP请求。

  4. @RequestMapping:用于映射一个URL路径到控制器的处理方法上。

  5. @RequestParam:用于从请求中获取单个参数的值。

  6. @PathVariable:用于从请求路径中获取参数的值。

  7. @Autowired:自动注入一个bean,用于依赖注入。

  8. @Service:标识一个业务服务类。

  9. @Repository:标识一个数据仓库,主要用于持久化数据。

  10. @Component:标识一个组件,用于扫描。

  11. @Bean:声明一个Spring Bean,用于创建对象。

  12. @Value:用于从配置文件中读取一个值,并将其注入到bean中。

  13. @Configuration:标识一个配置类,用于声明配置信息。

  14. @Async:标识一个异步任务,用于执行异步操作。

  15. @Transactional:标识一个事务,用于声明事务的范围。

控制器(Controller)

在使用controller前,必须要引入spring-boot-starter-web这个包

新建Controller

首先新建类FirstController,并且在类前加注解@RestController表示该类为一个控制器,默认请求方法是:GET

新建方法,书写如下:

@RequestMapping("/helloworld")
public String helloworld(String name){
    System.out.println("从页面返回了"+name);
    return "hello  " + name;
}

@RequestMapping("/helloworld")中的地址,在浏览器中浏览它会调用该地址下的方法helloworld,从而执行方法中的逻辑,可以是数据库操作,计算等等

在浏览器中输入localhost:8081/helloworld?name=李明,可以看到返回成功

在进行更加详细的操作之前,说一下作者理解的【接口】。

接口,这里特指软件的接口,作者认为是对某些请求进行出的一个生产者。对某个网站进行抓包的时候,可以看到不仅仅有网站本身的url,还有其他的一些奇奇怪怪的url,这些url就是所说的接口,编辑controller这个过程也是编辑接口的过程

接口有返回和请求的资源,接下来是说明springboot中的controller可以接收、返回什么类型

路径变量

如果需要传参,可以使用下面的方法:

  1. 在请求路径中加入参数,使用占位符{}表示。

  2. 在方法的形参中加入对应的参数,使用@PathVariable注解表示该参数从路径中获取。

@RestController
public class HelloController {
    @RequestMapping("/hello/{name}")
    public String hello(@PathVariable String name) {
        return "Hello " + name + "!";
    }
}

形参和返回类型

控制器的形参类型可以是以下任意一种:

  • 基本数据类型(如 int、long 等)

  • 包装类(如 Integer、Long 等)

  • 字符串

  • POJO(普通 Java 对象)类

  • 数组

  • 列表

  • 哈希表

下面是一个控制器方法的示例,该方法接收一个字符串类型的形参:

@RequestMapping("/greet")
public String greet(@RequestParam("name") String name) {
    return "Hello, " + name + "!";
}

控制器的返回类型可以是以下任意一种:

  • void:当请求完成后不需要返回任何值时使用。

  • 基本数据类型

  • 包装类

  • 字符串

  • POJO 类

  • 集合(如列表、哈希表等)

  • ModelAndView(视图类,不推荐使用)

以下是一个控制器方法的示例,该方法返回一个 POJO 类:

@RequestMapping("/getUser")
public User getUser() {
    User user = new User();
    user.setId(1);
    user.setName("John Doe");
    return user;
}

RestFul

RESTful API是一种使用HTTP协议实现的Web服务,通过请求的方法(GET、POST、PUT、DELETE)和请求路径(URL)来操作资源。

控制器默认的HTTP请求方法是GET。此方法不需要任何请求主体。可以发送请求参数和路径变量来自定义或动态URL。

下面是一个使用 @RestController 实现 RESTful 风格控制器的例子:

@RestController
public class UserController {
    @RequestMapping(value = "/users", method = RequestMethod.GET)
    public List<User> getUsers() {
        //返回所有用户
    }

    @RequestMapping(value = "/users/{id}", method = RequestMethod.GET)
    public User getUser(@PathVariable int id) {
        //返回单个用户
    }

    @RequestMapping(value = "/users", method = RequestMethod.POST)
    public void addUser(@RequestBody User user) {
        //添加用户
    }

    @RequestMapping(value = "/users/{id}", method = RequestMethod.PUT)
    public void updateUser(@PathVariable int id, @RequestBody User user) {
        //更新用户
    }

    @RequestMapping(value = "/users/{id}", method = RequestMethod.DELETE)
    public void deleteUser(@PathVariable int id) {
        //删除用户
    }
}

AJAX

返回值使用@ResponseBody作为响应体输出资源,形参中使用@RequestBody作请求体请求资源

@RequestMapping(value = "/json")
public @ResponseBody String json(@RequestBody QueryBean qb) {
    System.out.println("这是我从AJAX传过来的数据"+qb.getId());
    JSONObject jsonObject = new JSONObject();

    jsonObject.put("name", "斗地主");
    jsonObject.put("status", "success");
    return jsonObject.toString();
}

JSONObject类是org.json包里面的,引入依赖即可

<dependency>
    <groupId>org.json</groupId>
    <artifactId>json</artifactId>
    <version>20220320</version>
</dependency>

实例中用的对象包装json中的键值对,该方式传入的形参还可以有如下:

String。该方法可以将json中的某个键值对进行拆分拿值,方法中参数名称必须和data指定的json格式对应

比如说请求的json格式为:{name:"1234",password:"1234"},接收的方法如下:

@RequestMapping("/login")
@ResponseBody
public string login(String name, String password){
    System.out.println("这是我从AJAX传过来的数据"+name+" "+password);
    JSONObject jsonObject = new JSONObject();

    jsonObject.put("name", "张三");
    jsonObject.put("status", "success");
    return jsonObject.toString();
}

数据库资源管理

数据库连接池

springboot 内置默认支持HikariCP数据库连接池,这里使用Druid作为线程池,Druid是阿里巴巴的一个开源项目,是一个数据库连接池的实现,结合了C3P0、DBCP、PROXOOL等DB池的优点。Druid不但提供连接池的功能,还提供监控功能,可以实时查看数据库连接池和SQL查询的工作情况。

引入依赖

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>druid-spring-boot-starter</artifactId>
    <version>1.2.8</version>
</dependency>

可以看到,即使使用druid,也是需要引入spring-boot-starter-jdbc,否则就会报.....ClassNotFoundException: org....jdbc....embedded.EmbeddedDatabaseType。

原因是Spring Boot底层都是采用Spring Data的方式进行统一处理。EmbeddedDatabaseType类在spring-boot-starter-jdbc依赖中。

SpringBoot属于Spring“全家桶”组件,Druid属于阿里巴巴旗下开发产品,所以SpringBoot整合Druid并不是由Spring组件提供依赖,而是由阿里巴巴提供。

注意,引入依赖之后,必须要对其进行配置,否则会报错

配置编写

以下配置是比较简单的配置,包含了druid监控平台的开启

spring:
  datasource:
    # 数据库访问配置, 使用druid数据源(默认数据源是HikariDataSource)
    type: com.alibaba.druid.pool.DruidDataSource
    druid:
      # 连接线程池
      driver-class-name: xxxxx
      url: xxxxx
      username: root
      password: 1234
      # 访问druid监控
      stat-view-servlet:
        enabled: true
        # 配置Servlet的访问路径:访问路径为/druid/**时,跳转到StatViewServlet,会自动转到Druid监控后台
        url-pattern: /druid/*
        # 是否能够重置数据
        reset-enable: false
        # 设置监控后台的访问账户及密码
        login-username: root
        login-password: 1234

Mybatis

有了连接池,我们还需要进行数据库的增删查改的操作,这个时候就可以引入Mybatis了,Mybatis是一款优秀的持久层框架。它支持定制化SQL、存储过程以及高级映射。学习过ssm框架的都不会陌生,这里就不介绍它的作用了。

引入依赖

<dependency>
    <groupId>org.mybatis.spring.boot</groupId>
    <artifactId>mybatis-spring-boot-starter</artifactId>
    <version>2.1.3</version>
</dependency>

配置文件

mybatis:
  # 指定 mapper.xml 的位置,通常位于resources文件夹中
  mapper-locations: classpath:mapper/*.xml
  # 扫描实体类的位置,在此处指明扫描实体类的包,在 mapper.xml 中就可以不写实体类的全路径名
  type-aliases-package: com.example.bean

Mysql

连接mysql时,首先引入mysql的驱动包

<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
</dependency>

配置文件中的url添加为jdbc:mysql://127.0.0.1:3306/database,driver-class-name添加为com.mysql.cj.jdbc.Driver

配置完毕,开始编写数据库交互

编写实体类

在Mybatis中,实体类需要满足以下要求:

  1. 属性名必须与数据库字段名一一对应

  2. 属性必须标记为public或者有对应的getter和setter方法

  3. 实体类必须有一个无参数的构造函数(在新建类时自动新建)。

在Mysql中添加名叫Test01的表,属性为idname,添加包TableBean,里面加入实体类Test01,对应表添加属性和gettersetter

public class Test01 {
    private int id;
    private String name;

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

添加映射文件

resources中添加mapper文件夹,并添加Test01.xml。该位置和配置文件中的mapper-locations: classpath:mapper/*.xml一致,添加如下文本:

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!-- namespace:命名空间,用于隔离sql,用于动态代理 -->
<mapper namespace="com.example.Mapper.Test01Mapper">
    <select id="selectall" resultType="Test01">
        Select * from test01
    </select>
</mapper>

添加映射类

添加映射类,加@Mapper标明该接口为映射类,也可以在启动类中添加@MapperScan注解扫描指定包下的 mapper 接口,这样就不用在每个 mapper 接口上都标注 @Mapper 注解了。

@Mapper
public interface Test01Mapper {
    List<Test01> selectall();
}

Service编写

首先编写service接口,新建Service包,添加Test01Service.java,方法与mapper接口一致

public interface Test01Service {
    public List<Test01> selectall();
}

其次编写实现类,添加Implement包,添加Test01Impl.javaimplements Test01Service

@Service("Test01Service")
public class Test01Impl implements Test01Service {

    @Autowired
    Test01Mapper mapper;

    @Override
    public List<Test01> selectall() {
        return mapper.selectall();
    }
}

Controller调用

首先在controller里面织入mapper类

@Autowired
private Test01Service service;

然后编写controller后直接调用即可

@RequestMapping(value = "/mybatisTest")
    public String mybatisTest(){
        List<Test01> list = service.selectall();
        for (int i = 0; i < list.size(); i++) {
            System.out.println("id: "+list.get(i).getId()+"name: "+list.get(i).getName());
        }
        return list.toString();
    }

开启项目,在浏览器中输入localhost:8081/mybatisTest,就可以看到调用了数据库并返回资源

实际上,controller也可以直接调用mapper接口来对数据库进行操作,但是不推荐,像

@Autowired
private Test01Mapper mapper;

Redis

Redis 是完全开源的,遵守 BSD 协议,是一个高性能的 key-value 数据库。它的性能极高,Redis能读的速度是110000次/s,写的速度是81000次/s,具有丰富的特性。springboot中当然也可以整合redis,这里已经默认读者安装了redis,并且懂得了如何操作redis。

引入依赖

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>

配置文件

spring:
  redis:
    # Redis服务器地址
    host: 127.0.0.1
    #Redis服务器连接端口
    port: 6379
    # Redis服务器连接密码(默认为空)
    #password:
    # Redis数据库索引(默认为0)
    database: 1
    # 连接超时时间(毫秒)
    timeout: 5000

工具类编写

来源:Spring Boot整合Redis代码详解,四步搞定! - 腾讯云开发者社区-腾讯云 (tencent.com)

可以自行更改,使用时直接调用即可

package com.example.springbootredisdemo.Utils;

import java.util.Map;
import java.util.Set;
import java.util.concurrent.TimeUnit;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;

/**
 * redis工具类
 */
@Component
public class RedisOperator {

//    @Autowired
//    private RedisTemplate<String, Object> redisTemplate;

    @Autowired
    private StringRedisTemplate redisTemplate;

    // Key(键),简单的key-value操作

    /**
     * 实现命令:TTL key,以秒为单位,返回给定 key的剩余生存时间(TTL, time to live)。
     *
     * @param key
     * @return
     */
    public long ttl(String key) {
        return redisTemplate.getExpire(key);
    }

    /**
     * 实现命令:expire 设置过期时间,单位秒
     *
     * @param key
     * @return
     */
    public void expire(String key, long timeout) {
        redisTemplate.expire(key, timeout, TimeUnit.SECONDS);
    }

    /**
     * 实现命令:INCR key,增加key一次
     *
     * @param key
     * @return
     */
    public long incr(String key, long delta) {
        return redisTemplate.opsForValue().increment(key, delta);
    }

    /**
     * 实现命令:KEYS pattern,查找所有符合给定模式 pattern的 key
     */
    public Set<String> keys(String pattern) {
        return redisTemplate.keys(pattern);
    }

    /**
     * 实现命令:DEL key,删除一个key
     *
     * @param key
     */
    public void del(String key) {
        redisTemplate.delete(key);
    }

    // String(字符串)

    /**
     * 实现命令:SET key value,设置一个key-value(将字符串值 value关联到 key)
     *
     * @param key
     * @param value
     */
    public void set(String key, String value) {
        redisTemplate.opsForValue().set(key, value);
    }

    /**
     * 实现命令:SET key value EX seconds,设置key-value和超时时间(秒)
     *
     * @param key
     * @param value
     * @param timeout
     *            (以秒为单位)
     */
    public void set(String key, String value, long timeout) {
        redisTemplate.opsForValue().set(key, value, timeout, TimeUnit.SECONDS);
    }

    /**
     * 实现命令:GET key,返回 key所关联的字符串值。
     *
     * @param key
     * @return value
     */
    public String get(String key) {
        return (String)redisTemplate.opsForValue().get(key);
    }

    // Hash(哈希表)

    /**
     * 实现命令:HSET key field value,将哈希表 key中的域 field的值设为 value
     *
     * @param key
     * @param field
     * @param value
     */
    public void hset(String key, String field, Object value) {
        redisTemplate.opsForHash().put(key, field, value);
    }

    /**
     * 实现命令:HGET key field,返回哈希表 key中给定域 field的值
     *
     * @param key
     * @param field
     * @return
     */
    public String hget(String key, String field) {
        return (String) redisTemplate.opsForHash().get(key, field);
    }

    /**
     * 实现命令:HDEL key field [field ...],删除哈希表 key 中的一个或多个指定域,不存在的域将被忽略。
     *
     * @param key
     * @param fields
     */
    public void hdel(String key, Object... fields) {
        redisTemplate.opsForHash().delete(key, fields);
    }

    /**
     * 实现命令:HGETALL key,返回哈希表 key中,所有的域和值。
     *
     * @param key
     * @return
     */
    public Map<Object, Object> hgetall(String key) {
        return redisTemplate.opsForHash().entries(key);
    }

    // List(列表)

    /**
     * 实现命令:LPUSH key value,将一个值 value插入到列表 key的表头
     *
     * @param key
     * @param value
     * @return 执行 LPUSH命令后,列表的长度。
     */
    public long lpush(String key, String value) {
        return redisTemplate.opsForList().leftPush(key, value);
    }

    /**
     * 实现命令:LPOP key,移除并返回列表 key的头元素。
     *
     * @param key
     * @return 列表key的头元素。
     */
    public String lpop(String key) {
        return (String)redisTemplate.opsForList().leftPop(key);
    }

    /**
     * 实现命令:RPUSH key value,将一个值 value插入到列表 key的表尾(最右边)。
     *
     * @param key
     * @param value
     * @return 执行 LPUSH命令后,列表的长度。
     */
    public long rpush(String key, String value) {
        return redisTemplate.opsForList().rightPush(key, value);
    }

}

Controller调用

@Autowired
private RedisOperator redisOperator;

@RequestMapping("/redisTest")
public String redisTest(String value){
    redisOperator.set("test",value);
    String redis_key = redisOperator.get("test");
    System.out.println("redis_key: "+redis_key+" 删除");
    redisOperator.del("test");
    return redis_key;
}

配置类

为了替代以前的XML配置文件,使用@Configuration标记类为配置类。该类通过Java代码定义了Spring的配置,如Bean的定义,组件扫描,属性配置等。

使用该注解的类可以通过使用@Bean注解定义一个方法,以定义需要创建的对象。例如:

@Configuration
public class MyConfig {
  @Bean
  public MyBean myBean() {
    return new MyBean();
  }
}

使用@Bean创建的对象,在其他类中可以通过注入的方式使用。例如:在其他类中使用@Autowired注入该对象,就可以使用它。

例如:

@Service
public class ExampleService {
  @Autowired
  private ExampleBean exampleBean;

  public void showMessage() {
    System.out.println(exampleBean.getMessage());
  }
}

拦截器

Spring Boot拦截器是一种拦截Web请求的机制,用于检查请求的合法性、安全性、认证等。在Spring Boot中使用拦截器,可在以下情况下执行操作:

  • 在将请求发送到控制器之前

  • 在将响应发送给客户端之前

Spring Boot拦截器的使用主要分为以下几步:

  1. 定义拦截器:实现org.springframework.web.servlet.HandlerInterceptor接口。

  2. 在拦截器上注册:通过实现org.springframework.web.servlet.config.annotation.InterceptorRegistry接口来注册拦截器。

  3. 在需要被拦截的方法上添加拦截器:通过使用@Interceptor注解在方法上添加拦截器。

下面是一个Spring Boot拦截器的例子:

@Component
public class LogInterceptor implements HandlerInterceptor {

  @Override
  public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
    // 在请求处理之前进行调用(Controller方法调用之前)
    System.out.println("Interceptor preHandle");
    return true;
  }

  @Override
  public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
      ModelAndView modelAndView) {
    // 请求处理之后进行调用,但是在视图被渲染之前(Controller方法调用之后)
    System.out.println("Interceptor postHandle");
  }

  @Override
  public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {
    // 在整个请求结束之后被调用,也就是在DispatcherServlet 渲染了对应的视图之后执行(主要是用于进行资源清理工作)
    System.out.println("Interceptor afterCompletion");
  }

}

配置拦截器:

@Configuration
public class WebMvcConfig implements WebMvcConfigurer {

  @Autowired
  private LogInterceptor logInterceptor;

  @Override
  public void addInterceptors(InterceptorRegistry registry) {
    // 注册拦截器
    registry.addInterceptor(logInterceptor).addPathPatterns("/**");
  }

}

addPathPatterns方法的参数是一个字符串数组,表示需要拦截的路径模式。每个字符串可以匹配多个URL,可以使用通配符来实现,例如"/user/*"表示匹配以/user/开头的URL。

过滤器

过滤器是用于拦截应用程序的HTTP请求和响应的对象。通过使用过滤器,可以在两个实例上执行两个操作 -

  • 在将请求发送到控制器之前

  • 在向客户发送响应之前。

可以通过实现javax.servlet.Filter接口或者继承javax.servlet.FilterAdapter类来实现过滤器。

一个简单的案例:

@Component
public class MyFilter implements Filter {

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        HttpServletRequest request = (HttpServletRequest) servletRequest;
        System.out.println("Remote Host:"+request.getRemoteHost()); // 打印远程host
      	System.out.println("Remote Address:"+request.getRemoteAddr()); // 打印远程地址
        System.out.println("This is MyFilter, URL :" + request.getRequestURI());
        // 过滤特定url
        String servletPath = request.getServletPath();
        if (servletPath.equals("/myFilter")) {
          // 执行过滤操作
        }
        filterChain.doFilter(servletRequest, servletResponse);
    }
}

通过@Component注解将自定义过滤器注册到IoC容器中。实现doFilter方法,在请求到达控制器之前对请求进行处理。最后通过调用filterChain.doFilter方法将请求传递到下一个过滤器或者控制器。

也可以通过@WebFilter注解标识该Filter的作用范围:

@WebFilter(urlPatterns = "/myFilter")
public class MyFilter implements Filter {

  @Override
  public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain)
      throws IOException, ServletException {
    HttpServletRequest request = (HttpServletRequest) servletRequest;
    String servletPath = request.getServletPath();
    if (servletPath.equals("/myFilter")) {
      // 执行过滤操作
    }
    filterChain.doFilter(servletRequest, servletResponse);
  }
}

该案例中只有以/myFilter开头的请求才会被该Filter处理。

文件处理

Spring Boot支持使用两种方式来实现文件的上传和下载:

  1. 基于 HTTP 协议的文件上传/下载。这种方式通过 HTTP 协议将文件传输到 Web 服务器,并从 Web 服务器获取文件。

  2. 基于存储技术的文件上传/下载,例如基于文件系统或基于数据库的文件上传/下载。

在以下示例中,我们将详细介绍使用 HTTP 协议的文件上传/下载。

文件上传:

  1. 首先,创建一个文件上传视图,其中包含一个文件选择器,用于选择要上传的文件。

  2. 然后,创建一个控制器,该控制器接收并处理文件上传请求。

  3. 在控制器中,使用 MultipartFile 接口来处理文件。

  4. 最后,使用 FileOutputStream 将文件保存到指定目录。

文件下载:

  1. 首先,创建一个下载文件视图,该视图提供用户下载文件的途径。

  2. 然后,创建一个控制器,该控制器接收并处理文件下载请求。

  3. 在控制器中,使用 FileInputStream 读取文件内容。

  4. 最后,使用 OutputStream 将文件内容写入 HTTP 响应中,以实现下载文件的目的。

下面是一个使用Spring Boot实现文件上传与下载的案例:

  1. 创建一个 Spring Boot 项目

  2. 创建一个控制器类,使用 @RestController 注解,并创建文件上传和下载的接口

@RestController
public class FileController {
    @PostMapping("/upload")
    public String fileUpload(@RequestParam("file") MultipartFile file) throws IOException {
        File convertFile = new File("/var/tmp/" + file.getOriginalFilename());
        convertFile.createNewFile();
        FileOutputStream fout = new FileOutputStream(convertFile);
        fout.write(file.getBytes());
        fout.close();
        return "File is upload successfully";
    }

    @GetMapping("/download")
    public ResponseEntity<Resource> fileDownload(@RequestParam String fileName) {
        String filename = "/var/tmp/mysql.png";
        File file = new File(filename);
        InputStreamResource resource = new InputStreamResource(new FileInputStream(file));
        HttpHeaders headers = new HttpHeaders();

        headers.add("Content-Disposition", String.format("attachment; filename=\\" % s\\"", file.getName()));
        headers.add("Cache-Control", "no-cache, no-store, must-revalidate");
        headers.add("Pragma", "no-cache");
        headers.add("Expires", "0");

        ResponseEntity<Object>
            responseEntity = ResponseEntity.ok().headers(headers).contentLength(
            file.length()).contentType(MediaType.parseMediaType("application/txt")).body(resource);

        return responseEntity;
    }
}
  1. 在 application.yml 中配置文件存储目录

file:
  path: /files/
  1. 在 main 函数中设置文件存储目录

@Value("${file.path}")
private String filePath;

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

@Override
public void run(String... args) throws Exception {
    File file = new File(filePath);
    if (!file.exists()) {
        file.mkdir();
    }
}

日志

转载:Spring Boot日志 - Spring Boot教程 (yiibai.com)

Spring Boot使用Apache Commons日志记录进行所有内部日志记录。Spring Boot的默认配置支持使用Java Util Logging,Log4j2和Logback。 使用这些,可以配置控制台日志记录以及文件日志记录。

如果使用的是Spring Boot Starters,Logback将为日志记录提供良好的支持。 此外,Logback还提供对Common Logging,Util Logging,Log4J和SLF4J的良好支持。

日志格式

默认的Spring Boot Log格式显示在下面给出的屏幕截图中。它提供以下信息 -

  • 提供日志日期和时间的日期和时间。

  • 日志级别显示有:INFO,ERROR或WARN。

  • 进程ID。

  • ---是一个分隔符。

  • 线程名称括在方括号[]中。

  • 记录器名称,显示源类名称。

  • 日志消息。

控制台日志输出

默认日志消息将打印到控制台窗口。 默认情况下,INFOERRORWARN日志消息将打印在日志文件中。

如果必须启用调试级别日志,请使用以下命令在启动应用程序时添加调试标志:

java –jar demo.jar --debug

还可以将调试模式添加到application.properties文件中,如下所示:

debug = true

文件日志输出

默认情况下,所有日志都将在控制台窗口中打印,而不是在文件中打印。 如果要在文件中打印日志,则需要在application.properties文件中设置属性logging.filelogging.path

可以使用下面显示的属性指定日志文件路径。 请注意,日志文件名是spring.log

logging.path = /var/tmp/

使用下面显示的属性指定自己的日志文件名:

logging.file = /var/tmp/mylog.log

注意:文件将在达到10MB后自动旋转生成。

日志级别

Spring Boot支持所有记录器级别,例如:TRACEDEBUGINFOWARNERRORFATALOFF。在application.properties 文件中定义Root logger,如下所示 -

logging.level.root = WARN

注 - Logback不支持“FATAL”级别日志。 它映射到“ERROR”级别日志。

配置Logback

Logback支持基于XML的配置来处理Spring Boot Log配置。日志配置详细信息在logback.xml文件中配置。logback.xml文件应放在classpath下。 可以使用下面给出的代码在Logback.xml文件中配置ROOT级别日志 -

<?xml version = "1.0" encoding = "UTF-8"?>
<configuration>
   <root level = "INFO">
   </root>
</configuration>

在下面给出的Logback.xml文件中配置控制台appender

<?xml version = "1.0" encoding = "UTF-8"?>
<configuration>
   <appender name = "STDOUT" class = "ch.qos.logback.core.ConsoleAppender"></appender>
   <root level = "INFO">
      <appender-ref ref = "STDOUT"/> 
   </root>
</configuration>

使用下面给出的代码在Logback.xml文件中配置文件appender。 请注意,需要在文件追加器中指定日志文件路径。

<?xml version = "1.0" encoding = "UTF-8"?>
<configuration>
   <appender name = "FILE" class = "ch.qos.logback.core.FileAppender">
      <File>/var/tmp/mylog.log</File>
   </appender>   
   <root level = "INFO">
      <appender-ref ref = "FILE"/>
   </root>
</configuration>

使用下面给出的代码在logback.xml文件中定义日志模式。还使用下面给出的代码在控制台或文件日志附加程序中定义支持的日志模式集 -

<pattern>[%d{yyyy-MM-dd'T'HH:mm:ss.sss'Z'}] [%C] [%t] [%L] [%-5p] %m%n</pattern>

完整的logback.xml文件的代码如下所示。必须将其放在类路径中。

<?xml version = "1.0" encoding = "UTF-8"?>
<configuration>
   <appender name = "STDOUT" class = "ch.qos.logback.core.ConsoleAppender">
      <encoder>
         <pattern>[%d{yyyy-MM-dd'T'HH:mm:ss.sss'Z'}] [%C] [%t] [%L] [%-5p] %m%n</pattern>
      </encoder>
   </appender>

   <appender name = "FILE" class = "ch.qos.logback.core.FileAppender">
      <File>/var/tmp/mylog.log</File>
      <encoder>
         <pattern>[%d{yyyy-MM-dd'T'HH:mm:ss.sss'Z'}] [%C] [%t] [%L] [%-5p] %m%n</pattern>
      </encoder>
   </appender>

   <root level = "INFO">
      <appender-ref ref = "FILE"/>
      <appender-ref ref = "STDOUT"/> 
   </root>
</configuration>

下面给出的代码显示了如何在Spring Boot主类文件中添加slf4j logger。

package com.yiibai.demo;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class DemoApplication {
   private static final Logger logger = LoggerFactory.getLogger(DemoApplication.class);

   public static void main(String[] args) {
      logger.info("this is a info message");
      logger.warn("this is a warn message");
      logger.error("this is a error message");
      SpringApplication.run(DemoApplication.class, args);
   }
}

原理拆解

springboot启动原理

详细原理:springboot原理(核心原理、启动流程、执行流程) - 简书 (jianshu.com)

springboot是基于springmvc、spring容器和Mybatis的整合容器。

  1. 初始化监听器,以及添加到SpringApplication的自定义监听器。

  2. 发布ApplicationStartedEvent事件,如果想监听ApplicationStartedEvent事件,你可以这样定义:public class ApplicationStartedListener implements ApplicationListener,然后通过SpringApplication.addListener(..)添加进去即可。

  3. 装配参数和环境,确定是web环境还是非web环境。

  4. 装配完环境后,就触发ApplicationEnvironmentPreparedEvent事件。

  5. 如果SpringApplication的showBanner属性被设置为true,则打印启动的Banner。

  6. 创建ApplicationContext,会根据是否是web环境,来决定创建什么类型的ApplicationContext。

  7. 装配Context的环境变量,注册Initializers、beanNameGenerator等。

  8. 发布ApplicationPreparedEvent事件。

  9. 注册springApplicationArguments、springBootBanner,加载资源等

  10. 遍历调用所有SpringApplicationRunListener的contextLoaded()方法。

  11. 调用ApplicationContext的refresh()方法,装配context beanfactory等非常重要的核心组件。

  12. 查找当前ApplicationContext中是否注册有CommandLineRunner,如果有,则遍历执行它们。

  13. 发布ApplicationReadyEvent事件,启动完毕,表示服务已经可以开始正常提供服务了。通常我们这里会监听这个事件来打印一些监控性质的日志,表示应用正常启动了。

SpringBoot会触发其他的一些事件,这些事件按下列顺序触发:

  • ApplicationStartingEvent:项目刚启动时触发,此时除了注册监听器和初始器之外,其他所有处理都没有开始;

  • ApplicationEnvironmentPreparedEvent:上下文得到环境信息之后触发,此时上下文创建还没有创建;

  • ApplicationPreparedEvent:bean的定义信息加载完成之后触发,此时bean还没有初始化;

  • ApplicationReadyEvent:在所有bean初始化完毕,所有回调处理完成,系统准备处理服务请求时触发;

  • ApplicationFailedEvent:启动过程出现异常时候触发。

springboot自动装配原理

转载:SpringBoot———自动装配原理追JAVA的小菜鸟的博客-CSDN博客springboot自动装配原理

首先进入@SpringBootApplication,可以看到:

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(
    excludeFilters = {@Filter(
    type = FilterType.CUSTOM,
    classes = {TypeExcludeFilter.class}
), @Filter(
    type = FilterType.CUSTOM,
    classes = {AutoConfigurationExcludeFilter.class}
)}
)
public @interface SpringBootApplication {

里面有三个重要的注解:@SpringBootConfiguration@EnableAutoConfiguration@ComponentScan

@SpringBootConfiguration

作用: 声明定义Bean,嵌套了@Component组件

  1. @SpringBootConfiguration源码是@Configuration:表示该类为主配置类,可用来装配bean

  2. @Configuration的源码是@Component:说明Spring的配置类也是Spring的一个组件。

  3. 它是JavaConfig形式的基于Spring IOC容器的配置类使用的一种注解。SpringBoot本质上就是一个Spring应用,通过这个注解来加载IOC容器的配置。所以在启动类里面标注了@Configuration,意味着它也是一个IOC容器的配置类

原码如下:

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Configuration
@Indexed
public @interface SpringBootConfiguration {
    @AliasFor(
        annotation = Configuration.class
    )
    boolean proxyBeanMethods() default true;
}

@ComponentScan

作用:扫描主配置类包的所有包下的类,相当于xml配置文件中的context:component-scan。eg:pojo中的User类

@EnableAutoConfiguration(重要)

作用:开启自动装配类

点击进入@EnableAutoConfiguration原码如下:

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import({AutoConfigurationImportSelector.class})
public @interface EnableAutoConfiguration {
    String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";

    Class<?>[] exclude() default {};

    String[] excludeName() default {};
}

@EnableAutoConfiguration里有两个重要注解,分别为:@AutoConfigurationPackage@Import

@AutoConfigurationPackage

作用:给Spring容器中导入一个Registrar注册器组件

进入原码:

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@Import({AutoConfigurationPackages.Registrar.class})
public @interface AutoConfigurationPackage {
    String[] basePackages() default {};

    Class<?>[] basePackageClasses() default {};
}

@AutoConfigurationPackage@ComponentScan一样,也是将主配置类所在的包及其子包里面的组件扫描到IOC容器中,但是区别是:

  • @AutoConfigurationPackage扫描@Enitity@MapperScan等第三方依赖的注解

  • @ComponentScan只扫描@Controller@Service@Component@Repository这些常见注解。所以这两个注解扫描的对象是不一样的。

@Import(核心注解)

作用:通过import导入第三方提供的bean的配置类:AutoConfigurationImportSelector,其作用是给容器中导入组件

进入AutoConfigurationImportSelector.java,可以看到该类中第63行有selectImports()方法,并调用了getAutoConfigurationEntry()

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

进入getAutoConfigurationEntry()方法,它又调用了getCandidateConfigurations()

    protected AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) {
        if (!this.isEnabled(annotationMetadata)) {
            return EMPTY_ENTRY;
        } else {
            AnnotationAttributes attributes = this.getAttributes(annotationMetadata);
            List<String> configurations = this.getCandidateConfigurations(annotationMetadata, attributes);
            configurations = this.removeDuplicates(configurations);
            Set<String> exclusions = this.getExclusions(annotationMetadata, attributes);
            this.checkExcludedClasses(configurations, exclusions);
            configurations.removeAll(exclusions);
            configurations = this.getConfigurationClassFilter().filter(configurations);
            this.fireAutoConfigurationImportEvents(configurations, exclusions);
            return new AutoConfigurationEntry(configurations, exclusions);
        }
    }

继续往下,找到getCandidateConfigurations()方法

    protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
        List<String> configurations = ImportCandidates.load(AutoConfiguration.class, this.getBeanClassLoader()).getCandidates();
        Assert.notEmpty(configurations, "No auto configuration classes found in META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports. If you are using a custom packaging, make sure that file is correct.");
        return configurations;
    }

然后进入查看SpringFactoriesLoader.load()方法

    public static ImportCandidates load(Class<?> annotation, ClassLoader classLoader) {
        Assert.notNull(annotation, "'annotation' must not be null");
        ClassLoader classLoaderToUse = decideClassloader(classLoader);
        String location = String.format("META-INF/spring/%s.imports", annotation.getName());
        Enumeration<URL> urls = findUrlsInClasspath(classLoaderToUse, location);
        List<String> importCandidates = new ArrayList();

        while(urls.hasMoreElements()) {
            URL url = (URL)urls.nextElement();
            importCandidates.addAll(readCandidateConfigurations(url));
        }

        return new ImportCandidates(importCandidates);
    }


文章作者: Vsoapmac
版权声明: 本站所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 soap的会员制餐厅
后端
喜欢就支持一下吧