Supposons que vous ayez l'interface non conventionnelle ToStringService
suivante et sa classe d'implémentation 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();
}
}
Inutile de dire que lorsque vous exécutez toString ()
de SimpleServiceImpl
," SimpleServiceImpl "est affiché.
Créez une classe qui hérite de ProxyConfig
et utilise n'importe quelle classe par proxy. Le nom est approprié.
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();
}
}
Ensuite, créez une classe de conseiller qui utilise SimpleService
.
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<>();
//Méthode toString()Cible
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()));
}
}
}
Il est supposé que "proxy: SimpleServiceImpl" sera affiché quand toString ()
of SimpleServiceImpl
est appelé.
Maintenant, testons si le processus écrit dans ToStringInterceptor
est appelé.
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"));
}
}
Le résultat est ……
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: penser:
En passant, lorsque j'ai appliqué un point d'arrêt à service = Adviser.apply (service);
, j'ai pu confirmer qu'il était proxy par JdkDynamicAopProxy
.
Si vous pensez à toString ()
ici, la définition de la méthode est définie dans la classe ʻObject ... mais cette classe ʻObject
n'implémente aucune interface.
Donc, si vous effectuez un proxy avec le JdkDynamicAopProxy
basé sur l'interface, vous ne pouvez pas interrompre le processus avec toString ()
.
Donc, pour proxy avec la classe d'entité, proxy avec CGLIB.
ToStringAdvisor.java
public ToStringAdvisor() {
proxy = new ToStringProxy<>();
//Proxy avec CGLIB
proxy.setProxyTargetClass(true);
//Classe d'entité toString()Cible
String expression = "execution(* " + SimpleServiceImpl.class.getName() + ".toString())";
AspectJExpressionPointcutAdvisor pointcutAdvisor = new AspectJExpressionPointcutAdvisor();
pointcutAdvisor.setAdvice(new ToStringInterceptor());
pointcutAdvisor.setExpression(expression);
proxy.addAdvisor(SimpleService.class, pointcutAdvisor);
En conséquence, félicitations et félicitations.
En fait, j'appelais toString ()
en envoyant un proxy pour une interface plus compliquée, mais je ne pouvais pas remarquer la cause car je pouvais également appeler toString ()
sur l'interface.
Il n'y a que deux types de proxy, JdkDynamicAopProxy
et CGLIB
, mais c'est profond: confondu:.
Cependant, si je veux mettre le processus dans toString ()
d'une interface spécifique, dois-je le proxy avec CGLIB
pour toutes les classes qui implémentent cette interface?
Recommended Posts