现在AI越来越火,大多数项目迟早要用上SpringAI啦,而SpringBoot3才支持SpringAI,所以需要对项目进行升级

修改pom依赖

修改父pom依赖

我们需要到若依父项目的pom.xml文件(就是在项目结构最外面的那个xml文件)中修改一下依赖,
下图是我的项目的pom.xml文件(未经修改)
alt 图片1
修改后如下图,主要修改了<java.version>、<spring-boot.version>、<tomcat.version>、<logback.version>、<spring-security.version>和<spring-framework.version>,请注意如果你也是升级到SpringBoot3.2.5,可以的话最好就按照我这里的版本来,因为SpringBoot3.2.5是要求对应的tomcat、logback、spring-security、spring-framework版本的:
alt 图片2
在pom中新增以下四个依赖配置

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
<!-- 新增四个配置依赖 -->
<!-- Jaxb -->
<dependency>
<groupId>javax.xml.bind</groupId>
<artifactId>jaxb-api</artifactId>
<version>2.3.1</version>
</dependency>

<!-- servlet -->
<dependency>
<groupId>jakarta.servlet</groupId>
<artifactId>jakarta.servlet-api</artifactId>
<version>6.0.0</version>
</dependency>

<!-- mybatis-plus -->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.5.5</version>
</dependency>

<!-- mysql -->
<dependency>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
<version>8.1.0</version>
</dependency>

修改admin的pom依赖

修改其mysql驱动包:

1
2
3
4
5
<!-- Mysql驱动包 -->
<dependency>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
</dependency>

修改common的pom依赖

修改其servlet包,并添加mybatis-plus和mybatis-spring依赖:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<!-- 修改 servlet包 -->
<dependency>
<groupId>jakarta.servlet</groupId>
<artifactId>jakarta.servlet-api</artifactId>
</dependency>
<!-- 添加 mybatis-plus -->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
</dependency>

<!-- 添加 mybatis-spring -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>3.0.3</version>
</dependency>

修改framework的pom依赖

修改其验证码:

1
2
3
4
5
6
7
8
9
10
11
<!-- 修改 验证码 -->
<dependency>
<groupId>pro.fessional</groupId>
<artifactId>kaptcha</artifactId>
<exclusions>
<exclusion>
<artifactId>servlet-api</artifactId>
<groupId>jakarta.servlet</groupId>
</exclusion>
</exclusions>
</dependency>

修改所有pom包内的druid-spring-boot-starter

将所有pom内的druid-spring-boot-starter,修改为druid-spring-boot-3-starter
alt 图片3

修改类

DruidConfig类

将类内的

1
2
import com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceBuilder;
import com.alibaba.druid.spring.boot.autoconfigure.properties.DruidStatProperties;

修改为:(就是在boot后面加了个3)

1
2
import com.alibaba.druid.spring.boot3.autoconfigure.DruidDataSourceBuilder;
import com.alibaba.druid.spring.boot3.autoconfigure.properties.DruidStatProperties;

SecurityConfig类

由于版本变化,SecurityConfig类的写法也发生了变化,变化后的整个类为:

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
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
package com.ruoyi.framework.config;

import com.ruoyi.framework.security.filter.JwtAuthenticationTokenFilter;
import com.ruoyi.framework.security.handle.AuthenticationEntryPointImpl;
import com.ruoyi.framework.security.handle.LogoutSuccessHandlerImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration;
import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.security.web.authentication.logout.LogoutFilter;
import org.springframework.web.filter.CorsFilter;

/**
* spring security配置
*
* @author ruoyi
*/
@EnableMethodSecurity(securedEnabled = true, jsr250Enabled = true)
@Configuration
public class SecurityConfig
{
/**
* 自定义用户认证逻辑
*/
@Autowired
private UserDetailsService userDetailsService;

/**
* 认证失败处理类
*/
@Autowired
private AuthenticationEntryPointImpl unauthorizedHandler;

/**
* 退出处理类
*/
@Autowired
private LogoutSuccessHandlerImpl logoutSuccessHandler;

/**
* token认证过滤器
*/
@Autowired
private JwtAuthenticationTokenFilter authenticationTokenFilter;

/**
* 跨域过滤器
*/
@Autowired
private CorsFilter corsFilter;

@Bean
public AuthenticationManager authenticationManager(AuthenticationConfiguration authenticationConfiguration) throws Exception {
return authenticationConfiguration.getAuthenticationManager();
}


@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
// 使用Lambda DSL重构配置链
.csrf(csrf -> csrf.disable()) // 禁用CSRF[2,5](@ref)
.headers(headers -> headers
.cacheControl(cache -> cache.disable()) // 禁用缓存控制头
.frameOptions(frame -> frame.disable()) // 禁用X-Frame-Options
)
.exceptionHandling(exceptions -> exceptions
.authenticationEntryPoint(unauthorizedHandler) // 认证失败处理器[2](@ref)
)
.sessionManagement(session -> session
.sessionCreationPolicy(SessionCreationPolicy.STATELESS) // 无状态会话[2,4](@ref)
)
.authorizeHttpRequests(auth -> auth // 新版授权配置方式[2,3](@ref)
// 开放端点配置
.requestMatchers(
"/login",
"/register",
"/captchaImage"
).permitAll()
// 静态资源访问
.requestMatchers(
HttpMethod.GET,
"/",
"/*.html",
"/*/*.html",
"/*/*.css",
"/*/*.js",
"/profile/*"
).permitAll()
// 开放Swagger和Druid端点
.requestMatchers(
"/swagger-ui.html",
"/swagger-resources/*",
"/webjars/*",
"/*/api-docs",
"/druid/*"
).permitAll()
// 其他请求需要认证
.anyRequest().authenticated()
)
// 登出配置
.logout(logout -> logout
.logoutUrl("/logout")
.logoutSuccessHandler(logoutSuccessHandler) // 登出成功处理器[6,8](@ref)
)
// 过滤器配置
.addFilterBefore(authenticationTokenFilter, UsernamePasswordAuthenticationFilter.class) // JWT过滤器[6,8](@ref)
.addFilterBefore(corsFilter, JwtAuthenticationTokenFilter.class) // CORS过滤器
.addFilterBefore(corsFilter, LogoutFilter.class);

return http.build();
}
/**
* 强散列哈希加密实现
*/
@Bean
public BCryptPasswordEncoder bCryptPasswordEncoder()
{
return new BCryptPasswordEncoder();
}

/**
* 身份认证接口
*/
protected void configure(AuthenticationManagerBuilder auth) throws Exception
{
auth.userDetailsService(userDetailsService).passwordEncoder(bCryptPasswordEncoder());
}
}

Java EE转Jakarta EE

如果你的IDEA是2022.3以上的版本,可以使用IDEA来进行转换
alt 图片4

而如果你的IDEA版本是2022.2以下的版本,那么你需要手动进行转换。
比如把import javax.annotation 替换成 import jakarta.annotation:

1
2
3
4
javax.annotation 替换成 jakarta.annotation
javax.servlet 替换成 jakarta.servlet
javax.validation 替换成 jakarta.validation
javax.xxxxxxxxxx 替换成 jakarta.xxxxxxxxxx

但是,以下包不用转:

1
2
3
4
5
6
7
8
javax.imageio.ImageIO;
javax.net.ssl.HostnameVerifier;
javax.net.ssl.HttpsURLConnection;
javax.net.ssl.SSLContext;
javax.net.ssl.SSLSession;
javax.net.ssl.TrustManager;
javax.net.ssl.X509TrustManager;
javax.sql.DataSource

yml文件内redis配置

修改application.yml的spring.redis配置为spring.data.redis

1
2
3
4
spring:
data:
redis:
......

其他问题

报错java.lang.IllegalArgumentException: Name for argument of type [java.lang.String] not specified, and parameter name information not available via reflection. Ensure that the compiler uses the ‘-parameters’ flag.

这是因为使用了@PathVariable接收参数的接口,导致报错
解决办法:指定接收参数名,如@PathVariable(“dictType”)或者@PathVariable(name = “dictType”)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// 调用这个接口会报错
@GetMapping(value = "/type/{dictType}")
public AjaxResult dictType(@PathVariable String dictType)
{
List<SysDictData> data = dictTypeService.selectDictDataByType(dictType);
if (StringUtils.isNull(data))
{
data = new ArrayList<SysDictData>();
}
return success(data);
}

// 修改之后
@GetMapping(value = "/type/{dictType}")
public AjaxResult dictType(@PathVariable("dictType") String dictType)
{
List<SysDictData> data = dictTypeService.selectDictDataByType(dictType);
if (StringUtils.isNull(data))
{
data = new ArrayList<SysDictData>();
}
return success(data);
}

报错No static resource api/option/batchEditScore

Spring Boot 3.x 对 URL 路径匹配规则更加严格,请检查对应的接口的路径
以下是我报错时的代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
前端代码:
export function batchEditScore(optionIds) {
return request({
url: '/api/option/batchEditScore/',
method: 'post',
data: optionIds
})
}

后端对应接口:
/**
* 完成一次投票后批量修改选项分数,并批量插入投票记录
*/
@PostMapping("/api/option/batchEditScore")
public AjaxResult batchEditScore(@RequestBody List<Integer> selectedCardsIds, HttpServletRequest request)
{
........
return success();
}

因为前端代码路径里多了一个/,所以报错了,由此可见Spring Boot 3.x 对 URL 路径匹配规则更加严格了,去掉后即可解决:

1
2
3
4
5
6
7
8
前端代码:
export function batchEditScore(optionIds) {
return request({
url: '/api/option/batchEditScore',
method: 'post',
data: optionIds
})
}