Spring Boot migration, from version 2 to 3

Spring Boot 3 has been released some time ago. I procrastinated the upgrade of my project until now. I finally decided to do it, and I’d like to share what I did and the problems I found, hoping that will help who’s trying to do the migration too.

Let’s start saying that the Spring Project guys published a guide, some time ago. I advice you to take a look at their guide. It’s surely more complete than my experience.

My project was already developed using Java 17 and Spring Boot 2.7.x, so I had a little advantage.

javax to jakarta

The first thing that stands out when you upgrade the version of spring boot on maven or gradle, is that there are a lot of errors due to the javax package. This is due to the upgrade to Jakarta EE 9. It seems that Oracle transfered the technology and documentation, but not the name and trademark, when transfered the Java EE project to the open source Jakarta project.
Some useful links about this topic are:

About the package name in your code, it’s just an annoying activity, and maybe someone published some script, ide or maven/gradle plugins to automatize it. In the second article I linked above, there are some solutions for this. I preferred to change my code by hand, I had a lot of occurrences, but not so much to become overwhelming.

About the dependencies, and the libraries… Well it could be not so easy. Luckily the main libraries were updated, but if you are using old libraries that are not still updated, It could be a problem, and you should refer to their docs.

ActiveMQ

After the update of Spring Boot version, I immediately noticed an error regarding the dependency:

org.springframework.boot:spring-boot-starter-activemq

Yeah, the ActiveMQ 5 starter is not in the Spring Boot packages anymore. I looked around, and also tried to search it on the Spring Initializr tool, and this was the sad news:

I was using ActiveMQ embedded with the in-memory configuration. I had to choice:

  • Install a standalone ActiveMQ server
  • Switch to Artemis

The first solution tempted me, but I had another problem: How to run all the integration tests involving the ActiveMQ embedded, and running during the build with gradle? I thought about creating a Testcontainer, I also thought about deploying an ActiveMQ Server as a container on my Continuos integration platform…
I thought…
I thought…
And I decided to switch to Artemis. I use the embedded queue server just to notify an external service, that doesn’t fail if some message is missing (It’s because I use the in-memory configuration), furthermore that external service doesn’t care about the queue broker I use.

So, I basically changed the dependency to

org.springframework.boot:spring-boot-starter-artemis

I dropped away the ActiveMQ properties from my application.properties file and everything worked automagically. I just had to change the way I was browsing the queue in some tests.
Switching from org.apache.activemq.ActiveMQConnectionFactory, to the browse method of JmsTemplate. This was the occasion to clean up some old test classes, and refactor.

I just changed this:

ActiveMQConnectionFactory connectionFactory = new ActiveMQConnectionFactory(
                "", "", this.urlJms);
        connectionFactory.setTrustAllPackages(true);
        try (Connection connection = connectionFactory.createConnection()) {
            connection.start();
            Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
            Queue logQueue = session.createQueue(this.queueName);
            QueueBrowser browser = session.createBrowser(logQueue);
            Enumeration<?> browserEnumeration = browser.getEnumeration();
            //... 

to this:

TextMessage browse = this.jmsTemplate.<TextMessage>browse(this.queueName, (session, browser) -> {
            Enumeration<?> browserEnumeration = browser.getEnumeration();
            //...

and everything worked fine.
I don’t remember why I used to browse the queue with the ConnectionFactory and not with the jmsTemplate method, but I am happy to have that code removed from my codebase.

SpringDoc

I use SpringDoc in my project to automate the generation of API documentation. As reported on the official site:
Since I was using the 1.6.x version, I had to upgrade. I gladly discovered that the configuration of dependencies simplified. Instead of three dependencies:

implementation 'org.springdoc:springdoc-openapi-ui:1.6.15'
implementation 'org.springdoc:springdoc-openapi-webmvc-core:1.6.15'
implementation 'org.springdoc:springdoc-openapi-security:1.6.15'

Now I just need this one:

implementation 'org.springdoc:springdoc-openapi-starter-webmvc-ui:2.0.4'

I didn’t need to make changes to my code, everything worked without worries. Good job SpringDoc :)

Really, I love this project, the developers of the community are very kind and available if you experience problems and open issues on their github project. I warmly invite you to donate to this project, following the link reported on their Home Page.

Spring Security

Maybe the most of time spent around the migration was about Spring Security. In the latest version (6.x) there are some changes that impacted on my code. Nothing to lose sleep, but I hade to make some changes to my code to avoid errors and make it all work without troubles.

First of all, antMatchers. It seems that the AbstractRequestMatcherRegistry doesn’t declare the antMatchers method anymore. If we take a look at the Spring API Docs:

  • Version 5.x
  • Version 6.x (The current version is 6.0.2 (now that I’m writing the article). Since the API Docs points to the current path, this link will change in the future, so please refer to 6.0.2 version to understand what I’m exposing).

We see that in the 5.x version it was:

@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
  http
    .authorizeHttpRequests((authorizeHttpRequests) ->
            authorizeHttpRequests
                    .antMatchers("/**").hasRole("USER")
    )
    .formLogin(withDefaults());
  return http.build();
}

And in the 6.0.x it is:

@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
  http
    .authorizeHttpRequests((authorizeHttpRequests) ->
            authorizeHttpRequests
                    .requestMatchers("/**").hasRole("USER")
    )
    .formLogin(withDefaults());
  return http.build();
}

No more antMatchers? Yes and no. I tried to use the proposed method, but I had some problems with permitAll policy necessary to allow access to the swagger ui and the openapi docs, for example.
I solved this problem with:

@Bean
  public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
      http.authorizeHttpRequests((authz) -> authz
                      .requestMatchers(antMatcher("/v3/api-docs/**")).permitAll();

What’s that antMatcher method there?
It’s a static method from the class AntPathRequestMatcher. You can import it like this:

import static org.springframework.security.web.util.matcher.AntPathRequestMatcher.antMatcher;

Another little error I saw was about the EnableGlobalMethodSecurity annotation, easily resolved using EnableMethodSecurity.

When I tryed to build my project and I ran the tests, I was stunned that all the tests with MockMvc didn’t work. The error was about security. I looked at the tests implementation and I had something like this:

@Test
@WithMockUser(username = "user", authorities = { "ROLE_USER"})
public void testCaseWithAuthentication() throws Exception  {

WithMockUser wasn’t working as expected. I digged around and found that MockMVC allows to specify a Security post-processor for the mocked http request, using the method with. I dropped the annotation and added the post-processor in the MockMVC request creation, like this:

@Test
public void testCaseWithAuthentication() throws Exception  {
  //[...]
  MvcResult mvcResult = mockMvc.perform(post("/myService")
                        .with(SecurityMockMvcRequestPostProcessors.user("user").roles("USER"))
                        .contentType(MediaType.APPLICATION_JSON)
                        //etc.

I’m pretty sure that the SecurityMockMvcRequestPostProcessors.authorities method works as well, but I didn’t try.

This was what I did to make Spring Security work in my project. There are pages like this where there is a far more complete documentation about the migration. Check your current Spring Boot version to find the appropriate docs.

Conclusions

I hope that my experience with the Spring Boot 3 migration helped you, if you want to share it to your contacts on the social media, you can do it by clicking on the social icons at the beginning of this post, otherwise you can copy the url and share it wherever you like.

If you want to reach me, you will find all my contacts in the About page. Sooner or later I will enable comments on my posts, in the meantime, if you have questions, contact me, I’ll be happy to answer.