Update Redis configuration to use bean classloader

Spring Session 2.0 made changes to Redis configuration facilities so that the `RedisTemplate` used by `RedisOperationsSessionRepository` isn't exposed as a bean anymore. This has a consequence that bean `ClassLoader` isn't applied automatically which causes issues in Spring Boot applications that use DevTools.

This commit restores the previous behavior by updating Redis configuration classes to implement `BeanClassLoaderAware` callback and apply the application `ClassLoader` to `RedisTemplate`. The analogous change was made to reactive Redis configuration.

Closes gh-968
This commit is contained in:
Vedran Pavic
2018-01-18 21:36:28 +01:00
parent d0ee9fd16a
commit ffa1bca898
2 changed files with 35 additions and 20 deletions

View File

@@ -1,5 +1,5 @@
/* /*
* Copyright 2014-2017 the original author or authors. * Copyright 2014-2018 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@@ -23,6 +23,7 @@ import java.util.concurrent.Executor;
import org.apache.commons.logging.LogFactory; import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.BeanClassLoaderAware;
import org.springframework.beans.factory.InitializingBean; import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.ObjectProvider; import org.springframework.beans.factory.ObjectProvider;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
@@ -70,7 +71,8 @@ import org.springframework.util.StringValueResolver;
@Configuration @Configuration
@EnableScheduling @EnableScheduling
public class RedisHttpSessionConfiguration extends SpringHttpSessionConfiguration public class RedisHttpSessionConfiguration extends SpringHttpSessionConfiguration
implements EmbeddedValueResolverAware, ImportAware, SchedulingConfigurer { implements BeanClassLoaderAware, EmbeddedValueResolverAware, ImportAware,
SchedulingConfigurer {
static final String DEFAULT_CLEANUP_CRON = "0 * * * * *"; static final String DEFAULT_CLEANUP_CRON = "0 * * * * *";
@@ -94,12 +96,13 @@ public class RedisHttpSessionConfiguration extends SpringHttpSessionConfiguratio
private Executor redisSubscriptionExecutor; private Executor redisSubscriptionExecutor;
private ClassLoader classLoader;
private StringValueResolver embeddedValueResolver; private StringValueResolver embeddedValueResolver;
@Bean @Bean
public RedisOperationsSessionRepository sessionRepository() { public RedisOperationsSessionRepository sessionRepository() {
RedisTemplate<Object, Object> redisTemplate = createRedisTemplate( RedisTemplate<Object, Object> redisTemplate = createRedisTemplate();
this.redisConnectionFactory, this.defaultRedisSerializer);
RedisOperationsSessionRepository sessionRepository = new RedisOperationsSessionRepository( RedisOperationsSessionRepository sessionRepository = new RedisOperationsSessionRepository(
redisTemplate); redisTemplate);
sessionRepository.setApplicationEventPublisher(this.applicationEventPublisher); sessionRepository.setApplicationEventPublisher(this.applicationEventPublisher);
@@ -205,6 +208,11 @@ public class RedisHttpSessionConfiguration extends SpringHttpSessionConfiguratio
this.redisSubscriptionExecutor = redisSubscriptionExecutor; this.redisSubscriptionExecutor = redisSubscriptionExecutor;
} }
@Override
public void setBeanClassLoader(ClassLoader classLoader) {
this.classLoader = classLoader;
}
@Override @Override
public void setEmbeddedValueResolver(StringValueResolver resolver) { public void setEmbeddedValueResolver(StringValueResolver resolver) {
this.embeddedValueResolver = resolver; this.embeddedValueResolver = resolver;
@@ -235,16 +243,15 @@ public class RedisHttpSessionConfiguration extends SpringHttpSessionConfiguratio
this.cleanupCron); this.cleanupCron);
} }
private static RedisTemplate<Object, Object> createRedisTemplate( private RedisTemplate<Object, Object> createRedisTemplate() {
RedisConnectionFactory redisConnectionFactory,
RedisSerializer<Object> defaultRedisSerializer) {
RedisTemplate<Object, Object> redisTemplate = new RedisTemplate<>(); RedisTemplate<Object, Object> redisTemplate = new RedisTemplate<>();
redisTemplate.setKeySerializer(new StringRedisSerializer()); redisTemplate.setKeySerializer(new StringRedisSerializer());
redisTemplate.setHashKeySerializer(new StringRedisSerializer()); redisTemplate.setHashKeySerializer(new StringRedisSerializer());
if (defaultRedisSerializer != null) { if (this.defaultRedisSerializer != null) {
redisTemplate.setDefaultSerializer(defaultRedisSerializer); redisTemplate.setDefaultSerializer(this.defaultRedisSerializer);
} }
redisTemplate.setConnectionFactory(redisConnectionFactory); redisTemplate.setConnectionFactory(this.redisConnectionFactory);
redisTemplate.setBeanClassLoader(this.classLoader);
redisTemplate.afterPropertiesSet(); redisTemplate.afterPropertiesSet();
return redisTemplate; return redisTemplate;
} }

View File

@@ -1,5 +1,5 @@
/* /*
* Copyright 2014-2017 the original author or authors. * Copyright 2014-2018 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@@ -18,6 +18,7 @@ package org.springframework.session.data.redis.config.annotation.web.server;
import java.util.Map; import java.util.Map;
import org.springframework.beans.factory.BeanClassLoaderAware;
import org.springframework.beans.factory.ObjectProvider; import org.springframework.beans.factory.ObjectProvider;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.EmbeddedValueResolverAware; import org.springframework.context.EmbeddedValueResolverAware;
@@ -53,11 +54,7 @@ import org.springframework.web.server.session.WebSessionManager;
*/ */
@Configuration @Configuration
public class RedisWebSessionConfiguration extends SpringWebSessionConfiguration public class RedisWebSessionConfiguration extends SpringWebSessionConfiguration
implements EmbeddedValueResolverAware, ImportAware { implements BeanClassLoaderAware, EmbeddedValueResolverAware, ImportAware {
private static final RedisSerializer<String> keySerializer = new StringRedisSerializer();
private static final RedisSerializer<Object> valueSerializer = new JdkSerializationRedisSerializer();
private Integer maxInactiveIntervalInSeconds = MapSession.DEFAULT_MAX_INACTIVE_INTERVAL_SECONDS; private Integer maxInactiveIntervalInSeconds = MapSession.DEFAULT_MAX_INACTIVE_INTERVAL_SECONDS;
@@ -67,12 +64,15 @@ public class RedisWebSessionConfiguration extends SpringWebSessionConfiguration
private ReactiveRedisConnectionFactory redisConnectionFactory; private ReactiveRedisConnectionFactory redisConnectionFactory;
private ClassLoader classLoader;
private StringValueResolver embeddedValueResolver; private StringValueResolver embeddedValueResolver;
@Bean @Bean
public ReactiveRedisOperationsSessionRepository sessionRepository() { public ReactiveRedisOperationsSessionRepository sessionRepository() {
ReactiveRedisTemplate<String, Object> reactiveRedisTemplate = createReactiveRedisTemplate();
ReactiveRedisOperationsSessionRepository sessionRepository = new ReactiveRedisOperationsSessionRepository( ReactiveRedisOperationsSessionRepository sessionRepository = new ReactiveRedisOperationsSessionRepository(
createDefaultTemplate(this.redisConnectionFactory)); reactiveRedisTemplate);
sessionRepository sessionRepository
.setDefaultMaxInactiveInterval(this.maxInactiveIntervalInSeconds); .setDefaultMaxInactiveInterval(this.maxInactiveIntervalInSeconds);
if (StringUtils.hasText(this.redisNamespace)) { if (StringUtils.hasText(this.redisNamespace)) {
@@ -107,6 +107,11 @@ public class RedisWebSessionConfiguration extends SpringWebSessionConfiguration
this.redisConnectionFactory = redisConnectionFactoryToUse; this.redisConnectionFactory = redisConnectionFactoryToUse;
} }
@Override
public void setBeanClassLoader(ClassLoader classLoader) {
this.classLoader = classLoader;
}
@Override @Override
public void setEmbeddedValueResolver(StringValueResolver resolver) { public void setEmbeddedValueResolver(StringValueResolver resolver) {
this.embeddedValueResolver = resolver; this.embeddedValueResolver = resolver;
@@ -127,12 +132,15 @@ public class RedisWebSessionConfiguration extends SpringWebSessionConfiguration
this.redisFlushMode = attributes.getEnum("redisFlushMode"); this.redisFlushMode = attributes.getEnum("redisFlushMode");
} }
private static ReactiveRedisTemplate<String, Object> createDefaultTemplate( private ReactiveRedisTemplate<String, Object> createReactiveRedisTemplate() {
ReactiveRedisConnectionFactory connectionFactory) { RedisSerializer<String> keySerializer = new StringRedisSerializer();
RedisSerializer<Object> valueSerializer = new JdkSerializationRedisSerializer(
this.classLoader);
RedisSerializationContext<String, Object> serializationContext = RedisSerializationContext RedisSerializationContext<String, Object> serializationContext = RedisSerializationContext
.<String, Object>newSerializationContext(valueSerializer) .<String, Object>newSerializationContext(valueSerializer)
.key(keySerializer).hashKey(keySerializer).build(); .key(keySerializer).hashKey(keySerializer).build();
return new ReactiveRedisTemplate<>(connectionFactory, serializationContext); return new ReactiveRedisTemplate<>(this.redisConnectionFactory,
serializationContext);
} }
} }