Improve JDBC configuration
This commit improves JDBC configuration by introducing `@SpringSessionDataSource` qualifier for explicitly declaring a `DataSource` to be used by Spring Session. This is in particular useful in scenarios with multiple `DataSource` beans present in the application context. As a consequence, JDBC configuration is simplified and no longer registers a Spring Session specific `JdbcTemplate` bean. Closes gh-863
This commit is contained in:
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2014-2016 the original author or authors.
|
* Copyright 2014-2017 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.
|
||||||
@@ -13,6 +13,7 @@
|
|||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package org.springframework.session.jdbc.config.annotation.web.http;
|
package org.springframework.session.jdbc.config.annotation.web.http;
|
||||||
|
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
@@ -20,6 +21,7 @@ import java.util.Map;
|
|||||||
import javax.sql.DataSource;
|
import javax.sql.DataSource;
|
||||||
|
|
||||||
import org.springframework.beans.factory.BeanClassLoaderAware;
|
import org.springframework.beans.factory.BeanClassLoaderAware;
|
||||||
|
import org.springframework.beans.factory.ObjectProvider;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.beans.factory.annotation.Qualifier;
|
import org.springframework.beans.factory.annotation.Qualifier;
|
||||||
import org.springframework.context.EmbeddedValueResolverAware;
|
import org.springframework.context.EmbeddedValueResolverAware;
|
||||||
@@ -33,14 +35,11 @@ import org.springframework.core.convert.support.GenericConversionService;
|
|||||||
import org.springframework.core.serializer.support.DeserializingConverter;
|
import org.springframework.core.serializer.support.DeserializingConverter;
|
||||||
import org.springframework.core.serializer.support.SerializingConverter;
|
import org.springframework.core.serializer.support.SerializingConverter;
|
||||||
import org.springframework.core.type.AnnotationMetadata;
|
import org.springframework.core.type.AnnotationMetadata;
|
||||||
import org.springframework.jdbc.core.JdbcOperations;
|
|
||||||
import org.springframework.jdbc.core.JdbcTemplate;
|
|
||||||
import org.springframework.jdbc.support.lob.LobHandler;
|
import org.springframework.jdbc.support.lob.LobHandler;
|
||||||
import org.springframework.scheduling.annotation.EnableScheduling;
|
import org.springframework.scheduling.annotation.EnableScheduling;
|
||||||
import org.springframework.session.config.annotation.web.http.SpringHttpSessionConfiguration;
|
import org.springframework.session.config.annotation.web.http.SpringHttpSessionConfiguration;
|
||||||
import org.springframework.session.jdbc.JdbcOperationsSessionRepository;
|
import org.springframework.session.jdbc.JdbcOperationsSessionRepository;
|
||||||
import org.springframework.transaction.PlatformTransactionManager;
|
import org.springframework.transaction.PlatformTransactionManager;
|
||||||
import org.springframework.util.ClassUtils;
|
|
||||||
import org.springframework.util.StringUtils;
|
import org.springframework.util.StringUtils;
|
||||||
import org.springframework.util.StringValueResolver;
|
import org.springframework.util.StringValueResolver;
|
||||||
|
|
||||||
@@ -78,17 +77,17 @@ public class JdbcHttpSessionConfiguration extends SpringHttpSessionConfiguration
|
|||||||
|
|
||||||
private StringValueResolver embeddedValueResolver;
|
private StringValueResolver embeddedValueResolver;
|
||||||
|
|
||||||
@Bean
|
|
||||||
public JdbcTemplate springSessionJdbcOperations(DataSource dataSource) {
|
|
||||||
return new JdbcTemplate(dataSource);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
public JdbcOperationsSessionRepository sessionRepository(
|
public JdbcOperationsSessionRepository sessionRepository(
|
||||||
@Qualifier("springSessionJdbcOperations") JdbcOperations jdbcOperations,
|
@SpringSessionDataSource ObjectProvider<DataSource> springSessionDataSource,
|
||||||
|
ObjectProvider<DataSource> dataSource,
|
||||||
PlatformTransactionManager transactionManager) {
|
PlatformTransactionManager transactionManager) {
|
||||||
JdbcOperationsSessionRepository sessionRepository =
|
DataSource dataSourceToUse = springSessionDataSource.getIfAvailable();
|
||||||
new JdbcOperationsSessionRepository(jdbcOperations, transactionManager);
|
if (dataSourceToUse == null) {
|
||||||
|
dataSourceToUse = dataSource.getObject();
|
||||||
|
}
|
||||||
|
JdbcOperationsSessionRepository sessionRepository = new JdbcOperationsSessionRepository(
|
||||||
|
dataSourceToUse, transactionManager);
|
||||||
String tableName = getTableName();
|
String tableName = getTableName();
|
||||||
if (StringUtils.hasText(tableName)) {
|
if (StringUtils.hasText(tableName)) {
|
||||||
sessionRepository.setTableName(tableName);
|
sessionRepository.setTableName(tableName);
|
||||||
@@ -104,7 +103,7 @@ public class JdbcHttpSessionConfiguration extends SpringHttpSessionConfiguration
|
|||||||
else if (this.conversionService != null) {
|
else if (this.conversionService != null) {
|
||||||
sessionRepository.setConversionService(this.conversionService);
|
sessionRepository.setConversionService(this.conversionService);
|
||||||
}
|
}
|
||||||
else if (deserializingConverterSupportsCustomClassLoader()) {
|
else {
|
||||||
GenericConversionService conversionService = createConversionServiceWithBeanClassLoader();
|
GenericConversionService conversionService = createConversionServiceWithBeanClassLoader();
|
||||||
sessionRepository.setConversionService(conversionService);
|
sessionRepository.setConversionService(conversionService);
|
||||||
}
|
}
|
||||||
@@ -115,7 +114,7 @@ public class JdbcHttpSessionConfiguration extends SpringHttpSessionConfiguration
|
|||||||
* This must be a separate method because some ClassLoaders load the entire method
|
* This must be a separate method because some ClassLoaders load the entire method
|
||||||
* definition even if an if statement guards against it loading. This means that older
|
* definition even if an if statement guards against it loading. This means that older
|
||||||
* versions of Spring would cause a NoSuchMethodError if this were defined in
|
* versions of Spring would cause a NoSuchMethodError if this were defined in
|
||||||
* {@link #sessionRepository(JdbcOperations, PlatformTransactionManager)}.
|
* {@link #sessionRepository(ObjectProvider, ObjectProvider, PlatformTransactionManager)}.
|
||||||
*
|
*
|
||||||
* @return the default {@link ConversionService}
|
* @return the default {@link ConversionService}
|
||||||
*/
|
*/
|
||||||
@@ -163,10 +162,6 @@ public class JdbcHttpSessionConfiguration extends SpringHttpSessionConfiguration
|
|||||||
return this.tableName;
|
return this.tableName;
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean deserializingConverterSupportsCustomClassLoader() {
|
|
||||||
return ClassUtils.hasConstructor(DeserializingConverter.class, ClassLoader.class);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setImportMetadata(AnnotationMetadata importMetadata) {
|
public void setImportMetadata(AnnotationMetadata importMetadata) {
|
||||||
Map<String, Object> enableAttrMap = importMetadata
|
Map<String, Object> enableAttrMap = importMetadata
|
||||||
.getAnnotationAttributes(EnableJdbcHttpSession.class.getName());
|
.getAnnotationAttributes(EnableJdbcHttpSession.class.getName());
|
||||||
@@ -192,4 +187,5 @@ public class JdbcHttpSessionConfiguration extends SpringHttpSessionConfiguration
|
|||||||
public static PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer() {
|
public static PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer() {
|
||||||
return new PropertySourcesPlaceholderConfigurer();
|
return new PropertySourcesPlaceholderConfigurer();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,44 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2014-2017 the original author or authors.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.springframework.session.jdbc.config.annotation.web.http;
|
||||||
|
|
||||||
|
import java.lang.annotation.Documented;
|
||||||
|
import java.lang.annotation.ElementType;
|
||||||
|
import java.lang.annotation.Retention;
|
||||||
|
import java.lang.annotation.RetentionPolicy;
|
||||||
|
import java.lang.annotation.Target;
|
||||||
|
|
||||||
|
import javax.sql.DataSource;
|
||||||
|
|
||||||
|
import org.springframework.beans.factory.annotation.Qualifier;
|
||||||
|
import org.springframework.session.jdbc.JdbcOperationsSessionRepository;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Qualifier annotation for a {@link DataSource} to be injected in
|
||||||
|
* {@link JdbcOperationsSessionRepository}.
|
||||||
|
*
|
||||||
|
* @author Vedran Pavic
|
||||||
|
* @since 2.0.0
|
||||||
|
*/
|
||||||
|
@Target({ ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER, ElementType.TYPE,
|
||||||
|
ElementType.ANNOTATION_TYPE })
|
||||||
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
|
@Documented
|
||||||
|
@Qualifier
|
||||||
|
public @interface SpringSessionDataSource {
|
||||||
|
|
||||||
|
}
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2014-2016 the original author or authors.
|
* Copyright 2014-2017 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,12 +23,14 @@ import org.junit.Rule;
|
|||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.junit.rules.ExpectedException;
|
import org.junit.rules.ExpectedException;
|
||||||
|
|
||||||
import org.springframework.beans.factory.UnsatisfiedDependencyException;
|
import org.springframework.beans.factory.BeanCreationException;
|
||||||
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
|
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
|
||||||
import org.springframework.context.annotation.Bean;
|
import org.springframework.context.annotation.Bean;
|
||||||
import org.springframework.context.annotation.Configuration;
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
import org.springframework.context.annotation.Primary;
|
||||||
import org.springframework.context.support.PropertySourcesPlaceholderConfigurer;
|
import org.springframework.context.support.PropertySourcesPlaceholderConfigurer;
|
||||||
import org.springframework.core.convert.ConversionService;
|
import org.springframework.core.convert.ConversionService;
|
||||||
|
import org.springframework.jdbc.core.JdbcOperations;
|
||||||
import org.springframework.jdbc.support.lob.LobHandler;
|
import org.springframework.jdbc.support.lob.LobHandler;
|
||||||
import org.springframework.mock.env.MockEnvironment;
|
import org.springframework.mock.env.MockEnvironment;
|
||||||
import org.springframework.session.jdbc.JdbcOperationsSessionRepository;
|
import org.springframework.session.jdbc.JdbcOperationsSessionRepository;
|
||||||
@@ -67,10 +69,10 @@ public class JdbcHttpSessionConfigurationTests {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void noDataSourceConfiguration() {
|
public void noDataSourceConfiguration() {
|
||||||
this.thrown.expect(UnsatisfiedDependencyException.class);
|
this.thrown.expect(BeanCreationException.class);
|
||||||
this.thrown.expectMessage("springSessionJdbcOperations");
|
this.thrown.expectMessage("sessionRepository");
|
||||||
|
|
||||||
registerAndRefresh(EmptyConfiguration.class);
|
registerAndRefresh(NoDataSourceConfiguration.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -145,6 +147,78 @@ public class JdbcHttpSessionConfigurationTests {
|
|||||||
.isEqualTo(MAX_INACTIVE_INTERVAL_IN_SECONDS);
|
.isEqualTo(MAX_INACTIVE_INTERVAL_IN_SECONDS);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void qualifiedDataSourceConfiguration() {
|
||||||
|
registerAndRefresh(QualifiedDataSourceConfiguration.class);
|
||||||
|
|
||||||
|
JdbcOperationsSessionRepository repository = this.context
|
||||||
|
.getBean(JdbcOperationsSessionRepository.class);
|
||||||
|
DataSource dataSource = this.context.getBean("qualifiedDataSource", DataSource.class);
|
||||||
|
assertThat(repository).isNotNull();
|
||||||
|
assertThat(dataSource).isNotNull();
|
||||||
|
JdbcOperations jdbcOperations = (JdbcOperations) ReflectionTestUtils
|
||||||
|
.getField(repository, "jdbcOperations");
|
||||||
|
assertThat(jdbcOperations).isNotNull();
|
||||||
|
assertThat(ReflectionTestUtils.getField(jdbcOperations, "dataSource"))
|
||||||
|
.isEqualTo(dataSource);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void primaryDataSourceConfiguration() {
|
||||||
|
registerAndRefresh(PrimaryDataSourceConfiguration.class);
|
||||||
|
|
||||||
|
JdbcOperationsSessionRepository repository = this.context
|
||||||
|
.getBean(JdbcOperationsSessionRepository.class);
|
||||||
|
DataSource dataSource = this.context.getBean("primaryDataSource", DataSource.class);
|
||||||
|
assertThat(repository).isNotNull();
|
||||||
|
assertThat(dataSource).isNotNull();
|
||||||
|
JdbcOperations jdbcOperations = (JdbcOperations) ReflectionTestUtils
|
||||||
|
.getField(repository, "jdbcOperations");
|
||||||
|
assertThat(jdbcOperations).isNotNull();
|
||||||
|
assertThat(ReflectionTestUtils.getField(jdbcOperations, "dataSource"))
|
||||||
|
.isEqualTo(dataSource);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void qualifiedAndPrimaryDataSourceConfiguration() {
|
||||||
|
registerAndRefresh(QualifiedAndPrimaryDataSourceConfiguration.class);
|
||||||
|
|
||||||
|
JdbcOperationsSessionRepository repository = this.context
|
||||||
|
.getBean(JdbcOperationsSessionRepository.class);
|
||||||
|
DataSource dataSource = this.context.getBean("qualifiedDataSource", DataSource.class);
|
||||||
|
assertThat(repository).isNotNull();
|
||||||
|
assertThat(dataSource).isNotNull();
|
||||||
|
JdbcOperations jdbcOperations = (JdbcOperations) ReflectionTestUtils
|
||||||
|
.getField(repository, "jdbcOperations");
|
||||||
|
assertThat(jdbcOperations).isNotNull();
|
||||||
|
assertThat(ReflectionTestUtils.getField(jdbcOperations, "dataSource"))
|
||||||
|
.isEqualTo(dataSource);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void namedDataSourceConfiguration() {
|
||||||
|
registerAndRefresh(NamedDataSourceConfiguration.class);
|
||||||
|
|
||||||
|
JdbcOperationsSessionRepository repository = this.context
|
||||||
|
.getBean(JdbcOperationsSessionRepository.class);
|
||||||
|
DataSource dataSource = this.context.getBean("dataSource", DataSource.class);
|
||||||
|
assertThat(repository).isNotNull();
|
||||||
|
assertThat(dataSource).isNotNull();
|
||||||
|
JdbcOperations jdbcOperations = (JdbcOperations) ReflectionTestUtils
|
||||||
|
.getField(repository, "jdbcOperations");
|
||||||
|
assertThat(jdbcOperations).isNotNull();
|
||||||
|
assertThat(ReflectionTestUtils.getField(jdbcOperations, "dataSource"))
|
||||||
|
.isEqualTo(dataSource);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void multipleDataSourceConfiguration() {
|
||||||
|
this.thrown.expect(BeanCreationException.class);
|
||||||
|
this.thrown.expectMessage("sessionRepository");
|
||||||
|
|
||||||
|
registerAndRefresh(MultipleDataSourceConfiguration.class);
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void customLobHandlerConfiguration() {
|
public void customLobHandlerConfiguration() {
|
||||||
registerAndRefresh(CustomLobHandlerConfiguration.class);
|
registerAndRefresh(CustomLobHandlerConfiguration.class);
|
||||||
@@ -188,13 +262,13 @@ public class JdbcHttpSessionConfigurationTests {
|
|||||||
|
|
||||||
@Configuration
|
@Configuration
|
||||||
@EnableJdbcHttpSession
|
@EnableJdbcHttpSession
|
||||||
static class EmptyConfiguration {
|
static class NoDataSourceConfiguration {
|
||||||
}
|
}
|
||||||
|
|
||||||
static class BaseConfiguration {
|
static class BaseConfiguration {
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
public DataSource dataSource() {
|
public DataSource defaultDataSource() {
|
||||||
return mock(DataSource.class);
|
return mock(DataSource.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -239,6 +313,70 @@ public class JdbcHttpSessionConfigurationTests {
|
|||||||
extends BaseConfiguration {
|
extends BaseConfiguration {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Configuration
|
||||||
|
@EnableJdbcHttpSession
|
||||||
|
static class QualifiedDataSourceConfiguration extends BaseConfiguration {
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
@SpringSessionDataSource
|
||||||
|
public DataSource qualifiedDataSource() {
|
||||||
|
return mock(DataSource.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Configuration
|
||||||
|
@EnableJdbcHttpSession
|
||||||
|
static class PrimaryDataSourceConfiguration extends BaseConfiguration {
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
@Primary
|
||||||
|
public DataSource primaryDataSource() {
|
||||||
|
return mock(DataSource.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Configuration
|
||||||
|
@EnableJdbcHttpSession
|
||||||
|
static class QualifiedAndPrimaryDataSourceConfiguration extends BaseConfiguration {
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
@SpringSessionDataSource
|
||||||
|
public DataSource qualifiedDataSource() {
|
||||||
|
return mock(DataSource.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
@Primary
|
||||||
|
public DataSource primaryDataSource() {
|
||||||
|
return mock(DataSource.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Configuration
|
||||||
|
@EnableJdbcHttpSession
|
||||||
|
static class NamedDataSourceConfiguration extends BaseConfiguration {
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public DataSource dataSource() {
|
||||||
|
return mock(DataSource.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Configuration
|
||||||
|
@EnableJdbcHttpSession
|
||||||
|
static class MultipleDataSourceConfiguration extends BaseConfiguration {
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public DataSource secondaryDataSource() {
|
||||||
|
return mock(DataSource.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
@Configuration
|
@Configuration
|
||||||
@EnableJdbcHttpSession
|
@EnableJdbcHttpSession
|
||||||
static class CustomLobHandlerConfiguration extends BaseConfiguration {
|
static class CustomLobHandlerConfiguration extends BaseConfiguration {
|
||||||
|
|||||||
Reference in New Issue
Block a user