To keep up with Spring 6, Spring Boot released 3.0.0 on November 24, 2022. The current version is 3.0.1 (2022-12-23). Spring 6 requires JDK 17+, and naturally Spring Boot 3 requires JDK 17+ to work. For those who have been clinging to JDK 8, upgrading to Spring Boot 3 is a big challenge.
What are the significant features that Spring Boot brings to the table?
- Relies on Spring 6, requires Java 17 minimum, Java 19 compatible
- support for generating GraalVM native images, replacing the experimental Spring Native project
- requires Java EE 9 and supports Jakarta EE 10 as a minimum
- dependency migration from Java EE to Jakarta EE API
- Upgrade to Tomcat 10
For guidance on upgrading from Spring Boot 2.x to Spring Boot 3, see the official Spring Boot 3.0 Migration Guide.
For Spring Boot 1 projects, you must first upgrade to Spring Boot 2. For early Spring Boot 2, the first step is to upgrade to Spring Boot 2.7.x. Then change the JDK to version 17 or later, compile, run, and change the code if you have problems.
The most significant API change is javax
to jakarta.servlet
, for example
- javax.servlet -> jakarta.servlet
- javax.annotations -> jakarta.annotations
- javax.persistence -> jakarta.persistence
- javax.transaction -> jakarta.transaction
I have a project with Spring Boot 2.7.6 + JDK 17, upgraded directly to Spring Boot 3.0.1. then tried to compile with mvn compile
, due to changes in API package names such as javax.annotation.PostConstruct
, javax.servlet.http.HttpServletRequest
etc. will cause compilation failure, directly replace javax
with jakarta
, such as jakarta.annotation.PostConstruct
, jakarta.servlet.http.HttpServletRequest
will work.
Finally, it can be compiled by mvn compile
, but it will throw an exception at runtime.
|
|
When I come to the CalcEngineFactory code, it is clear that there is an annotation @Named
, why it is not registered as a Spring bean anymore? If I replace the annotation @Named
with Spring’s @Component, there is no problem. The problem is solved, let’s find the reason, debug, to org.springframework.context.annotation.ClassPathBeanDefinitionScanner.doScan(String... basePackages)
method.
Classes annotated with @Named
are not considered SpringBean by the findCandidateComponents(basePackage)
method, while those annotated with @Component
are. Continue following the method scanCandidateComponents(basePackage)
, you can see the following code.
The logic of isCandidateComponent(metadataReader)
returns false
for classes annotated with @Named
and true for classes annotated with @Component
.
The isCandidateComponent()
method uses excludeFilters
and includedFilters
to determine true
or false
.
Focus on this includedFilters
, which in Spring Boot 3 contains three AnnotationTypeFilters
, which are as follows
- annotationType: interface org.springframework.stereotype.Component
- annotationType: interface jakarta.annotation.ManagedBean
- annotationType: interface jakarta.inject.Named
It is true that there is @Named
, but it is not javax.inject.Named
, which we used earlier, but jakarta.inject.Named
. Since javax.inject.Named
comes from the javax.inject:javax.inject
dependency, it will compile fine after upgrading to Spring Boot 3, but will throw an exception at runtime.
This is why the @Component
annotation solves the problem, but of course the sub-interfaces @Service
, @Controller
, etc. can also be registered as Spring beans with @Component
.
This is why the @Component
annotation solves the problem, but of course sub-interfaces that use @Component
, such as @Service
, @Controller
and so on, can also be registered as Spring beans.
If you still insist on @Named
, then remove the javax.inject:javax.inject
dependency and replace it with the @jakarta.inject.Named
annotation. After upgrading to Spring Boot 3, have in mind whether javax
should be replaced with jakarta
.
If you compare the code comments of the Spring 5 and 6 ClassPathBeanDefinitionScanner
classes, it also illustrates the change from @javax.inject.Named
to @jakarta.inject.Named
.
If you still want Spring to treat classes annotated with @javax.inject.Named
as Spring beans, you need to see if you can affect the value of the includeFilters: List<TypeFilter>
variable in the org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider
. The relevant code in the ComponentScanAnnotationParser
class is as follows.
|
|
That uses the useDefaultFilters
and includeFilters
attributes of the @ComponentScan annotation, so we can add the following configuration to the Spring Boot Java configuration or startup class.
|
|
This will add annotationType: interface javax.inject.Named
to the default includeFilters.
Classes annotated with @javax.inject.Named
will be treated as Spring beans as well. However, I don’t recommend this approach and try to use the Spring Boot 3 (Spring 6) compliant approach with @jakarta.inject.Named
instead.
Finally, annotation classes other than the @Named
annotation in javax.inject:javax.inject
may also not work properly and require attention.
Reference:
https://yanbin.blog/upgrade-to-spring-boot-3-javax-inject-named-unvailable/