We have just upgraded to the Spring Boot 4 and started getting a problem with Redis Auto configuration and war deployment to Tomcat. The idea is to use Spring Session Data Redis instead of Tomcat's in-memory sessions.
It seems like SessionDataRedisAutoConfiguration depends on ServerProperties class, which is related to embedded Tomcat and not available in Tomcat's classpath.
The exception we get is
2025-12-10T17:03:03.315+01:00 ERROR 98276 --- [redis-demo] [on(4)-127.0.0.1] o.s.boot.SpringApplication : Application run failed
java.lang.IllegalStateException: Failed to generate bean name for imported class 'org.springframework.boot.session.data.redis.autoconfigure.SessionDataRedisAutoConfiguration'
at org.springframework.context.annotation.ConfigurationClassBeanDefinitionReader.registerBeanDefinitionForImportedConfigurationClass(ConfigurationClassBeanDefinitionReader.java:172) ~[spring-context-7.0.1.jar:7.0.1]
at org.springframework.context.annotation.ConfigurationClassBeanDefinitionReader.loadBeanDefinitionsForConfigurationClass(ConfigurationClassBeanDefinitionReader.java:145) ~[spring-context-7.0.1.jar:7.0.1]
at org.springframework.context.annotation.ConfigurationClassBeanDefinitionReader.loadBeanDefinitions(ConfigurationClassBeanDefinitionReader.java:124) ~[spring-context-7.0.1.jar:7.0.1]
at org.springframework.context.annotation.ConfigurationClassPostProcessor.processConfigBeanDefinitions(ConfigurationClassPostProcessor.java:454) ~[spring-context-7.0.1.jar:7.0.1]
at org.springframework.context.annotation.ConfigurationClassPostProcessor.postProcessBeanDefinitionRegistry(ConfigurationClassPostProcessor.java:306) ~[spring-context-7.0.1.jar:7.0.1]
at org.springframework.context.support.PostProcessorRegistrationDelegate.invokeBeanDefinitionRegistryPostProcessors(PostProcessorRegistrationDelegate.java:349) ~[spring-context-7.0.1.jar:7.0.1]
at org.springframework.context.support.PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(PostProcessorRegistrationDelegate.java:118) ~[spring-context-7.0.1.jar:7.0.1]
at org.springframework.context.support.AbstractApplicationContext.invokeBeanFactoryPostProcessors(AbstractApplicationContext.java:784) ~[spring-context-7.0.1.jar:7.0.1]
at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:602) ~[spring-context-7.0.1.jar:7.0.1]
at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:765) ~[spring-boot-4.0.0.jar:4.0.0]
at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:454) ~[spring-boot-4.0.0.jar:4.0.0]
at org.springframework.boot.SpringApplication.run(SpringApplication.java:321) ~[spring-boot-4.0.0.jar:4.0.0]
at org.springframework.boot.web.servlet.support.SpringBootServletInitializer.run(SpringBootServletInitializer.java:204) ~[spring-boot-4.0.0.jar:4.0.0]
at org.springframework.boot.web.servlet.support.SpringBootServletInitializer.createRootApplicationContext(SpringBootServletInitializer.java:180) ~[spring-boot-4.0.0.jar:4.0.0]
at org.springframework.boot.web.servlet.support.SpringBootServletInitializer.onStartup(SpringBootServletInitializer.java:108) ~[spring-boot-4.0.0.jar:4.0.0]
at org.springframework.web.SpringServletContainerInitializer.onStartup(SpringServletContainerInitializer.java:171) ~[spring-web-7.0.1.jar:7.0.1]
at org.apache.catalina.core.StandardContext.startInternal(StandardContext.java:4416) ~[catalina.jar:11.0.15]
at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:164) ~[catalina.jar:11.0.15]
at org.apache.catalina.core.ContainerBase.addChild(ContainerBase.java:564) ~[catalina.jar:11.0.15]
at org.apache.catalina.core.StandardHost.addChild(StandardHost.java:663) ~[catalina.jar:11.0.15]
at org.apache.catalina.startup.HostConfig.manageApp(HostConfig.java:1746) ~[catalina.jar:11.0.15]
at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:104) ~[na:na]
at java.base/java.lang.reflect.Method.invoke(Method.java:565) ~[na:na]
at org.apache.tomcat.util.modeler.BaseModelMBean.invoke(BaseModelMBean.java:249) ~[tomcat-coyote.jar:11.0.15]
at java.management/com.sun.jmx.interceptor.DefaultMBeanServerInterceptor.invoke(DefaultMBeanServerInterceptor.java:628) ~[na:na]
at java.management/com.sun.jmx.mbeanserver.JmxMBeanServer.invoke(JmxMBeanServer.java:790) ~[na:na]
at org.apache.catalina.mbeans.MBeanFactory.createStandardContext(MBeanFactory.java:414) ~[catalina.jar:11.0.15]
at org.apache.catalina.mbeans.MBeanFactory.createStandardContext(MBeanFactory.java:368) ~[catalina.jar:11.0.15]
at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:104) ~[na:na]
at java.base/java.lang.reflect.Method.invoke(Method.java:565) ~[na:na]
at org.apache.tomcat.util.modeler.BaseModelMBean.invoke(BaseModelMBean.java:249) ~[tomcat-coyote.jar:11.0.15]
at java.management/com.sun.jmx.interceptor.DefaultMBeanServerInterceptor.invoke(DefaultMBeanServerInterceptor.java:628) ~[na:na]
at java.management/com.sun.jmx.mbeanserver.JmxMBeanServer.invoke(JmxMBeanServer.java:790) ~[na:na]
at java.management/com.sun.jmx.remote.security.MBeanServerAccessController.invoke(MBeanServerAccessController.java:437) ~[na:na]
at java.management.rmi/javax.management.remote.rmi.RMIConnectionImpl.doOperationInner(RMIConnectionImpl.java:1401) ~[na:na]
at java.management.rmi/javax.management.remote.rmi.RMIConnectionImpl.lambda$doOperation$0(RMIConnectionImpl.java:1330) ~[na:na]
at java.base/jdk.internal.vm.ScopedValueContainer.callWithoutScope(ScopedValueContainer.java:162) ~[na:na]
at java.base/jdk.internal.vm.ScopedValueContainer.call(ScopedValueContainer.java:147) ~[na:na]
at java.base/java.lang.ScopedValue$Carrier.call(ScopedValue.java:419) ~[na:na]
at java.base/javax.security.auth.Subject.callAs(Subject.java:331) ~[na:na]
at java.management.rmi/javax.management.remote.rmi.RMIConnectionImpl.doOperation(RMIConnectionImpl.java:1330) ~[na:na]
at java.management.rmi/javax.management.remote.rmi.RMIConnectionImpl.invoke(RMIConnectionImpl.java:790) ~[na:na]
at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:104) ~[na:na]
at java.base/java.lang.reflect.Method.invoke(Method.java:565) ~[na:na]
at java.rmi/sun.rmi.server.UnicastServerRef.dispatch(UnicastServerRef.java:351) ~[na:na]
at java.rmi/sun.rmi.transport.Transport.serviceCall(Transport.java:166) ~[na:na]
at java.rmi/sun.rmi.transport.tcp.TCPTransport.handleMessages(TCPTransport.java:543) ~[na:na]
at java.rmi/sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.run0(TCPTransport.java:744) ~[na:na]
at java.rmi/sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.run(TCPTransport.java:623) ~[na:na]
at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1090) ~[na:na]
at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:614) ~[na:na]
at java.base/java.lang.Thread.run(Thread.java:1474) ~[na:na]
Caused by: java.lang.IllegalArgumentException: Could not find class [org.springframework.boot.web.server.autoconfigure.ServerProperties]
at org.springframework.util.ClassUtils.resolveClassName(ClassUtils.java:353) ~[spring-core-7.0.1.jar:7.0.1]
at org.springframework.core.annotation.TypeMappedAnnotation.adapt(TypeMappedAnnotation.java:451) ~[spring-core-7.0.1.jar:7.0.1]
at org.springframework.core.annotation.TypeMappedAnnotation.getValue(TypeMappedAnnotation.java:384) ~[spring-core-7.0.1.jar:7.0.1]
at org.springframework.core.annotation.TypeMappedAnnotation.asMap(TypeMappedAnnotation.java:273) ~[spring-core-7.0.1.jar:7.0.1]
at org.springframework.core.annotation.AbstractMergedAnnotation.asAnnotationAttributes(AbstractMergedAnnotation.java:191) ~[spring-core-7.0.1.jar:7.0.1]
at org.springframework.context.annotation.AnnotationBeanNameGenerator.determineBeanNameFromAnnotation(AnnotationBeanNameGenerator.java:143) ~[spring-context-7.0.1.jar:7.0.1]
at org.springframework.context.annotation.AnnotationBeanNameGenerator.generateBeanName(AnnotationBeanNameGenerator.java:110) ~[spring-context-7.0.1.jar:7.0.1]
at org.springframework.context.annotation.ConfigurationClassBeanDefinitionReader.registerBeanDefinitionForImportedConfigurationClass(ConfigurationClassBeanDefinitionReader.java:168) ~[spring-context-7.0.1.jar:7.0.1]
... 51 common frames omitted
Caused by: java.lang.ClassNotFoundException: org.springframework.boot.web.server.autoconfigure.ServerProperties
at org.apache.catalina.loader.WebappClassLoaderBase.loadClass(WebappClassLoaderBase.java:1225) ~[catalina.jar:11.0.15]
at org.apache.catalina.loader.WebappClassLoaderBase.loadClass(WebappClassLoaderBase.java:1056) ~[catalina.jar:11.0.15]
at java.base/java.lang.Class.forName0(Native Method) ~[na:na]
at java.base/java.lang.Class.forName(Class.java:547) ~[na:na]
at org.springframework.util.ClassUtils.forName(ClassUtils.java:302) ~[spring-core-7.0.1.jar:7.0.1]
at org.springframework.util.ClassUtils.resolveClassName(ClassUtils.java:343) ~[spring-core-7.0.1.jar:7.0.1]
... 58 common frames omitted
I believe we can overcome this problem if define our own bean of type SessionRepositoryCustomizer<RedisSessionRepository>, but for now I would like to understand if what we have is correct in terms of approach, or maybe some library/dependency is missing, that would prevent the issue from happening.
See this snippet, that allows application to run (at least):
@SpringBootApplication(exclude = {SessionDataRedisAutoConfiguration.class, SessionAutoConfiguration.class})
@EnableConfigurationProperties({SessionDataRedisProperties.class, SessionProperties.class })
public class RedisDemoApplication {
public static void main(String[] args) {
SpringApplication.run(RedisDemoApplication.class, args);
}
@Bean
@Primary
@Order(Ordered.HIGHEST_PRECEDENCE)
SessionRepositoryCustomizer<RedisSessionRepository> springBootSessionRepositoryCustomizer(
SessionProperties sessionProperties, SessionDataRedisProperties sessionDataRedisProperties) {
String cleanupCron = sessionDataRedisProperties.getCleanupCron();
if (cleanupCron != null) {
throw new InvalidConfigurationPropertyValueException("spring.session.data.redis.cleanup-cron",
cleanupCron, "Cron-based cleanup is only supported when "
+ "spring.session.data.redis.repository-type is set to indexed.");
}
return (sessionRepository) -> {
PropertyMapper map = PropertyMapper.get();
map.from(sessionProperties.determineTimeout(() -> Duration.ofMinutes(1)))
.to(sessionRepository::setDefaultMaxInactiveInterval);
map.from(sessionDataRedisProperties::getNamespace).to(sessionRepository::setRedisKeyNamespace);
map.from(sessionDataRedisProperties::getFlushMode).to(sessionRepository::setFlushMode);
map.from(sessionDataRedisProperties::getSaveMode).to(sessionRepository::setSaveMode);
};
}
}
Example project can be found here https://github.com/rand0m86/redis-demo