An upgrade guide/advice from my recent Spring Boot upgrade
Spring Boot 3 is out and it’s about time to embrace all the cool features come with the version of Spring Boot.

Prerequisites
- Java 17. Spring Boot 3 is based on Spring Framework 6 and requires java 17 or above. Make sure you upgrade to java 17 before moving further.
- Upgrade to Spring Boot 2.7.x. It’s recommended to upgrade from 2.7.x to 3.0. Please upgrade to 2.7 first to minimize the upgrade effort.
Common Changes
Update the parent version from 2.7.x to 3.0.0
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.0.0</version>
<relativePath />
</parent>Add the following dependency to help migrating properties at runtime, remove this dependency after completing the migration.
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-properties-migrator</artifactId>
<scope>runtime</scope>
</dependency>Spring Boot 3 uses Jakarta EE instead of Java EE, change package names java.* to jakarta.* accordingly. Some of the packages I found during migration are as follow:
javax.persistence.*
javax.servlet.*
javax.validation.*
javax.annotation.*
javax.transaction.*
javax.xml.*Be aware that package such as javax.sql.* and javax.crypto.* come from Java 17 JDK, not from EE 8, so they are safe to stay.
There are more details in the official migration guide.
Errors During My Upgrade
The following are issues I encountered during upgrading my liquibase and springbatch project. A little tip here is to run mvn dependency:tree and check if there’s any dependency got override, better to remove the override version. For example, I had override snakeyaml to 1.31 for fixing vulnerabilities, and Spring Boot 3 comes with 1.33, so just remove
<snakeyaml.version>1.31</snakeyam.version> ----- removeEnding slash matters now, meaning “GET /some/greeting/” and “GET /some/greeting” are different by default. It breaks some of my BDD tests due to trailing “/”.
Swagger doesn’t work after Spring Boot 3 upgrade, dependency needs to be changed.
---- from
<dependency>
<groupId>org.springdoc</groupId>
<artifactId>springdoc-openapi-ui</artifactId>
<scope>1.6.14</scope>
</dependency>
---- to
<dependency>
<groupId>org.springdoc</groupId>
<artifactId>springdoc-openapi-starter-webmvc-ui</artifactId>
<scope>2.0.0</scope>
</dependency>Patch call from RestTemplate doesn’t work, dependency needs to be changed.
---- from
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<version>4.5.13</version>
</dependency>
---- to
<dependency>
<groupId>org.apache.httpcomponents.client5</groupId>
<artifactId>httpclient5</artifactId>
<version>5.2.1</version>
</dependency>org.springframework.web.multipart.commons.CommonsMultipartFile is no longer supported, in fact the commons package is gone. I couldn’t find a replacement for it, so created CommonsMultipartFile class in my local.
org.springframework.http.HttpStatus changes to org.springframework.http.HttpStatusCode.
Spring Security 6 doesn’t support WebSecurityConfigureAdapter anymore.
//before upgrade
@Configuration
@RequiredArgsConstructor
public class SecurityConfig extends WebSecurityConfigureAdapter {
...
@Override
@Bean
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}@Override
protected void configure(HttpSecurity http) throws Exception {
...
}
}//after upgrade
@Configuration
@RequiredArgsConstructor
public class SecurityConfig {
...
@Bean
public AuthenticationManager authenticationManager(
AuthenticationConfiguration authenticationConfiguration) throws Exception {
return authenticationConfiguration.getAuthenticationManager();
}@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
...
return http.build();
}
}Liquibase Related Errors
Liquibase.exception.UnexpectedLiquibaseException: java.io.FileNotFoundException: JAR entry … not found.
I created a ticket about “changelog” gets removed in stack overflow https://stackoverflow.com/questions/75059387/changelog-directory-not-found-after-spring-boot-3-upgrade
In short, use <liquibase.version>4.18.0</liquibase.version> to override liquibase 4.17 comes with Spring Boot 3.
AddAfterColumn is not allowed on prostgresql, remove afterColumn if it’s used in liquibase script.
<column
name="base_code"
afterColumn="term_code" ----- remove this line
type="varchar(50)"
/>Spring Batch Related Errors
Spring Boot 3 comes with Spring Batch 5, which requires some metadata tables changes. Run the following SQLs(works with POSTGRESQL 14) if spring batch is used in your project.
ALTER TABLE BATCH_JOB_EXECUTION DROP COLUMN JOB_CONFIGURATION_LOCATION;ALTER TABLE BATCH_STEP_EXECUTION ADD CREATE_TIME TIMESTAMP NOT NULL DEFAULT '1970-01-01 00:00:00';
ALTER TABLE BATCH_STEP_EXECUTION ALTER COLUMN START_TIME DROP NOT NULL;ALTER TABLE BATCH_JOB_EXECUTION_PARAMS DROP COLUMN DATE_VAL;
ALTER TABLE BATCH_JOB_EXECUTION_PARAMS DROP COLUMN LONG_VAL;
ALTER TABLE BATCH_JOB_EXECUTION_PARAMS DROP COLUMN DOUBLE_VAL;ALTER TABLE BATCH_JOB_EXECUTION_PARAMS ALTER COLUMN TYPE_CD TYPE VARCHAR(100);
ALTER TABLE BATCH_JOB_EXECUTION_PARAMS RENAME COLUMN TYPE_CD TO PARAMETER_TYPE;
ALTER TABLE BATCH_JOB_EXECUTION_PARAMS ALTER COLUMN KEY_NAME TYPE VARCHAR(100);
ALTER TABLE BATCH_JOB_EXECUTION_PARAMS RENAME COLUMN KEY_NAME TO PARAMETER_NAME;
ALTER TABLE BATCH_JOB_EXECUTION_PARAMS ALTER COLUMN STRING_VAL TYPE VARCHAR(2500);
ALTER TABLE BATCH_JOB_EXECUTION_PARAMS RENAME COLUMN STRING_VAL TO PARAMETER_VALUE;
@EnableBatchProcessing annotation is now discouraged.
@EnableBatchProcessing is no longer required and should be removed from applications that want to use Boot’s auto-configuration. It caused issues on autowiring data source during the upgrade.
Changes in Batch config
//with v4
@Slf4j
@Configuration
@EnableBatchProcessing
@RequiredArgsConstructor
public class BatchApp {
privte final JobBuilderFactory jobBuilderFactory;
privat final StepBuilderFactory stepBuilderFactory;private final MyTasklet myTasklet;
private final MyReader myReader;
private final MyProcessor myProcessor;
private final MyWriter myWriter; @Bean
public Job myJob() {
return jobBuilderFactory
.get("myjob")
.incrementer(new RunIdIncrementer())
.start(deleteStep())
.next(fileStep())
.preventRestart()
.build();
} @Bean
public Step deleteStep() {
return stepBuilderFactory
.get("deleteStep")
.tasklet(myTasklet)
.build();
} @Bean
public Step fileStep() {
return stepBuilderFactory
.get("fileStep")
.<Term, Terminology>chunk(100)
.reader(myReader)
.processor(myProcessor)
.writer(myWriter)
.build();
}
}
//with v5
@Slf4j
@Configuration
@RequiredArgsConstructor
public class BatchApp {
privte final JobRepository jobRepository;
privat final PlatformTransactionManager platformTransactionManager;private final MyTasklet myTasklet;
private final MyReader myReader;
private final MyProcessor myProcessor;
private final MyWriter myWriter; @Bean
public Job myJob() {
return new JobBuilder("myjob", jobRepository)
.incrementer(new RunIdIncrementer())
.start(deleteStep())
.next(fileStep())
.preventRestart()
.build();
} @Bean
public Step deleteStep() {
return new StepBuilder("deleteStep", jobRepository)
.tasklet(myTasklet, platformTransactionManager)
.build();
} @Bean
public Step fileStep() {
return new StepBuilder("fileStep", jobRepository)
.<Term, Terminology>chunk(100, platformTransactionManager)
.reader(myReader)
.processor(myProcessor)
.writer(myWriter)
.build();
}
}
Method write changed its signature from taking in a List to Chunk
@Component
@StepScope
@RequiredArgsConstructor
public class MyWriter implements ItemWriter<Terminology> {
private final TerminologyRepo terminologyRepo; @Override
// public void write(List<? extends Terminology> records) throws Exception {
public void write(List<? extends Terminology> records) throws Exception {
terminologyRepo.saveAll(records);
}
}Job parameter can be any type now.
---public class JobParameter implements Serializable {
+++public class JobParameter<T> implements Serializable {--- private Object parameter;
+++ private T value;--- private ParameterType parameterType;
+++ private Class<T> type;}---- before
JobParameter param = new JobParameter("id", false);
---- now
JobParameter param = new JobParameter("id", String.class);
ResourceLoader can’t get s3 resource by path after upgrade.
---- before
public class MyReader extends StaxEventItemReader<Term> {
@Autowired
private final ResourceLoader resourceLoader;
...
this.setResource(resrouceLoader.getResource(s3Path));
}
---- now
public class MyReader extends StaxEventItemReader<Term> {
...
this.setResource(new InputStreamResource(S3ObjectInputStream));
}For more details, please check Spring Batch 5 migration guide.
Summary:
- Spring Boot 3 uses Jakarta instead of Java EE.
- Update Liquibase dependency to a working version like 4.18
- Changes based on Spring Batch 5 metadata tables change.
- Changes third party dependencies with Jakarta support accordingly.
Comments
Post a Comment