springboot速通

SpringBoot低级

SpringBoot概述

SpringBoot提供了一种快速使用Spring的方式,基于约定优于配置,可以让开发人员不必配置与逻辑业务之间进行思维的切换,全身心的投入到逻辑业务的代码编写中,从而大大提高了开发的效率,一定程度上缩短了项目周期。

Spring缺点:

  • 配置繁琐
  • 依赖繁琐

SpringBoot功能:

  • 自动配置
    • springboot的自动配置是一个运行时的过程,考虑了众多因素才决定Spring配置应该用哪个,不该用哪个。该过程是SpringBoot自动完成的
  • 起步依赖
    • 就是Maven将具备某种过功能的某些包打包到一起,并提供了一些默认的功能,然后传给项目
  • 辅助功能
    • 提供了一些大型项目的非功能性特性,如嵌入式服务器、安全、指标、健康检测等

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

SpringBoot快速入门

引导类Demo1Application

java 复制代码
package com.example.demo;

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

@SpringBootApplication
public class Demo1Application {

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

}

HelloApplication

java 复制代码
package com.example.demo.demos.web;

import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class HelloSpringBoot {

    @RequestMapping("/hello")
    public String hello(){
        return "hello Spring Boot 嘤嘤嘤";
    }
}

application.yml

yml 复制代码
server:
  port: 8080

index.html

html 复制代码
<html>
<body>
<h1>hello word!!!</h1>
<p>this is a html page</p>
</body>
</html>

运行项目后,我们可以在浏览器网页栏搜索localhost:8080就会打开对应的web程序

小结

  • SpringBoot在创建项目时,使用jar的打包方式
  • SpringBoot的引导类,是项目入口,运行main方法就可以完全启动项目
  • 使用SpringBoot和Spring构建的项目,业务代码编写方式完全一样

SpringBoot起步依赖原理分析

SpringBoo;t提供的一种依赖管理机制,极大地简化了Spring应用程序的开发过程

  • 起步依赖是一种特殊的依赖,它将一组相关的依赖项打包在一起,方便开发者快速引入。例如,spring-boot-starter-web 包含了开发 Web 应用所需的所有依赖,如 Spring MVC、Tomcat 等。

  • 作用:

    • 简化依赖管理:无需依次添加每个组件,秩序引入一个起步依赖即可
    • 版本一致性:依赖版本由Spring Boot进行维护,确保版本兼容
    • 自动配置:引入岂不依赖后,SpringBoot会自动进行相关配置,减少手动配置的工作量
  • 原理:

    • Maven依赖传递机制:当引入一个起步依赖时,Maven 会自动解析并引入该依赖中声明的所有传递依赖。
    • 自动配置机制:Spring Boot 通过 @EnableAutoConfiguration 注解(通常由 @SpringBootApplication 指定)来启用自动配置。
  • 小结:

    • 在spring-boor-start-parent中定义了各种技术的版本信息,组合了一套最优搭配的技术版本
    • 在各种starter中,定义了完成该功能需要的坐标集合,其中大部分版本信息来自于父工程
    • 我们的工程继承parent,引入starter后,通过依赖传递,就可以简单方便的获取jar包,并且不会存在版本冲突等问题

SpringBoot配置

配置文件分类

SpringBoot是基于约定的,所以很多配置都有默认值,但如果想使用自己的配置替换默认配置的话,就可以使用application.properties或者application.yml(application.yaml)进行配置

  • 特点:
    • 全局作用域:配置项对整个应用程序有效
    • 自动加载:SpringBoot会自动加载application.propertiesapplication.yml中的配置
    • 优先级:同一级目录下,properties>yml>yaml
    • 默认配置文件名称:application

yaml

  • 是一种能够直观的被电脑识别的数据序列化格式,并且容易被人类阅读,容易和脚本语言交互,可以被支持YAML库的不同变成语言导入
  • YAML文件是以数据为核心的,比传统的xml方式更加简洁
  • YAML文件的拓展名可以使用.yml或者.yaml

比较各种配置文件

  • properties:

properties 复制代码
server.port=8080
servet.address=127.0.0.1
  • xml:

xml 复制代码
<server>
	<port>8080</port>
    <address>127.0.0.1</address>
</server>
  • yml: (简洁,直观,以数据为核心)

yaml 复制代码
server:
	port: 8080
	address: 127.0.0.1

YAML:基本语法

  • 大小写敏感
  • 数据值前边必须有空格作为分隔符
  • 使用缩进表示层级关系
  • 缩进下空格数目不重要,但必须有,并且相同层级的元素左侧对齐即可
  • '# '表示注释,从这个字符一直到行尾,都会被解析器忽略

YAML:数据格式

  • 对象(map): 键值对的集合
  • 数组(address): 一组按次序排序的值
  • 纯量: 单个的、不可再分的值
yaml 复制代码
server:
   port: 8080

person:
   name: zhangsan
   
# map的行内写法
person2: {name: zhangsan}

address:
  - beijing
  - shanghai
    
# 数组的行内写法
address2: [beijing,shanghai]

mag1: 'hello \n world'   # 单引号忽略转义字符
map2: "hello \n world"   # 双引号识别转义字符

# 参数引用
name = lisi
person:
	name: ${name}

读取配置文件内容

HelloController类

java 复制代码
package com.example.demo.demos.web;

import com.example.demo.Person;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.env.Environment;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class HelloController {

    @Value("${name}")
    private String name;
    @Value("${person.name}")
    private String name2;
    @Value("${address[0]}")
    private String address;
    @Value("${mag1}")
    private String mg1;
    @Value("${mag2}")
    private String mg2;

    @Autowired
    private Environment evn;
    @Autowired
    private Person person;

    // 方式1: 以@Value注解方式进行
    @RequestMapping("/hello3")
    public String hello3() {
        System.out.println(name);
        System.out.println(name2);
        System.out.println(address);
        System.out.println(mg1);
        System.out.println(mg2);

        System.out.println(person);
        return "hello3";
    }
    // 方式2: 以Environment类进行
    @RequestMapping("/hello2")
    public String hello2() {
        System.out.println(evn.getProperty("name"));
        System.out.println(evn.getProperty("address[0]"));
        System.out.println(evn.getProperty("mag1"));
        System.out.println(evn.getProperty("mag2"));
        System.out.println(evn.getProperty("person.name"));
        return evn.getProperty("person.name");
    }


}

application.yml

yml 复制代码
server:
   port: 8080

name: abc

person:
  name: zhangsam
  age: 200

# map的行内写法
person2: {name: zhangsan}


address:
  - beijing
  - shanghai

# 数组的行内写法
address2: [beijing,shanghai]

mag1: 'hello \n world'   # 单引号忽略转义字符
mag2: "hello \n world"   # 双引号识别转义字符

Person类

java 复制代码
package com.example.demo;

import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;

@Component
@ConfigurationProperties(prefix = "person")
public class Person {
    // 这些属性名要和对应的yaml里面的一一对应
    private String name;
    private int age;

    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public int getAge() {
        return age;
    }
    public void setAge(int age) {
        this.age = age;
    }
    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}

小结:三种方式获取数据

  1. 用@Value注解修饰属性
  2. 使用@Autowired注入一个Environment对象,然后通过该对象的getProperty属性获取
  3. 定义一个新Bean容器管理的类,在类上添加注解@Configuration,默认识别第一级的,可以通过修改参数prefix定义前缀

profile

我们在开发Spring Boot应用时,通常同一套程序会被安装到不同环境,比如:开发、测试、生产等。其中数据库地址、服务器端口等等配置都不同,如果每次打包都要修改配置文件,那么非常麻烦,profile功能就是来进行动态配置切换的

  • profile配置方式
    • 多profile文件方式
    • yml多文档方式
  • prifile激活方式
    • 配置文件
    • 虚拟机参数
    • 命令行参数

多profile文件方式

  • application.properties
properties 复制代码
name = abc
spring.profiles.active==dev
  • application-dev.properties
properties 复制代码
spring.profiles.active==dev
  • application-pro.properties
properties 复制代码
spring.profiles.active==pro
  • application-test.properties
properties 复制代码
spring.profiles.active==test

yml多文档方式

yml 复制代码
# 通过激活不同的 Profile,Spring Boot 会加载与该 Profile 对应的配置文件
---
server:
  port: 8081
spring:
  config:
    activate:
      on-profile: pro
---
server:
  port: 8082
spring:
  config:
    activate:
      on-profile: dev
---
server:
  port: 8083
spring:
  config:
    activate:
      on-profile: test
---

spring:
  profiles:
    active: dev

Profile-小结

  1. profile 是用来完成不同环境下,配置动态切换功能的。
  2. profile 配置方式
    • 多profile文件方式:提供多个配置文件,每个代表一种环境。
      • application-dev.properties/yml 开发环境
      • application-test.properties/yml 测试环境
      • application-pro.properties/yml 生产环境
    • yml多文档方式:
      • 在yml中使用 --- 分隔不同配置
  3. profile 激活方式
    • 配置文件:再配置文件中配置:spring.profiles.active=dev
    • 虚拟机参数:在VM options 指定:-Dspring.profiles.active=dev
    • 命令行参数:java -jar xxx.jar --spring.profiles.active=dev

内部配置加载顺序

  • 我们之前的配置文件都是放在配置文件,都是放在resources文件夹,根据当前目录下,优先级的高低判断谁先被加载,比如properties > yml > yaml

  • 实际开发这种,我们写的配置文件并不是都放在一起的,甚至"东一个,西一个",这时候就需要判断谁先被加载

SpringBoot程序启动时,会从以下位置加载配置文件

  1. file:/config/: 当前项目下的/config目录下
  2. file:/ 当前项目的根目录
  3. classpath:/config: classpath的/config目录下
  4. classpath: classpath的根目录下

加载顺序为上文的排序顺序,高优先级配置的属性会生效

小结

  • 内部配置,是寄托于IDEA编辑器实现的。对于编写配置(命令,实现功能),我们不仅可以在IDEA中实现,还可以打开黑窗口,输入命令,在外部实现。例如把spring项目打成jar包,在"黑窗口"运行,大家可以看我SpringBoot配置--Profile这篇博客的命令行参数内容。

对于内部配置加载顺序,记住三点

  1. 靠近项目,优先被加载

  2. 具有config下的文件,在同等情况下,优先被加载

  3. 同一级目录下,优先级:properties >yml >yaml

  4. 上面配置的第一种和第二种是不会被打包到对应的jar包里面的,因此如果我们使用package的jar包运行只会识别classpath路径下的配置文件

外部配置加载顺序

外部配置一般都是通过命令行参数实现,我们可以运行

复制代码
java -jar yourapp.jar --serve.port=8090

后面那部分就是修改我们的端口号,多个参数可以之间空格隔开

复制代码
java -jar yourapp.jar --serve.port=9090 --serve.servelet.context-path=/hehe

甚至我们可以直接加载外部配置文件

复制代码
java -jar yourapp.jar --spring.config.location="路径"

当然,我们也可以直接将配置属性复制到对应的jar包的同一目录下,jar包运行时也会自动加载

SpringBoot官网外部配置加载顺序

  • 默认属性(通过设置SpringApplication.setDefaultProperties指定)。
  • 在你的@Configuration类上的@PropertySource注释。请注意,这些属性源在应用上下文刷新之前不会添加到Environment中。因此,这对于配置某些属性(如logging.和spring.main.)来说为时已晚,因为这些属性在应用上下文刷新之前被读取。
  • 配置数据(例如application.properties文件)。
  • 随机值属性源(RandomValuePropertySource),其属性仅在random.*。
  • 操作系统环境变量。
  • Java系统属性(System.getProperties())。
  • 来自java:comp/env的JNDI属性。
  • ServletContext init参数。
  • 来自SPRING_APPLICATION_JSON的属性(内联JSON嵌入在环境变量或系统属性中)。
  • 命令行参数。
  • 测试中的属性属性。在@SpringBootTest和用于测试应用特定切片的测试注释中可用。
  • 测试中的@DynamicPropertySource注释。
  • 测试中的@TestPropertySource注释。
  • 当devtools激活时,在$HOME/.config/spring-boot目录中的Devtools全局设置属性。

越往下优先级越高

SpringBoot整合其他框架

Junit

实现步骤

  1. 搭建SpringBoot工程
  2. 引入starter-test依赖
  3. 编写测试类
  4. 添加测试相关注解
    1. RunWith(SpringRunner.class)
    2. SpringBootTest(classer = 启动类.class)
  5. 编写测试方法

如果我们是在源根目录下写的test测试类,那么测试类中的注解@SpringBootTest的括号中不需要添加任何信息

UserService类

java 复制代码
package com.lele.springboottest;

import org.springframework.stereotype.Service;

@Service
public class UserService {
    public void add(){
        System.out.println("add...");
    }
}

测试类UserServiceTest

java 复制代码
package com.lele.springboottest;

import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

/**
 * UserService的测试类
 */

@SpringBootTest(classes = SpringbootTestApplication.class)
public class UserServiceTest {
    @Autowired
    private UserService userService;
    @Test
    public void testadd(){
        userService.add();
        System.out.println("hello ");
    }
}

mybatis

实现步骤

  1. 搭建SpringBoot工程
  2. 引入mybatis起步依赖,添加mysql驱动
  3. 编写DataSource 和MyBatis相关配置
  4. 定义表和实体类
  5. 编写dao和mapper文件/纯注解开发
  6. 测试

首先创建数据库并添加两条记录

sql 复制代码
create database springboot;
use springboot;
create table t_user(
    id int(11) NOT NULL primary key auto_increment,
    username varchar(32) default null,
    password varchar(32) default null
);

insert into t_user (id,username,password) values(1,'zhangsan',123),
                                                (2,'lisi',234);

​ 我们在下面实现了两种方式的mybatis的注入:注解xml文件

User类

java 复制代码
package com.lele.springbotmybatis.domain;

import org.springframework.stereotype.Component;


public class User {
    int id;
    String name;
    int age;

    public User(int id, String name, int age) {
        this.id = id;
        this.name = name;
        this.age = age;
    }

    @Override
    public String toString() {
        return "User{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", age=" + age +
                '}';
    }

    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;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }
}

UserMapper接口

java 复制代码
package com.lele.springbotmybatis.mapper;

import com.lele.springbotmybatis.domain.User;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Select;
import org.springframework.stereotype.Repository;

import java.util.List;

@Mapper
@Repository
public interface UserMapper {
    @Select("select * from t_user")
    public List<User> findAll();
}

UserXmlMapper接口

java 复制代码
package com.lele.springbotmybatis.mapper;

import com.lele.springbotmybatis.domain.User;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Select;
import org.springframework.stereotype.Repository;

import java.util.List;

@Mapper
@Repository
public interface UserMapper {
    @Select("select * from t_user")
    public List<User> findAll();
}

启动类省略,都一样没任何变化

UserXmlMapper.xml

xml 复制代码
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mbatis-3-mapper.dtd">
<mapper namespace="com.lele.springbotmybatis.mapper.UserXmlMapper">
    <select id="findAll" resultType="user">
        select * from t_user
    </select>
</mapper>

appliaction.yml

yml 复制代码
# dataSource
spring:
  datasource:
    url: jdbc:mysql://localhost:3306/springboot
    username: root
    password: 159357258zx.
    driver-class-name: com.mysql.cj.jdbc.Driver

mybatis:
  mapper-locations: classpath:mapper/*Mapper.xml
  type-aliases-package: com.lele.springbotmybatis.domain.User
# config-location:        # 指定mybatis核心配置文件

测试类

java 复制代码
package com.lele.springbotmybatis;

import com.lele.springbotmybatis.domain.User;
import com.lele.springbotmybatis.mapper.UserMapper;
import com.lele.springbotmybatis.mapper.UserXmlMapper;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

import java.util.List;

@SpringBootTest
class SpringbotMybatisApplicationTests{
    @Autowired
    private UserMapper userMapper;
    @Autowired
    private UserXmlMapper userXmlMapper;
    @Test
    public void testfindALl() {
        List<User> all = userMapper.findAll();
        for (User user : all) {
            System.out.println(user);
        }
    }
    @Test
    public void testfindALl2() {
        List<User> all = userXmlMapper.findAll2();
        for (User user : all) {
            System.out.println(user);
        }
    }

}

SpringBoot高级(原理分析)

SpringBoot其实是对Spring的高度封装,虽然用起来很方便,但是我们不知道它为什么这么方便、或者它在哪方面还能进行拓展。通过学习SpringBoot的原理分析,我们能够更好的使用SpringBoot,同时也能学到很多好的设计思想

自动配置

Condition

  • Condition:在Spring4.0增加的条件判断功能,可以实现选择性的创建Bean操作

思考:SpringBoot是如何知道要创建哪个依赖的?

可以看到,当我没有导入redis依赖而直接获取Bean时,运行出错

java 复制代码
package com.lele.springbootcondition;

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

@SpringBootApplication
public class SpringbootConditionApplication {

    public static void main(String[] args) {
        // 启动Springboot项目,并返回IOC容器
        ConfigurableApplicationContext context =
                SpringApplication.run(SpringbootConditionApplication.class, args);

        // 获取Bean,redisTemplate
        Object redisTemplate = context.getBean("redisTemplate");
        System.out.println(redisTemplate);
        
        context.close();
    }
}

错误信息:No bean named 'redisTemplate' available

但是当我添加依赖后,成功输出

xml 复制代码
<dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
        </dependency>
  • 因此我们思考,Condition可能与依赖有关,但它怎么知道我导入了相应的坐标呢

案例:在Spring的IOC容器中有一个User的Bean,现要求导入Jedis坐标后加载Bean,没导入则不加载

核心配置类UserConfig

java 复制代码
package com.lele.springbootcondition.config;

import com.lele.springbootcondition.Condition.ClassCondition;
import com.lele.springbootcondition.Condition.ConditionOnClass;
import com.lele.springbootcondition.domain.User;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Conditional;
import org.springframework.context.annotation.Configuration;
import com.alibaba.fastjson.JSON;

@Configuration
public class UserConfig {
    // 下面可以添加一个注解@Conditional,它需要传入一个Condition接口的实现类
    // 接口Condtion里面定义了一个方法,我的实现类里面需要重写该方法
    // 该方法返回一个boolean类型,true表示加载Bean,否则不加载
    @Bean
    //@Conditional(ClassCondition.class)
    @ConditionOnClass("com.alibaba.fastjson.JSON")
    public User user(){
        return new User();
    }
}

第一种方式 是直接使用直接使用自带的@Conditional

复制代码
  //@Conditional(ClassCondition.class)

然后定义一个实现Condition接口的方法

ClassCondition类

java 复制代码
package com.lele.springbootcondition.Condition;

import com.alibaba.fastjson.JSON;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.context.annotation.Condition;
import org.springframework.context.annotation.ConditionContext;
import org.springframework.core.type.AnnotatedTypeMetadata;

import java.util.Map;

public class ClassCondition implements Condition {

    /**
     *
     * @param context   上下文对象,用于获取环境、IOC容器、ClassLoader对象
     * @param metadata      注解元对象,用于获取注解定义的属性值
     * @return
     */
    @Override
    public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
        // 1. 需求:导入Jedis类则加载Bean,否则不加载
        /*boolean flag;
        try {
            Class<?> cls = Class.forName("com.alibaba.fastjson.JSON");
            flag = true;
        } catch (ClassNotFoundException e) {
            flag = false;
        }
        return flag;*/

        //2. 需求: 导入通过注解属性值value指定的坐标后创建Bean
        // 获取注解属性值 value
        Map<String, Object> map =
                metadata.getAnnotationAttributes(ConditionOnClass.class.getName());
        // System.out.println(map);
        String[] value = (String[])map.get("value");
        boolean flag = false;
        try {
            for (String className : value) {
                Class<?> cls = Class.forName(className);
            }
            flag = true;
        } catch (ClassNotFoundException e) {
            flag = false;
        }

        return flag;
    }
}

第二种方式是我们自己定义一个注解,但还是要用系统的注解@Conditino覆盖我们自己的

ConditionOnClass注解

java 复制代码
package com.lele.springbootcondition.Condition;

import org.springframework.context.annotation.Conditional;

import java.lang.annotation.*;

@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Conditional(ClassCondition.class)
public @interface ConditionOnClass {
    String[] value();
}

启动类SpringbootConditionApplication

java 复制代码
package com.lele.springbootcondition;

import com.lele.springbootcondition.domain.User;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;

@SpringBootApplication
public class SpringbootConditionApplication {

    public static void main(String[] args) {
        // 启动Springboot项目,并返回IOC容器
        ConfigurableApplicationContext context =
                SpringApplication.run(SpringbootConditionApplication.class, args);

//        // 获取Bean,redisTemplate
//        Object redisTemplate = context.getBean("redisTemplate");
//        System.out.println(redisTemplate);

        Object bean = context.getBean("user");
        System.out.println(bean);

        context.close();
    }
}
  • 除了Conditional注解外,我们能还有其他注解就不一一演示了,都放在小结里面

切换内置Web服务器

SpringBoot的web环境中默认使用tomcat作为内置服务器,其中SpringBoot提供了4种内置服务器供我们选择,我们可以很方便的进行切换

我们可以在外部库/Maven:org:springframework.boot/spring-boot-autoconfigure-2.6.13,jar

/org/springframework/boot/autofigure/web/embedded里面

  1. Jetty
  2. Metty
  3. Tomcat
  4. Undertow

我们默认都是选择tomcat进行切换的,但是如果想要切换,可以修改配置文件pom.xml,在web的大包里面排除Tomcat的,然后外面再导入对应其他web内置服务器即可

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

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

@Enable*注解

  • SpringBoot种提供了很多Enable开头的注解,这些注解都是用于东岸提开启某些功能的。而其底层原理就是使用@Import注解导入一些配置类,实现Bean的动态加载

思考

SpringBoot工程是否可以直接获取Jar包种定义的Bean?

答案肯定是不行的,SpringBoot无法直接引用别人jar包里的Bean

那么为什么我们之前引入一个Redis的起步依赖就可以直接获取到RedisTemplate呢?

演示

接下来我们引入了两个模块工程springboot-enable和springboot-enable-other

springboot-enable-other的作用单纯就是提供bean类

  • User类
java 复制代码
package com.xh.config;

public class UserConfig {
}
  • UserConfig配置类
java 复制代码
package com.xh.config;

import com.xh.domain.User;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/** 表示配置类 */
@Configuration
public class UserConfig {
    /** 注入 */
    @Bean
    public User user(){
        return new User();
    }
}

然后就是在springboot-enable工程中pom.xml中添加新的依赖

xml 复制代码
        <dependency>
            <groupId>com.xh</groupId>
            <artifactId>springboot-enable-other</artifactId>
            <version>0.0.1-SNAPSHOT</version>
        </dependency>

然后修改启动类中添加获取User类 的代码

java 复制代码
public static void main(String[] args) {
        ConfigurableApplicationContext context = SpringApplication.run(SpringbootEnableApplication.class, args);

        // 获取 Bean
        Object user = context.getBean("user");
        System.out.println(user);
    }

运行项目,然后就报错了,提示错误信息是No qualfied bean named 'user' available

  • 通过演示我们可以看到SpringBoot不能直接获取我们在其他工程中定义的Bean

  • 原因:在启动类中@SpringBootApplication注解中有一个@ComponentScan注解,这个注解扫描的范围是当前引导类所在包及其子包

  • 我们项目的引导类包路径为:om.xh.springbootenable

    而 UserConfig 所在的包路径为:com.xh.config

java 复制代码
@ComponentScan(
    excludeFilters = {@Filter(
    type = FilterType.CUSTOM,
    classes = {TypeExcludeFilter.class}
), @Filter(
    type = FilterType.CUSTOM,
    classes = {AutoConfigurationExcludeFilter.class}
)}
)

方案:

  1. 使用@ComponentScan:我们可以在引导类上使用@ComponentScan注解扫描配置类所在的包
java 复制代码
@SpringBootApplication
@ComponentScan("com.xh.config")
public class SpringbootEnableApplication {

    public static void main(String[] args) {
        ConfigurableApplicationContext context = SpringApplication.run(SpringbootEnableApplication.class, args);

        // 获取 Bean
        Object user = context.getBean("user");
        System.out.println(user);
    }
}

这样的方案虽然可以解决这个问题,但是看起来不太行,如果获取的包的特别多的话,那么可能太多了,所以这种方案不推荐

  1. 使用@Import注解:被@Import注解所导入的类,都会被Spring创建,并放入IOC容器中,如图可以看到@Import注解的value值是一个数组,可以传多个值
java 复制代码
// @Import的定义
package org.springframework.context.annotation;

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Import {
    Class<?>[] value();
}

修改引导类

java 复制代码
@SpringBootApplication
//@ComponentScan("com.xh.config")
@Import(UserConfig.class)
public class SpringbootEnableApplication {

    public static void main(String[] args) {
        ConfigurableApplicationContext context = SpringApplication.run(SpringbootEnableApplication.class, args);

        // 获取 Bean
        Object user = context.getBean("user");
        System.out.println(user);
    }
}

启动类后可以正常运行,方案稍微比方案一好一些,需要记住很多类的名字,所以仍然不是很方便

  1. 对@Import注解进行封装

对springboot-enable-other工程中编写注解@EnableUser,在注解中使用@Import注解导入userConfig,并且添加@Import的元注解

java 复制代码
package com.xh.config;

import org.springframework.context.annotation.Import;
import java.lang.annotation.*;

@Import(UserConfig.class)
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface EnableUser {
}

修改springboot-enable工程的引导类,现在可以使用我们自定义的注解

java 复制代码
@SpringBootApplication
//@ComponentScan("com.xh.config")
//@Import(UserConfig.class)
@EnableUser
public class SpringbootEnableApplication {

    public static void main(String[] args) {
        ConfigurableApplicationContext context = SpringApplication.run(SpringbootEnableApplication.class, args);

        // 获取 Bean
        Object user = context.getBean("user");
        System.out.println(user);
    }
}

这种自定义注解的方式和方案二是一个原理,只不过是在使用上简化了一下。

@Import注解

@Enable*底层依赖于@Import注解导入一些类,使用@Import导入的类会被Spring加载到IOC容器中,而@Import提供4种用法。SpringbootAppliation中选择用第三种实现方式

  1. 导入Bean
  2. 导入配置类
  3. 导入ImportSelector实现类,一般用于加载配置文件中的类
  4. 导入ImportBeanDefinitionRegistrat实现类

导入Bean

java 复制代码
/**
 * Import 4 种用法
 * 1. 导入Bean
 * 2. 导入配置类
 * 3. 导入 ImportSelector 实现类。一般用于加载配置文件中的类
 * 4. 导入 ImportBeanDefinitionRegistrar 实现类。
 */
@SpringBootApplication
//@ComponentScan("com.xh.config")
//@Import(UserConfig.class)
//@EnableUser
@Import(User.class)
public class SpringbootEnableApplication {

    public static void main(String[] args) {
        ConfigurableApplicationContext context = SpringApplication.run(SpringbootEnableApplication.class, args);

        // 获取 Bean
//        Object user = context.getBean("user");
//        System.out.println(user);

        // 由于使用 @Import 注解导入 User.class 获取到的 Bean 名称不叫 user
        // 所以通过类型获取 Bean
        User user = context.getBean(User.class);
        System.out.println("user:" + user);
        // 获取 Spring 容器中所有 UserBean 的名称以及 Bean 对应的值
        Map<String, User> map = context.getBeansOfType(User.class);
        System.out.println("map:" + map);
    }
}

导入配置类

这里我们创建了一个新的对象Role,看看一个配置类是否可以加载两个对象

Role

java 复制代码
package com.xh.domain;

public class Role {
}

在UserConfig里面添加对应方法

java 复制代码
    @Bean
    public Role role(){
        return new Role();
    }

然后在启动类上修改为对应的注解

java 复制代码
@SpringBootApplication
//@ComponentScan("com.xh.config")
//@EnableUser
//@Import(User.class)
@Import(UserConfig.class)
public class SpringbootEnableApplication {

    public static void main(String[] args) {
        ConfigurableApplicationContext context = SpringApplication.run(SpringbootEnableApplication.class, args);

        // 获取 Bean
//        Object user = context.getBean("user");
//        System.out.println(user);

        // 由于使用 @Import 注解导入 User.class 获取到的 Bean 名称不叫 user
        // 所以通过类型获取 Bean
        User user = context.getBean(User.class);
        System.out.println("user:" + user);
        Role role = context.getBean(Role.class);
        System.out.println("role:" + role);
        // 获取 Spring 容器中所有 UserBean 的名称以及 Bean 对应的值
//        Map<String, User> map = context.getBeansOfType(User.class);
//        System.out.println("map:" + map);
    }
}

导入ImportSelector实现类

首先我们先来看看ImportSelector接口,里面有一个方法selectImports,参数是一个注解元对象,可以用来获取一些注解的属性等,然后返回String类型数组,一些类的全限定名

java 复制代码
package org.springframework.context.annotation;

import java.util.function.Predicate;
import org.springframework.core.type.AnnotationMetadata;

public interface ImportSelector {
    String[] selectImports(AnnotationMetadata importingClassMetadata);
}

然后我们就来实现该接口

java 复制代码
package com.xh.config;

import org.springframework.context.annotation.ImportSelector;
import org.springframework.core.type.AnnotationMetadata;

public class MyImportSelector implements ImportSelector {
    @Override
    public String[] selectImports(AnnotationMetadata importingClassMetadata) {
        // 放入 user、role 的全限定名之后,就会自动去加载 user 和对应的 role 了
        // return new String[]{"com.xh.domain.User", "com.xh.domain.Role"};
        
        // 或者可以使用方法获取
        // 或者我们可以直接导入配置文件,然后从配置文件上动态获取
        return new String[]{User.class.getName(), Role.class.getName()};
    }
}

最后修改为对应注解

java 复制代码
@SpringBootApplication
//@ComponentScan("com.xh.config")
//@EnableUser
//@Import(User.class)
//@Import(UserConfig.class)
@Import(MyImportSelector.class)
public class SpringbootEnableApplication {

    public static void main(String[] args) {
        ConfigurableApplicationContext context = SpringApplication.run(SpringbootEnableApplication.class, args);

        // 获取 Bean
//        Object user = context.getBean("user");
//        System.out.println(user);

        // 由于使用 @Import 注解导入 User.class 获取到的 Bean 名称不叫 user
        // 所以通过类型获取 Bean
        User user = context.getBean(User.class);
        System.out.println("user:" + user);
        Role role = context.getBean(Role.class);
        System.out.println("role:" + role);
//        // 获取 Spring 容器中所有 UserBean 的名称以及 Bean 对应的值
//        Map<String, User> map = context.getBeansOfType(User.class);
//        System.out.println("map:" + map);
    }
}

导入ImportBeanDefinitionRegistrar实现类

java 复制代码
package com.example.springbootenableother.config;

import com.example.springbootenableother.domain.User;
import org.springframework.beans.factory.support.AbstractBeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.context.annotation.ImportBeanDefinitionRegistrar;
import org.springframework.core.type.AnnotationMetadata;

/**
 * @author XH
 * @create 2021/12/13
 * @since 1.0.0
 */
public class MyImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {

    @Override
    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
        //在 IOC 容器中注册 Bean,Bean 名称为 user,类型为 User.class
        // 获取 beanDefinition
        AbstractBeanDefinition beanDefinition = BeanDefinitionBuilder.rootBeanDefinition(User.class).getBeanDefinition();
        // 注入 user
        registry.registerBeanDefinition("user", beanDefinition);
    }
}
java 复制代码
@SpringBootApplication
//@EnableUser
//@Import(MyImportSelector.class)
@Import(MyImportBeanDefinitionRegistrar.class)
public class SpringbootEnableApplication {

    public static void main(String[] args) {
        ConfigurableApplicationContext context = SpringApplication.run(SpringbootEnableApplication.class, args);

        // 根据类型获取
        Object user = context.getBean(User.class);
        System.out.println(user);
        // 根据 Bean 名称获取
        Object user1 = context.getBean("user");
        System.out.println(user1);
//        Object role = context.getBean(Role.class);
//        System.out.println(role);
    }
}

@EnableAutoConfiguration注解

  • @EnableAutoConfiguration注解内部使用@Import(AutoConfigurationImportSelector.class)来加载配置类
  • 配置文案金位置:META/spring.factories,该配置文件中定义了大量的配置类,当SpringBoot应用启动时,会自动加载这些配置类,初始化Bean
  • 并不是所有的Bean都会被初始化,在配置类中使用了Condition注解来加载满足条件的Bean

小结

  • 自定义条件
    • 定义条件类:自定义类实现接口,重写matches方法,在matches方法中进行逻辑判断,返回boolean值。matches方法两个参数
      • context:上下文对象,可以获取属性值,获取类加载器,获取BeanFactory等
      • metadata:元数据对象,用于获取注解属性
    • 判断条件:在初始化Bean时,使用@Conditional(条件类.class)注解
  • SpringBoot提供的其他常用条件注解:
    • ConditionalOnProperty:判断配置文件中是否有对应属性和值才初始化Bean
    • ConditionalOnClass: 判断环境中是否有对应字节码文件才初始化Bean
    • ConditionalOnMissingBean: 判断环境中没有对应Bean才初始化
  • 切换web内置服务器:Jetty、Metty、Tomcat、UnderTow
  • SpringBoot 底层是使用 @Import 注解导入一些配置类,实现 Bean 的动态加载。
    例如 @SpringBootApplication 其中的 @EnableAutoConfiguration 就使用了 @Import 来实现导入其他的类
  • @EnableAutoConfiguration注解通过注解的方式启动自动配置类

SpringBoot 监听机制

SpringBoot的监听机制,其实就是对java提供的事件监听机制的封装

java监听机制

java中的事件监听机制定义了以下几个角色

  • 事件:Event,继承java.util.EventObject类的对象
  • 事件源:Source,任意对象Object
  • 监听器:Listener,实现java.util.EventListener接口的对象

SpringBoot监听机制

SpringBoot在项目启动时,会对几个监听器进行回调,我们可以实现这些监听器接口,在项目启动时完成一些操作

ApplicationContextInitializer、SpringApplicationRunListener、CommandLineRunner、AapplicationRunner

基于上面四个接口我们分别实现了四个类

  • MyApplicationContextInitializer
java 复制代码
package com.lele.springbootlistener.listener;

import org.springframework.context.ApplicationContextInitializer;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.stereotype.Component;

@Component
public class MyApplicationContextInitializer implements ApplicationContextInitializer {
    @Override
    public void initialize(ConfigurableApplicationContext applicationContext) {
        System.out.println("ApplicationContextInitializer... + initializing...");
    }
}
  • MyApplicationRunner
java 复制代码
package com.lele.springbootlistener.listener;

import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.stereotype.Component;

@Component
public class MyApplicationRunner implements ApplicationRunner {
    @Override
    public void run(ApplicationArguments args) throws Exception {
        System.out.println("MyApplicationRunner run");
    }
}
  • CommandLineRunner
java 复制代码
package com.lele.springbootlistener.listener;

import org.springframework.boot.CommandLineRunner;
import org.springframework.stereotype.Component;

@Component
public class MyCommandLineRunner implements CommandLineRunner {
    @Override
    public void run(String... args) throws Exception {
        System.out.println("MyCommandLineRunner.run(String[])");
    }
}
  • MySpringApplicationRunListener
java 复制代码
package com.lele.springbootlistener.listener;

import org.springframework.boot.ConfigurableBootstrapContext;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.SpringApplicationRunListener;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.stereotype.Component;

import java.time.Duration;
// 这里一开始和前面一样可以使用@Component注解并且去掉构造函数
// 但是我们看springboot自己定义的发现需要两个参数和构造但是又不能自动注入,所以要驱动@Component注解
public class MySpringApplicationRunListener implements SpringApplicationRunListener {
    public MySpringApplicationRunListener(SpringApplication application, String[] args) {

    }

    @Override
    public void starting(ConfigurableBootstrapContext bootstrapContext) {
        System.out.println("MySpringApplicationRunListener starting");
    }

    @Override
    public void environmentPrepared(ConfigurableBootstrapContext bootstrapContext, ConfigurableEnvironment environment) {
        System.out.println("MySpringApplicationRunListener environmentPrepared");
    }

    @Override
    public void contextLoaded(ConfigurableApplicationContext context) {
        System.out.println("MySpringApplicationRunListener contextLoaded");
    }

    @Override
    public void contextPrepared(ConfigurableApplicationContext context) {
        System.out.println("MySpringApplicationRunListener contextPrepared");
    }

    @Override
    public void started(ConfigurableApplicationContext context, Duration timeTaken) {
        System.out.println("MySpringApplicationRunListener started");
    }

    @Override
    public void ready(ConfigurableApplicationContext context, Duration timeTaken) {
        System.out.println("MySpringApplicationRunListener ready");
    }

    @Override
    public void failed(ConfigurableApplicationContext context, Throwable exception) {
        System.out.println("MySpringApplicationRunListener failed");
    }
}

定义好后我们可以启动项目,发现只有MyApplicationRunner和MySpringApplicationRunListener成功运行了,这表明系统会自动运行,但是如果我们想要运行另外两个该如何实现呢?

  • 在classpath:(即resources目录下)新建一个META-INF/spring.factories,系统会自动调用里面的配置文件

  • 配置文件里面格式context.listener.classes = [ 监听器全类名 ],如:

java 复制代码
org.springframework.context.ApplicationContextInitializer=\
  com.lele.springbootlistener.listener.MyApplicationContextInitializer
org.springframework.boot.SpringApplicationRunListener=\
  com.lele.springbootlistener.listener.MySpringApplicationRunListener

SpringBoot启动流程分析

复制代码
┌───────────────────────────────────────────┐
│ 1. main() 调用入口                         │
│                                           │
│ public static void main(String[] args) {  │
│     SpringApplication.run(App.class, args);│
│ }                                         │
└───────────────────────────────────────────┘
                ↓
┌───────────────────────────────────────────┐
│ 2. 创建 SpringApplication 实例           │
│   - 绑定 sources、listeners、initializers │
│   - 推断应用类型(SERVLET/REACTIVE/NONE) │
└───────────────────────────────────────────┘
                ↓
┌───────────────────────────────────────────┐
│ 3. 预备阶段(prepareEnvironment)         │
│   - 创建并配置 Environment(PropertySources)│
│   - 应用 `EnvironmentPostProcessor`       │
│   - 触发 `ApplicationEnvironmentPreparedEvent` │
└───────────────────────────────────────────┘
                ↓
┌───────────────────────────────────────────┐
│ 4. 创建 ApplicationContext               │
│   - 根据应用类型选择:                     │
│     • AnnotationConfigServletWebServerApplicationContext │
│     • AnnotationConfigReactiveWebServerApplicationContext│
│     • AnnotationConfigApplicationContext  │
│   - 触发 `ApplicationContextInitializedEvent` │
└───────────────────────────────────────────┘
                ↓
┌───────────────────────────────────────────┐
│ 5. 上下文预处理(prepareContext)         │
│   - 注册 `BeanDefinition`(扫描 @SpringBootConfiguration 来源)│
│   - 执行 `ApplicationContextInitializer`  │
│   - 触发 `ContextRefreshedEvent` 前的事件│
└───────────────────────────────────────────┘
                ↓
┌───────────────────────────────────────────┐
│ 6. 刷新上下文(refresh)                  │
│   • BeanFactory 后处理:                     │
│     -- `BeanDefinitionRegistryPostProcessor`│
│     -- `BeanFactoryPostProcessor`           │
│   • 注册 BeanPostProcessor                │
│   • 初始化单例 Bean(依赖注入、生命周期回调) │
│   • 触发 `ContextRefreshedEvent`          │
└───────────────────────────────────────────┘
                ↓
┌───────────────────────────────────────────┐
│ 7. 启动 Web 容器(仅 Servlet/Reactive)    │
│   - 嵌入式 Tomcat/Jetty/Netty 启动         │
│   - 触发 `ServletWebServerInitializedEvent` │
└───────────────────────────────────────────┘
                ↓
┌───────────────────────────────────────────┐
│ 8. 执行 Runner(CommandLineRunner、ApplicationRunner)│
│   - 按 `@Order` 顺序执行                    │
│   - 触发 `ApplicationStartedEvent` & `ApplicationReadyEvent` │
└───────────────────────────────────────────┘
                ↓
┌───────────────────────────────────────────┐
│ 9. 应用启动完成,进入正常运行态           │
└───────────────────────────────────────────┘

SpringBoot监控概述

SpringBoot自带监控功能Actuator,可以帮助实现对程序内部运行情况监控,比如监控状况、Bean加载情况、配置属性、日志信息等。

使用步骤

  1. 导入依赖坐标
xml 复制代码
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
  1. 访问http://localhost:8080/acruator

可以看到所有支持的连接,默认只有:

复制代码
/actuator
/actuator/health
/actuator/health/{component}
/actuator/health/{component}/{instance}
/actuator/info

具体的使用方法

  • 引入上述的依赖

  • 通过下面的配置启用所有的监控端点,这些端点默认是禁用的

yml 复制代码
management:
  endpoints:
    web:
      exposure:
        include: "*"
  • 或者启用下面配置开启部分监控端点

java 复制代码
management:
  endpoints:
    web:
      exposure:
        exclude: beans,trace
  • "*"号代表启用所有的监控端点,可以单独启用,例如,healthinfometrics

  • Actuator 默认所有的监控点路径都在/actuator/*,当然如果有需要这个路径也支持定制。

java 复制代码
management:
  endpoints:
    web:
      base-path: /manage #记得要加上/
  • 设置完重启后,再次访问地址就会变成/manage/*

Autuator的REST接口

Actuator监控分为两类:原生端点和用户自定义端点

  • 自定义端点:主要指拓展性,根据自己的实际应用,比如Redis、MyBatis等
  • 原生端点:在应用程序里提供众多Web接口,通过它们了解应用程序运行时的内部状态
    • 应用配置类:可以查看应用在运行期的静态信息:例如自动配置信息、加载的springbean信息、yml文件配置信息、环境信息等
    • 度量指标类:主要是运行期的动态信息,例如堆栈、请求连等
    • 操作控制类:主要是指shurdown,用户可以发送一个请求将应用的控制功能关闭
GET /auditevents 显示应用暴露的审计事件 (比如认证进入、订单失败)
GET /beans 展示了所有 bean 的别名、类型、是否单例、类的地址、依赖等信息
GET /conditions 使用 conditions 可以在应用运行时查看代码了某个配置在什么条件下生效,或者某个自动配置为什么没有生效。
GET /configprops 描述配置属性(包含默认值)如何注入Bean
GET /env 获取全部环境属性
GET /env/{name} 根据名称获取特定的环境属性值
GET /flyway 提供一份 Flyway 数据库迁移信息
GET /liquidbase 显示Liquibase 数据库迁移的纤细信息
GET /health 报告应用程序的健康指标,这些值由 HealthIndicator 的实现类提供,用来检查应用的运行状态,"UP"表示健康,"down"不健康
GET /heapdump 返回一个 GZip 压缩的 JVM 堆 dump
GET /httptrace 显示HTTP足迹,最近100个HTTP request/repsponse
GET /info 获取我们自己配置在配置文件中以 info 开头的配置信息
GET /logfile 返回log file中的内容(如果 logging.file 或者 logging.path 被设置)
GET /loggers 可以查看当前应用的日志级别等信息
GET /metrics 是一个非常重要的监控端点,其监控内容覆盖了 JVM 内存、堆、类加载、处理器和 tomcat 容器等一些重要指标
GET /metrics/{name} 报告指定名称的应用程序度量值
GET /scheduledtasks 展示应用中的定时任务信息
GET /sessions 如果我们使用了 Spring Session 展示应用中的 HTTP sessions 信息
POST /shutdown 关闭应用程序,要求endpoints.shutdown.enabled设置为true
GET /mappings 描述全部的 URI路径,以及它们和控制器(包含Actuator端点)的映射关系
GET /threaddump 生成当前线程活动的快照,主要展示了线程名、线程ID、线程的状态、是否等待锁资源等信息。

SpringBoot监控-图形化界面使用

这个要针对两个模块springboot-admin-server和springboot-admin-clients进行

对于springboot-admin-server

  • 首先我们要导入依赖坐标admin-starter-server

xml 复制代码
<dependency>
            <groupId>de.codecentric</groupId>
            <artifactId>spring-boot-admin-starter-server</artifactId>
        </dependency>
  • 然后在引导类上启用监控功能@EnableAdminServer即可

java 复制代码
package com.lele.springbootadminserver;

import de.codecentric.boot.admin.server.config.EnableAdminServer;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@EnableAdminServer
@SpringBootApplication
public class SpringbootAdminServerApplication {

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

}

对于springboot-admin-clients

  • 导入依赖坐标admin-starter-client

xml 复制代码
<dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>de.codecentric</groupId>
            <artifactId>spring-boot-admin-starter-client</artifactId>
        </dependency>
  • 配置相关信息:server地址、显示health详细信息、开启所有监控

properties 复制代码
# 执行admin.server地址
spring.boot.admin.client.url=http://localhost:9000

# 这一句是表示在health那里显示详细信息
management.endpoint.health.show-details=always

# 开启所有监控
management.endpoints.web.exposure.include=*

server.port=8080

SpringBoot项目部署

SpringBoot项目开发支持两种方式部署到服务器

  • jar包
  • war包

注意:为了防止打包出来的名字过于繁琐,我们可以先定义一个finalName标签进行改名

复制代码
<build>
    <finalName>springboot-package</finalName>
    <plugins>
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-compiler-plugin</artifactId>
            <version>3.8.1</version>
            <configuration>
                <source>1.8</source>
                <target>1.8</target>
                <encoding>UTF-8</encoding>
            </configuration>
        </plugin>
       
    </plugins>
</build>
  • jar包

    • 利用的是本地的Tomcat进行打包,使用命令行mvn clean package进行打包或者到右边Maven的生存期package

    • 然后就可以运行jar包java -jar finalName.jar

  • war包

    • 利用外部的Tomcat进行打包部署到其他Tomcat服务器上

    • 首先要修改配置类

    java 复制代码
    @SpringBootApplication() 
    public class SpringbootstudyApplication extends SpringBootServletInitializer {
    	//继承SpringBootServletInitializer 重写 configure
    	@Override
    	protected SpringApplicationBuilder configure(SpringApplicationBuilder builder){
    		return builder.sources(SpringbootstudyApplication.class);
    	}
    
    	public static void main(String[] args) {
    		SpringApplication.run(SpringbootstudyApplication.class, args);
    	}
    }
    • 然后还要在pom.xml里面修改打包方式<package>war</package>就可以了
相关推荐
冷雨夜中漫步4 分钟前
Java中如何使用lambda表达式分类groupby
java·开发语言·windows·llama
在未来等你5 分钟前
互联网大厂Java求职面试:云原生架构与微服务设计中的复杂挑战
java·微服务·ai·云原生·秒杀系统·rag·分布式系统
浮游本尊8 分钟前
Java学习第4天 - 异常处理与集合框架
java
知其然亦知其所以然32 分钟前
Spring AI 入门实战:我用七个关键词,彻底搞懂了它的核心概念!
java·后端·spring
码农颜34 分钟前
java 设计模式_行为型_19命令模式
java·设计模式·命令模式
魔镜魔镜_谁是世界上最漂亮的小仙女40 分钟前
java-JDBC
java·后端
gorgor在码农44 分钟前
Spring Boot多数据源切换:三种实现方式详解与实战
java·spring boot·后端·mybatis·mybatis plus·多数据源切换
jarenyVO1 小时前
Spring Cloud Gateway 全面学习指南
java·gateway
一名小码农1 小时前
线上GC count突增问题排查及修复记录
java·gc·调优·线上问题·死循环·线上
言一木1 小时前
【springboot组件开发】三方中间件自定义自动装载(rabbitmq/rocketmq/cmq)
spring boot·java-rocketmq·java-rabbitmq·中组件开发