Published on

java经典框架-springboot (续)

Authors
  • avatar
    Name
    MissTree
    Twitter

Redis

说明:在 SpringBoot2.x之后,原来使用的jedis 被替换为了 lettuce?

  • jedis:采用的査连,多个线程操作的话,是不安全的,如果想要避免不安全的,使用jedis pool 连接池! 更像 B0 模式
  • lettuce:采用netty,实例可以再多个线程中进行共享,不存在线程不安全的情况!可以减少线程数据了,更像 Nio 横式

基本使用

// 1. 添加依赖
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>

// 2. 配置 Redis 连接信息
spring.redis.host=127.0.0.1
spring.redis.port=6379

// 3. 使用 RedisTemplate 操作 Redis
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class RedisController {
    @Autowired
    private RedisTemplate<String, Object> redisTemplate;

    @GetMapping("/redis")
    public String redis() {
        redisTemplate.opsForValue().set("key", "value");
        Object value = redisTemplate.opsForValue().get("key");
        return value.toString();
    }
}

RedisTemplate 是 Spring Boot 提供的一个用于操作 Redis 的模板类,它封装了 Redis 的各种操作,可以方便地进行数据的读写操作。但是在实际开发中往往不太适合我们企业级自定义的开发,所以一般我们都是自定义 RedisTemplate 的配置类的序列化,以便在本地redis-cli可以直接查看而不是查看转译的。

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;

@Configuration
public class RedisConfig {

    @Bean
    public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory connectionFactory) {
        RedisTemplate<String, Object> template = new RedisTemplate<>();
        template.setConnectionFactory(connectionFactory);

        // 设置键的序列化方式(String)
        StringRedisSerializer stringSerializer = new StringRedisSerializer();
        template.setKeySerializer(stringSerializer);
        template.setHashKeySerializer(stringSerializer);

        // 设置值的序列化方式(JSON)
        GenericJackson2JsonRedisSerializer jsonSerializer = new GenericJackson2JsonRedisSerializer();
        template.setValueSerializer(jsonSerializer);
        template.setHashValueSerializer(jsonSerializer);

        // 设置默认序列化方式(可选)
        template.setDefaultSerializer(jsonSerializer);

        // 初始化 RedisTemplate 的其他配置
        template.afterPropertiesSet();

        return template;
    }
}

redis工具类

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

import java.util.concurrent.TimeUnit;

@Component
public class RedisUtil {

    @Autowired
    private RedisTemplate<String, Object> redisTemplate;

    // ==================== String 相关操作 ====================

    /**
     * 设置键值对(默认永不过期)
     */
    public void set(String key, Object value) {
        redisTemplate.opsForValue().set(key, value);
    }

    /**
     * 设置键值对,并设置过期时间
     */
    public void set(String key, Object value, long timeout, TimeUnit unit) {
        redisTemplate.opsForValue().set(key, value, timeout, unit);
    }

    /**
     * 获取键对应的值
     */
    public Object get(String key) {
        return redisTemplate.opsForValue().get(key);
    }

    // ==================== Hash 相关操作 ====================

    /**
     * 设置 Hash 字段
     */
    public void hSet(String key, String field, Object value) {
        redisTemplate.opsForHash().put(key, field, value);
    }

    /**
     * 获取 Hash 字段值
     */
    public Object hGet(String key, String field) {
        return redisTemplate.opsForHash().get(key, field);
    }

    /**
     * 获取整个 Hash
     */
    public Object hGetAll(String key) {
        return redisTemplate.opsForHash().entries(key);
    }

    // ==================== List 相关操作 ====================

    /**
     * 左端插入 List
     */
    public void lPush(String key, Object value) {
        redisTemplate.opsForList().leftPush(key, value);
    }

    /**
     * 右端插入 List
     */
    public void rPush(String key, Object value) {
        redisTemplate.opsForList().rightPush(key, value);
    }

    /**
     * 获取 List 范围内的元素
     */
    public Object lRange(String key, long start, long end) {
        return redisTemplate.opsForList().range(key, start, end);
    }

    // ==================== Set 相关操作 ====================

    /**
     * 添加 Set 元素
     */
    public void sAdd(String key, Object value) {
        redisTemplate.opsForSet().add(key, value);
    }

    /**
     * 获取 Set 所有元素
     */
    public Object sMembers(String key) {
        return redisTemplate.opsForSet().members(key);
    }

    // ==================== ZSet 相关操作 ====================

    /**
     * 添加 ZSet 元素(带分数)
     */
    public void zAdd(String key, double score, Object value) {
        redisTemplate.opsForZSet().add(key, value, score);
    }

    /**
     * 获取 ZSet 范围内的元素(按分数升序)
     */
    public Object zRange(String key, long start, long end) {
        return redisTemplate.opsForZSet().range(key, start, end);
    }

    // ==================== 通用操作 ====================

    /**
     * 删除键
     */
    public boolean delete(String key) {
        return Boolean.TRUE.equals(redisTemplate.delete(key));
    }

    /**
     * 判断键是否存在
     */
    public boolean hasKey(String key) {
        return Boolean.TRUE.equals(redisTemplate.hasKey(key));
    }

    /**
     * 设置过期时间
     */
    public boolean expire(String key, long timeout, TimeUnit unit) {
        return Boolean.TRUE.equals(redisTemplate.expire(key, timeout, unit));
    }

    /**
     * 获取剩余过期时间
     */
    public long getExpire(String key) {
        return redisTemplate.getExpire(key, TimeUnit.SECONDS);
    }

    /**
     * 清空所有 Redis 数据(慎用!)
     */
    public void flushDB() {
        redisTemplate.execute((connection) -> {
            connection.flushDb();
            return null;
        });
    }
}

任务

异步任务

在执行一些不需要立即返回结果的任务时,可以使用异步任务来提高系统的并发性能。Spring Boot 提供了异步任务的支持,可以通过以下步骤实现异步任务:

// 1. 在启动类上添加 @EnableAsync 注解开启异步任务支持
@SpringBootApplication
@EnableAsync
public class SpringbootApplication {
    public static void main(String[] args) {
        SpringApplication.run(SpringbootApplication.class, args);
    }
}

// 2. 在需要异步执行的方法上添加 @Async 注解
@Service
public class AsyncService {
    @Async
    public void asyncMethod() {
         try {
            Thread.sleep(10000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("test");
    }
  
}

// 3. 调用异步方法
@RestController
public class AsyncController {
    @Autowired
    private AsyncService asyncService;

    @GetMapping("/async")
    public String async() {
        asyncService.asyncMethod();
        return "success";
    }
}

定时任务

cron 表达式或者标准 cron

// 1. 在启动类上添加 @EnableScheduling 注解开启定时任务支持
@SpringBootApplication
@EnableScheduling
public class SpringbootApplication {
    public static void main(String[] args) {
        SpringApplication.run(SpringbootApplication.class, args);
    }
}

// 2. 在需要定时执行的方法上添加 @Scheduled 注解
@Service
public class ScheduledService {
    @Scheduled(cron = "0/5 * * * * ?") // 每5秒执行一次    0 */5 * * * ? 每5分钟执行一次
    // @Scheduled(cron = "0 * 19 * * ?")  每分钟执行一次   0 15 10 ? *1-6 每个月的周一到周六 10.15分钟执行一次
    public void scheduledMethod() {
        System.out.println("定时任务执行");
    }
}

邮件任务

// 1. 在 application.properties 中配置邮件服务器信息
spring.mail.host=smtp.example.com  // SMTP 服务器地址  smtp.qq.com
spring.mail.username=your-email@example.com
spring.mail.password=your-email-password // QQ邮箱授权码
spring.mail.properties.mail.smtp.ssl.enable=true

// 2. 创建邮件发送器
import org.springframework.mail.SimpleMailMessage;
import org.springframework.mail.javamail.JavaMailSenderImpl;
import org.springframework.mail.javamail.MimeMessageHelper;

@Service
public class EmailService {
    @Autowired
    private JavaMailSender mailSender;

    public void sendEmail() {
        SimpleMailMessage mailMessage = new SimpleMailMessage();
        mailMessage.setFrom("515072052@qq.com"); // 确保发件人地址与配置文件
        mailMessage.setSubject("测试邮件");
        mailMessage.setText("测试邮件内容");
        mailMessage.setTo("515072052@qq.com");
        mailSender.send(mailMessage);
    } 
    // 复杂邮件发送
    public void sendComplexEmail() {
        MimeMessage message = mailSender.createMimeMessage();
        // 组装
        MimeMessageHelper helper = new MimeMessageHelper(message, true);
        // 正文
        helper.setText("<html><head></head><body><h1 style='color:red'>你好,这是一个HTML文本</h1></body></html>", true);
        helper.setSubject("测试邮件");

        // 附件
        helper.addAttachment("test.txt", new java.io.File("C:\\Users\\admin\\Desktop\\服务器常用命令.txt"));
        helper.addAttachment("test.png", new java.io.File("C:\\Users\\admin\\Pictures\\22222222222.jpg"));

        helper.setFrom("515072052@qq.com");
        helper.setTo("8xxxx48@qq.com");
    }

}

// 3. 调用邮件发送器发送邮件
@RestController
public class EmailController {
    @Autowired
    private EmailService emailService;

    @GetMapping("/sendEmail")
    public String sendEmail() {
        emailService.sendComplexEmail();
        return "success";
    }
}

注意 :QQ邮箱需要开启 SMTP 服务,并获取授权码作为密码,而不是邮箱密码。application中的username看情况定,可能有几个邮箱别名,要使用当前授权码的邮箱别名。

自定义邮件发送

分布式

分布式系统由多个独立计算机(节点)组成,通过网络协同工作,对外表现为一个整体系统。每个节点可以独立部署、扩展和维护。

单一系统、分布式、云服务 | 特性 | 单一系统 | 分布式系统 | 云开发 | | --- | --- | --- | --- | | 架构 | 单体 | 微服务/分布式 | 基于云服务(IaaS/PaaS/Serverless) | | ​扩展性 | 差(垂直扩展) | 高(水平扩展) | 极高(自动扩缩容) | | ​运维复杂度 | 低 | 高 | 低(云平台托管) | | ​成本 | 固定(硬件投入) | 较高(需管理多节点) | 按需付费(灵活控制) | | ​适用场景 | 小型应用 | 大型企业级系统 | 快速开发、初创公司 | | | | | |

未来趋势

  • 云原生(Cloud Native)​:微服务 + 容器化(Docker/K8s) + Serverless。
  • 边缘计算(Edge Computing)​:计算靠近数据源(如 IoT 设备)。
  • ​Serverless 进化:更多 BaaS 服务,降低开发门槛。

RPC


RPC【Remote Procedure Call】是指远程过程调用,是一种进程间通信方式,他是一种技术的思想,而不是规范,它允许程序调用另一个地址空间(通常是共享网络的另一台机器上)的过程或函数,而不用程序员显式编码这个远程调用的细节。即程序员无论是调用本地的还是远程的的数,本质上编写的调用代码基本相同。 也就是说两台服务器A,B,一个应用部署在A服务器上,想要调用B服务器上应用提供的函数的方法,由于不在一个内存空间,不能直接调用,需要通过网络来表达调用的语义和传达调用的数据。为什么要用RPC呢?就是无法在一个进程内,甚至一个计算机内通过本地调用的方式完成的需求,比如不同的系统间的通讯,甚至不同的组织间的通讯,由于计算能力需要横向扩展,需要在多台机器组成的集群上部署应用,RPC就是要像调用本地的函数一样去调远程函数;

逻辑流程:

  • 将服务器B上的函数注册到服务器A上,作为一个provider。
  • 客户端H在服务器A上调用函数,就像调用本地函数一样,实际上是通过网络调用服务器B上的函数,服务器A上有一个proxy,负责接收客户端的请求,并将请求转发给服务器B上的provider给客户端H消费。

常见框架:

  • gRPC(微服务、跨语言推荐):基于 HTTP/2 和 Protocol Buffers,支持多种语言。
  • Thrift:由 Facebook 开发,支持多种语言,使用 IDL 定义接口。
  • Dubbo(推荐):阿里巴巴开源的 RPC 框架,支持多种协议和负载均衡策略的Java微服务。
  • Spring Feign(spring cloud推荐):基于 HTTP 的 RPC 框架,支持 Spring Cloud。

Dubbo

Dubbo 是阿里巴巴开源的 RPC 框架,支持多种协议和负载均衡策略的Java微服务。Dubbo 提供了服务治理、服务发现、负载均衡等功能,可以帮助开发者构建高性能、高可用的分布式系统。提供了多种多种语言,如 Java、Go、Node、Python 等支持。

插件 zkclient-spring-boot-starter 和 dubbo-spring-boot-starter 在 spring boot 项目使用时会有坑点,比如日志会有冲突:解决办法,在依赖中使用 exclusions 标签排除 slf4j-log4j12 依赖。

使用方式:

  • zookeeper 作为注册中心
  • dubbo-admin 作为监控中心,查看哪些服务被注册,哪些服务被调用
  • dubbo jar包作为服务提供者

步骤:

  • 服务提供者:引入依赖,配置文件,实现接口,启动类添加注解
      1. 引入依赖
      <!-- Dubbo Spring Boot Starter -->
      <dependency>
          <groupId>org.apache.dubbo</groupId>
          <artifactId>dubbo-spring-boot-starter</artifactId>
          <version>3.2.0</version>
      </dependency>
    
      <!-- Zookeeper 客户端 -->
      <dependency>
          <groupId>org.apache.curator</groupId>
          <artifactId>curator-recipes</artifactId>
          <version>5.3.0</version>
      </dependency>
      <dependency>
          <groupId>org.apache.curator</groupId>
          <artifactId>curator-framework</artifactId>
          <version>5.3.0</version>
      </dependency>
    
    • 2.配置注册中心的地址,以及服务发现名,和要扫描的包
      # application.yml
      spring:
        application:
          name: dubbo-provider-demo
    
      dubbo:
        application:
          name: dubbo-provider-demo
        protocol:
          name: dubbo
          port: 20880
        registry:
          address: zookeeper://127.0.0.1:2181
        scan:
          base-packages: com.example.provider.service
    
    • 3.在想要被注册的服务上面~增加一个注解 @Service (使用的是dubbo的而不是spring的)
      // UserService.java
      public interface UserService {
          String getUserName(Long id);
          List<String> getUserRoles(Long id);
      }
    
      // UserServiceImpl.java
      import org.apache.dubbo.config.annotation.DubboService;
    
      @DubboService
      public class UserServiceImpl implements UserService {
          @Override
          public String getUserName(Long id) {
              return "User-" + id;
          }
    
          @Override
          public List<String> getUserRoles(Long id) {
              return Arrays.asList("admin", "user");
          }
      }
    
  • 消费者使用
      1. 引入依赖
      1. 配置注册中心的地址,配置自己的股务名~
      # application.yml
      spring:
        application:
          name: dubbo-consumer-demo
    
      dubbo:
        application:
          name: dubbo-consumer-demo
        registry:
          address: zookeeper://127.0.0.1:2181
        consumer:
          check: false
    
    
      1. 在需要使用的地方使用 @Reference 注解,指定要使用的服务名,就可以直接使用这个服务了
      // UserServiceConsumer.java
      import org.apache.dubbo.config.annotation.DubboReference;
      import org.springframework.stereotype.Component;
    
      @Component
      public class UserServiceConsumer {
          
          @DubboReference
          private UserService userService;
          
          public String getUserName(Long id) {
              return userService.getUserName(id);
          }
          
          public List<String> getUserRoles(Long id) {
              return userService.getUserRoles(id);
          }
      }
    
      // UserController.java
      import org.springframework.web.bind.annotation.GetMapping;
      import org.springframework.web.bind.annotation.PathVariable;
      import org.springframework.web.bind.annotation.RestController;
    
      @RestController
      public class UserController {
          
          private final UserServiceConsumer userServiceConsumer;
          
          public UserController(UserServiceConsumer userServiceConsumer) {
              this.userServiceConsumer = userServiceConsumer;
          }
          
          @GetMapping("/user/{id}/name")
          public String getUserName(@PathVariable Long id) {
              return userServiceConsumer.getUserName(id);
          }
          
          @GetMapping("/user/{id}/roles")
          public List<String> getUserRoles(@PathVariable Long id) {
              return userServiceConsumer.getUserRoles(id);
          }
      }
    

这样,消费者就可以通过 Dubbo 框架调用服务提供者的服务了。若是服务提供者有多个实例,Dubbo 会自动进行负载均衡,选择一个最优的实例进行调用。 如果想看可视化监控,可以使用 dubbo-admin。 dubbo-admin支持如下功能:

  • 服务治理:查看服务提供者和消费者的信息,查看服务调用的实时数据。
  • 服务监控:查看服务调用的实时数据,包括调用次数、调用时间等。
  • 服务测试:可以测试服务是否可用,以及服务的性能。
  • 服务配置:可以配置服务的参数,修改服务负载均衡的权重。

Nacos

Nacos 是阿里巴巴开源的微服务注册中心,支持服务发现、配置管理、动态 DNS 解析等功能。Nacos 可以帮助开发者构建高性能、高可用的分布式系统。 在上面的例子中,我们使用了 Zookeeper 作为注册中心,现在我们使用 Nacos 作为注册中心。

<!-- pom.xml -->
<dependency>
    <groupId>com.alibaba.nacos</groupId>
    <artifactId>nacos-client</artifactId>
    <version>2.2.0</version>
</dependency>

配置

# application.yml  provider
spring:
  application:
    name: dubbo-provider-demo

dubbo:
  application:
    name: dubbo-provider-demo
  protocol:
    name: dubbo
    port: 20880
  registry:
    address: nacos://127.0.0.1:8848
    parameters:
      namespace: public
  scan:
    base-packages: com.example.provider.service

# application.yml  consumer
spring:
  application:
    name: dubbo-consumer-demo

dubbo:
  application:
    name: dubbo-consumer-demo
  registry:
    address: nacos://127.0.0.1:8848
    parameters:
      namespace: public
  consumer:
    check: false    

接口和实现类不变,只需要修改注册中心的地址即可。


Swagger

Swagger 是一个用于生成API文档的工具,它可以帮助开发人员快速地创建和查看API文档,并且可以与Spring Boot无缝集成。

安装依赖

  <!-- spring 2.x 版本swagger -->
		<dependency>
			<groupId>io.springfox</groupId>
			<artifactId>springfox-swagger2</artifactId>
		</dependency>
		<dependency>
			<groupId>io.springfox</groupId>
			<artifactId>springfox-swagger-ui</artifactId>
		</dependency>
  <!-- spring 3.x 版本 swagger -->
    <dependency>
      <groupId>org.springdoc</groupId>
      <artifactId>springdoc-openapi-starter-webmvc-ui</artifactId>
      <version>2.5.0</version> <!-- 使用最新版本 -->
    </dependency>

配置

spring:
  autoconfigure:
    // 若是配置了 security 要关闭,否则要权限
    exclude: org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration

springdoc:
  swagger-ui:
    path: /swagger-ui.html  # Swagger UI 访问路径
    tags-sorter: alpha      # 按字母排序标签
    operations-sorter: alpha
  api-docs:
    path: /v3/api-docs      # OpenAPI JSON 路径
  default-produces-media-type: application/json
  info:
    title: Spring Boot 3 API
    version: 1.0.0
    description: API Documentation

项目目录下的config文件夹配置

// spring 2.x 版本swagger
@Configuration
@EnableSwagger2
public class SwaggerConfig {
    @Bean
    public Docket api() {
        return new Docket(DocumentationType.SWAGGER_2)
                .select()
                // 通过 grounpName 配置多个模块分组 要求有多个Docket
                .grounpName("default")
                //为当前包路径 指定扫描的controller
                .apis(RequestHandlerSelectors.basePackage("com.misstree.demo.controller"))
                // 过滤规则
                .paths(PathSelectors.any())
                .build();
    }
    // 配置swagger 信息apiInfo()用来创建该API的基本信息(这些信息会展示在文档页面中)
    public ApiInfo apiInfo() {
      // 作者信息
        Contact contact = new Contact("misstree", "https://github.com/misstree", "misstree@163.com");
        return new ApiInfo(
          title: "Spring Boot 2 API",
          description: "API Documentation",
          version: "1.0.0",
          contact: contact,
          license: new License("Apache 2.0", "http://www.apache.org/licenses/LICENSE-2.0.html"),
          licenseUrl: "http://www.apache.org/licenses/LICENSE-2.0.html",
          new ArrayList()
        )
    }
    }

}

// spring 3.x 版本 swagger
import org.springframework.context.annotation.Profile;

@Configuration
@Profile({"dev", "test"}) // 仅在 dev 和 test 环境下启用,生产环境不启用保证安全和性能
public class SwaggerConfig {
    @Bean
    public OpenApi openApi() {
        return new OpenApi()
        .info(new Info()
        .title("Spring Boot 3 API")
        .version("1.0.0")
        .description("API Documentation")
      );
    }
}

使用

// spring 2.x 版本swagger
package com.example.controller;

import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import io.swagger.v3.oas.annotations.responses.ApiResponses;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class HelloController {

    @Operation(summary = "获取用户信息", description = "根据用户 ID 获取用户信息")
    @ApiResponses(value = {
        @ApiResponse(responseCode = "200", description = "成功获取用户信息"),
        @ApiResponse(responseCode = "404", description = "用户未找到")
    })
    @GetMapping("/users/{id}")
    public String getUser(@PathVariable @Parameter(description = "用户 ID") Long id) {
        return "User with id: " + id;
    }
}
// spring 3.x 版本swagger
package com.misstree.demo.controller;

import com.misstree.demo.commentsMaper.Comments;
import com.misstree.demo.mapper.CommentsMaper;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.Parameters;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import io.swagger.v3.oas.annotations.responses.ApiResponses;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.List;

@Data
@AllArgsConstructor
@NoArgsConstructor
@RestController
// 功能模块的名称 描述
@Tag(name = "评论模块", description = "用于博客文章的评论管理")
@RequestMapping("/comments") // 接口请求路径前缀
public class CommentsController {

    @Autowired
    private CommentsMaper commentsMaper;

    @GetMapping("/getAllComments")
    // 描述接口的功能
    @Operation(summary = "获取评论列表")
    // 设置 dto 方式请求数据
    @Parameters({
            @Parameter(name = "name", description = "评论人姓名"),
            @Parameter(name = "content", description = "评论内容"),
            @Parameter(name = "time", description = "评论时间"),
    })
    // 设置响应的结果
    @ApiResponses({
            @ApiResponse(responseCode = "200", description = "获取评论列表成功"),
            @ApiResponse(responseCode = "400", description = "参数错误"),
            @ApiResponse(responseCode = "401", description = "没有权限"),
            @ApiResponse(responseCode = "403", description = "禁止访问"),
    })
    public List<Comments> getComments(){
        return commentsMaper.gets();
    }
}

访问

http://localhost:8080/swagger-ui.html swagger

打包与部署

在项目开发完成后,需要使用 Maven 或 Gradle 构建工具将项目打包成可执行的 jar 文件,并部署到服务器上运行。

Maven

Maven下载对应的压缩文件,windows 下载 apache-maven-3.x.x-bin.zip 文件。将下载好的文件解压到指定目录D:\apache-maven-3.9.9,配置环境变量D:\apache-maven-3.9.9\bin,在命令行输入 mvn -v 查看是否配置成功。

在项目文件夹中执行命令
mvn clean package -Dmaven.test.skip=true

-Dmaven.test.skip=true 跳过测试

这样在target 目录下会生成一个可执行的 jar 文件,使用命令 java -jar xxx.jar 启动项目。

修改服务配置

  • java -jar xxx.jar --server.port=8880
  • java -jar xxx.jar --spring.profiles.active=dev
  • java -jar xxx.jar --server.port=8880 --spring.profiles.active=dev --spring.datasource.url=jdbc:mysql://localhost:3306/test

项目配置优先级:

命令行参数 > 环境变量 > 配置文件 > 默认配置

  • 命令行参数:java -jar xxx.jar --server.port=8880
  • 环境变量:在启动项目时,通过设置环境变量来传递配置参数,例如:export JAVA_OPTS="-Dserver.port=8880"
  • 配置文件:在项目打包好文件target层级下设置,例如:application.properties 或 application.yml,本质的等同于之前说的 配置文件优先级的设置
  • 默认配置:在 Spring Boot 的默认配置resources/application.properties 中设置参数