Some useful links about Spring Boot 3:
- Spring Boot 3.0 Migration Guide
- Spring Boot 3.0 Goes GA
- Spring Framework 6.0 goes GA
- Spring Framework 6.0 goes GA
Recently I have migrated:
- External Share for Jira
- External Share for Confluence
- Contract Signature for Jira
- Contract Signature for Confluence
- Approval Path for Jira
- Approval Path for Confluence
to Spring Boot 3. Here are some of the problems I have encountered during the migration.
Logback with janino
We had Logback configuration with janino to take advantage of the if statement
.
I found that using if
in the root config element (configuration)
does not work.
I am not sure why. I just refactored our logging config to not use janino.
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<! if can not be used on this level !>
</configuration>
Java Mail
After replacing all javax dependencies to jakarta first error was:
Caused by: java.lang.IllegalStateException: Not provider of jakarta.mail.util.StreamProvider was found
at jakarta.mail.util.FactoryFinder.find(FactoryFinder.java:64)
at jakarta.mail.util.StreamProvider.provider(StreamProvider.java:177)
Probably one of Spring dependencies (web?) did implement javax.mail. Now I had to add this to pom.xml:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-mail</artifactId>
</dependency>
Freemarker - request attributes
We use freemarker to render most of our web pages. Some filters put attributes in the request, later we use those attributes in freemarker templates. In Spring Boot 3 those attributes are not available in templates by default. To expose request attributes in freemarker configuration have to be tweaked like this:
freemarker:
expose-request-attributes: true
Error pages (freemarker again)
This one was interesting. Request attributes are added to freemarker model also in Spring Soot 3 template model is exposed as request attribute. If there is problem while rendering web page (page from freemarker template) then Spring Boot render error page. In our case error page is also created from freemarker template.
So where is the problem?
Error page inherits all request attributes from original request.
Error page rendering goes thur same flow as rendering normal page,
expose-request-attributes: true
kicks in.
AbstractTemplateView tries to put again request attributes into model but
model already have all request attributes inside. If duplicate is found then:
class AbstractTemplateView {
{
if (model.containsKey(attribute) && !this.allowRequestOverride) {
throw new ServletException("Cannot expose request attribute '" + attribute +
"' because of an existing model object of the same name");
}
}
}
One option is to set allowRequestOverride to true which I preferred not to do, it can be a source of some hard-to-find bugs later. What I found I can do:
@Configuration
public static class CustomFreemarkerConfig {
@Bean
FreeMarkerViewResolver freeMarkerViewResolver(FreeMarkerProperties freeMarkerProperties) {
FreeMarkerViewResolver resolver = new CustomFreeMarkerViewResolver();
freeMarkerProperties.applyToMvcViewResolver(resolver);
return resolver;
}
private static class CustomFreeMarkerViewResolver extends FreeMarkerViewResolver {
@Override
protected AbstractUrlBasedView instantiateView() {
return new CustomFreeMarkerView();
}
private static class CustomFreeMarkerView extends FreeMarkerView {
@Override
protected void exposeModelAsRequestAttributes(Map<String, Object> model, HttpServletRequest request) {
// do nothing
}
}
}
}
I can do nothing! I decided not to expose the model as a request attribute. I do not use it so that solves my problem.
JOOQ dsl.transaction / @Transactional
Cannot use ContextTransactionalCallable with TransactionProvider of type class org.jooq.impl.DefaultTransactionProvider
We did mix dsl.transaction / @Transactional . Now I migrated all to annotation @Transactional because dsl.transaction was used just a few times.
HttpMethod is not enum
That was pain. In some places, we put HttpMethod from Spring in our model and persisted it to JSON. Now HttpMethod is not enum. Easy to refactor but still 🤦.