# 环境

Spring Boot 3.2.0-SNAPSHOT 需要 Java 17 ,并且可以兼容到 Java 20,包括 Java 20。还需要 Spring Framework 6.1.0-M1 或以上版本。

Maven 3.6.3 及其以上
Gradle 7.x (7.5 及其以上) 和 8.x
java17 及以上
idea

# 入门

  1. 在 idea 中新建项目
  2. 选择 Spring Initializr
  3. 填入相关内容后(类型选择 Maven)
  4. 点击下一步
  5. Web 中选择 Spring Web
  6. 点击创建

# 项目创建成功后

# 目录

.idea 为 idea 的文件目录不用管
.mvm 为 maven 的文件目录
src 为代码目录,以后的编码环境都在此目录下
.gitignore 为 git 的排除目录
HELP.md 为描述文档
mvnw Maven Wrappe
mvnw.cmd 适用于 Windows 环境
pom.xml 管理 maven 的配置文件

# Hello World!

在 src/main/java/com.*.* 下新建 Controller 目录
(* 号分别为组名和工件名)
在 Controller 新建文件 "TestController.java"

1
2
3
4
5
6
7
8
9
10
11
12

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

@RestController
public class TestController {
@RequestMapping("/home")
String home() {
return "Hello World!";
}
}

点击运行
浏览器使用 get 或者 post 访问 http://127.0.0.1:8080/home
即输出 Hello Werld!

# 规范书写

# 目录结构书写

src/main/java: 存放 Java 源代码文件的目录。这里通常包括以下子目录:
com/example/yourproject: 项目的主要 Java 包。这里存放应用程序的核心类和逻辑。
config: 存放应用程序的配置类。
controller: 存放 Spring MVC 控制器类。
service: 存放业务逻辑层的接口和实现类。
mapper/repository: 存放数据访问层 (如 JPA 或 JDBC) 的接口和实现类。
model: 存放实体类或数据传输对象 (DTO)。

src/main/resources: 存放应用程序的配置文件和其他资源文件。这里通常包括以下内容:
application.properties 或 application.yml: 应用程序的主要配置文件。
static: 存放静态资源文件,如 CSS、JavaScript、图片等。
templates: 存放 Thymeleaf、JSP 或其他模板引擎的视图文件。
data.sql: 初始化数据库的 SQL 脚本。

src/test/java: 存放单元测试和集成测试的 Java 源代码文件。

pom.xml: Maven 项目的 POM (Project Object Model) 文件,用于管理依赖项和构建过程。

application.properties 或 application.yml: 应用程序的主要配置文件,用于设置数据源、服务器端口、日志级别等。

mvnw 和 mvnw.cmd: Maven Wrapper 文件,用于在没有安装 Maven 的环境下构建和运行项目。

# 详细目录

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57

servicex // 项目名
|- servicex-auth // 模块1
|- servicex-common // 模块2
|- servicex-gateway // 模块3
|- servicex-system // 模块4
|- src
|- main // 业务逻辑
|- assembly // 基于maven assembly插件的服务化打包方案
|- bin // 模块脚本(启动、停止、重启)
|- sbin // 管理员角色使用的脚本(环境检查、系统检测等等)
|- assembly.xml // 配置文件
|- java // 源码
|- com
|- hadoopx
|- servicex
|- annotation // 注解
|- aspect // 面向切面编程
|- config // 配置文件POJO
|- filter // 过滤器
|- constant // 存放常量
|- utils // 工具
|- exception // 异常
|- controller // 控制层(将请求通过URL匹配,分配到不同的接收器/方法进行处理,然后返回结果)
|- service // 服务层接口
|- impl // 服务层实现
|- mapper/repository // 数据访问层,与数据库交互为service提供接口
|- model/entity/domain // 实体对象
|- dto // 持久层需要的实体对象(用于服务层与持久层之间的数据传输对象)
|- vo // 视图层需要的实体对象(用于服务层与视图层之间的数据传输对象)
|- *Application.java // 入口启动类
|- resources // 资源
|- static // 静态资源(html、css、js、图片等)
|- templates // 视图模板(jsp、thymeleaf等)
|- mapper // 存放数据访问层对应的XML配置
|- *Mapper.xml
|- ...
|- application.yml // 公共配置
|- application-dev.yml // 开发环境配置
|- application-prod.yml // 生产环境配置
|- banner.txt
|- logback.xml // 日志配置
|- test // 测试源码
|- java
|- com
|- hadoopx
|- servicex
|- system
|- 根据具体情况按源码目录结构存放编写的测试用例
|- target // 编译打包输出目录(自动生成,不需要创建)
|- pom.xml // 该模块的POM文件
|- sql // 项目需要的SQL脚本
|- doc // 精简版的开发、运维手册
|- .gitignore // 哪些文件不用传到版本管控工具中
|- pom.xml // 工程总POM文件
|- README.md // 注意事项
External Libraries // 相关JAR包依赖

# restful 风格

URL 路由中只使用名词来定位资源,用 HTTP 协议里的动词(GET、POST、PUT、DELETE)来实现资源的增删改查操作。

举例

  • 增加一个 id /id 请求协议:POST
  • 删除一个 id /id 请求协议:DELETE
  • 修改一个 id /id 请求协议:PUT
  • 查找一个 id /id 请求协议:GET

# 完整访问数据库的例子

# mysql

# 创建数据库

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

-- 导出 test 的数据库结构
DROP DATABASE IF EXISTS `test`;
CREATE DATABASE IF NOT EXISTS `test` /*!40100 DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci */ /*!80016 DEFAULT ENCRYPTION='N' */;
USE `test`;

-- 导出 表 test.user 结构
DROP TABLE IF EXISTS `user`;
CREATE TABLE IF NOT EXISTS `user` (
`id` int NOT NULL AUTO_INCREMENT,
`user_name` varchar(50) NOT NULL,
`pass_word` varchar(50) NOT NULL,
`name` varchar(50) NOT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `user_pk_2` (`user_name`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;

创建好表后往里面添加几条数据

# 导包

1
2
3
4
5
6

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

# 配置

在 resources 文件夹下新建 application.yml

1
2
3
4
5
6
7
8

spring:
datasource:
driver-class-name: com.mysql.jdbc.Driver
url: jdbc:mysql://localhost:3306/test?serverTimezone=GMT%2B8&useSSL=true
username: root #你的数据库用户名
password: abab #你的数据库密码

# 书写实体类

即表的模型

# 导包

lombok 可以直接使用注解写 get、set、toString 等方法

1
2
3
4
5
6
7

<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.32</version>
<scope>provided</scope>
</dependency>

在 model 包下创建 User.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

package com.test.test.model;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {
public Integer Id;
public String UserName;
public String PassWord;
public String Name;
}

# mybatis

mybatis 是用于操作数据的框架,可以使开发更方便

我们这个例子使用 Mybatis + 注解的形式

# 导包

1
2
3
4
5
6

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

# 配置

因为我们的实体类使用的驼峰命名法,而数据库使用的下划线分割法,所以要加转化配置

application.yml

1
2
3
4
5

mybatis:
configuration:
#下划线转驼峰
map-underscore-to-camel-case: true

# 编码

  1. 于 Controller 创建 TestController 控制器
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17


import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class TestController {
@Autowired
private TestService testService;

@GetMapping("id")
User getId(){
return testService.getId();
}

}
  • @RestController 标注为控制器
  • @Autowired 自动导入
  • @GetMapping (“id”) 接收请求类型为 get 链接为 /id
  1. Service 中创建 TestService
1
2
3
4

public interface TestService {
public User getId();
}

这里面写的就是你的接口

  1. Service/Impl 中写实现类 TestServiceImpl
1
2
3
4
5
6
7
8
9
10
11
12
13

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service
public class TestServiceImpl implements TestService {
@Autowired
private TestMapper testMapper;
@Override
public User getId() {
return testMapper.getId();
}
}

@Service 标注为 Service

  1. Mapper 中创建接口 TestMapper
1
2
3
4
5
6
7

@Mapper
@Repository
public interface TestMapper {
@Select("select * from user where id=1")
public User getId();
}

@Mapper 当我们在 Mapper 接口上使用 @Mapper 注解时,MyBatis 会自动为这个接口生成一个实现类
@Select 使用 @Select 注解可以直接在 Mapper 接口方法上定义对应的 SQL 语句

写好后点击运行
在浏览器访问链接 (http://127.0.0.1:8080/id)
就可以访问到数据库里的数据了!

流程为浏览器发送请求,Controller 通过 @GetMapping (“id”) 接收请求到 getId 方法中,
在调用 Service 层的 getId () 方法,在通过 Service 调用 Mapper 层的接口操作数据库返回成 User 实体

提问:为什么不直接 Controller 层调用 Mapper 层返回实体呢?要 Service 有什么用呢?

因为 Service 层是实现业务逻辑的主要地方。这里不仅包括具体的业务操作,比如计算、数据处理等,还包括对数据的校验、业务规则的应用等。

# mybatis plus (访问数据库)

# 过滤器 (Filter) 拦截器 (Interceptor)

  1. 过滤器 (Filter) 阶段:

    • 过滤器是基于 Java Servlet 规范的一部分。它们在请求到达 Spring 的前端控制器 DispatcherServlet 之前运行。
    • 过滤器可以对进入的请求进行预处理,也可以对发送回客户端的响应进行后处理。它们通常用于日志记录、身份验证、编码请求体、跨站点请求伪造 (CSRF) 保护等。
    • 如果有多个过滤器,它们的执行顺序根据在 web 配置中的声明顺序或通过 @Order 注解指定的顺序。
  2. 拦截器链 (Interceptor Chain) 的 preHandle 方法执行:

    • 请求通过所有过滤器后,接下来会到达 Spring 的 DispatcherServlet。
    • 在 DispatcherServlet 将请求路由到相应的 Controller 之前,配置的拦截器的 preHandle 方法会被调用。
    • 拦截器可以进行更细粒度的控制,比如权限检查、日志记录、事务管理等。如果 preHandle 返回 false,则停止请求的进一步处理。
  3. 请求到达 Controller:

    • 如果拦截器的 preHandle 方法返回 true,请求继续向下执行,最终到达目标 Controller。
    • Controller 处理请求,并返回相应的模型视图或响应体。
  4. 拦截器链的 postHandle 和 afterCompletion 方法执行:

    • 一旦 Controller 处理完请求,拦截器链中的 postHandle 方法会被调用,接着是视图渲染。
    • 最后,在响应发送回客户端之后,afterCompletion 方法被调用,这是进行资源清理的好时机。

整个流程如下:请求进入 → 过滤器 → DispatcherServlet → 拦截器的 preHandle → Controller → 拦截器的 postHandle → 视图渲染 → 拦截器的 afterCompletion。
过滤器和拦截器虽然在功能上有所重叠,但它们各自有不同的用途和适用场景。过滤器更接近于底层的请求处理,而拦截器提供了更细粒度的控制,且完全集成在 Spring 的请求处理流程中。

# 过滤器 (filter) 的应用

举例:发送的 /id 请求验证 token,/ab 验证请求的请求头中携带 "req-name" 的值是否为 "abcde"

# 代码书写

@WebFilter+@ServletComponentScan(“com.zhengxl.filterdemo.filter”)
@WebFilter (urlPatterns = “/*”) 为拦截所有请求

@ServletComponentScan (“com.zhengxl.filterdemo.filter”) 放在启动器类上

1
2
3
4
5
6
7
8
9
10

@SpringBootApplication
@ServletComponentScan("com.zhengxl.filterdemo.filter")
public class TestApplication {

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

}

于 filter 中创建 TokenFilter

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56

import jakarta.servlet.*;
import jakarta.servlet.annotation.WebFilter;
import jakarta.servlet.http.HttpServletRequest;
import org.springframework.stereotype.Component;

import java.io.IOException;

@Component
@WebFilter(urlPatterns = "/*") // 监听所有路径
public class TokenFilter implements Filter {
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
HttpServletRequest httpRequest = (HttpServletRequest) servletRequest;
String path = httpRequest.getServletPath(); // 获取请求的路径

// 根据请求的路径执行不同的验证逻辑
switch (path) {
case "/id":
// 验证token
String tokenValue = httpRequest.getHeader("token");
if (isValidToken(tokenValue)) {
filterChain.doFilter(servletRequest, servletResponse);
} else {
sendInvalidResponse(servletResponse, "Invalid token");
}
break;
case "/ab":
// 验证req-name
String reqNameValue = httpRequest.getHeader("req-name");
if ("abcdef".equals(reqNameValue)) {
filterChain.doFilter(servletRequest, servletResponse);
} else {
sendInvalidResponse(servletResponse, "Invalid req-name value");
}
break;
default:
// 对于其他路径,直接继续过滤链
filterChain.doFilter(servletRequest, servletResponse);
break;
}
}

// 验证token的有效性
private boolean isValidToken(String token) {
// 这里仅为示例,实际应用中应替换为实际的token验证逻辑
return token != null && token.equals("validToken");
}

// 发送无效响应
private void sendInvalidResponse(ServletResponse servletResponse, String message) throws IOException {
servletResponse.setContentType("text/plain");
servletResponse.setCharacterEncoding("UTF-8");
servletResponse.getWriter().write(message);
}
}

# 拦截器 (Interceptor) 的应用

  • preHandle
    在 Controller 方法处理之前

  • postHandle
    调用前提:preHandle 返回 true
    在 Controller 方法处理之后,DispatcherServlet 进行试图的渲染之前

  • afterCompletion
    调用前提:preHandle 返回 true
    DispatcherServlet 进行试图的渲染之后

在 filter 中新建文件 TestInterceptor

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26

@Component
public class TestInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
// 一个简单的安全校验,要求请求头中必须包含 req-name : yihuihui
String header = request.getHeader("req-name");
if ("yihuihui".equals(header)) {
return true;
}

System.out.println("请求头错误: {"+header+"}");
return false;
}

@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
System.out.println("执行完毕!");
response.setHeader("res", "postHandler");
}

@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
System.out.println("回收");
}
}

然后加入到 spring 的配置中
在 config 中新建 TestConfig 文件
实现接口,实现 addInterceptors 方法

1
2
3
4
5
6
7
8
9
10
11
12
13

@Configuration
public class TestConfig implements WebMvcConfigurer {
@Autowired
TestInterceptor testInterceptor;

@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(testInterceptor)
.addPathPatterns("/**")//指定该类拦截的url
.excludePathPatterns("/static/**");//过滤静态资源
}
}

# 跨域请求

在 config 中创建 TestConfig

1
2
3
4
5
6
7
8
9
10
11

@Configuration
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")
.allowCredentials(true)
//放行特定的域
.allowedOrigins("https://example.com", "https://another-example.com")
.allowedMethods(new String[]{"GET","POST","PUT","DELETE"})
.allowedHeaders("*")
.exposedHeaders("*");
}

# spring 邮件系统

# 导包

1
2
3
4
5
6

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-mail</artifactId>
<version>3.3.0</version>
</dependency>

# 开启邮件服务

以 outlook 邮件为例子

# 添加配置

application.yml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

spring:
mail:
# 邮箱账号
username: 123456@outlook.com
# 邮箱密码
password: '123456'
# SMTP服务器地址
host: smtp-mail.outlook.com
properties:
mail:
smtp:
# 是否需要身份验证
auth: true
starttls:
# 是否启用STARTTLS安全连接
enable: true
# 是否要求STARTTLS安全连接
required: true
# 邮件服务器端口,587是STARTTLS的标准端口
port: 587
# 默认编码设置
default-encoding: UTF-8

# 编写工具类

在 utils 中创建 EmailUtils

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28

@Component
public class EmailUtils {

@Autowired
private JavaMailSender javaMailSender;
@Value("${spring.mail.username}")
private String from;

/**
*
* @param to 邮件接收人
* @param subject 邮件主题
* @param text 邮件正文
*/
public void sendMail(String to,String subject,String text){

SimpleMailMessage message=new SimpleMailMessage();
message.setFrom(from);
message.setTo(to);
message.setSubject(subject);
message.setText(text);
message.setSentDate(new Date());

javaMailSender.send(message);

}
}

在需要使用的地方调用这个方法就可以了

# 定时任务管理

# 实现

首先在启动器上加 @EnableScheduling 注解

1
2
3
4
5
6
7
8

@SpringBootApplication
@EnableScheduling//开启定时任务管理
public class TestApplication {
public static void main(String[] args) {
SpringApplication.run(TestApplication.class, args);
}
}

然后使用 @Scheduled 注解

1
2
3
4
5
6
7
8
9
@Component
public class TaskUtils {
public static int i=0;
@Scheduled(cron = "0/5 * * * * ? ")
public void execute(){
i+=5;
System.out.println("时间:"+i+"秒");
}
}

@Scheduled (cron = "0/5 * * * * ?") 代表每 5 秒执行

# cron

cron 表达式语法
格式:秒 分 时 日 月 周 年

说明 是否必填 允许值 通配符
0-59 , - * /
0-59 , - * /
0-23 , - * /
1-31 , - * ? / L W
1-12 or JAN-DEC , - * /
1-7 or SUM-SAT , - * ? / L #
empty or 1970-2099 , - * /

通配符:
* 表示所有值。例如:在秒的字段上设置 “*”, 即每一秒钟都会触发。

? 表示不指定值。使用的场景为不需要关心当前设置这个字段的值。例如:不关心周几 "2 3 * * * ?"

- 表示区间。例如:在小时上设置 “1-3”, 表示 1,2,3 点都会触发。

, 表示指定多个值,例如:在周字段上设置 “MON,WED,FRI” 表示周一,周三和周五触发

/ 用于递增触发。如在秒上面设置 "5/15" 表示从 5 秒开始,每增 15 秒触发 (5,20,35,50)。在月字段上设置’1/3’所示每月 1 号开始,每隔三天触发一次。

L 表示最后的意思。在日字段设置上,表示当月的最后一天 (依据当前月份,如果是二月还会依据是否是润年 [leap]), 在周字段上表示星期六,相当于 "7" 或 "SAT"。如果在 "L" 前加上数字,则表示该数据的最后一个。例如在周字段上设置 "6L" 这样的格式,则表示 “本月最后一个星期五 "

W 表示离指定日期的最近那个工作日 (周一至周五). 例如在日字段上设置 "15W",表示离每月 15 号最近的那个工作日触发。如果 15 号正好是周六,则找最近的周五 (14 号) 触发,如果 15 号是周未,则找最近的下周一 (16 号) 触发。如果 15 号正好在工作日 (周一至周五),则就在该天触发。如果指定格式为 “1W”, 它则表示每月 1 号往后最近的工作日触发。如果 1 号正是周六,则将在 3 号下周一触发。(注,“W"前只能设置具体的数字,不允许区间”-").

'L’和 'W’可以一组合使用。如果在日字段上设置 "LW", 则表示在本月的最后一个工作日触发(一般指发工资) 。

# 序号 (表示每月的第几个周几),例如在周字段上设置 "6#3" 表示在每月的第三个周六。注意如果指定 "#5", 正好第五周没有周六,则不会触发该配置 (用在母亲节和父亲节再合适不过了)

周字段的设置,若使用英文字母是不区分大小写的 MON 与 mon 相同。

可通过在线生成 Cron 表达式的工具:http://cron.qqe2.com/ 来生成自己想要的表达式

# zone

zone: 与 cron 参数一起使用,指定 cron 表达式的时间区域。默认情况下,它使用服务器的本地时区。

1
2
3
4

@Scheduled(cron = "0 0 * * * *", zone = "America/New_York") // 指定时区为纽约
public void executeHourlyInNY() {
}

# fixedRate

fixedRate:以固定的间隔执行任务,单位为毫秒。这个间隔是指任务开始之间的时间间隔。

1
2
3
4
5

@Scheduled(fixedRate = 5000)
public void taskWithFixedRate() {
System.out.println("每5秒执行一次:" + new Date());
}

fixedRateString: 与 fixedRate 相同,但允许使用字符串表达式。

1
2
3
4

@Scheduled(fixedRateString = "${fixed.rate.in.milliseconds}") // 从配置文件中读取固定频率时间
public void executeWithFixedRateString() {
}

# fixedDelay

fixedDelay:在任务执行完成后,等待固定的延迟时间再执行下一次任务,单位也是毫秒。这个间隔是指任务完成之后到下一次开始之间的时间间隔。

1
2
3
4
5

@Scheduled(fixedDelay = 5000)
public void taskWithFixedDelay() {
System.out.println("任务完成后5秒再执行:" + new Date());
}

fixedDelayString: 与 fixedDelay 相同,但允许使用字符串表达式,这在从配置文件中读取值时非常有用。

1
2
3
4

@Scheduled(fixedDelayString = "${fixed.delay.in.milliseconds}") // 从配置文件中读取固定延迟时间
public void executeWithFixedDelayString() {
}

# initialDelay

initialDelay:这不是单独使用的参数,而是与 fixedRate 或 fixedDelay 一起使用,来延迟任务的首次执行时间。

1
2
3
4
5

@Scheduled(fixedRate = 5000, initialDelay = 10000)
public void taskWithInitialDelay() {
System.out.println("首次延迟10秒后,每5秒执行一次:" + new Date());
}

initialDelayString: 与 initialDelay 相同,但允许使用字符串表达式。

1
2
3
4

@Scheduled(fixedRate = 5000, initialDelayString = "${initial.delay.in.milliseconds}") // 从配置文件中读取初始延迟时间
public void executeWithInitialDelayString() {
}

# 执行 python

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34

public String py2(){
try {
// 创建一个ProcessBuilder实例,指定要运行的Python脚本的路径
ProcessBuilder processBuilder = new ProcessBuilder("python","D:/demo/python/gitvideo/getURL.py");
// ProcessBuilder processBuilder = new ProcessBuilder("python","D:/demo/python/gitvideo/getURL.py","argparse的参数");
// 启动进程
Process process = processBuilder.start();

// 以GBK编码读取进程的输出流,GBK编码通常用于中文Windows环境
BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream(), "GBK"));
String line;
// 逐行读取输出流中的内容,并打印到控制台
while ((line = reader.readLine()) != null) {
System.out.println(line);
}
// 打印一个标记,表明脚本的输出已经读取完毕
System.out.println(1233);

// 等待进程结束,并获取退出码
int exitCode = process.waitFor();
if (exitCode != 0) {
// 如果退出码不为0,表示脚本执行中发生了错误
return "Error executing script";
}

// 返回最后一行脚本的输出,这可能不是你想要的,通常我们会在循环中处理每一行输出
return line;
} catch (IOException | InterruptedException e) {
// 捕获并打印异常信息
e.printStackTrace();
return "Error: " + e.getMessage();
}
}

# 打包 jar

在 pox.xml 中添加 spring-boot-maven-plugin 插件

1
2
3
4
5
6
7
8
9

<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>

保存后于命令行运行

1
2

mvn package

查看 target 目录,你应该看到.jar 后缀结尾的文件

可使用下面命令运行

1
2

java -jar [文件名]

# 注解

# 类注解

# @SpringBootApplication

@SpringBootApplication。这个注解被称为元注解,它结合了 @SpringBootConfiguration、@EnableAutoConfiguration 和 @ComponentScan。

# @RestController

标示为 Controller 控制器

# 方法注解

# @RequestMapping

标示为路由

1
2
3
4
5
6

//用法
@RequestMapping("/home")
String home() {
return "Hello World!";
}

浏览器使用 get 或者 post 访问 http://127.0.0.1:8080/home
即输出 Hello Werld!

阅读次数

请我喝[茶]~( ̄▽ ̄)~*

douk 微信支付

微信支付

douk 支付宝

支付宝