Angenommen, Sie haben die folgende unkonventionelle ToStringService-Schnittstelle und ihre Implementierungsklasse ToStringServiceImpl.
ToStringService.java
public interface SimpleService {
void foo();
}
ToStringServiceImpl.java
import org.springframework.stereotype.Service;
@Service
public class SimpleServiceImpl implements SimpleService {
@Override
public void foo() {
System.out.println("foo");
}
@Override
public String toString() {
return this.getClass().getSimpleName();
}
}
Wenn Sie "toString ()" von "SimpleServiceImpl" ausführen, wird natürlich "SimpleServiceImpl" ausgegeben.
Erstellen Sie eine Klasse, die "ProxyConfig" erbt und eine beliebige Klasse als Proxy verwendet. Der Name ist angemessen.
ToStringProxy.java
import java.util.Collections;
import java.util.List;
import java.util.Map;
import org.springframework.aop.Advisor;
import org.springframework.aop.framework.ProxyConfig;
import org.springframework.aop.framework.ProxyFactory;
import org.springframework.util.ClassUtils;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
public class ToStringProxy<T> extends ProxyConfig {
private MultiValueMap<Class<?>, Advisor> advisorMap = new LinkedMultiValueMap<>();
public T apply(T target) {
List<Advisor> advisors = findAdvisors(target.getClass());
return createProxy(target, advisors);
}
public void addAdvisor(Class<?> target, Advisor... advisors) {
for (Advisor advisor : advisors) {
this.advisorMap.add(target, advisor);
}
}
@SuppressWarnings("unchecked")
private T createProxy(T target, List<Advisor> advisors) {
ProxyFactory proxyFactory = new ProxyFactory();
proxyFactory.setTarget(target);
proxyFactory.addAdvisors(advisors);
if (!isProxyTargetClass()) {
proxyFactory.setInterfaces(ClassUtils.getAllInterfaces(target));
}
proxyFactory.setProxyTargetClass(isProxyTargetClass());
proxyFactory.setExposeProxy(isExposeProxy());
proxyFactory.setFrozen(isFrozen());
proxyFactory.setOpaque(isOpaque());
proxyFactory.setOptimize(isOptimize());
return (T) proxyFactory.getProxy();
}
private List<Advisor> findAdvisors(Class<?> targetClass) {
for (Map.Entry<Class<?>, List<Advisor>> entry : advisorMap.entrySet()) {
if (entry.getKey().isAssignableFrom(targetClass)) {
return entry.getValue();
}
}
return Collections.emptyList();
}
}
Erstellen Sie als Nächstes eine Advisor-Klasse, die "SimpleService" als Proxy verwendet.
ToStringAdvisor.java
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import org.springframework.aop.aspectj.AspectJExpressionPointcutAdvisor;
import org.springframework.stereotype.Component;
@Component
public class ToStringAdvisor {
private ToStringProxy<SimpleService> proxy;
public ToStringAdvisor() {
proxy = new ToStringProxy<>();
//Methode toString()Ziel
String expression = "execution(* " + SimpleService.class.getName() + ".toString())";
AspectJExpressionPointcutAdvisor pointcutAdvisor = new AspectJExpressionPointcutAdvisor();
pointcutAdvisor.setAdvice(new ToStringInterceptor());
pointcutAdvisor.setExpression(expression);
proxy.addAdvisor(SimpleService.class, pointcutAdvisor);
}
public SimpleService apply(SimpleService target) {
return proxy.apply(target);
}
private static class ToStringInterceptor implements MethodInterceptor {
private ToStringInterceptor() {}
@Override
public Object invoke(MethodInvocation invocation) throws Throwable {
return "proxied: ".concat(String.valueOf(invocation.proceed()));
}
}
}
Es wird angenommen, dass "proxied: SimpleServiceImpl" ausgegeben wird, wenn "toString ()" von "SimpleServiceImpl" aufgerufen wird.
Testen wir nun, ob der in ToStringInterceptor
geschriebene Prozess aufgerufen wird.
ProxySampleApplicationTests.java
import static org.hamcrest.CoreMatchers.equalTo;
import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
@RunWith(SpringRunner.class)
@SpringBootTest
public class ProxySampleApplicationTests {
@Autowired
SimpleService service;
@Autowired
ToStringAdvisor adviser;
@Test
public void proxiedToStringTest() {
service = adviser.apply(service);
Assert.assertThat(service.toString(), equalTo("proxied: SimpleServiceImpl"));
}
}
Ergebnis ist ……
errorlog.txt
java.lang.AssertionError:
Expected: "proxied: SimpleServiceImpl"
but: was "SimpleServiceImpl"
at org.hamcrest.MatcherAssert.assertThat(MatcherAssert.java:20)
at org.junit.Assert.assertThat(Assert.java:956)
at org.junit.Assert.assertThat(Assert.java:923)
at com.neriudon.example.ProxySampleApplicationTests.proxiedToStringTest(ProxySampleApplicationTests.java:27)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
at org.springframework.test.context.junit4.statements.RunBeforeTestExecutionCallbacks.evaluate(RunBeforeTestExecutionCallbacks.java:73)
at org.springframework.test.context.junit4.statements.RunAfterTestExecutionCallbacks.evaluate(RunAfterTestExecutionCallbacks.java:83)
at org.springframework.test.context.junit4.statements.RunBeforeTestMethodCallbacks.evaluate(RunBeforeTestMethodCallbacks.java:75)
at org.springframework.test.context.junit4.statements.RunAfterTestMethodCallbacks.evaluate(RunAfterTestMethodCallbacks.java:86)
at org.springframework.test.context.junit4.statements.SpringRepeat.evaluate(SpringRepeat.java:84)
at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:251)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:97)
at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
at org.springframework.test.context.junit4.statements.RunBeforeTestClassCallbacks.evaluate(RunBeforeTestClassCallbacks.java:61)
at org.springframework.test.context.junit4.statements.RunAfterTestClassCallbacks.evaluate(RunAfterTestClassCallbacks.java:70)
at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.run(SpringJUnit4ClassRunner.java:190)
at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:86)
at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:538)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:760)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:460)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:206)
Hmm: Denken: Übrigens, als ich einen Haltepunkt auf "service = adviser.apply (service);" angewendet habe, konnte ich bestätigen, dass er von "JdkDynamicAopProxy" als Proxy verwendet wurde.
Wenn Sie hier an toString ()
denken, wird die Methodendefinition in der Object
-Klasse definiert ... aber diese Object
-Klasse implementiert keine Schnittstelle.
Wenn Sie also einen Proxy mit dem schnittstellenbasierten JdkDynamicAopProxy
verwenden, können Sie den Prozess nicht mit toString ()
unterbrechen.
Um einen Proxy mit der Entitätsklasse zu erstellen, müssen Sie einen Proxy mit CGLIB erstellen.
ToStringAdvisor.java
public ToStringAdvisor() {
proxy = new ToStringProxy<>();
//Proxy mit CGLIB
proxy.setProxyTargetClass(true);
//Entitätsklasse toString()Ziel
String expression = "execution(* " + SimpleServiceImpl.class.getName() + ".toString())";
AspectJExpressionPointcutAdvisor pointcutAdvisor = new AspectJExpressionPointcutAdvisor();
pointcutAdvisor.setAdvice(new ToStringInterceptor());
pointcutAdvisor.setExpression(expression);
proxy.addAdvisor(SimpleService.class, pointcutAdvisor);
Herzlichen Glückwunsch und Glückwunsch.
Eigentlich habe ich "toString ()" aufgerufen, indem ich eine kompliziertere Schnittstelle als Proxy verwendet habe, aber ich konnte die Ursache nicht bemerken, da ich "toString ()" auch auf der Schnittstelle aufrufen konnte. Es gibt nur zwei Arten von Proxys, "JdkDynamicAopProxy" und "CGLIB", aber es ist tiefgreifend: verwirrt :.
Wenn ich den Prozess jedoch in "toString ()" einer bestimmten Schnittstelle einfügen möchte, muss ich ihn für alle Klassen, die diese Schnittstelle implementieren, mit "CGLIB" vertreten?