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:
@@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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 {
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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 {
|
||||
}
|
||||
|
||||
}
|
||||
710
integration-tests/src/test/java/org/springframework/core/env/EnvironmentSystemIntegrationTests.java
vendored
Normal file
710
integration-tests/src/test/java/org/springframework/core/env/EnvironmentSystemIntegrationTests.java
vendored
Normal 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";
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
26
integration-tests/src/test/java/org/springframework/core/env/scan1/Config.java
vendored
Normal file
26
integration-tests/src/test/java/org/springframework/core/env/scan1/Config.java
vendored
Normal 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 {
|
||||
|
||||
}
|
||||
32
integration-tests/src/test/java/org/springframework/core/env/scan1/DevConfig.java
vendored
Normal file
32
integration-tests/src/test/java/org/springframework/core/env/scan1/DevConfig.java
vendored
Normal 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();
|
||||
}
|
||||
|
||||
}
|
||||
32
integration-tests/src/test/java/org/springframework/core/env/scan1/ProdConfig.java
vendored
Normal file
32
integration-tests/src/test/java/org/springframework/core/env/scan1/ProdConfig.java
vendored
Normal 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();
|
||||
}
|
||||
|
||||
}
|
||||
25
integration-tests/src/test/java/org/springframework/core/env/scan2/DevBean.java
vendored
Normal file
25
integration-tests/src/test/java/org/springframework/core/env/scan2/DevBean.java
vendored
Normal 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 {
|
||||
}
|
||||
26
integration-tests/src/test/java/org/springframework/core/env/scan2/ProdBean.java
vendored
Normal file
26
integration-tests/src/test/java/org/springframework/core/env/scan2/ProdBean.java
vendored
Normal 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 {
|
||||
|
||||
}
|
||||
@@ -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());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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{}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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() { }
|
||||
}
|
||||
Reference in New Issue
Block a user