项目最终还是升级了jdk,8作为长期免费商用的最后一个版本,商业型公司不换也无伤大雅。但个人开发者能拥抱新技术还是拥抱新技术好些。毕竟一年也就1000来人名币(购买jdk的话)。Java8是2014年发布的,距今已达11年之久,如果个人开发者仍旧停留在8,那是不合适的,受限于技术,8相较于后续版本是有一些局限性的,所以,使用新技术吧。虽然java21是23年发布的,最新的已经到24了,不过升级到了21,再24也就不远了。
1、javax->jakarta
在jdk11这个版本,javax除了包含在jdk包中javax.sql之外的都从javax变成了jakarta。
例如jakarta.servlet、jakarta.annotation、jakarta.validation、jakarta.mail等等,都从javax替换成了jakarta。
2、springboot版本
升级到jdk21之后,springboot的版本也会随之变化。从jdk17开始不支持springboot2,需要升级到3。
spring framework方面
sp3是基于spring framework6这个框架,该框架全面支持Jakarta ee 9,并优化了很多核心功能。相较之下,sp2使用的是spring framework 5。
原生镜像支持方面
sp3引入了对graalvm的支持。graalvm是一个高性能的jvm和多语言运行时能够将java编译成原生可执行文件。
配置文件方面
对于部分配置在3中进行了调整,例如在2中redis的配置是属于spring的下一级, 而到了3中,则是在spring.data下。3对于配置属性进行了更严格的校验和提示。
性能方面
3通过优化核心代码和引入原生镜像支持,优化了启动时间。内存方面3也通过更高效的内存管理和资源分配,减少了应用的内存占用。对于在资源受限的情况下运行的应用比较有帮助。此外还提升了在高并发和高负载的情况下的瓶颈(相较于sp2)。
兼容性和迁移方面
依赖方面
大多数依赖可以通过引入父项目进行管理
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.2.5</version>
</parent>
少部分依赖需要手动调整版本,依赖版本可以通过Maven Repository: Search/Browse/Explore这个网站查询版本,找一个和jdk差不多时间出来的版本,换上看看有没有冲突就行。
api方面
因为依赖升级,导致部分api不可用或是有了更好的替代。需要更新api的逻辑。
以上是一些比较宽泛的修改,接下来是正式遇到的问题。
3、api修改
3.1、security
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
这个依赖的版本随着jdk升级到21,版本从5.x变成了6.x。其中部分api不可用。需要做出调整。
在配置类方面,5.x是通过继承WebSecurityConfigurerAdapter这个类,重写里面的configure方法来实现对配置的修改,而到了6.x,WebSecurityConfigurerAdapter这个类被删除,只能通过bean的方式进行配置修改。
明文不加密
在security5.x版本就已经提示过NoOpPasswordEncoder这个类将过期,建议换一个使用。在6.x中想要实现密码明文不加密的效果只能从写PasswordEncoder,自定义加密规则。下面是5.x和6.x的写法。
加密规则是不加密,匹配规则是equals。简单粗暴。
# 6.x
@Bean
public PasswordEncoder passwordEncoder() {
return new PasswordEncoder() {
@Override
public String encode(CharSequence rawPassword) {
return rawPassword.toString();
}
@Override
public boolean matches(CharSequence rawPassword, String encodedPassword) {
return rawPassword.toString().equals(encodedPassword);
}
};
}
# 5.x
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception { auth.userDetailsService(userDetailsService).passwordEncoder(NoOpPasswordEncoder.getInstance());
}
访问设置
6.x版本修改了部分api,并且增加了链式表达,并且增加了一些新的api,例如登出成功后的操作,如果登录页面放在静态文件里面,可以设置登出成功就回到登陆页面。此外也有登录失败去往什么页面,会有一个重定向的操作。
6.x
@Bean
protected SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http.csrf(AbstractHttpConfigurer::disable)//关闭csrf跨站伪造访问
.exceptionHandling(new HandleAccessDeniedException())
.authorizeHttpRequests(auth -> {
auth.requestMatchers("/api/user/login", "/api/user/logout").permitAll();//能直接访问
auth.anyRequest().authenticated();//需要权限
}).httpBasic(Customizer.withDefaults())//添加form表单支持,因为配置了url,默认不再支持form表单登录↓
.formLogin(loginConfigurer -> loginConfigurer.loginPage("/api/user/login").permitAll())
.logout(logoutConfigurer -> logoutConfigurer.logoutSuccessUrl("/api/user/login").permitAll())
.addFilterBefore(customerAuthenticationFilter, UsernamePasswordAuthenticationFilter.class);
return http.build();
}
5.x
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
//设置访问权限,匿名访问
.antMatchers("/user/login").anonymous()
.antMatchers("/exam/**").hasRole("admin")
//其他的都能直接访问
.anyRequest().permitAll()
.and()
//开启异常处理
.exceptionHandling()
//设定为自定义的异常处理类.
.accessDeniedHandler(new HandleAccessDeniedException())
.and()
//添加form表单支持,因为配置了url,默认不再支持form表单登录
.formLogin()
.and()
.addFilterBefore(customerAuthenticationFilter, UsernamePasswordAuthenticationFilter.class)
//关闭csrf跨站伪造访问
.csrf().disable();
}
AuthenticationManager
在5.x版本是通过父类创建对象,到了6.x版本则是通过provider创建对象。
6.x
@Bean
public AuthenticationManager authenticationManager() {
DaoAuthenticationProvider provider = new DaoAuthenticationProvider();
provider.setUserDetailsService(userDetailsService);
provider.setPasswordEncoder(passwordEncoder());
return new ProviderManager(provider);
}
5.x
@Bean
@Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
3.2、MP
如果项目采用的是mybatis-plus,升级到sp3.x之后,需要同步升级mp。
在sp2中mybatisplus依赖名称是:
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.5.6</version>
</dependency>
而到了3.x后需要换成
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-spring-boot3-starter</artifactId>
<version>3.5.10</version>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-jsqlparser</artifactId>
<version>3.5.10</version>
</dependency>
多了个3。另外还需要引入jsqlparser这个依赖,否则拦截器可能不生效。
3.3、swagger
swagger提供了丰富的注解,不管是导出到apifox还是使用原生的swagger文档,都是相当方便的。从sp2到sp3,swagger也需要作出不少修改。
3.3.1、注解
@Api->@Tag(name=")
@ApiModel->@Schema(title="")
@ApiModelProperty->@Parameter(name="")
@ApiOperation->@Operation(summery="")
更多的可以去看看文档,这里只是几个比较常用的注解
3.3.2、依赖
旧版本依赖是这个,而到了sp3中需要改成springdoc的版本。
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger-ui</artifactId>
<version>2.9.2</version>
</dependency>
<dependency>
<groupId>org.springdoc</groupId>
<artifactId>springdoc-openapi-starter-webmvc-api</artifactId>
<version>2.8.8</version>
</dependency>
新版本的swagger支持更丰富的注释,同时也更加符合openapi的规范。OpenAPI 3 Library for spring-boot官方文档将就看吧。
3.4、跨域配置
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")
.allowedOriginPatterns("*")
.allowedHeaders("*")
.allowedMethods("*")
.maxAge(3600)
.allowCredentials(true);
}
在这个配置类中,sp2可以通过allowedOrigins("/*")来设置通配,而到了sp3之后,任然使用这个api的话会被警告不允许使用/*,所以要么按照allowedOrigins("string1","string2"...)这个格式,将允许的放进去,要么换成allowedOriginPatterns("/*")。出于安全考虑,确实应该限制跨域访问的源。
4、springframework
不同的依赖中可能都有spf这个包,不同的依赖中版本还可能不一样,所以最好是通过maven的插件,手动排除一些依赖,以免在需要spf6的地方用上了spf5。