[JAVA] Eine Geschichte, die von String () von Interface abhängig ist und von JdkDynamicAopProxy vertreten wird

Entwicklungsumgebung

Hintergrund

Service-Implementierung

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.

Proxyisieren

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.

Versuche zu testen

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.

JdkDynamicAopProxy ist nutzlos

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.

Gelöst durch Proxy mit CGLIB

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.

Hintergrund zum Posting

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?

Recommended Posts

Eine Geschichte, die von String () von Interface abhängig ist und von JdkDynamicAopProxy vertreten wird
Eine Geschichte, die süchtig nach EntityNotFoundException von getOne of JpaRepository ist
Eine Geschichte, die mich während des Komponententests von der impliziten Typkonvertierung von ActiveRecord abhängig machte
Eine Geschichte über die Verbindung zu einem CentOS 8-Server mit einem alten Ansible
Die Geschichte von toString () beginnt mit der Übergabe eines Arrays an System.out.println
Eine Geschichte, die süchtig nach Platzhaltern für JDBC-Vorlagen ist
Die Geschichte, einen Reverse-Proxy mit ProxyServlet zu erstellen
Eine Geschichte über den Versuch, mit Mockito auszukommen
Eine Geschichte über die Reduzierung des Speicherverbrauchs auf 1/100 mit find_in_batches
Die Geschichte der Erstellung einer Task-Management-Anwendung mit Swing, Java
Die Geschichte, wie ein Docker-Container mit GitHub-Aktionen in die GitHub-Paketregistrierung und den Docker-Hub verschoben wird
Port C-Code mit vielen Typecasts zu Swift
Eine Geschichte voller Grundlagen von Spring Boot (gelöst)
Eine Reihe von Schritten zum Erstellen von Ergebnissen für Portfolios mit Rails
So verschieben Sie eine andere Klasse mit einer Schaltflächenaktion einer anderen Klasse.
Eine Geschichte, die bei NotSerializableException steckt
[Circle CI] Eine Geschichte, der ich bei Start Building verfallen war
Die Geschichte des Refactorings mit einem selbstgemachten Helfer zum ersten Mal in einer Rails-App
Versuchen Sie, die Idee eines zweidimensionalen Arrays mit einem eindimensionalen Array nachzuahmen
Eine Geschichte, die ich mit Java nur schwer herausfordern konnte
Eine Geschichte über das Erreichen der League Of Legends-API mit JAVA
Eine Geschichte, die mit der Einführung von Web Apple Pay zu kämpfen hatte
Bedingte Verzweigung mit fließender Schnittstelle
Ausnahmebehandlung mit einer Fluidschnittstelle
Erstellen Sie Ausnahmen mit einer Fluid-Schnittstelle
Die Geschichte, ein Projekt zu bauen, das Maven mit Ant gebaut hat
Eine Geschichte, die ich mit der Stream-API von Java8 einem Prozess schreiben wollte, der einer while-Anweisung entspricht
Ein Memo, das nüchtern von der Anfrage nach mehrteiligen / Formulardaten abhängig war
Die Geschichte, zu vergessen, eine Datei in Java zu schließen und zu scheitern
Die Geschichte eines Game Launcher mit automatischer Ladefunktion [Java]
Eine Geschichte, der ich beim Testen der API mit MockMVC verfallen war
Ich war süchtig nach einem einfachen Test von Jedis (Java-> Redis-Bibliothek)
Extrahieren Sie einen Teil einer Zeichenfolge in Ruby
Eine kleine süchtig machende Geschichte mit def initialize
Ich habe versucht, mit HCE-F von Android eine Funktion zu implementieren, die Felica Lite entspricht
So erhalten Sie die ID eines Benutzers, der sich in Swift bei Firebase authentifiziert hat
Der Name ist und das Alter ist Senden Sie eine Benachrichtigung an Slack mit der Java-freien Version von Sentry (mit Lambda)
Rails6 Ich möchte ein Array von Werten mit einem Kontrollkästchen erstellen
SpringSecurity Ich war süchtig danach, mich mit einem Hash-Passwort anzumelden (gelöst)
Ich habe versucht, eine Webanwendung voller Fehler mit Spring Boot zu klonen
Ich möchte rekursiv die Oberklasse und die Schnittstelle einer bestimmten Klasse erhalten
Eine Geschichte, dass eine Person, die ein solides iOS gemacht hat, möglicherweise von der Implementierung von Listener abhängig ist, wenn sie zu Android wechselt