📄 abstractaopproxytests.java
字号:
/*
* Copyright 2002-2004 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.aop.framework;
import java.lang.reflect.Method;
import java.lang.reflect.UndeclaredThrowableException;
import java.util.HashMap;
import java.util.List;
import javax.servlet.ServletException;
import javax.transaction.TransactionRequiredException;
import junit.framework.TestCase;
import org.aopalliance.aop.AspectException;
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import org.springframework.aop.Advisor;
import org.springframework.aop.AfterReturningAdvice;
import org.springframework.aop.framework.adapter.ThrowsAdviceInterceptorTests;
import org.springframework.aop.interceptor.NopInterceptor;
import org.springframework.aop.support.DefaultIntroductionAdvisor;
import org.springframework.aop.support.DefaultPointcutAdvisor;
import org.springframework.aop.support.DynamicMethodMatcherPointcutAdvisor;
import org.springframework.aop.support.StaticMethodMatcherPointcutAdvisor;
import org.springframework.aop.target.HotSwappableTargetSource;
import org.springframework.beans.IOther;
import org.springframework.beans.ITestBean;
import org.springframework.beans.TestBean;
/**
* @author Rod Johnson
* @author Juergen Hoeller
* @since 13-Mar-2003
* @version $Id: AbstractAopProxyTests.java,v 1.27 2004/04/14 18:55:17 johnsonr Exp $
*/
public abstract class AbstractAopProxyTests extends TestCase {
protected MockTargetSource mockTargetSource = new MockTargetSource();
public AbstractAopProxyTests(String arg0) {
super(arg0);
}
/**
* Make a clean target source available if code wants to use it.
* The target must be set. Verification will be automatic in tearDown
* to ensure that it was used appropriately by code.
* @see junit.framework.TestCase#setUp()
*/
protected void setUp() {
mockTargetSource.reset();
}
protected void tearDown() {
mockTargetSource.verify();
}
/**
* Set in CGLIB or JDK mode
* @param as
*/
protected abstract Object createProxy(AdvisedSupport as);
protected abstract AopProxy createAopProxy(AdvisedSupport as);
/**
* Is a target always required?
* @return
*/
protected boolean requiresTarget() {
return false;
}
public void testNoInterceptorsAndNoTarget() {
AdvisedSupport pc =
new AdvisedSupport(new Class[] { ITestBean.class });
// Add no interceptors
try {
AopProxy aop = createAopProxy(pc);
aop.getProxy();
fail("Shouldn't allow no interceptors");
} catch (AopConfigException ex) {
// Ok
}
}
private static class CheckMethodInvocationIsSameInAndOutInterceptor implements MethodInterceptor {
public Object invoke(MethodInvocation mi) throws Throwable {
Method m = mi.getMethod();
Object retval = mi.proceed();
assertEquals("Method invocation has same method on way back", m, mi.getMethod());
return retval;
}
}
/**
* ExposeInvocation must be set to true
*/
private static class CheckMethodInvocationViaThreadLocalIsSameInAndOutInterceptor implements MethodInterceptor {
public Object invoke(MethodInvocation mi) throws Throwable {
String task = "get invocation on way IN";
try {
MethodInvocation current = ExposeInvocationInterceptor.currentInvocation();
assertEquals(mi, current);
Object retval = mi.proceed();
task = "get invocation on way OUT";
assertEquals(current, ExposeInvocationInterceptor.currentInvocation());
return retval;
}
catch (AspectException ex) {
System.err.println(task + " for " + mi.getMethod());
ex.printStackTrace();
//fail("Can't find invocation: " + ex);
throw ex;
}
}
}
/**
* Same thing for a proxy.
* Only works when exposeProxy is set to true.
* Checks that the proxy is the same on the way in and out.
*/
public static class ProxyMatcherInterceptor implements MethodInterceptor {
public Object invoke(MethodInvocation mi) throws Throwable {
Object proxy = AopContext.currentProxy();
Object ret = mi.proceed();
// TODO why does this cause stack overflow?
//assertEquals(proxy, AopContext.currentProxy());
assertTrue(proxy == AopContext.currentProxy());
return ret;
}
}
/**
* Simple test that if we set values we can get them out again
*
*/
public void testValuesStick() {
int age1 = 33;
int age2 = 37;
String name = "tony";
TestBean target1 = new TestBean();
target1.setAge(age1);
ProxyFactory pf1 = new ProxyFactory(target1);
pf1.addAdvisor(new DefaultPointcutAdvisor(new NopInterceptor()));
pf1.addAdvisor(new DefaultPointcutAdvisor(new TimestampIntroductionInterceptor()));
ITestBean tb = (ITestBean) target1;
assertEquals(age1, tb.getAge());
tb.setAge(age2);
assertEquals(age2, tb.getAge());
assertNull(tb.getName());
tb.setName(name);
assertEquals(name, tb.getName());
}
/**
* Check that the two MethodInvocations necessary are independent and
* don't conflict.
* Check also proxy exposure.
*/
public void testOneAdvisedObjectCallsAnother() {
int age1 = 33;
int age2 = 37;
TestBean target1 = new TestBean();
ProxyFactory pf1 = new ProxyFactory(target1);
// Permit proxy and invocation checkers to get context from AopContext
pf1.setExposeProxy(true);
NopInterceptor di1 = new NopInterceptor();
pf1.addInterceptor(0, di1);
pf1.addInterceptor(1, new ProxyMatcherInterceptor());
pf1.addInterceptor(2, new CheckMethodInvocationIsSameInAndOutInterceptor());
pf1.addInterceptor(1, new CheckMethodInvocationViaThreadLocalIsSameInAndOutInterceptor());
// Must be first
pf1.addInterceptor(0, ExposeInvocationInterceptor.INSTANCE);
ITestBean advised1 = (ITestBean) pf1.getProxy();
advised1.setAge(age1); // = 1 invocation
TestBean target2 = new TestBean();
ProxyFactory pf2 = new ProxyFactory(target2);
pf2.setExposeProxy(true);
NopInterceptor di2 = new NopInterceptor();
pf2.addInterceptor(0, di2);
pf2.addInterceptor(1, new ProxyMatcherInterceptor());
pf2.addInterceptor(2, new CheckMethodInvocationIsSameInAndOutInterceptor());
pf2.addInterceptor(1, new CheckMethodInvocationViaThreadLocalIsSameInAndOutInterceptor());
pf2.addInterceptor(0, ExposeInvocationInterceptor.INSTANCE);
//System.err.println(pf2.toProxyConfigString());
ITestBean advised2 = (ITestBean) createProxy(pf2);
advised2.setAge(age2);
advised1.setSpouse(advised2); // = 2 invocations
assertEquals("Advised one has correct age", age1, advised1.getAge()); // = 3 invocations
assertEquals("Advised two has correct age", age2, advised2.getAge());
// Means extra call on advised 2
assertEquals("Advised one spouse has correct age", age2, advised1.getSpouse().getAge()); // = 4 invocations on 1 and another one on 2
assertEquals("one was invoked correct number of times", 4, di1.getCount());
// Got hit by call to advised1.getSpouse().getAge()
assertEquals("one was invoked correct number of times", 3, di2.getCount());
}
public void testReentrance() {
int age1 = 33;
TestBean target1 = new TestBean();
ProxyFactory pf1 = new ProxyFactory(target1);
NopInterceptor di1 = new NopInterceptor();
pf1.addInterceptor(0, di1);
ITestBean advised1 = (ITestBean) createProxy(pf1);
advised1.setAge(age1); // = 1 invocation
advised1.setSpouse(advised1); // = 2 invocations
assertEquals("one was invoked correct number of times", 2, di1.getCount());
assertEquals("Advised one has correct age", age1, advised1.getAge()); // = 3 invocations
assertEquals("one was invoked correct number of times", 3, di1.getCount());
// = 5 invocations, as reentrant call to spouse is advised also
assertEquals("Advised spouse has correct age", age1, advised1.getSpouse().getAge());
assertEquals("one was invoked correct number of times", 5, di1.getCount());
}
public interface INeedsToSeeProxy {
int getCount();
void incrementViaThis();
void incrementViaProxy();
void increment();
}
public static class NeedsToSeeProxy implements INeedsToSeeProxy {
private int count;
public int getCount() {
return count;
}
public void incrementViaThis() {
this.increment();
}
public void incrementViaProxy() {
INeedsToSeeProxy thisViaProxy = (INeedsToSeeProxy) AopContext.currentProxy();
thisViaProxy.increment();
Advised advised = (Advised) thisViaProxy;
checkAdvised(advised);
}
protected void checkAdvised(Advised advised) {
}
public void increment() {
++count;
}
}
public static class TargetChecker extends NeedsToSeeProxy {
protected void checkAdvised(Advised advised) {
// TODO replace this check: no longer possible
//assertEquals(advised.getTarget(), this);
}
}
public void testTargetCanGetProxy() {
NopInterceptor di = new NopInterceptor();
INeedsToSeeProxy target = new TargetChecker();
ProxyFactory proxyFactory = new ProxyFactory(target);
proxyFactory.setExposeProxy(true);
assertTrue(proxyFactory.getExposeProxy());
proxyFactory.addInterceptor(0, di);
INeedsToSeeProxy proxied = (INeedsToSeeProxy) createProxy(proxyFactory);
assertEquals(0, di.getCount());
assertEquals(0, target.getCount());
proxied.incrementViaThis();
assertEquals("Increment happened", 1, target.getCount());
assertEquals("Only one invocation via AOP as use of this wasn't proxied", 1, di.getCount());
// 1 invocation
assertEquals("Increment happened", 1, proxied.getCount());
proxied.incrementViaProxy(); // 2 invoocations
assertEquals("Increment happened", 2, target.getCount());
assertEquals("3 more invocations via AOP as the first call was reentrant through the proxy", 4, di.getCount());
}
public void testTargetCantGetProxyByDefault() {
NeedsToSeeProxy et = new NeedsToSeeProxy();
ProxyFactory pf1 = new ProxyFactory(et);
assertFalse(pf1.getExposeProxy());
INeedsToSeeProxy proxied = (INeedsToSeeProxy) createProxy(pf1);
try {
proxied.incrementViaProxy();
fail("Should have failed to get proxy as exposeProxy wasn't set to true");
}
catch (AspectException ex) {
// Ok
}
}
public void testContext() throws Throwable {
testContext(true);
}
public void testNoContext() throws Throwable {
testContext(false);
}
/**
* @param context if true, want context
* @throws Throwable
*/
private void testContext(final boolean context) throws Throwable {
final String s = "foo";
// Test return value
MethodInterceptor mi = new MethodInterceptor() {
public Object invoke(MethodInvocation invocation) throws Throwable {
if (!context) {
assertNoInvocationContext();
} else {
assertTrue("have context", ExposeInvocationInterceptor.currentInvocation() != null);
}
return s;
}
};
AdvisedSupport pc = new AdvisedSupport(new Class[] { ITestBean.class });
if (context) {
pc.addInterceptor(ExposeInvocationInterceptor.INSTANCE);
}
pc.addInterceptor(mi);
// Keep CGLIB happy
if (requiresTarget()) {
pc.setTarget(new TestBean());
}
AopProxy aop = createAopProxy(pc);
assertNoInvocationContext();
ITestBean tb = (ITestBean) aop.getProxy();
assertNoInvocationContext();
assertTrue("correct return value", tb.getName() == s);
}
public static class OwnSpouse extends TestBean {
public ITestBean getSpouse() {
return this;
}
}
/**
* Test that the proxy returns itself when the
* target returns <code>this</code>
* @throws Throwable
*/
public void testTargetReturnsThis() throws Throwable {
// Test return value
TestBean raw = new OwnSpouse();
AdvisedSupport pc = new AdvisedSupport(new Class[] { ITestBean.class });
pc.setTarget(raw);
ITestBean tb = (ITestBean) createProxy(pc);
assertTrue("this return is wrapped in proxy", tb.getSpouse() == tb);
}
/*
public void testCanAttach() throws Throwable {
final TrapInterceptor tii = new TrapInvocationInterceptor();
ProxyConfig pc = new ProxyConfigSupport(new Class[] { ITestBean.class }, false);
pc.addInterceptor(tii);
pc.addInterceptor(new MethodInterceptor() {
public Object invoke(MethodInvocation invocation) throws Throwable {
assertTrue("Saw same interceptor", invocation == tii.invocation);
return null;
}
});
AopProxy aop = new AopProxy(pc, new MethodInvocationFactorySupport());
ITestBean tb = (ITestBean) aop.getProxy();
tb.getSpouse();
assertTrue(tii.invocation != null);
// TODO strengthen this
// assertTrue(tii.invocation.getProxy() == tb);
assertTrue(tii.invocation.getThis() == null);
}
*/
public void testDeclaredException() throws Throwable {
final Exception expectedException = new Exception();
// Test return value
MethodInterceptor mi = new MethodInterceptor() {
public Object invoke(MethodInvocation invocation) throws Throwable {
throw expectedException;
}
};
AdvisedSupport pc = new AdvisedSupport(new Class[] { ITestBean.class });
pc.addInterceptor(ExposeInvocationInterceptor.INSTANCE);
pc.addInterceptor(mi);
// We don't care about the object
mockTargetSource.setTarget(new Object());
pc.setTargetSource(mockTargetSource);
AopProxy aop = createAopProxy(pc);
try {
ITestBean tb = (ITestBean) aop.getProxy();
// Note: exception param below isn't used
tb.exceptional(expectedException);
fail("Should have thrown exception raised by interceptor");
}
catch (Exception thrown) {
assertEquals("exception matches", expectedException, thrown);
}
}
/**
* An interceptor throws a checked exception not on the method signature.
* For efficiency, we don't bother unifying java.lang.reflect and
* net.sf.cglib UndeclaredThrowableException
* @throws Throwable
*/
public void testUndeclaredCheckedException() throws Throwable {
final Exception unexpectedException = new Exception();
// Test return value
MethodInterceptor mi = new MethodInterceptor() {
public Object invoke(MethodInvocation invocation) throws Throwable {
throw unexpectedException;
}
};
AdvisedSupport pc = new AdvisedSupport(new Class[] { ITestBean.class });
pc.addInterceptor(ExposeInvocationInterceptor.INSTANCE);
pc.addInterceptor(mi);
// We don't care about the object
pc.setTarget(new TestBean());
AopProxy aop = createAopProxy(pc);
ITestBean tb = (ITestBean) aop.getProxy();
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -