Move integration tests to dedicated module

This commit moves the dependency management and test source files
related to integration tests to a dedicated module.
This allows us to focus the root project on building the Spring
Framework.

See gh-23282
This commit is contained in:
Brian Clozel
2019-07-23 11:28:31 +02:00
parent 6008c61680
commit 998f6af290
41 changed files with 25 additions and 18 deletions

View File

@@ -0,0 +1,144 @@
/*
* Copyright 2002-2019 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
*
* https://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.aop.config;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.aop.framework.Advised;
import org.springframework.aop.support.AopUtils;
import org.springframework.context.ApplicationContext;
import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.mock.web.MockHttpSession;
import org.springframework.tests.sample.beans.ITestBean;
import org.springframework.tests.sample.beans.TestBean;
import org.springframework.util.ClassUtils;
import org.springframework.util.SerializationTestUtils;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import org.springframework.web.context.support.XmlWebApplicationContext;
import static java.lang.String.format;
import static org.assertj.core.api.Assertions.assertThat;
/**
* Integration tests for scoped proxy use in conjunction with aop: namespace.
* Deemed an integration test because .web mocks and application contexts are required.
*
* @author Rob Harrop
* @author Juergen Hoeller
* @author Chris Beams
* @see org.springframework.aop.config.AopNamespaceHandlerTests
*/
public class AopNamespaceHandlerScopeIntegrationTests {
private static final String CONTEXT = format("classpath:%s-context.xml",
ClassUtils.convertClassNameToResourcePath(AopNamespaceHandlerScopeIntegrationTests.class.getName()));
private ApplicationContext context;
@BeforeEach
public void setUp() {
XmlWebApplicationContext wac = new XmlWebApplicationContext();
wac.setConfigLocations(CONTEXT);
wac.refresh();
this.context = wac;
}
@Test
public void testSingletonScoping() throws Exception {
ITestBean scoped = (ITestBean) this.context.getBean("singletonScoped");
assertThat(AopUtils.isAopProxy(scoped)).as("Should be AOP proxy").isTrue();
boolean condition = scoped instanceof TestBean;
assertThat(condition).as("Should be target class proxy").isTrue();
String rob = "Rob Harrop";
String bram = "Bram Smeets";
assertThat(scoped.getName()).isEqualTo(rob);
scoped.setName(bram);
assertThat(scoped.getName()).isEqualTo(bram);
ITestBean deserialized = (ITestBean) SerializationTestUtils.serializeAndDeserialize(scoped);
assertThat(deserialized.getName()).isEqualTo(bram);
}
@Test
public void testRequestScoping() throws Exception {
MockHttpServletRequest oldRequest = new MockHttpServletRequest();
MockHttpServletRequest newRequest = new MockHttpServletRequest();
RequestContextHolder.setRequestAttributes(new ServletRequestAttributes(oldRequest));
ITestBean scoped = (ITestBean) this.context.getBean("requestScoped");
assertThat(AopUtils.isAopProxy(scoped)).as("Should be AOP proxy").isTrue();
boolean condition = scoped instanceof TestBean;
assertThat(condition).as("Should be target class proxy").isTrue();
ITestBean testBean = (ITestBean) this.context.getBean("testBean");
assertThat(AopUtils.isAopProxy(testBean)).as("Should be AOP proxy").isTrue();
boolean condition1 = testBean instanceof TestBean;
assertThat(condition1).as("Regular bean should be JDK proxy").isFalse();
String rob = "Rob Harrop";
String bram = "Bram Smeets";
assertThat(scoped.getName()).isEqualTo(rob);
scoped.setName(bram);
RequestContextHolder.setRequestAttributes(new ServletRequestAttributes(newRequest));
assertThat(scoped.getName()).isEqualTo(rob);
RequestContextHolder.setRequestAttributes(new ServletRequestAttributes(oldRequest));
assertThat(scoped.getName()).isEqualTo(bram);
assertThat(((Advised) scoped).getAdvisors().length > 0).as("Should have advisors").isTrue();
}
@Test
public void testSessionScoping() throws Exception {
MockHttpSession oldSession = new MockHttpSession();
MockHttpSession newSession = new MockHttpSession();
MockHttpServletRequest request = new MockHttpServletRequest();
request.setSession(oldSession);
RequestContextHolder.setRequestAttributes(new ServletRequestAttributes(request));
ITestBean scoped = (ITestBean) this.context.getBean("sessionScoped");
assertThat(AopUtils.isAopProxy(scoped)).as("Should be AOP proxy").isTrue();
boolean condition1 = scoped instanceof TestBean;
assertThat(condition1).as("Should not be target class proxy").isFalse();
ITestBean scopedAlias = (ITestBean) this.context.getBean("sessionScopedAlias");
assertThat(scopedAlias).isSameAs(scoped);
ITestBean testBean = (ITestBean) this.context.getBean("testBean");
assertThat(AopUtils.isAopProxy(testBean)).as("Should be AOP proxy").isTrue();
boolean condition = testBean instanceof TestBean;
assertThat(condition).as("Regular bean should be JDK proxy").isFalse();
String rob = "Rob Harrop";
String bram = "Bram Smeets";
assertThat(scoped.getName()).isEqualTo(rob);
scoped.setName(bram);
request.setSession(newSession);
assertThat(scoped.getName()).isEqualTo(rob);
request.setSession(oldSession);
assertThat(scoped.getName()).isEqualTo(bram);
assertThat(((Advised) scoped).getAdvisors().length > 0).as("Should have advisors").isTrue();
}
}

View File

@@ -0,0 +1,317 @@
/*
* Copyright 2002-2019 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
*
* https://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.aop.framework.autoproxy;
import java.io.IOException;
import java.lang.reflect.Method;
import java.util.List;
import javax.servlet.ServletException;
import org.junit.jupiter.api.Test;
import org.springframework.aop.support.AopUtils;
import org.springframework.aop.support.StaticMethodMatcherPointcutAdvisor;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.lang.Nullable;
import org.springframework.tests.aop.advice.CountingBeforeAdvice;
import org.springframework.tests.aop.advice.MethodCounter;
import org.springframework.tests.aop.interceptor.NopInterceptor;
import org.springframework.tests.sample.beans.ITestBean;
import org.springframework.tests.transaction.CallCountingTransactionManager;
import org.springframework.transaction.NoTransactionException;
import org.springframework.transaction.interceptor.TransactionInterceptor;
import static org.assertj.core.api.Assertions.assertThat;
/**
* Integration tests for auto proxy creation by advisor recognition working in
* conjunction with transaction management resources.
*
* @see org.springframework.aop.framework.autoproxy.AdvisorAutoProxyCreatorTests
*
* @author Rod Johnson
* @author Chris Beams
*/
public class AdvisorAutoProxyCreatorIntegrationTests {
private static final Class<?> CLASS = AdvisorAutoProxyCreatorIntegrationTests.class;
private static final String CLASSNAME = CLASS.getSimpleName();
private static final String DEFAULT_CONTEXT = CLASSNAME + "-context.xml";
private static final String ADVISOR_APC_BEAN_NAME = "aapc";
private static final String TXMANAGER_BEAN_NAME = "txManager";
/**
* Return a bean factory with attributes and EnterpriseServices configured.
*/
protected BeanFactory getBeanFactory() throws IOException {
return new ClassPathXmlApplicationContext(DEFAULT_CONTEXT, CLASS);
}
@Test
public void testDefaultExclusionPrefix() throws Exception {
DefaultAdvisorAutoProxyCreator aapc = (DefaultAdvisorAutoProxyCreator) getBeanFactory().getBean(ADVISOR_APC_BEAN_NAME);
assertThat(aapc.getAdvisorBeanNamePrefix()).isEqualTo((ADVISOR_APC_BEAN_NAME + DefaultAdvisorAutoProxyCreator.SEPARATOR));
assertThat(aapc.isUsePrefix()).isFalse();
}
/**
* If no pointcuts match (no attrs) there should be proxying.
*/
@Test
public void testNoProxy() throws Exception {
BeanFactory bf = getBeanFactory();
Object o = bf.getBean("noSetters");
assertThat(AopUtils.isAopProxy(o)).isFalse();
}
@Test
public void testTxIsProxied() throws Exception {
BeanFactory bf = getBeanFactory();
ITestBean test = (ITestBean) bf.getBean("test");
assertThat(AopUtils.isAopProxy(test)).isTrue();
}
@Test
public void testRegexpApplied() throws Exception {
BeanFactory bf = getBeanFactory();
ITestBean test = (ITestBean) bf.getBean("test");
MethodCounter counter = (MethodCounter) bf.getBean("countingAdvice");
assertThat(counter.getCalls()).isEqualTo(0);
test.getName();
assertThat(counter.getCalls()).isEqualTo(1);
}
@Test
public void testTransactionAttributeOnMethod() throws Exception {
BeanFactory bf = getBeanFactory();
ITestBean test = (ITestBean) bf.getBean("test");
CallCountingTransactionManager txMan = (CallCountingTransactionManager) bf.getBean(TXMANAGER_BEAN_NAME);
OrderedTxCheckAdvisor txc = (OrderedTxCheckAdvisor) bf.getBean("orderedBeforeTransaction");
assertThat(txc.getCountingBeforeAdvice().getCalls()).isEqualTo(0);
assertThat(txMan.commits).isEqualTo(0);
assertThat(test.getAge()).as("Initial value was correct").isEqualTo(4);
int newAge = 5;
test.setAge(newAge);
assertThat(txc.getCountingBeforeAdvice().getCalls()).isEqualTo(1);
assertThat(test.getAge()).as("New value set correctly").isEqualTo(newAge);
assertThat(txMan.commits).as("Transaction counts match").isEqualTo(1);
}
/**
* Should not roll back on servlet exception.
*/
@Test
public void testRollbackRulesOnMethodCauseRollback() throws Exception {
BeanFactory bf = getBeanFactory();
Rollback rb = (Rollback) bf.getBean("rollback");
CallCountingTransactionManager txMan = (CallCountingTransactionManager) bf.getBean(TXMANAGER_BEAN_NAME);
OrderedTxCheckAdvisor txc = (OrderedTxCheckAdvisor) bf.getBean("orderedBeforeTransaction");
assertThat(txc.getCountingBeforeAdvice().getCalls()).isEqualTo(0);
assertThat(txMan.commits).isEqualTo(0);
rb.echoException(null);
// Fires only on setters
assertThat(txc.getCountingBeforeAdvice().getCalls()).isEqualTo(0);
assertThat(txMan.commits).as("Transaction counts match").isEqualTo(1);
assertThat(txMan.rollbacks).isEqualTo(0);
Exception ex = new Exception();
try {
rb.echoException(ex);
}
catch (Exception actual) {
assertThat(actual).isEqualTo(ex);
}
assertThat(txMan.rollbacks).as("Transaction counts match").isEqualTo(1);
}
@Test
public void testRollbackRulesOnMethodPreventRollback() throws Exception {
BeanFactory bf = getBeanFactory();
Rollback rb = (Rollback) bf.getBean("rollback");
CallCountingTransactionManager txMan = (CallCountingTransactionManager) bf.getBean(TXMANAGER_BEAN_NAME);
assertThat(txMan.commits).isEqualTo(0);
// Should NOT roll back on ServletException
try {
rb.echoException(new ServletException());
}
catch (ServletException ex) {
}
assertThat(txMan.commits).as("Transaction counts match").isEqualTo(1);
}
@Test
public void testProgrammaticRollback() throws Exception {
BeanFactory bf = getBeanFactory();
Object bean = bf.getBean(TXMANAGER_BEAN_NAME);
boolean condition = bean instanceof CallCountingTransactionManager;
assertThat(condition).isTrue();
CallCountingTransactionManager txMan = (CallCountingTransactionManager) bf.getBean(TXMANAGER_BEAN_NAME);
Rollback rb = (Rollback) bf.getBean("rollback");
assertThat(txMan.commits).isEqualTo(0);
rb.rollbackOnly(false);
assertThat(txMan.commits).as("Transaction counts match").isEqualTo(1);
assertThat(txMan.rollbacks).isEqualTo(0);
// Will cause rollback only
rb.rollbackOnly(true);
assertThat(txMan.rollbacks).isEqualTo(1);
}
}
@SuppressWarnings("serial")
class NeverMatchAdvisor extends StaticMethodMatcherPointcutAdvisor {
public NeverMatchAdvisor() {
super(new NopInterceptor());
}
/**
* This method is solely to allow us to create a mixture of dependencies in
* the bean definitions. The dependencies don't have any meaning, and don't
* <b>do</b> anything.
*/
public void setDependencies(List<?> l) {
}
/**
* @see org.springframework.aop.MethodMatcher#matches(java.lang.reflect.Method, java.lang.Class)
*/
@Override
public boolean matches(Method m, @Nullable Class<?> targetClass) {
return false;
}
}
class NoSetters {
public void A() {
}
public int getB() {
return -1;
}
}
@SuppressWarnings("serial")
class OrderedTxCheckAdvisor extends StaticMethodMatcherPointcutAdvisor implements InitializingBean {
/**
* Should we insist on the presence of a transaction attribute or refuse to accept one?
*/
private boolean requireTransactionContext = false;
public void setRequireTransactionContext(boolean requireTransactionContext) {
this.requireTransactionContext = requireTransactionContext;
}
public boolean isRequireTransactionContext() {
return requireTransactionContext;
}
public CountingBeforeAdvice getCountingBeforeAdvice() {
return (CountingBeforeAdvice) getAdvice();
}
@Override
public void afterPropertiesSet() throws Exception {
setAdvice(new TxCountingBeforeAdvice());
}
@Override
public boolean matches(Method method, @Nullable Class<?> targetClass) {
return method.getName().startsWith("setAge");
}
private class TxCountingBeforeAdvice extends CountingBeforeAdvice {
@Override
public void before(Method method, Object[] args, Object target) throws Throwable {
// do transaction checks
if (requireTransactionContext) {
TransactionInterceptor.currentTransactionStatus();
}
else {
try {
TransactionInterceptor.currentTransactionStatus();
throw new RuntimeException("Shouldn't have a transaction");
}
catch (NoTransactionException ex) {
// this is Ok
}
}
super.before(method, args, target);
}
}
}
class Rollback {
/**
* Inherits transaction attribute.
* Illustrates programmatic rollback.
*/
public void rollbackOnly(boolean rollbackOnly) {
if (rollbackOnly) {
setRollbackOnly();
}
}
/**
* Extracted in a protected method to facilitate testing
*/
protected void setRollbackOnly() {
TransactionInterceptor.currentTransactionStatus().setRollbackOnly();
}
/**
* @org.springframework.transaction.interceptor.RuleBasedTransaction ( timeout=-1 )
* @org.springframework.transaction.interceptor.RollbackRule ( "java.lang.Exception" )
* @org.springframework.transaction.interceptor.NoRollbackRule ( "ServletException" )
*/
public void echoException(Exception ex) throws Exception {
if (ex != null) {
throw ex;
}
}
}

View File

@@ -0,0 +1,136 @@
/*
* Copyright 2002-2019 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
*
* https://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.cache.annotation;
import java.util.Collections;
import java.util.List;
import org.junit.jupiter.api.Test;
import org.springframework.aop.Advisor;
import org.springframework.aop.framework.Advised;
import org.springframework.aop.support.AopUtils;
import org.springframework.cache.CacheManager;
import org.springframework.cache.interceptor.BeanFactoryCacheOperationSourceAdvisor;
import org.springframework.cache.support.NoOpCacheManager;
import org.springframework.context.annotation.AdviceMode;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.stereotype.Repository;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
/**
* Integration tests for the @EnableCaching annotation.
*
* @author Chris Beams
* @since 3.1
*/
@SuppressWarnings("resource")
public class EnableCachingIntegrationTests {
@Test
public void repositoryIsClassBasedCacheProxy() {
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
ctx.register(Config.class, ProxyTargetClassCachingConfig.class);
ctx.refresh();
assertCacheProxying(ctx);
assertThat(AopUtils.isCglibProxy(ctx.getBean(FooRepository.class))).isTrue();
}
@Test
public void repositoryUsesAspectJAdviceMode() {
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
ctx.register(Config.class, AspectJCacheConfig.class);
// this test is a bit fragile, but gets the job done, proving that an
// attempt was made to look up the AJ aspect. It's due to classpath issues
// in .integration-tests that it's not found.
assertThatExceptionOfType(Exception.class).isThrownBy(
ctx::refresh)
.withMessageContaining("AspectJCachingConfiguration");
}
private void assertCacheProxying(AnnotationConfigApplicationContext ctx) {
FooRepository repo = ctx.getBean(FooRepository.class);
assertThat(isCacheProxy(repo)).isTrue();
}
private boolean isCacheProxy(FooRepository repo) {
if (AopUtils.isAopProxy(repo)) {
for (Advisor advisor : ((Advised)repo).getAdvisors()) {
if (advisor instanceof BeanFactoryCacheOperationSourceAdvisor) {
return true;
}
}
}
return false;
}
@Configuration
@EnableCaching(proxyTargetClass=true)
static class ProxyTargetClassCachingConfig {
@Bean
CacheManager mgr() {
return new NoOpCacheManager();
}
}
@Configuration
static class Config {
@Bean
FooRepository fooRepository() {
return new DummyFooRepository();
}
}
@Configuration
@EnableCaching(mode=AdviceMode.ASPECTJ)
static class AspectJCacheConfig {
@Bean
CacheManager cacheManager() {
return new NoOpCacheManager();
}
}
interface FooRepository {
List<Object> findAll();
}
@Repository
static class DummyFooRepository implements FooRepository {
@Override
@Cacheable("primary")
public List<Object> findAll() {
return Collections.emptyList();
}
}
}

View File

@@ -0,0 +1,404 @@
/*
* Copyright 2002-2019 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
*
* https://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.context.annotation.jsr330;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import javax.inject.Named;
import javax.inject.Singleton;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.aop.support.AopUtils;
import org.springframework.beans.factory.annotation.AnnotatedBeanDefinition;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.ClassPathBeanDefinitionScanner;
import org.springframework.context.annotation.ScopeMetadata;
import org.springframework.context.annotation.ScopeMetadataResolver;
import org.springframework.context.annotation.ScopedProxyMode;
import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.mock.web.MockHttpSession;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import org.springframework.web.context.support.GenericWebApplicationContext;
import static org.assertj.core.api.Assertions.assertThat;
/**
* @author Mark Fisher
* @author Juergen Hoeller
* @author Chris Beams
*/
public class ClassPathBeanDefinitionScannerJsr330ScopeIntegrationTests {
private static final String DEFAULT_NAME = "default";
private static final String MODIFIED_NAME = "modified";
private ServletRequestAttributes oldRequestAttributes;
private ServletRequestAttributes newRequestAttributes;
private ServletRequestAttributes oldRequestAttributesWithSession;
private ServletRequestAttributes newRequestAttributesWithSession;
@BeforeEach
public void setUp() {
this.oldRequestAttributes = new ServletRequestAttributes(new MockHttpServletRequest());
this.newRequestAttributes = new ServletRequestAttributes(new MockHttpServletRequest());
MockHttpServletRequest oldRequestWithSession = new MockHttpServletRequest();
oldRequestWithSession.setSession(new MockHttpSession());
this.oldRequestAttributesWithSession = new ServletRequestAttributes(oldRequestWithSession);
MockHttpServletRequest newRequestWithSession = new MockHttpServletRequest();
newRequestWithSession.setSession(new MockHttpSession());
this.newRequestAttributesWithSession = new ServletRequestAttributes(newRequestWithSession);
}
@AfterEach
public void tearDown() throws Exception {
RequestContextHolder.setRequestAttributes(null);
}
@Test
public void testPrototype() {
ApplicationContext context = createContext(ScopedProxyMode.NO);
ScopedTestBean bean = (ScopedTestBean) context.getBean("prototype");
assertThat(bean).isNotNull();
assertThat(context.isPrototype("prototype")).isTrue();
assertThat(context.isSingleton("prototype")).isFalse();
}
@Test
public void testSingletonScopeWithNoProxy() {
RequestContextHolder.setRequestAttributes(oldRequestAttributes);
ApplicationContext context = createContext(ScopedProxyMode.NO);
ScopedTestBean bean = (ScopedTestBean) context.getBean("singleton");
assertThat(context.isSingleton("singleton")).isTrue();
assertThat(context.isPrototype("singleton")).isFalse();
// should not be a proxy
assertThat(AopUtils.isAopProxy(bean)).isFalse();
assertThat(bean.getName()).isEqualTo(DEFAULT_NAME);
bean.setName(MODIFIED_NAME);
RequestContextHolder.setRequestAttributes(newRequestAttributes);
// not a proxy so this should not have changed
assertThat(bean.getName()).isEqualTo(MODIFIED_NAME);
// singleton bean, so name should be modified even after lookup
ScopedTestBean bean2 = (ScopedTestBean) context.getBean("singleton");
assertThat(bean2.getName()).isEqualTo(MODIFIED_NAME);
}
@Test
public void testSingletonScopeIgnoresProxyInterfaces() {
RequestContextHolder.setRequestAttributes(oldRequestAttributes);
ApplicationContext context = createContext(ScopedProxyMode.INTERFACES);
ScopedTestBean bean = (ScopedTestBean) context.getBean("singleton");
// should not be a proxy
assertThat(AopUtils.isAopProxy(bean)).isFalse();
assertThat(bean.getName()).isEqualTo(DEFAULT_NAME);
bean.setName(MODIFIED_NAME);
RequestContextHolder.setRequestAttributes(newRequestAttributes);
// not a proxy so this should not have changed
assertThat(bean.getName()).isEqualTo(MODIFIED_NAME);
// singleton bean, so name should be modified even after lookup
ScopedTestBean bean2 = (ScopedTestBean) context.getBean("singleton");
assertThat(bean2.getName()).isEqualTo(MODIFIED_NAME);
}
@Test
public void testSingletonScopeIgnoresProxyTargetClass() {
RequestContextHolder.setRequestAttributes(oldRequestAttributes);
ApplicationContext context = createContext(ScopedProxyMode.TARGET_CLASS);
ScopedTestBean bean = (ScopedTestBean) context.getBean("singleton");
// should not be a proxy
assertThat(AopUtils.isAopProxy(bean)).isFalse();
assertThat(bean.getName()).isEqualTo(DEFAULT_NAME);
bean.setName(MODIFIED_NAME);
RequestContextHolder.setRequestAttributes(newRequestAttributes);
// not a proxy so this should not have changed
assertThat(bean.getName()).isEqualTo(MODIFIED_NAME);
// singleton bean, so name should be modified even after lookup
ScopedTestBean bean2 = (ScopedTestBean) context.getBean("singleton");
assertThat(bean2.getName()).isEqualTo(MODIFIED_NAME);
}
@Test
public void testRequestScopeWithNoProxy() {
RequestContextHolder.setRequestAttributes(oldRequestAttributes);
ApplicationContext context = createContext(ScopedProxyMode.NO);
ScopedTestBean bean = (ScopedTestBean) context.getBean("request");
// should not be a proxy
assertThat(AopUtils.isAopProxy(bean)).isFalse();
assertThat(bean.getName()).isEqualTo(DEFAULT_NAME);
bean.setName(MODIFIED_NAME);
RequestContextHolder.setRequestAttributes(newRequestAttributes);
// not a proxy so this should not have changed
assertThat(bean.getName()).isEqualTo(MODIFIED_NAME);
// but a newly retrieved bean should have the default name
ScopedTestBean bean2 = (ScopedTestBean) context.getBean("request");
assertThat(bean2.getName()).isEqualTo(DEFAULT_NAME);
}
@Test
public void testRequestScopeWithProxiedInterfaces() {
RequestContextHolder.setRequestAttributes(oldRequestAttributes);
ApplicationContext context = createContext(ScopedProxyMode.INTERFACES);
IScopedTestBean bean = (IScopedTestBean) context.getBean("request");
// should be dynamic proxy, implementing both interfaces
assertThat(AopUtils.isJdkDynamicProxy(bean)).isTrue();
boolean condition = bean instanceof AnotherScopeTestInterface;
assertThat(condition).isTrue();
assertThat(bean.getName()).isEqualTo(DEFAULT_NAME);
bean.setName(MODIFIED_NAME);
RequestContextHolder.setRequestAttributes(newRequestAttributes);
// this is a proxy so it should be reset to default
assertThat(bean.getName()).isEqualTo(DEFAULT_NAME);
RequestContextHolder.setRequestAttributes(oldRequestAttributes);
assertThat(bean.getName()).isEqualTo(MODIFIED_NAME);
}
@Test
public void testRequestScopeWithProxiedTargetClass() {
RequestContextHolder.setRequestAttributes(oldRequestAttributes);
ApplicationContext context = createContext(ScopedProxyMode.TARGET_CLASS);
IScopedTestBean bean = (IScopedTestBean) context.getBean("request");
// should be a class-based proxy
assertThat(AopUtils.isCglibProxy(bean)).isTrue();
boolean condition = bean instanceof RequestScopedTestBean;
assertThat(condition).isTrue();
assertThat(bean.getName()).isEqualTo(DEFAULT_NAME);
bean.setName(MODIFIED_NAME);
RequestContextHolder.setRequestAttributes(newRequestAttributes);
// this is a proxy so it should be reset to default
assertThat(bean.getName()).isEqualTo(DEFAULT_NAME);
RequestContextHolder.setRequestAttributes(oldRequestAttributes);
assertThat(bean.getName()).isEqualTo(MODIFIED_NAME);
}
@Test
public void testSessionScopeWithNoProxy() {
RequestContextHolder.setRequestAttributes(oldRequestAttributesWithSession);
ApplicationContext context = createContext(ScopedProxyMode.NO);
ScopedTestBean bean = (ScopedTestBean) context.getBean("session");
// should not be a proxy
assertThat(AopUtils.isAopProxy(bean)).isFalse();
assertThat(bean.getName()).isEqualTo(DEFAULT_NAME);
bean.setName(MODIFIED_NAME);
RequestContextHolder.setRequestAttributes(newRequestAttributesWithSession);
// not a proxy so this should not have changed
assertThat(bean.getName()).isEqualTo(MODIFIED_NAME);
// but a newly retrieved bean should have the default name
ScopedTestBean bean2 = (ScopedTestBean) context.getBean("session");
assertThat(bean2.getName()).isEqualTo(DEFAULT_NAME);
}
@Test
public void testSessionScopeWithProxiedInterfaces() {
RequestContextHolder.setRequestAttributes(oldRequestAttributesWithSession);
ApplicationContext context = createContext(ScopedProxyMode.INTERFACES);
IScopedTestBean bean = (IScopedTestBean) context.getBean("session");
// should be dynamic proxy, implementing both interfaces
assertThat(AopUtils.isJdkDynamicProxy(bean)).isTrue();
boolean condition = bean instanceof AnotherScopeTestInterface;
assertThat(condition).isTrue();
assertThat(bean.getName()).isEqualTo(DEFAULT_NAME);
bean.setName(MODIFIED_NAME);
RequestContextHolder.setRequestAttributes(newRequestAttributesWithSession);
// this is a proxy so it should be reset to default
assertThat(bean.getName()).isEqualTo(DEFAULT_NAME);
bean.setName(MODIFIED_NAME);
IScopedTestBean bean2 = (IScopedTestBean) context.getBean("session");
assertThat(bean2.getName()).isEqualTo(MODIFIED_NAME);
bean2.setName(DEFAULT_NAME);
assertThat(bean.getName()).isEqualTo(DEFAULT_NAME);
RequestContextHolder.setRequestAttributes(oldRequestAttributesWithSession);
assertThat(bean.getName()).isEqualTo(MODIFIED_NAME);
}
@Test
public void testSessionScopeWithProxiedTargetClass() {
RequestContextHolder.setRequestAttributes(oldRequestAttributesWithSession);
ApplicationContext context = createContext(ScopedProxyMode.TARGET_CLASS);
IScopedTestBean bean = (IScopedTestBean) context.getBean("session");
// should be a class-based proxy
assertThat(AopUtils.isCglibProxy(bean)).isTrue();
boolean condition1 = bean instanceof ScopedTestBean;
assertThat(condition1).isTrue();
boolean condition = bean instanceof SessionScopedTestBean;
assertThat(condition).isTrue();
assertThat(bean.getName()).isEqualTo(DEFAULT_NAME);
bean.setName(MODIFIED_NAME);
RequestContextHolder.setRequestAttributes(newRequestAttributesWithSession);
// this is a proxy so it should be reset to default
assertThat(bean.getName()).isEqualTo(DEFAULT_NAME);
bean.setName(MODIFIED_NAME);
IScopedTestBean bean2 = (IScopedTestBean) context.getBean("session");
assertThat(bean2.getName()).isEqualTo(MODIFIED_NAME);
bean2.setName(DEFAULT_NAME);
assertThat(bean.getName()).isEqualTo(DEFAULT_NAME);
RequestContextHolder.setRequestAttributes(oldRequestAttributesWithSession);
assertThat(bean.getName()).isEqualTo(MODIFIED_NAME);
}
private ApplicationContext createContext(final ScopedProxyMode scopedProxyMode) {
GenericWebApplicationContext context = new GenericWebApplicationContext();
ClassPathBeanDefinitionScanner scanner = new ClassPathBeanDefinitionScanner(context);
scanner.setIncludeAnnotationConfig(false);
scanner.setScopeMetadataResolver(new ScopeMetadataResolver() {
@Override
public ScopeMetadata resolveScopeMetadata(BeanDefinition definition) {
ScopeMetadata metadata = new ScopeMetadata();
if (definition instanceof AnnotatedBeanDefinition) {
AnnotatedBeanDefinition annDef = (AnnotatedBeanDefinition) definition;
for (String type : annDef.getMetadata().getAnnotationTypes()) {
if (type.equals(javax.inject.Singleton.class.getName())) {
metadata.setScopeName(BeanDefinition.SCOPE_SINGLETON);
break;
}
else if (annDef.getMetadata().getMetaAnnotationTypes(type).contains(javax.inject.Scope.class.getName())) {
metadata.setScopeName(type.substring(type.length() - 13, type.length() - 6).toLowerCase());
metadata.setScopedProxyMode(scopedProxyMode);
break;
}
else if (type.startsWith("javax.inject")) {
metadata.setScopeName(BeanDefinition.SCOPE_PROTOTYPE);
}
}
}
return metadata;
}
});
// Scan twice in order to find errors in the bean definition compatibility check.
scanner.scan(getClass().getPackage().getName());
scanner.scan(getClass().getPackage().getName());
context.registerAlias("classPathBeanDefinitionScannerJsr330ScopeIntegrationTests.SessionScopedTestBean", "session");
context.refresh();
return context;
}
public static interface IScopedTestBean {
String getName();
void setName(String name);
}
public static abstract class ScopedTestBean implements IScopedTestBean {
private String name = DEFAULT_NAME;
@Override
public String getName() { return this.name; }
@Override
public void setName(String name) { this.name = name; }
}
@Named("prototype")
public static class PrototypeScopedTestBean extends ScopedTestBean {
}
@Named("singleton")
@Singleton
public static class SingletonScopedTestBean extends ScopedTestBean {
}
public static interface AnotherScopeTestInterface {
}
@Named("request")
@RequestScoped
public static class RequestScopedTestBean extends ScopedTestBean implements AnotherScopeTestInterface {
}
@Named
@SessionScoped
public static class SessionScopedTestBean extends ScopedTestBean implements AnotherScopeTestInterface {
}
@Target({ElementType.FIELD, ElementType.PARAMETER, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@javax.inject.Scope
public static @interface RequestScoped {
}
@Target({ElementType.FIELD, ElementType.PARAMETER, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@javax.inject.Scope
public static @interface SessionScoped {
}
}

View File

@@ -0,0 +1,341 @@
/*
* Copyright 2002-2019 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
*
* https://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.context.annotation.scope;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.aop.support.AopUtils;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.ClassPathBeanDefinitionScanner;
import org.springframework.context.annotation.ScopedProxyMode;
import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.mock.web.MockHttpSession;
import org.springframework.stereotype.Component;
import org.springframework.web.context.annotation.RequestScope;
import org.springframework.web.context.annotation.SessionScope;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import org.springframework.web.context.support.GenericWebApplicationContext;
import static org.assertj.core.api.Assertions.assertThat;
import static org.springframework.context.annotation.ScopedProxyMode.DEFAULT;
import static org.springframework.context.annotation.ScopedProxyMode.INTERFACES;
import static org.springframework.context.annotation.ScopedProxyMode.NO;
import static org.springframework.context.annotation.ScopedProxyMode.TARGET_CLASS;
/**
* @author Mark Fisher
* @author Juergen Hoeller
* @author Chris Beams
* @author Sam Brannen
*/
public class ClassPathBeanDefinitionScannerScopeIntegrationTests {
private static final String DEFAULT_NAME = "default";
private static final String MODIFIED_NAME = "modified";
private ServletRequestAttributes oldRequestAttributes = new ServletRequestAttributes(new MockHttpServletRequest());
private ServletRequestAttributes newRequestAttributes = new ServletRequestAttributes(new MockHttpServletRequest());
private ServletRequestAttributes oldRequestAttributesWithSession;
private ServletRequestAttributes newRequestAttributesWithSession;
@BeforeEach
public void setUp() {
MockHttpServletRequest oldRequestWithSession = new MockHttpServletRequest();
oldRequestWithSession.setSession(new MockHttpSession());
this.oldRequestAttributesWithSession = new ServletRequestAttributes(oldRequestWithSession);
MockHttpServletRequest newRequestWithSession = new MockHttpServletRequest();
newRequestWithSession.setSession(new MockHttpSession());
this.newRequestAttributesWithSession = new ServletRequestAttributes(newRequestWithSession);
}
@AfterEach
public void tearDown() throws Exception {
RequestContextHolder.resetRequestAttributes();
}
@Test
public void singletonScopeWithNoProxy() {
RequestContextHolder.setRequestAttributes(oldRequestAttributes);
ApplicationContext context = createContext(NO);
ScopedTestBean bean = (ScopedTestBean) context.getBean("singleton");
// should not be a proxy
assertThat(AopUtils.isAopProxy(bean)).isFalse();
assertThat(bean.getName()).isEqualTo(DEFAULT_NAME);
bean.setName(MODIFIED_NAME);
RequestContextHolder.setRequestAttributes(newRequestAttributes);
// not a proxy so this should not have changed
assertThat(bean.getName()).isEqualTo(MODIFIED_NAME);
// singleton bean, so name should be modified even after lookup
ScopedTestBean bean2 = (ScopedTestBean) context.getBean("singleton");
assertThat(bean2.getName()).isEqualTo(MODIFIED_NAME);
}
@Test
public void singletonScopeIgnoresProxyInterfaces() {
RequestContextHolder.setRequestAttributes(oldRequestAttributes);
ApplicationContext context = createContext(INTERFACES);
ScopedTestBean bean = (ScopedTestBean) context.getBean("singleton");
// should not be a proxy
assertThat(AopUtils.isAopProxy(bean)).isFalse();
assertThat(bean.getName()).isEqualTo(DEFAULT_NAME);
bean.setName(MODIFIED_NAME);
RequestContextHolder.setRequestAttributes(newRequestAttributes);
// not a proxy so this should not have changed
assertThat(bean.getName()).isEqualTo(MODIFIED_NAME);
// singleton bean, so name should be modified even after lookup
ScopedTestBean bean2 = (ScopedTestBean) context.getBean("singleton");
assertThat(bean2.getName()).isEqualTo(MODIFIED_NAME);
}
@Test
public void singletonScopeIgnoresProxyTargetClass() {
RequestContextHolder.setRequestAttributes(oldRequestAttributes);
ApplicationContext context = createContext(TARGET_CLASS);
ScopedTestBean bean = (ScopedTestBean) context.getBean("singleton");
// should not be a proxy
assertThat(AopUtils.isAopProxy(bean)).isFalse();
assertThat(bean.getName()).isEqualTo(DEFAULT_NAME);
bean.setName(MODIFIED_NAME);
RequestContextHolder.setRequestAttributes(newRequestAttributes);
// not a proxy so this should not have changed
assertThat(bean.getName()).isEqualTo(MODIFIED_NAME);
// singleton bean, so name should be modified even after lookup
ScopedTestBean bean2 = (ScopedTestBean) context.getBean("singleton");
assertThat(bean2.getName()).isEqualTo(MODIFIED_NAME);
}
@Test
public void requestScopeWithNoProxy() {
RequestContextHolder.setRequestAttributes(oldRequestAttributes);
ApplicationContext context = createContext(NO);
ScopedTestBean bean = (ScopedTestBean) context.getBean("request");
// should not be a proxy
assertThat(AopUtils.isAopProxy(bean)).isFalse();
assertThat(bean.getName()).isEqualTo(DEFAULT_NAME);
bean.setName(MODIFIED_NAME);
RequestContextHolder.setRequestAttributes(newRequestAttributes);
// not a proxy so this should not have changed
assertThat(bean.getName()).isEqualTo(MODIFIED_NAME);
// but a newly retrieved bean should have the default name
ScopedTestBean bean2 = (ScopedTestBean) context.getBean("request");
assertThat(bean2.getName()).isEqualTo(DEFAULT_NAME);
}
@Test
public void requestScopeWithProxiedInterfaces() {
RequestContextHolder.setRequestAttributes(oldRequestAttributes);
ApplicationContext context = createContext(INTERFACES);
IScopedTestBean bean = (IScopedTestBean) context.getBean("request");
// should be dynamic proxy, implementing both interfaces
assertThat(AopUtils.isJdkDynamicProxy(bean)).isTrue();
boolean condition = bean instanceof AnotherScopeTestInterface;
assertThat(condition).isTrue();
assertThat(bean.getName()).isEqualTo(DEFAULT_NAME);
bean.setName(MODIFIED_NAME);
RequestContextHolder.setRequestAttributes(newRequestAttributes);
// this is a proxy so it should be reset to default
assertThat(bean.getName()).isEqualTo(DEFAULT_NAME);
RequestContextHolder.setRequestAttributes(oldRequestAttributes);
assertThat(bean.getName()).isEqualTo(MODIFIED_NAME);
}
@Test
public void requestScopeWithProxiedTargetClass() {
RequestContextHolder.setRequestAttributes(oldRequestAttributes);
ApplicationContext context = createContext(TARGET_CLASS);
IScopedTestBean bean = (IScopedTestBean) context.getBean("request");
// should be a class-based proxy
assertThat(AopUtils.isCglibProxy(bean)).isTrue();
boolean condition = bean instanceof RequestScopedTestBean;
assertThat(condition).isTrue();
assertThat(bean.getName()).isEqualTo(DEFAULT_NAME);
bean.setName(MODIFIED_NAME);
RequestContextHolder.setRequestAttributes(newRequestAttributes);
// this is a proxy so it should be reset to default
assertThat(bean.getName()).isEqualTo(DEFAULT_NAME);
RequestContextHolder.setRequestAttributes(oldRequestAttributes);
assertThat(bean.getName()).isEqualTo(MODIFIED_NAME);
}
@Test
public void sessionScopeWithNoProxy() {
RequestContextHolder.setRequestAttributes(oldRequestAttributesWithSession);
ApplicationContext context = createContext(NO);
ScopedTestBean bean = (ScopedTestBean) context.getBean("session");
// should not be a proxy
assertThat(AopUtils.isAopProxy(bean)).isFalse();
assertThat(bean.getName()).isEqualTo(DEFAULT_NAME);
bean.setName(MODIFIED_NAME);
RequestContextHolder.setRequestAttributes(newRequestAttributesWithSession);
// not a proxy so this should not have changed
assertThat(bean.getName()).isEqualTo(MODIFIED_NAME);
// but a newly retrieved bean should have the default name
ScopedTestBean bean2 = (ScopedTestBean) context.getBean("session");
assertThat(bean2.getName()).isEqualTo(DEFAULT_NAME);
}
@Test
public void sessionScopeWithProxiedInterfaces() {
RequestContextHolder.setRequestAttributes(oldRequestAttributesWithSession);
ApplicationContext context = createContext(INTERFACES);
IScopedTestBean bean = (IScopedTestBean) context.getBean("session");
// should be dynamic proxy, implementing both interfaces
assertThat(AopUtils.isJdkDynamicProxy(bean)).isTrue();
boolean condition = bean instanceof AnotherScopeTestInterface;
assertThat(condition).isTrue();
assertThat(bean.getName()).isEqualTo(DEFAULT_NAME);
bean.setName(MODIFIED_NAME);
RequestContextHolder.setRequestAttributes(newRequestAttributesWithSession);
// this is a proxy so it should be reset to default
assertThat(bean.getName()).isEqualTo(DEFAULT_NAME);
bean.setName(MODIFIED_NAME);
IScopedTestBean bean2 = (IScopedTestBean) context.getBean("session");
assertThat(bean2.getName()).isEqualTo(MODIFIED_NAME);
bean2.setName(DEFAULT_NAME);
assertThat(bean.getName()).isEqualTo(DEFAULT_NAME);
RequestContextHolder.setRequestAttributes(oldRequestAttributesWithSession);
assertThat(bean.getName()).isEqualTo(MODIFIED_NAME);
}
@Test
public void sessionScopeWithProxiedTargetClass() {
RequestContextHolder.setRequestAttributes(oldRequestAttributesWithSession);
ApplicationContext context = createContext(TARGET_CLASS);
IScopedTestBean bean = (IScopedTestBean) context.getBean("session");
// should be a class-based proxy
assertThat(AopUtils.isCglibProxy(bean)).isTrue();
boolean condition1 = bean instanceof ScopedTestBean;
assertThat(condition1).isTrue();
boolean condition = bean instanceof SessionScopedTestBean;
assertThat(condition).isTrue();
assertThat(bean.getName()).isEqualTo(DEFAULT_NAME);
bean.setName(MODIFIED_NAME);
RequestContextHolder.setRequestAttributes(newRequestAttributesWithSession);
// this is a proxy so it should be reset to default
assertThat(bean.getName()).isEqualTo(DEFAULT_NAME);
bean.setName(MODIFIED_NAME);
IScopedTestBean bean2 = (IScopedTestBean) context.getBean("session");
assertThat(bean2.getName()).isEqualTo(MODIFIED_NAME);
bean2.setName(DEFAULT_NAME);
assertThat(bean.getName()).isEqualTo(DEFAULT_NAME);
RequestContextHolder.setRequestAttributes(oldRequestAttributesWithSession);
assertThat(bean.getName()).isEqualTo(MODIFIED_NAME);
}
private ApplicationContext createContext(ScopedProxyMode scopedProxyMode) {
GenericWebApplicationContext context = new GenericWebApplicationContext();
ClassPathBeanDefinitionScanner scanner = new ClassPathBeanDefinitionScanner(context);
scanner.setIncludeAnnotationConfig(false);
scanner.setBeanNameGenerator((definition, registry) -> definition.getScope());
scanner.setScopedProxyMode(scopedProxyMode);
// Scan twice in order to find errors in the bean definition compatibility check.
scanner.scan(getClass().getPackage().getName());
scanner.scan(getClass().getPackage().getName());
context.refresh();
return context;
}
static interface IScopedTestBean {
String getName();
void setName(String name);
}
static abstract class ScopedTestBean implements IScopedTestBean {
private String name = DEFAULT_NAME;
@Override
public String getName() { return this.name; }
@Override
public void setName(String name) { this.name = name; }
}
@Component
static class SingletonScopedTestBean extends ScopedTestBean {
}
static interface AnotherScopeTestInterface {
}
@Component
@RequestScope(proxyMode = DEFAULT)
static class RequestScopedTestBean extends ScopedTestBean implements AnotherScopeTestInterface {
}
@Component
@SessionScope(proxyMode = DEFAULT)
static class SessionScopedTestBean extends ScopedTestBean implements AnotherScopeTestInterface {
}
}

View File

@@ -0,0 +1,710 @@
/*
* Copyright 2002-2019 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
*
* https://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.core.env;
import java.io.File;
import java.io.IOException;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.beans.factory.xml.XmlBeanDefinitionReader;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.EnvironmentAware;
import org.springframework.context.annotation.AnnotatedBeanDefinitionReader;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ClassPathBeanDefinitionScanner;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.context.annotation.Profile;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.context.support.FileSystemXmlApplicationContext;
import org.springframework.context.support.GenericApplicationContext;
import org.springframework.context.support.GenericXmlApplicationContext;
import org.springframework.context.support.StaticApplicationContext;
import org.springframework.core.io.ClassPathResource;
import org.springframework.jca.context.ResourceAdapterApplicationContext;
import org.springframework.jca.support.SimpleBootstrapContext;
import org.springframework.jca.work.SimpleTaskWorkManager;
import org.springframework.mock.env.MockEnvironment;
import org.springframework.mock.env.MockPropertySource;
import org.springframework.mock.web.MockServletConfig;
import org.springframework.mock.web.MockServletContext;
import org.springframework.util.FileCopyUtils;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.context.support.AbstractRefreshableWebApplicationContext;
import org.springframework.web.context.support.AnnotationConfigWebApplicationContext;
import org.springframework.web.context.support.GenericWebApplicationContext;
import org.springframework.web.context.support.StandardServletEnvironment;
import org.springframework.web.context.support.StaticWebApplicationContext;
import org.springframework.web.context.support.XmlWebApplicationContext;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
import static org.springframework.beans.factory.support.BeanDefinitionBuilder.rootBeanDefinition;
import static org.springframework.context.ConfigurableApplicationContext.ENVIRONMENT_BEAN_NAME;
import static org.springframework.core.env.EnvironmentSystemIntegrationTests.Constants.DERIVED_DEV_BEAN_NAME;
import static org.springframework.core.env.EnvironmentSystemIntegrationTests.Constants.DERIVED_DEV_ENV_NAME;
import static org.springframework.core.env.EnvironmentSystemIntegrationTests.Constants.DEV_BEAN_NAME;
import static org.springframework.core.env.EnvironmentSystemIntegrationTests.Constants.DEV_ENV_NAME;
import static org.springframework.core.env.EnvironmentSystemIntegrationTests.Constants.ENVIRONMENT_AWARE_BEAN_NAME;
import static org.springframework.core.env.EnvironmentSystemIntegrationTests.Constants.PROD_BEAN_NAME;
import static org.springframework.core.env.EnvironmentSystemIntegrationTests.Constants.PROD_ENV_NAME;
import static org.springframework.core.env.EnvironmentSystemIntegrationTests.Constants.TRANSITIVE_BEAN_NAME;
import static org.springframework.core.env.EnvironmentSystemIntegrationTests.Constants.XML_PATH;
/**
* System integration tests for container support of the {@link Environment} API.
*
* <p>
* Tests all existing BeanFactory and ApplicationContext implementations to ensure that:
* <ul>
* <li>a standard environment object is always present
* <li>a custom environment object can be set and retrieved against the factory/context
* <li>the {@link EnvironmentAware} interface is respected
* <li>the environment object is registered with the container as a singleton bean (if an
* ApplicationContext)
* <li>bean definition files (if any, and whether XML or @Configuration) are registered
* conditionally based on environment metadata
* </ul>
*
* @author Chris Beams
* @author Sam Brannen
* @see org.springframework.context.support.EnvironmentIntegrationTests
*/
@SuppressWarnings("resource")
public class EnvironmentSystemIntegrationTests {
private final ConfigurableEnvironment prodEnv = new StandardEnvironment();
private final ConfigurableEnvironment devEnv = new StandardEnvironment();
private final ConfigurableEnvironment prodWebEnv = new StandardServletEnvironment();
@BeforeEach
public void setUp() {
prodEnv.setActiveProfiles(PROD_ENV_NAME);
devEnv.setActiveProfiles(DEV_ENV_NAME);
prodWebEnv.setActiveProfiles(PROD_ENV_NAME);
}
@Test
public void genericApplicationContext_standardEnv() {
ConfigurableApplicationContext ctx = new GenericApplicationContext(newBeanFactoryWithEnvironmentAwareBean());
ctx.refresh();
assertHasStandardEnvironment(ctx);
assertEnvironmentBeanRegistered(ctx);
assertEnvironmentAwareInvoked(ctx, ctx.getEnvironment());
}
@Test
public void genericApplicationContext_customEnv() {
GenericApplicationContext ctx = new GenericApplicationContext(newBeanFactoryWithEnvironmentAwareBean());
ctx.setEnvironment(prodEnv);
ctx.refresh();
assertHasEnvironment(ctx, prodEnv);
assertEnvironmentBeanRegistered(ctx);
assertEnvironmentAwareInvoked(ctx, prodEnv);
}
@Test
public void xmlBeanDefinitionReader_inheritsEnvironmentFromEnvironmentCapableBDR() {
GenericApplicationContext ctx = new GenericApplicationContext();
ctx.setEnvironment(prodEnv);
new XmlBeanDefinitionReader(ctx).loadBeanDefinitions(XML_PATH);
ctx.refresh();
assertThat(ctx.containsBean(DEV_BEAN_NAME)).isFalse();
assertThat(ctx.containsBean(PROD_BEAN_NAME)).isTrue();
}
@Test
public void annotatedBeanDefinitionReader_inheritsEnvironmentFromEnvironmentCapableBDR() {
GenericApplicationContext ctx = new GenericApplicationContext();
ctx.setEnvironment(prodEnv);
new AnnotatedBeanDefinitionReader(ctx).register(Config.class);
ctx.refresh();
assertThat(ctx.containsBean(DEV_BEAN_NAME)).isFalse();
assertThat(ctx.containsBean(PROD_BEAN_NAME)).isTrue();
}
@Test
public void classPathBeanDefinitionScanner_inheritsEnvironmentFromEnvironmentCapableBDR_scanProfileAnnotatedConfigClasses() {
// it's actually ConfigurationClassPostProcessor's Environment that gets the job done here.
GenericApplicationContext ctx = new GenericApplicationContext();
ctx.setEnvironment(prodEnv);
ClassPathBeanDefinitionScanner scanner = new ClassPathBeanDefinitionScanner(ctx);
scanner.scan("org.springframework.core.env.scan1");
ctx.refresh();
assertThat(ctx.containsBean(DEV_BEAN_NAME)).isFalse();
assertThat(ctx.containsBean(PROD_BEAN_NAME)).isTrue();
}
@Test
public void classPathBeanDefinitionScanner_inheritsEnvironmentFromEnvironmentCapableBDR_scanProfileAnnotatedComponents() {
GenericApplicationContext ctx = new GenericApplicationContext();
ctx.setEnvironment(prodEnv);
ClassPathBeanDefinitionScanner scanner = new ClassPathBeanDefinitionScanner(ctx);
scanner.scan("org.springframework.core.env.scan2");
ctx.refresh();
assertThat(scanner.getEnvironment()).isEqualTo(ctx.getEnvironment());
assertThat(ctx.containsBean(DEV_BEAN_NAME)).isFalse();
assertThat(ctx.containsBean(PROD_BEAN_NAME)).isTrue();
}
@Test
public void genericXmlApplicationContext() {
GenericXmlApplicationContext ctx = new GenericXmlApplicationContext();
assertHasStandardEnvironment(ctx);
ctx.setEnvironment(prodEnv);
ctx.load(XML_PATH);
ctx.refresh();
assertHasEnvironment(ctx, prodEnv);
assertEnvironmentBeanRegistered(ctx);
assertEnvironmentAwareInvoked(ctx, prodEnv);
assertThat(ctx.containsBean(DEV_BEAN_NAME)).isFalse();
assertThat(ctx.containsBean(PROD_BEAN_NAME)).isTrue();
}
@Test
public void classPathXmlApplicationContext() {
ConfigurableApplicationContext ctx = new ClassPathXmlApplicationContext(XML_PATH);
ctx.setEnvironment(prodEnv);
ctx.refresh();
assertEnvironmentBeanRegistered(ctx);
assertHasEnvironment(ctx, prodEnv);
assertEnvironmentAwareInvoked(ctx, ctx.getEnvironment());
assertThat(ctx.containsBean(DEV_BEAN_NAME)).isFalse();
assertThat(ctx.containsBean(PROD_BEAN_NAME)).isTrue();
}
@Test
public void fileSystemXmlApplicationContext() throws IOException {
ClassPathResource xml = new ClassPathResource(XML_PATH);
File tmpFile = File.createTempFile("test", "xml");
FileCopyUtils.copy(xml.getFile(), tmpFile);
// strange - FSXAC strips leading '/' unless prefixed with 'file:'
ConfigurableApplicationContext ctx =
new FileSystemXmlApplicationContext(new String[] {"file:" + tmpFile.getPath()}, false);
ctx.setEnvironment(prodEnv);
ctx.refresh();
assertEnvironmentBeanRegistered(ctx);
assertHasEnvironment(ctx, prodEnv);
assertEnvironmentAwareInvoked(ctx, ctx.getEnvironment());
assertThat(ctx.containsBean(DEV_BEAN_NAME)).isFalse();
assertThat(ctx.containsBean(PROD_BEAN_NAME)).isTrue();
}
@Test
public void annotationConfigApplicationContext_withPojos() {
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
assertHasStandardEnvironment(ctx);
ctx.setEnvironment(prodEnv);
ctx.register(EnvironmentAwareBean.class);
ctx.refresh();
assertEnvironmentAwareInvoked(ctx, prodEnv);
}
@Test
public void annotationConfigApplicationContext_withProdEnvAndProdConfigClass() {
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
assertHasStandardEnvironment(ctx);
ctx.setEnvironment(prodEnv);
ctx.register(ProdConfig.class);
ctx.refresh();
assertThat(ctx.containsBean(PROD_BEAN_NAME)).isTrue();
}
@Test
public void annotationConfigApplicationContext_withProdEnvAndDevConfigClass() {
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
assertHasStandardEnvironment(ctx);
ctx.setEnvironment(prodEnv);
ctx.register(DevConfig.class);
ctx.refresh();
assertThat(ctx.containsBean(DEV_BEAN_NAME)).isFalse();
assertThat(ctx.containsBean(TRANSITIVE_BEAN_NAME)).isFalse();
}
@Test
public void annotationConfigApplicationContext_withDevEnvAndDevConfigClass() {
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
assertHasStandardEnvironment(ctx);
ctx.setEnvironment(devEnv);
ctx.register(DevConfig.class);
ctx.refresh();
assertThat(ctx.containsBean(DEV_BEAN_NAME)).isTrue();
assertThat(ctx.containsBean(TRANSITIVE_BEAN_NAME)).isTrue();
}
@Test
public void annotationConfigApplicationContext_withImportedConfigClasses() {
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
assertHasStandardEnvironment(ctx);
ctx.setEnvironment(prodEnv);
ctx.register(Config.class);
ctx.refresh();
assertEnvironmentAwareInvoked(ctx, prodEnv);
assertThat(ctx.containsBean(PROD_BEAN_NAME)).isTrue();
assertThat(ctx.containsBean(DEV_BEAN_NAME)).isFalse();
assertThat(ctx.containsBean(TRANSITIVE_BEAN_NAME)).isFalse();
}
@Test
public void mostSpecificDerivedClassDrivesEnvironment_withDerivedDevEnvAndDerivedDevConfigClass() {
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
StandardEnvironment derivedDevEnv = new StandardEnvironment();
derivedDevEnv.setActiveProfiles(DERIVED_DEV_ENV_NAME);
ctx.setEnvironment(derivedDevEnv);
ctx.register(DerivedDevConfig.class);
ctx.refresh();
assertThat(ctx.containsBean(DEV_BEAN_NAME)).isTrue();
assertThat(ctx.containsBean(DERIVED_DEV_BEAN_NAME)).isTrue();
assertThat(ctx.containsBean(TRANSITIVE_BEAN_NAME)).isTrue();
}
@Test
public void mostSpecificDerivedClassDrivesEnvironment_withDevEnvAndDerivedDevConfigClass() {
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
ctx.setEnvironment(devEnv);
ctx.register(DerivedDevConfig.class);
ctx.refresh();
assertThat(ctx.containsBean(DEV_BEAN_NAME)).isFalse();
assertThat(ctx.containsBean(DERIVED_DEV_BEAN_NAME)).isFalse();
assertThat(ctx.containsBean(TRANSITIVE_BEAN_NAME)).isFalse();
}
@Test
public void annotationConfigApplicationContext_withProfileExpressionMatchOr() {
testProfileExpression(true, "p3");
}
@Test
public void annotationConfigApplicationContext_withProfileExpressionMatchAnd() {
testProfileExpression(true, "p1", "p2");
}
@Test
public void annotationConfigApplicationContext_withProfileExpressionNoMatchAnd() {
testProfileExpression(false, "p1");
}
@Test
public void annotationConfigApplicationContext_withProfileExpressionNoMatchNone() {
testProfileExpression(false, "p4");
}
private void testProfileExpression(boolean expected, String... activeProfiles) {
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
StandardEnvironment environment = new StandardEnvironment();
environment.setActiveProfiles(activeProfiles);
ctx.setEnvironment(environment);
ctx.register(ProfileExpressionConfig.class);
ctx.refresh();
assertThat(ctx.containsBean("expressionBean")).isEqualTo(expected);
}
@Test
public void webApplicationContext() {
GenericWebApplicationContext ctx = new GenericWebApplicationContext(newBeanFactoryWithEnvironmentAwareBean());
assertHasStandardServletEnvironment(ctx);
ctx.setEnvironment(prodWebEnv);
ctx.refresh();
assertHasEnvironment(ctx, prodWebEnv);
assertEnvironmentBeanRegistered(ctx);
assertEnvironmentAwareInvoked(ctx, prodWebEnv);
}
@Test
public void xmlWebApplicationContext() {
AbstractRefreshableWebApplicationContext ctx = new XmlWebApplicationContext();
ctx.setConfigLocation("classpath:" + XML_PATH);
ctx.setEnvironment(prodWebEnv);
ctx.refresh();
assertHasEnvironment(ctx, prodWebEnv);
assertEnvironmentBeanRegistered(ctx);
assertEnvironmentAwareInvoked(ctx, prodWebEnv);
assertThat(ctx.containsBean(DEV_BEAN_NAME)).isFalse();
assertThat(ctx.containsBean(PROD_BEAN_NAME)).isTrue();
}
@Test
public void staticApplicationContext() {
StaticApplicationContext ctx = new StaticApplicationContext();
assertHasStandardEnvironment(ctx);
registerEnvironmentBeanDefinition(ctx);
ctx.setEnvironment(prodEnv);
ctx.refresh();
assertHasEnvironment(ctx, prodEnv);
assertEnvironmentBeanRegistered(ctx);
assertEnvironmentAwareInvoked(ctx, prodEnv);
}
@Test
public void staticWebApplicationContext() {
StaticWebApplicationContext ctx = new StaticWebApplicationContext();
assertHasStandardServletEnvironment(ctx);
registerEnvironmentBeanDefinition(ctx);
ctx.setEnvironment(prodWebEnv);
ctx.refresh();
assertHasEnvironment(ctx, prodWebEnv);
assertEnvironmentBeanRegistered(ctx);
assertEnvironmentAwareInvoked(ctx, prodWebEnv);
}
@Test
public void annotationConfigWebApplicationContext() {
AnnotationConfigWebApplicationContext ctx = new AnnotationConfigWebApplicationContext();
ctx.setEnvironment(prodWebEnv);
ctx.setConfigLocation(EnvironmentAwareBean.class.getName());
ctx.refresh();
assertHasEnvironment(ctx, prodWebEnv);
assertEnvironmentBeanRegistered(ctx);
assertEnvironmentAwareInvoked(ctx, prodWebEnv);
}
@Test
public void registerServletParamPropertySources_AbstractRefreshableWebApplicationContext() {
MockServletContext servletContext = new MockServletContext();
servletContext.addInitParameter("pCommon", "pCommonContextValue");
servletContext.addInitParameter("pContext1", "pContext1Value");
MockServletConfig servletConfig = new MockServletConfig(servletContext);
servletConfig.addInitParameter("pCommon", "pCommonConfigValue");
servletConfig.addInitParameter("pConfig1", "pConfig1Value");
AbstractRefreshableWebApplicationContext ctx = new AnnotationConfigWebApplicationContext();
ctx.setConfigLocation(EnvironmentAwareBean.class.getName());
ctx.setServletConfig(servletConfig);
ctx.refresh();
ConfigurableEnvironment environment = ctx.getEnvironment();
assertThat(environment).isInstanceOf(StandardServletEnvironment.class);
MutablePropertySources propertySources = environment.getPropertySources();
assertThat(propertySources.contains(StandardServletEnvironment.SERVLET_CONTEXT_PROPERTY_SOURCE_NAME)).isTrue();
assertThat(propertySources.contains(StandardServletEnvironment.SERVLET_CONFIG_PROPERTY_SOURCE_NAME)).isTrue();
// ServletConfig gets precedence
assertThat(environment.getProperty("pCommon")).isEqualTo("pCommonConfigValue");
assertThat(propertySources.precedenceOf(PropertySource.named(StandardServletEnvironment.SERVLET_CONFIG_PROPERTY_SOURCE_NAME)))
.isLessThan(propertySources.precedenceOf(PropertySource.named(StandardServletEnvironment.SERVLET_CONTEXT_PROPERTY_SOURCE_NAME)));
// but all params are available
assertThat(environment.getProperty("pContext1")).isEqualTo("pContext1Value");
assertThat(environment.getProperty("pConfig1")).isEqualTo("pConfig1Value");
// Servlet* PropertySources have precedence over System* PropertySources
assertThat(propertySources.precedenceOf(PropertySource.named(StandardServletEnvironment.SERVLET_CONFIG_PROPERTY_SOURCE_NAME)))
.isLessThan(propertySources.precedenceOf(PropertySource.named(StandardEnvironment.SYSTEM_PROPERTIES_PROPERTY_SOURCE_NAME)));
// Replace system properties with a mock property source for convenience
MockPropertySource mockSystemProperties = new MockPropertySource(StandardEnvironment.SYSTEM_PROPERTIES_PROPERTY_SOURCE_NAME);
mockSystemProperties.setProperty("pCommon", "pCommonSysPropsValue");
mockSystemProperties.setProperty("pSysProps1", "pSysProps1Value");
propertySources.replace(StandardEnvironment.SYSTEM_PROPERTIES_PROPERTY_SOURCE_NAME, mockSystemProperties);
// assert that servletconfig params resolve with higher precedence than sysprops
assertThat(environment.getProperty("pCommon")).isEqualTo("pCommonConfigValue");
assertThat(environment.getProperty("pSysProps1")).isEqualTo("pSysProps1Value");
}
@Test
public void registerServletParamPropertySources_GenericWebApplicationContext() {
MockServletContext servletContext = new MockServletContext();
servletContext.addInitParameter("pCommon", "pCommonContextValue");
servletContext.addInitParameter("pContext1", "pContext1Value");
GenericWebApplicationContext ctx = new GenericWebApplicationContext();
ctx.setServletContext(servletContext);
ctx.refresh();
ConfigurableEnvironment environment = ctx.getEnvironment();
assertThat(environment).isInstanceOf(StandardServletEnvironment.class);
MutablePropertySources propertySources = environment.getPropertySources();
assertThat(propertySources.contains(StandardServletEnvironment.SERVLET_CONTEXT_PROPERTY_SOURCE_NAME)).isTrue();
// ServletContext params are available
assertThat(environment.getProperty("pCommon")).isEqualTo("pCommonContextValue");
assertThat(environment.getProperty("pContext1")).isEqualTo("pContext1Value");
// Servlet* PropertySources have precedence over System* PropertySources
assertThat(propertySources.precedenceOf(PropertySource.named(StandardServletEnvironment.SERVLET_CONTEXT_PROPERTY_SOURCE_NAME)))
.isLessThan(propertySources.precedenceOf(PropertySource.named(StandardEnvironment.SYSTEM_PROPERTIES_PROPERTY_SOURCE_NAME)));
// Replace system properties with a mock property source for convenience
MockPropertySource mockSystemProperties = new MockPropertySource(StandardEnvironment.SYSTEM_PROPERTIES_PROPERTY_SOURCE_NAME);
mockSystemProperties.setProperty("pCommon", "pCommonSysPropsValue");
mockSystemProperties.setProperty("pSysProps1", "pSysProps1Value");
propertySources.replace(StandardEnvironment.SYSTEM_PROPERTIES_PROPERTY_SOURCE_NAME, mockSystemProperties);
// assert that servletcontext init params resolve with higher precedence than sysprops
assertThat(environment.getProperty("pCommon")).isEqualTo("pCommonContextValue");
assertThat(environment.getProperty("pSysProps1")).isEqualTo("pSysProps1Value");
}
@Test
public void registerServletParamPropertySources_StaticWebApplicationContext() {
MockServletContext servletContext = new MockServletContext();
servletContext.addInitParameter("pCommon", "pCommonContextValue");
servletContext.addInitParameter("pContext1", "pContext1Value");
MockServletConfig servletConfig = new MockServletConfig(servletContext);
servletConfig.addInitParameter("pCommon", "pCommonConfigValue");
servletConfig.addInitParameter("pConfig1", "pConfig1Value");
StaticWebApplicationContext ctx = new StaticWebApplicationContext();
ctx.setServletConfig(servletConfig);
ctx.refresh();
ConfigurableEnvironment environment = ctx.getEnvironment();
MutablePropertySources propertySources = environment.getPropertySources();
assertThat(propertySources.contains(StandardServletEnvironment.SERVLET_CONTEXT_PROPERTY_SOURCE_NAME)).isTrue();
assertThat(propertySources.contains(StandardServletEnvironment.SERVLET_CONFIG_PROPERTY_SOURCE_NAME)).isTrue();
// ServletConfig gets precedence
assertThat(environment.getProperty("pCommon")).isEqualTo("pCommonConfigValue");
assertThat(propertySources.precedenceOf(PropertySource.named(StandardServletEnvironment.SERVLET_CONFIG_PROPERTY_SOURCE_NAME)))
.isLessThan(propertySources.precedenceOf(PropertySource.named(StandardServletEnvironment.SERVLET_CONTEXT_PROPERTY_SOURCE_NAME)));
// but all params are available
assertThat(environment.getProperty("pContext1")).isEqualTo("pContext1Value");
assertThat(environment.getProperty("pConfig1")).isEqualTo("pConfig1Value");
// Servlet* PropertySources have precedence over System* PropertySources
assertThat(propertySources.precedenceOf(PropertySource.named(StandardServletEnvironment.SERVLET_CONFIG_PROPERTY_SOURCE_NAME)))
.isLessThan(propertySources.precedenceOf(PropertySource.named(StandardEnvironment.SYSTEM_PROPERTIES_PROPERTY_SOURCE_NAME)));
// Replace system properties with a mock property source for convenience
MockPropertySource mockSystemProperties = new MockPropertySource(StandardEnvironment.SYSTEM_PROPERTIES_PROPERTY_SOURCE_NAME);
mockSystemProperties.setProperty("pCommon", "pCommonSysPropsValue");
mockSystemProperties.setProperty("pSysProps1", "pSysProps1Value");
propertySources.replace(StandardEnvironment.SYSTEM_PROPERTIES_PROPERTY_SOURCE_NAME, mockSystemProperties);
// assert that servletconfig params resolve with higher precedence than sysprops
assertThat(environment.getProperty("pCommon")).isEqualTo("pCommonConfigValue");
assertThat(environment.getProperty("pSysProps1")).isEqualTo("pSysProps1Value");
}
@Test
public void resourceAdapterApplicationContext() {
ResourceAdapterApplicationContext ctx = new ResourceAdapterApplicationContext(new SimpleBootstrapContext(new SimpleTaskWorkManager()));
assertHasStandardEnvironment(ctx);
registerEnvironmentBeanDefinition(ctx);
ctx.setEnvironment(prodEnv);
ctx.refresh();
assertHasEnvironment(ctx, prodEnv);
assertEnvironmentBeanRegistered(ctx);
assertEnvironmentAwareInvoked(ctx, prodEnv);
}
@Test
public void abstractApplicationContextValidatesRequiredPropertiesOnRefresh() {
{
ConfigurableApplicationContext ctx = new AnnotationConfigApplicationContext();
ctx.refresh();
}
{
ConfigurableApplicationContext ctx = new AnnotationConfigApplicationContext();
ctx.getEnvironment().setRequiredProperties("foo", "bar");
assertThatExceptionOfType(MissingRequiredPropertiesException.class).isThrownBy(
ctx::refresh);
}
{
ConfigurableApplicationContext ctx = new AnnotationConfigApplicationContext();
ctx.getEnvironment().setRequiredProperties("foo");
ctx.setEnvironment(new MockEnvironment().withProperty("foo", "fooValue"));
ctx.refresh(); // should succeed
}
}
private DefaultListableBeanFactory newBeanFactoryWithEnvironmentAwareBean() {
DefaultListableBeanFactory bf = new DefaultListableBeanFactory();
registerEnvironmentBeanDefinition(bf);
return bf;
}
private void registerEnvironmentBeanDefinition(BeanDefinitionRegistry registry) {
registry.registerBeanDefinition(ENVIRONMENT_AWARE_BEAN_NAME,
rootBeanDefinition(EnvironmentAwareBean.class).getBeanDefinition());
}
private void assertEnvironmentBeanRegistered(
ConfigurableApplicationContext ctx) {
// ensure environment is registered as a bean
assertThat(ctx.containsBean(ENVIRONMENT_BEAN_NAME)).isTrue();
}
private void assertHasStandardEnvironment(ApplicationContext ctx) {
Environment defaultEnv = ctx.getEnvironment();
assertThat(defaultEnv).isNotNull();
assertThat(defaultEnv).isInstanceOf(StandardEnvironment.class);
}
private void assertHasStandardServletEnvironment(WebApplicationContext ctx) {
// ensure a default servlet environment exists
Environment defaultEnv = ctx.getEnvironment();
assertThat(defaultEnv).isNotNull();
assertThat(defaultEnv).isInstanceOf(StandardServletEnvironment.class);
}
private void assertHasEnvironment(ApplicationContext ctx, Environment expectedEnv) {
// ensure the custom environment took
Environment actualEnv = ctx.getEnvironment();
assertThat(actualEnv).isNotNull();
assertThat(actualEnv).isEqualTo(expectedEnv);
// ensure environment is registered as a bean
assertThat(ctx.containsBean(ENVIRONMENT_BEAN_NAME)).isTrue();
}
private void assertEnvironmentAwareInvoked(ConfigurableApplicationContext ctx, Environment expectedEnv) {
assertThat(ctx.getBean(EnvironmentAwareBean.class).environment).isEqualTo(expectedEnv);
}
private static class EnvironmentAwareBean implements EnvironmentAware {
public Environment environment;
@Override
public void setEnvironment(Environment environment) {
this.environment = environment;
}
}
/**
* Mirrors the structure of beans and environment-specific config files in
* EnvironmentSystemIntegrationTests-context.xml
*/
@Configuration
@Import({DevConfig.class, ProdConfig.class})
static class Config {
@Bean
public EnvironmentAwareBean envAwareBean() {
return new EnvironmentAwareBean();
}
}
@Profile(DEV_ENV_NAME)
@Configuration
@Import(TransitiveConfig.class)
static class DevConfig {
@Bean
public Object devBean() {
return new Object();
}
}
@Profile(PROD_ENV_NAME)
@Configuration
static class ProdConfig {
@Bean
public Object prodBean() {
return new Object();
}
}
@Configuration
static class TransitiveConfig {
@Bean
public Object transitiveBean() {
return new Object();
}
}
@Profile(DERIVED_DEV_ENV_NAME)
@Configuration
static class DerivedDevConfig extends DevConfig {
@Bean
public Object derivedDevBean() {
return new Object();
}
}
@Profile("(p1 & p2) | p3")
@Configuration
static class ProfileExpressionConfig {
@Bean
public Object expressionBean() {
return new Object();
}
}
/**
* Constants used both locally and in scan* sub-packages
*/
public static class Constants {
public static final String XML_PATH = "org/springframework/core/env/EnvironmentSystemIntegrationTests-context.xml";
public static final String ENVIRONMENT_AWARE_BEAN_NAME = "envAwareBean";
public static final String PROD_BEAN_NAME = "prodBean";
public static final String DEV_BEAN_NAME = "devBean";
public static final String DERIVED_DEV_BEAN_NAME = "derivedDevBean";
public static final String TRANSITIVE_BEAN_NAME = "transitiveBean";
public static final String PROD_ENV_NAME = "prod";
public static final String DEV_ENV_NAME = "dev";
public static final String DERIVED_DEV_ENV_NAME = "derivedDev";
}
}

View File

@@ -0,0 +1,40 @@
/*
* Copyright 2002-2019 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
*
* https://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.core.env;
import org.junit.jupiter.api.Test;
import org.springframework.context.support.GenericApplicationContext;
import static org.springframework.beans.factory.support.BeanDefinitionBuilder.rootBeanDefinition;
public class PropertyPlaceholderConfigurerEnvironmentIntegrationTests {
@Test
@SuppressWarnings("deprecation")
public void test() {
GenericApplicationContext ctx = new GenericApplicationContext();
ctx.registerBeanDefinition("ppc",
rootBeanDefinition(org.springframework.beans.factory.config.PropertyPlaceholderConfigurer.class)
.addPropertyValue("searchSystemEnvironment", false)
.getBeanDefinition());
ctx.refresh();
ctx.getBean("ppc");
ctx.close();
}
}

View File

@@ -0,0 +1,26 @@
/*
* Copyright 2002-2019 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
*
* https://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.core.env.scan1;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
@Configuration
@Import({ DevConfig.class, ProdConfig.class })
class Config {
}

View File

@@ -0,0 +1,32 @@
/*
* Copyright 2002-2019 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
*
* https://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.core.env.scan1;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Profile;
@Profile(org.springframework.core.env.EnvironmentSystemIntegrationTests.Constants.DEV_ENV_NAME)
@Configuration
class DevConfig {
@Bean
public Object devBean() {
return new Object();
}
}

View File

@@ -0,0 +1,32 @@
/*
* Copyright 2002-2019 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
*
* https://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.core.env.scan1;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Profile;
@Profile(org.springframework.core.env.EnvironmentSystemIntegrationTests.Constants.PROD_ENV_NAME)
@Configuration
class ProdConfig {
@Bean
public Object prodBean() {
return new Object();
}
}

View File

@@ -0,0 +1,25 @@
/*
* Copyright 2002-2019 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
*
* https://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.core.env.scan2;
import org.springframework.context.annotation.Profile;
import org.springframework.stereotype.Component;
@Profile(org.springframework.core.env.EnvironmentSystemIntegrationTests.Constants.DEV_ENV_NAME)
@Component(org.springframework.core.env.EnvironmentSystemIntegrationTests.Constants.DEV_BEAN_NAME)
class DevBean {
}

View File

@@ -0,0 +1,26 @@
/*
* Copyright 2002-2019 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
*
* https://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.core.env.scan2;
import org.springframework.context.annotation.Profile;
import org.springframework.stereotype.Component;
@Profile(org.springframework.core.env.EnvironmentSystemIntegrationTests.Constants.PROD_ENV_NAME)
@Component(org.springframework.core.env.EnvironmentSystemIntegrationTests.Constants.PROD_BEAN_NAME)
class ProdBean {
}

View File

@@ -0,0 +1,111 @@
/*
* Copyright 2002-2012 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
*
* https://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.expression.spel.support;
import java.beans.PropertyEditor;
import org.springframework.beans.BeansException;
import org.springframework.beans.SimpleTypeConverter;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.core.convert.ConversionService;
import org.springframework.core.convert.TypeDescriptor;
import org.springframework.core.convert.support.DefaultConversionService;
import org.springframework.expression.TypeConverter;
/**
* Copied from Spring Integration for purposes of reproducing
* {@link Spr7538Tests}.
*/
class BeanFactoryTypeConverter implements TypeConverter, BeanFactoryAware {
private SimpleTypeConverter delegate = new SimpleTypeConverter();
private static ConversionService defaultConversionService;
private ConversionService conversionService;
public BeanFactoryTypeConverter() {
synchronized (this) {
if (defaultConversionService == null) {
defaultConversionService = new DefaultConversionService();
}
}
this.conversionService = defaultConversionService;
}
public BeanFactoryTypeConverter(ConversionService conversionService) {
this.conversionService = conversionService;
}
public void setConversionService(ConversionService conversionService) {
this.conversionService = conversionService;
}
@Override
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
if (beanFactory instanceof ConfigurableBeanFactory) {
Object typeConverter = ((ConfigurableBeanFactory) beanFactory).getTypeConverter();
if (typeConverter instanceof SimpleTypeConverter) {
delegate = (SimpleTypeConverter) typeConverter;
}
}
}
public boolean canConvert(Class<?> sourceType, Class<?> targetType) {
if (conversionService.canConvert(sourceType, targetType)) {
return true;
}
if (!String.class.isAssignableFrom(sourceType) && !String.class.isAssignableFrom(targetType)) {
// PropertyEditor cannot convert non-Strings
return false;
}
if (!String.class.isAssignableFrom(sourceType)) {
return delegate.findCustomEditor(sourceType, null) != null || delegate.getDefaultEditor(sourceType) != null;
}
return delegate.findCustomEditor(targetType, null) != null || delegate.getDefaultEditor(targetType) != null;
}
@Override
public boolean canConvert(TypeDescriptor sourceTypeDescriptor, TypeDescriptor targetTypeDescriptor) {
if (conversionService.canConvert(sourceTypeDescriptor, targetTypeDescriptor)) {
return true;
}
// TODO: what does this mean? This method is not used in SpEL so probably ignorable?
Class<?> sourceType = sourceTypeDescriptor.getObjectType();
Class<?> targetType = targetTypeDescriptor.getObjectType();
return canConvert(sourceType, targetType);
}
@Override
public Object convertValue(Object value, TypeDescriptor sourceType, TypeDescriptor targetType) {
if (targetType.getType() == Void.class || targetType.getType() == Void.TYPE) {
return null;
}
if (conversionService.canConvert(sourceType, targetType)) {
return conversionService.convert(value, sourceType, targetType);
}
if (!String.class.isAssignableFrom(sourceType.getType())) {
PropertyEditor editor = delegate.findCustomEditor(sourceType.getType(), null);
editor.setValue(value);
return editor.getAsText();
}
return delegate.convertIfNecessary(value, targetType.getType());
}
}

View File

@@ -0,0 +1,66 @@
/*
* Copyright 2002-2019 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
*
* https://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.expression.spel.support;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
import org.springframework.core.MethodParameter;
import org.springframework.core.convert.TypeDescriptor;
import org.springframework.expression.MethodExecutor;
public class Spr7538Tests {
@Disabled @Test
public void repro() throws Exception {
AlwaysTrueReleaseStrategy target = new AlwaysTrueReleaseStrategy();
BeanFactoryTypeConverter converter = new BeanFactoryTypeConverter();
StandardEvaluationContext context = new StandardEvaluationContext();
context.setTypeConverter(converter);
List<Foo> arguments = new ArrayList<>();
// !!!! With the below line commented you'll get NPE. Uncomment and everything is OK!
//arguments.add(new Foo());
List<TypeDescriptor> paramDescriptors = new ArrayList<>();
Method method = AlwaysTrueReleaseStrategy.class.getMethod("checkCompleteness", List.class);
paramDescriptors.add(new TypeDescriptor(new MethodParameter(method, 0)));
List<TypeDescriptor> argumentTypes = new ArrayList<>();
argumentTypes.add(TypeDescriptor.forObject(arguments));
ReflectiveMethodResolver resolver = new ReflectiveMethodResolver();
MethodExecutor executor = resolver.resolve(context, target, "checkCompleteness", argumentTypes);
Object result = executor.execute(context, target, arguments);
System.out.println("Result: " + result);
}
public static class AlwaysTrueReleaseStrategy {
public boolean checkCompleteness(List<Foo> messages) {
return true;
}
}
public static class Foo{}
}

View File

@@ -0,0 +1,250 @@
/*
* Copyright 2002-2019 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
*
* https://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.scheduling.annotation;
import java.util.concurrent.atomic.AtomicInteger;
import org.aspectj.lang.annotation.Aspect;
import org.junit.jupiter.api.Test;
import org.springframework.aop.aspectj.annotation.AnnotationAwareAspectJAutoProxyCreator;
import org.springframework.aop.support.AopUtils;
import org.springframework.beans.factory.BeanCreationException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.dao.annotation.PersistenceExceptionTranslationPostProcessor;
import org.springframework.dao.support.PersistenceExceptionTranslator;
import org.springframework.stereotype.Repository;
import org.springframework.tests.EnabledForTestGroups;
import org.springframework.tests.transaction.CallCountingTransactionManager;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.annotation.EnableTransactionManagement;
import org.springframework.transaction.annotation.Transactional;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
import static org.mockito.Mockito.mock;
import static org.springframework.tests.TestGroup.PERFORMANCE;
/**
* Integration tests cornering bug SPR-8651, which revealed that @Scheduled methods may
* not work well with beans that have already been proxied for other reasons such
* as @Transactional or @Async processing.
*
* @author Chris Beams
* @author Juergen Hoeller
* @since 3.1
*/
@SuppressWarnings("resource")
@EnabledForTestGroups(PERFORMANCE)
class ScheduledAndTransactionalAnnotationIntegrationTests {
@Test
void failsWhenJdkProxyAndScheduledMethodNotPresentOnInterface() {
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
ctx.register(Config.class, JdkProxyTxConfig.class, RepoConfigA.class);
assertThatExceptionOfType(BeanCreationException.class)
.isThrownBy(ctx::refresh)
.satisfies(ex -> assertThat(ex.getRootCause()).isInstanceOf(IllegalStateException.class));
}
@Test
void succeedsWhenSubclassProxyAndScheduledMethodNotPresentOnInterface() throws InterruptedException {
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
ctx.register(Config.class, SubclassProxyTxConfig.class, RepoConfigA.class);
ctx.refresh();
Thread.sleep(100); // allow @Scheduled method to be called several times
MyRepository repository = ctx.getBean(MyRepository.class);
CallCountingTransactionManager txManager = ctx.getBean(CallCountingTransactionManager.class);
assertThat(AopUtils.isCglibProxy(repository)).isEqualTo(true);
assertThat(repository.getInvocationCount()).isGreaterThan(0);
assertThat(txManager.commits).isGreaterThan(0);
}
@Test
void succeedsWhenJdkProxyAndScheduledMethodIsPresentOnInterface() throws InterruptedException {
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
ctx.register(Config.class, JdkProxyTxConfig.class, RepoConfigB.class);
ctx.refresh();
Thread.sleep(100); // allow @Scheduled method to be called several times
MyRepositoryWithScheduledMethod repository = ctx.getBean(MyRepositoryWithScheduledMethod.class);
CallCountingTransactionManager txManager = ctx.getBean(CallCountingTransactionManager.class);
assertThat(AopUtils.isJdkDynamicProxy(repository)).isTrue();
assertThat(repository.getInvocationCount()).isGreaterThan(0);
assertThat(txManager.commits).isGreaterThan(0);
}
@Test
void withAspectConfig() throws InterruptedException {
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
ctx.register(AspectConfig.class, MyRepositoryWithScheduledMethodImpl.class);
ctx.refresh();
Thread.sleep(100); // allow @Scheduled method to be called several times
MyRepositoryWithScheduledMethod repository = ctx.getBean(MyRepositoryWithScheduledMethod.class);
assertThat(AopUtils.isCglibProxy(repository)).isTrue();
assertThat(repository.getInvocationCount()).isGreaterThan(0);
}
@Configuration
@EnableTransactionManagement
static class JdkProxyTxConfig {
}
@Configuration
@EnableTransactionManagement(proxyTargetClass = true)
static class SubclassProxyTxConfig {
}
@Configuration
static class RepoConfigA {
@Bean
MyRepository repository() {
return new MyRepositoryImpl();
}
}
@Configuration
static class RepoConfigB {
@Bean
MyRepositoryWithScheduledMethod repository() {
return new MyRepositoryWithScheduledMethodImpl();
}
}
@Configuration
@EnableScheduling
static class Config {
@Bean
PlatformTransactionManager txManager() {
return new CallCountingTransactionManager();
}
@Bean
PersistenceExceptionTranslator peTranslator() {
return mock(PersistenceExceptionTranslator.class);
}
@Bean
static PersistenceExceptionTranslationPostProcessor peTranslationPostProcessor() {
return new PersistenceExceptionTranslationPostProcessor();
}
}
@Configuration
@EnableScheduling
static class AspectConfig {
@Bean
static AnnotationAwareAspectJAutoProxyCreator autoProxyCreator() {
AnnotationAwareAspectJAutoProxyCreator apc = new AnnotationAwareAspectJAutoProxyCreator();
apc.setProxyTargetClass(true);
return apc;
}
@Bean
static MyAspect myAspect() {
return new MyAspect();
}
}
@Aspect
public static class MyAspect {
private final AtomicInteger count = new AtomicInteger(0);
@org.aspectj.lang.annotation.Before("execution(* scheduled())")
public void checkTransaction() {
this.count.incrementAndGet();
}
}
public interface MyRepository {
int getInvocationCount();
}
@Repository
static class MyRepositoryImpl implements MyRepository {
private final AtomicInteger count = new AtomicInteger(0);
@Transactional
@Scheduled(fixedDelay = 5)
public void scheduled() {
this.count.incrementAndGet();
}
@Override
public int getInvocationCount() {
return this.count.get();
}
}
public interface MyRepositoryWithScheduledMethod {
int getInvocationCount();
void scheduled();
}
@Repository
static class MyRepositoryWithScheduledMethodImpl implements MyRepositoryWithScheduledMethod {
private final AtomicInteger count = new AtomicInteger(0);
@Autowired(required = false)
private MyAspect myAspect;
@Override
@Transactional
@Scheduled(fixedDelay = 5)
public void scheduled() {
this.count.incrementAndGet();
}
@Override
public int getInvocationCount() {
if (this.myAspect != null) {
assertThat(this.myAspect.count.get()).isEqualTo(this.count.get());
}
return this.count.get();
}
}
}

View File

@@ -0,0 +1,343 @@
/*
* Copyright 2002-2019 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
*
* https://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.transaction.annotation;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import javax.sql.DataSource;
import org.junit.jupiter.api.Test;
import org.springframework.aop.framework.Advised;
import org.springframework.aop.support.AopUtils;
import org.springframework.cache.Cache;
import org.springframework.cache.CacheManager;
import org.springframework.cache.concurrent.ConcurrentMapCache;
import org.springframework.cache.support.SimpleCacheManager;
import org.springframework.context.annotation.AdviceMode;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.ImportResource;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseBuilder;
import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseType;
import org.springframework.stereotype.Repository;
import org.springframework.tests.transaction.CallCountingTransactionManager;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.interceptor.BeanFactoryTransactionAttributeSourceAdvisor;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
/**
* Integration tests for the @EnableTransactionManagement annotation.
*
* @author Chris Beams
* @author Sam Brannen
* @since 3.1
*/
@SuppressWarnings("resource")
public class EnableTransactionManagementIntegrationTests {
@Test
public void repositoryIsNotTxProxy() {
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
ctx.register(Config.class);
ctx.refresh();
assertThat(isTxProxy(ctx.getBean(FooRepository.class))).isFalse();
}
@Test
public void repositoryIsTxProxy_withDefaultTxManagerName() {
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
ctx.register(Config.class, DefaultTxManagerNameConfig.class);
ctx.refresh();
assertTxProxying(ctx);
}
@Test
public void repositoryIsTxProxy_withCustomTxManagerName() {
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
ctx.register(Config.class, CustomTxManagerNameConfig.class);
ctx.refresh();
assertTxProxying(ctx);
}
@Test
public void repositoryIsTxProxy_withNonConventionalTxManagerName_fallsBackToByTypeLookup() {
assertTxProxying(new AnnotationConfigApplicationContext(Config.class, NonConventionalTxManagerNameConfig.class));
}
@Test
public void repositoryIsClassBasedTxProxy() {
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
ctx.register(Config.class, ProxyTargetClassTxConfig.class);
ctx.refresh();
assertTxProxying(ctx);
assertThat(AopUtils.isCglibProxy(ctx.getBean(FooRepository.class))).isTrue();
}
@Test
public void repositoryUsesAspectJAdviceMode() {
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
ctx.register(Config.class, AspectJTxConfig.class);
// this test is a bit fragile, but gets the job done, proving that an
// attempt was made to look up the AJ aspect. It's due to classpath issues
// in .integration-tests that it's not found.
assertThatExceptionOfType(Exception.class).isThrownBy(
ctx::refresh)
.withMessageContaining("AspectJJtaTransactionManagementConfiguration");
}
@Test
public void implicitTxManager() {
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
ctx.register(ImplicitTxManagerConfig.class);
ctx.refresh();
FooRepository fooRepository = ctx.getBean(FooRepository.class);
fooRepository.findAll();
CallCountingTransactionManager txManager = ctx.getBean(CallCountingTransactionManager.class);
assertThat(txManager.begun).isEqualTo(1);
assertThat(txManager.commits).isEqualTo(1);
assertThat(txManager.rollbacks).isEqualTo(0);
}
@Test
public void explicitTxManager() {
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
ctx.register(ExplicitTxManagerConfig.class);
ctx.refresh();
FooRepository fooRepository = ctx.getBean(FooRepository.class);
fooRepository.findAll();
CallCountingTransactionManager txManager1 = ctx.getBean("txManager1", CallCountingTransactionManager.class);
assertThat(txManager1.begun).isEqualTo(1);
assertThat(txManager1.commits).isEqualTo(1);
assertThat(txManager1.rollbacks).isEqualTo(0);
CallCountingTransactionManager txManager2 = ctx.getBean("txManager2", CallCountingTransactionManager.class);
assertThat(txManager2.begun).isEqualTo(0);
assertThat(txManager2.commits).isEqualTo(0);
assertThat(txManager2.rollbacks).isEqualTo(0);
}
@Test
public void apcEscalation() {
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
ctx.register(EnableTxAndCachingConfig.class);
ctx.refresh();
}
private void assertTxProxying(AnnotationConfigApplicationContext ctx) {
FooRepository repo = ctx.getBean(FooRepository.class);
assertThat(isTxProxy(repo)).isTrue();
// trigger a transaction
repo.findAll();
}
private boolean isTxProxy(FooRepository repo) {
if (!AopUtils.isAopProxy(repo)) {
return false;
}
return Arrays.stream(((Advised) repo).getAdvisors())
.anyMatch(BeanFactoryTransactionAttributeSourceAdvisor.class::isInstance);
}
@Configuration
@EnableTransactionManagement
@ImportResource("org/springframework/transaction/annotation/enable-caching.xml")
static class EnableTxAndCachingConfig {
@Bean
public PlatformTransactionManager txManager() {
return new CallCountingTransactionManager();
}
@Bean
public FooRepository fooRepository() {
return new DummyFooRepository();
}
@Bean
public CacheManager cacheManager() {
SimpleCacheManager mgr = new SimpleCacheManager();
ArrayList<Cache> caches = new ArrayList<>();
caches.add(new ConcurrentMapCache(""));
mgr.setCaches(caches);
return mgr;
}
}
@Configuration
@EnableTransactionManagement
static class ImplicitTxManagerConfig {
@Bean
public PlatformTransactionManager txManager() {
return new CallCountingTransactionManager();
}
@Bean
public FooRepository fooRepository() {
return new DummyFooRepository();
}
}
@Configuration
@EnableTransactionManagement
static class ExplicitTxManagerConfig implements TransactionManagementConfigurer {
@Bean
public PlatformTransactionManager txManager1() {
return new CallCountingTransactionManager();
}
@Bean
public PlatformTransactionManager txManager2() {
return new CallCountingTransactionManager();
}
@Override
public PlatformTransactionManager annotationDrivenTransactionManager() {
return txManager1();
}
@Bean
public FooRepository fooRepository() {
return new DummyFooRepository();
}
}
@Configuration
@EnableTransactionManagement
static class DefaultTxManagerNameConfig {
@Bean
PlatformTransactionManager transactionManager(DataSource dataSource) {
return new DataSourceTransactionManager(dataSource);
}
}
@Configuration
@EnableTransactionManagement
static class CustomTxManagerNameConfig {
@Bean
PlatformTransactionManager txManager(DataSource dataSource) {
return new DataSourceTransactionManager(dataSource);
}
}
@Configuration
@EnableTransactionManagement
static class NonConventionalTxManagerNameConfig {
@Bean
PlatformTransactionManager txManager(DataSource dataSource) {
return new DataSourceTransactionManager(dataSource);
}
}
@Configuration
@EnableTransactionManagement(proxyTargetClass=true)
static class ProxyTargetClassTxConfig {
@Bean
PlatformTransactionManager transactionManager(DataSource dataSource) {
return new DataSourceTransactionManager(dataSource);
}
}
@Configuration
@EnableTransactionManagement(mode=AdviceMode.ASPECTJ)
static class AspectJTxConfig {
@Bean
PlatformTransactionManager transactionManager(DataSource dataSource) {
return new DataSourceTransactionManager(dataSource);
}
}
@Configuration
static class Config {
@Bean
FooRepository fooRepository() {
JdbcFooRepository repos = new JdbcFooRepository();
repos.setDataSource(dataSource());
return repos;
}
@Bean
DataSource dataSource() {
return new EmbeddedDatabaseBuilder()
.setType(EmbeddedDatabaseType.HSQL)
.build();
}
}
interface FooRepository {
List<Object> findAll();
}
@Repository
static class JdbcFooRepository implements FooRepository {
public void setDataSource(DataSource dataSource) {
}
@Override
@Transactional
public List<Object> findAll() {
return Collections.emptyList();
}
}
@Repository
static class DummyFooRepository implements FooRepository {
@Override
@Transactional
public List<Object> findAll() {
return Collections.emptyList();
}
}
}

View File

@@ -0,0 +1,129 @@
/*
* Copyright 2002-2019 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
*
* https://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.transaction.annotation;
import org.junit.jupiter.api.Test;
import org.springframework.aop.support.AopUtils;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Configuration;
import static org.assertj.core.api.Assertions.assertThat;
/**
* Tests proving that regardless the proxy strategy used (JDK interface-based vs. CGLIB
* subclass-based), discovery of advice-oriented annotations is consistent.
*
* For example, Spring's @Transactional may be declared at the interface or class level,
* and whether interface or subclass proxies are used, the @Transactional annotation must
* be discovered in a consistent fashion.
*
* @author Chris Beams
*/
@SuppressWarnings("resource")
public class ProxyAnnotationDiscoveryTests {
@Test
public void annotatedServiceWithoutInterface_PTC_true() {
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
ctx.register(PTCTrue.class, AnnotatedServiceWithoutInterface.class);
ctx.refresh();
AnnotatedServiceWithoutInterface s = ctx.getBean(AnnotatedServiceWithoutInterface.class);
assertThat(AopUtils.isCglibProxy(s)).isTrue();
assertThat(s).isInstanceOf(AnnotatedServiceWithoutInterface.class);
}
@Test
public void annotatedServiceWithoutInterface_PTC_false() {
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
ctx.register(PTCFalse.class, AnnotatedServiceWithoutInterface.class);
ctx.refresh();
AnnotatedServiceWithoutInterface s = ctx.getBean(AnnotatedServiceWithoutInterface.class);
assertThat(AopUtils.isCglibProxy(s)).isTrue();
assertThat(s).isInstanceOf(AnnotatedServiceWithoutInterface.class);
}
@Test
public void nonAnnotatedService_PTC_true() {
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
ctx.register(PTCTrue.class, AnnotatedServiceImpl.class);
ctx.refresh();
NonAnnotatedService s = ctx.getBean(NonAnnotatedService.class);
assertThat(AopUtils.isCglibProxy(s)).isTrue();
assertThat(s).isInstanceOf(AnnotatedServiceImpl.class);
}
@Test
public void nonAnnotatedService_PTC_false() {
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
ctx.register(PTCFalse.class, AnnotatedServiceImpl.class);
ctx.refresh();
NonAnnotatedService s = ctx.getBean(NonAnnotatedService.class);
assertThat(AopUtils.isJdkDynamicProxy(s)).isTrue();
assertThat(s).isNotInstanceOf(AnnotatedServiceImpl.class);
}
@Test
public void annotatedService_PTC_true() {
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
ctx.register(PTCTrue.class, NonAnnotatedServiceImpl.class);
ctx.refresh();
AnnotatedService s = ctx.getBean(AnnotatedService.class);
assertThat(AopUtils.isCglibProxy(s)).isTrue();
assertThat(s).isInstanceOf(NonAnnotatedServiceImpl.class);
}
@Test
public void annotatedService_PTC_false() {
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
ctx.register(PTCFalse.class, NonAnnotatedServiceImpl.class);
ctx.refresh();
AnnotatedService s = ctx.getBean(AnnotatedService.class);
assertThat(AopUtils.isJdkDynamicProxy(s)).isTrue();
assertThat(s).isNotInstanceOf(NonAnnotatedServiceImpl.class);
}
}
@Configuration
@EnableTransactionManagement(proxyTargetClass=false)
class PTCFalse { }
@Configuration
@EnableTransactionManagement(proxyTargetClass=true)
class PTCTrue { }
interface NonAnnotatedService {
void m();
}
interface AnnotatedService {
@Transactional void m();
}
class NonAnnotatedServiceImpl implements AnnotatedService {
@Override
public void m() { }
}
class AnnotatedServiceImpl implements NonAnnotatedService {
@Override
@Transactional public void m() { }
}
class AnnotatedServiceWithoutInterface {
@Transactional public void m() { }
}