Skip to content

Commit efe3a35

Browse files
committed
Introduce AOP testing utilities
This commit introduces support in the spring-test module for obtaining a reference to the underlying target object hidden behind one or more proxies. Specifically this commit introduces AopTestUtils with two methods: - getTargetObject(Object) - getUltimateTargetObject(Object) Issue: SPR-13005
1 parent 1f54ba0 commit efe3a35

File tree

2 files changed

+234
-0
lines changed

2 files changed

+234
-0
lines changed
Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
/*
2+
* Copyright 2002-2015 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.test.util;
18+
19+
import org.springframework.aop.framework.Advised;
20+
import org.springframework.aop.support.AopUtils;
21+
22+
/**
23+
* {@code AopTestUtils} is a collection of AOP-related utility methods for
24+
* use in unit and integration testing scenarios.
25+
*
26+
* <p>For Spring's core AOP utilities, see
27+
* {@link org.springframework.aop.support.AopUtils AopUtils} and
28+
* {@link org.springframework.aop.framework.AopProxyUtils AopProxyUtils}.
29+
*
30+
* @author Sam Brannen
31+
* @since 4.2
32+
* @see org.springframework.aop.support.AopUtils
33+
* @see org.springframework.aop.framework.AopProxyUtils
34+
*/
35+
public class AopTestUtils {
36+
37+
/**
38+
* Get the <em>target</em> object of the supplied {@code candidate} object.
39+
* <p>If the supplied {@code candidate} is a Spring
40+
* {@linkplain AopUtils#isAopProxy proxy}, the target of the proxy will
41+
* be returned; otherwise, the {@code candidate} will be returned
42+
* <em>as is</em>.
43+
*
44+
* @param candidate the instance to check (potentially a Spring AOP proxy)
45+
* @return the target object or the {@code candidate}; never {@code null}
46+
* @throws IllegalStateException if an error occurs while unwrapping a proxy
47+
* @see Advised#getTargetSource()
48+
* @see #getUltimateTargetObject
49+
*/
50+
@SuppressWarnings("unchecked")
51+
public static <T> T getTargetObject(Object candidate) {
52+
try {
53+
if (AopUtils.isAopProxy(candidate) && (candidate instanceof Advised)) {
54+
return (T) ((Advised) candidate).getTargetSource().getTarget();
55+
}
56+
}
57+
catch (Exception e) {
58+
throw new IllegalStateException("Failed to unwrap proxied object.", e);
59+
}
60+
61+
// else
62+
return (T) candidate;
63+
}
64+
65+
/**
66+
* Get the ultimate <em>target</em> object of the supplied {@code candidate}
67+
* object, unwrapping not only a top-level proxy but also any number of
68+
* nested proxies.
69+
* <p>If the supplied {@code candidate} is a Spring
70+
* {@linkplain AopUtils#isAopProxy proxy}, the ultimate target of all
71+
* nested proxies will be returned; otherwise, the {@code candidate}
72+
* will be returned <em>as is</em>.
73+
*
74+
* @param candidate the instance to check (potentially a Spring AOP proxy)
75+
* @return the ultimate target object or the {@code candidate}; never
76+
* {@code null}
77+
* @throws IllegalStateException if an error occurs while unwrapping a proxy
78+
* @see Advised#getTargetSource()
79+
* @see org.springframework.aop.framework.AopProxyUtils#ultimateTargetClass
80+
*/
81+
@SuppressWarnings("unchecked")
82+
public static <T> T getUltimateTargetObject(Object candidate) {
83+
try {
84+
if (AopUtils.isAopProxy(candidate) && (candidate instanceof Advised)) {
85+
return (T) getUltimateTargetObject(((Advised) candidate).getTargetSource().getTarget());
86+
}
87+
}
88+
catch (Exception e) {
89+
throw new IllegalStateException("Failed to unwrap proxied object.", e);
90+
}
91+
92+
// else
93+
return (T) candidate;
94+
}
95+
96+
}
Lines changed: 138 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,138 @@
1+
/*
2+
* Copyright 2002-2015 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.test.util;
18+
19+
import org.junit.Test;
20+
21+
import org.springframework.aop.framework.ProxyFactory;
22+
import org.springframework.aop.support.AopUtils;
23+
24+
import static org.hamcrest.CoreMatchers.*;
25+
import static org.junit.Assert.*;
26+
import static org.springframework.test.util.AopTestUtils.*;
27+
28+
/**
29+
* Unit tests for {@link AopTestUtils}.
30+
*
31+
* @author Sam Brannen
32+
* @since 4.2
33+
*/
34+
public class AopTestUtilsTests {
35+
36+
private final FooImpl foo = new FooImpl();
37+
38+
39+
@Test
40+
public void getTargetObjectForNonProxiedObject() {
41+
Foo target = getTargetObject(foo);
42+
assertSame(foo, target);
43+
}
44+
45+
@Test
46+
public void getTargetObjectWrappedInSingleJdkDynamicProxy() {
47+
Foo target = getTargetObject(jdkProxy(foo));
48+
assertSame(foo, target);
49+
}
50+
51+
@Test
52+
public void getTargetObjectWrappedInSingleCglibProxy() {
53+
Foo target = getTargetObject(cglibProxy(foo));
54+
assertSame(foo, target);
55+
}
56+
57+
@Test
58+
public void getTargetObjectWrappedInDoubleJdkDynamicProxy() {
59+
Foo target = getTargetObject(jdkProxy(jdkProxy(foo)));
60+
assertNotSame(foo, target);
61+
}
62+
63+
@Test
64+
public void getTargetObjectWrappedInDoubleCglibProxy() {
65+
Foo target = getTargetObject(cglibProxy(cglibProxy(foo)));
66+
assertNotSame(foo, target);
67+
}
68+
69+
@Test
70+
public void getUltimateTargetObjectForNonProxiedObject() {
71+
Foo target = getUltimateTargetObject(foo);
72+
assertSame(foo, target);
73+
}
74+
75+
@Test
76+
public void getUltimateTargetObjectWrappedInSingleJdkDynamicProxy() {
77+
Foo target = getUltimateTargetObject(jdkProxy(foo));
78+
assertSame(foo, target);
79+
}
80+
81+
@Test
82+
public void getUltimateTargetObjectWrappedInSingleCglibProxy() {
83+
Foo target = getUltimateTargetObject(cglibProxy(foo));
84+
assertSame(foo, target);
85+
}
86+
87+
@Test
88+
public void getUltimateTargetObjectWrappedInDoubleJdkDynamicProxy() {
89+
Foo target = getUltimateTargetObject(jdkProxy(jdkProxy(foo)));
90+
assertSame(foo, target);
91+
}
92+
93+
@Test
94+
public void getUltimateTargetObjectWrappedInDoubleCglibProxy() {
95+
Foo target = getUltimateTargetObject(cglibProxy(cglibProxy(foo)));
96+
assertSame(foo, target);
97+
}
98+
99+
@Test
100+
public void getUltimateTargetObjectWrappedInCglibProxyWrappedInJdkDynamicProxy() {
101+
Foo target = getUltimateTargetObject(jdkProxy(cglibProxy(foo)));
102+
assertSame(foo, target);
103+
}
104+
105+
@Test
106+
public void getUltimateTargetObjectWrappedInCglibProxyWrappedInDoubleJdkDynamicProxy() {
107+
Foo target = getUltimateTargetObject(jdkProxy(jdkProxy(cglibProxy(foo))));
108+
assertSame(foo, target);
109+
}
110+
111+
private Foo jdkProxy(Foo foo) {
112+
ProxyFactory pf = new ProxyFactory();
113+
pf.setTarget(foo);
114+
pf.addInterface(Foo.class);
115+
Foo proxy = (Foo) pf.getProxy();
116+
assertTrue("Proxy is a JDK dynamic proxy", AopUtils.isJdkDynamicProxy(proxy));
117+
assertThat(proxy, instanceOf(Foo.class));
118+
return proxy;
119+
}
120+
121+
private Foo cglibProxy(Foo foo) {
122+
ProxyFactory pf = new ProxyFactory();
123+
pf.setTarget(foo);
124+
pf.setProxyTargetClass(true);
125+
Foo proxy = (Foo) pf.getProxy();
126+
assertTrue("Proxy is a CGLIB proxy", AopUtils.isCglibProxy(proxy));
127+
assertThat(proxy, instanceOf(FooImpl.class));
128+
return proxy;
129+
}
130+
131+
132+
static interface Foo {
133+
}
134+
135+
static class FooImpl implements Foo {
136+
}
137+
138+
}

0 commit comments

Comments
 (0)