Update proxyTargetClass-related Javadoc; add tests
There was some question about whether enabling subclass proxies via proxyTargetClass / proxy-target-class settings would break annotation- based demarcation of joinpoints due to inability to discover those annotations in various scenarios. The provided tests prove that in any conceivable case, these annotations (@Transactional, at least) are discovered in a consistent fashion, meaning that switching proxy strategies should be transparent to the application and honor intended annotation semantics.
This commit is contained in:
parent
124662189e
commit
1533822b0a
|
|
@ -125,9 +125,12 @@ public @interface EnableAsync {
|
|||
* to standard Java interface-based proxies. The default is {@code false}. <strong>
|
||||
* Applicable only if {@link #mode()} is set to {@link AdviceMode#PROXY}</strong>.
|
||||
*
|
||||
* <p>Note that subclass-based proxies require the async {@link #annotation()}
|
||||
* to be defined on the concrete class. Annotations in interfaces will
|
||||
* not work in that case (they will rather only work with interface-based proxies)!
|
||||
* <p>Note that setting this attribute to {@code true} will affect <em>all</em>
|
||||
* Spring-managed beans requiring proxying, not just those marked with {@code @Async}.
|
||||
* For example, other beans marked with Spring's {@code @Transactional} annotation
|
||||
* will be upgraded to subclass proxying at the same time. This approach has no
|
||||
* negative impact in practice unless one is explicitly expecting one type of proxy
|
||||
* vs another, e.g. in tests.
|
||||
*/
|
||||
boolean proxyTargetClass() default false;
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,127 @@
|
|||
/*
|
||||
* Copyright 2002-2011 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.transaction.annotation;
|
||||
|
||||
import static org.hamcrest.CoreMatchers.instanceOf;
|
||||
import static org.hamcrest.CoreMatchers.not;
|
||||
import static org.junit.Assert.assertThat;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.springframework.aop.support.AopUtils;
|
||||
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
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);
|
||||
assertTrue("expected a subclass proxy", AopUtils.isCglibProxy(s));
|
||||
assertThat(s, instanceOf(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);
|
||||
assertTrue("expected a subclass proxy", AopUtils.isCglibProxy(s));
|
||||
assertThat(s, instanceOf(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);
|
||||
assertTrue("expected a subclass proxy", AopUtils.isCglibProxy(s));
|
||||
assertThat(s, instanceOf(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);
|
||||
assertTrue("expected a jdk proxy", AopUtils.isJdkDynamicProxy(s));
|
||||
assertThat(s, not(instanceOf(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);
|
||||
assertTrue("expected a subclass proxy", AopUtils.isCglibProxy(s));
|
||||
assertThat(s, instanceOf(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);
|
||||
assertTrue("expected a jdk proxy", AopUtils.isJdkDynamicProxy(s));
|
||||
assertThat(s, not(instanceOf(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 {
|
||||
public void m() { }
|
||||
}
|
||||
|
||||
class AnnotatedServiceImpl implements NonAnnotatedService {
|
||||
@Transactional public void m() { }
|
||||
}
|
||||
|
||||
class AnnotatedServiceWithoutInterface {
|
||||
@Transactional public void m() { }
|
||||
}
|
||||
|
|
@ -146,9 +146,12 @@ public @interface EnableTransactionManagement {
|
|||
* {@code false}. <strong>Applicable only if {@link #mode()} is set to
|
||||
* {@link AdviceMode#PROXY}</strong>.
|
||||
*
|
||||
* <p>Note that subclass-based proxies require the {@link Transactional @Transactional}
|
||||
* to be defined on the concrete class. Annotations in interfaces will
|
||||
* not work in that case (they will rather only work with interface-based proxies)!
|
||||
* <p>Note that setting this attribute to {@code true} will affect <em>all</em>
|
||||
* Spring-managed beans requiring proxying, not just those marked with
|
||||
* {@code @Transactional}. For example, other beans marked with Spring's
|
||||
* {@code @Async} annotation will be upgraded to subclass proxying at the same
|
||||
* time. This approach has no negative impact in practice unless one is explicitly
|
||||
* expecting one type of proxy vs another, e.g. in tests.
|
||||
*/
|
||||
boolean proxyTargetClass() default false;
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue