2024-07-12
한어Русский языкEnglishFrançaisIndonesianSanskrit日本語DeutschPortuguêsΕλληνικάespañolItalianoSuomalainenLatina
Proxy bezieht sich auf einen Modus, in dem ein Objekt A das gleiche Verhalten wie B zeigen kann, indem es ein anderes Objekt B hält. Um das Protokoll für die Außenwelt zu öffnen, implementiert B häufig eine Schnittstelle, und A implementiert auch die Schnittstelle. Aber B ist die „echte“ Implementierungsklasse, während A eher „virtuell“ ist. Es leiht sich die Methoden von B aus, um die Schnittstellenmethoden zu implementieren. Obwohl A eine „Pseudoarmee“ ist, kann sie B verbessern und vor und nach dem Aufruf der Methode von B andere Dinge tun. Spring AOP verwendet dynamische Proxys, um das dynamische „Weben“ des Codes abzuschließen.
Die Vorteile der Verwendung eines Proxys gehen darüber hinaus. Wenn ein Projekt auf die von einem anderen Projekt bereitgestellte Schnittstelle angewiesen ist, die Schnittstelle des anderen Projekts jedoch instabil ist und das Protokoll häufig geändert wird, können Sie einen Proxy verwenden Sie müssen nur den Proxy ändern, nicht einen nach dem anderen. In diesem Sinne können wir dies für alle Schnittstellen tun, die sich an die Außenwelt anpassen, um zu verhindern, dass externer Code in unseren Code eindringt. Dies wird als defensive Programmierung bezeichnet. Möglicherweise gibt es viele andere Anwendungen für das Proxying.
Im obigen Beispiel ist Klasse A fest codiert, um B zu enthalten, das den statischen Proxy von B darstellt. Wenn das Objekt eines Proxys unsicher ist, handelt es sich um einen dynamischen Proxy. Derzeit gibt es zwei gängige Implementierungen eines dynamischen Proxys: den dynamischen JDK-Proxy und den dynamischen Cglib-Proxy.
Statischer Proxy ist ein Entwurfsmuster und eine Art Proxy-Muster. In einem statischen Proxy wurde die Proxy-Klasse definiert, bevor das Programm ausgeführt wird, und die Beziehung zwischen der Proxy-Klasse und der Proxy-Klasse wird zur Kompilierungszeit bestimmt. Dies bedeutet, dass sowohl die Proxy-Klasse als auch die Proxy-Klasse dieselbe Schnittstelle implementieren oder dieselbe übergeordnete Klasse erben. Die Proxy-Klasse enthält intern eine Instanz der Proxy-Klasse und ruft gleichzeitig die Methoden der Proxy-Klasse auf , es kann Fügen Sie vor und nach dem Anruf einige Ihrer eigenen Vorgänge hinzu, z. B. Protokollierung, Berechtigungsprüfung, Transaktionsverarbeitung usw.
Statischer Proxy besteht aus drei Komponenten: abstrakte Schnittstelle, Proxy-Klasse und Proxy-Klasse. Seine Implementierungsbeispiele sind wie folgt:
- public interface TargetInteface {
- void method1();
- void method2();
- int method3(Integer i);
- }
- public class TargetProxy implements TargetInteface {
-
- private Target target =new Target();
- @Override
- public void method1() {
- System.out.println("执行方法前...");
- target.method1();
- System.out.println("执行方法后...");
- }
-
- @Override
- public void method2() {
- System.out.println("执行方法前...");
- target.method2();
- System.out.println("执行方法后...");
- }
-
- @Override
- public int method3(Integer i) {
- System.out.println("执行方法前...");
- int method3 = target.method3(i);
- System.out.println("执行方法后...");
- return method3;
- }
- }
- public class Target implements TargetInteface {
- @Override
- public void method1() {
- System.out.println(" Target method1 running ...");
- }
-
- @Override
- public void method2() {
- System.out.println("Target method2 running ...");
- }
-
- @Override
- public int method3(Integer i) {
- System.out.println("Target method3 running ...");
- return i;
- }
- }
- public class TargetUser {
-
- public static void main(String[] args) {
- TargetInteface target = new TargetProxy();
- target.method1();
- System.out.println("-----------------------------");
- target.method2();
- System.out.println("-----------------------------");
- System.out.println(target.method3(3));
- }
- }
Ergebnisausgabe:
Vor der Ausführung der Methode...
Zielmethode1 wird ausgeführt …
Nach Ausführung der Methode...
-----------------------------
Vor der Ausführung der Methode...
Zielmethode2 wird ausgeführt …
Nach Ausführung der Methode...
-----------------------------
Vor der Ausführung der Methode...
Zielmethode 3 wird ausgeführt ...
Nach Ausführung der Methode...
3
Aus der Implementierung statischer Agenten ist nicht schwer zu erkennen, dass die Vorteile statischer Agenten in der einfachen Implementierung und dem einfachen Verständnis liegen. Die Mängel liegen jedoch auch auf der Hand: Wenn Sie einer neuen Klasse Proxy-Funktionen hinzufügen müssen, müssen Sie manuell eine neue Proxy-Klasse erstellen, was zu einem starken Anstieg der Anzahl der Klassen und einem Anstieg der Wartungskosten führt . Gleichzeitig ist der Kopplungsgrad zwischen der Proxy-Klasse und der Proxy-Klasse zu hoch. Wenn Methoden in der Proxy-Klasse hinzugefügt, gelöscht oder geändert werden, müssen auch entsprechende Methoden in der Proxy-Klasse hinzugefügt, gelöscht oder geändert werden , was die Kosten für die Codewartung senkt. Ein weiteres Problem besteht darin, dass, wenn das Proxy-Objekt die Implementierungsklassen mehrerer Zielschnittstellen repräsentiert, unterschiedliche Methoden in den mehreren Implementierungsklassen vorhanden sein müssen. Da das Proxy-Objekt dieselbe Schnittstelle wie das Zielobjekt implementieren muss (eigentlich eine Einschlussbeziehung). Zahlreiche Methoden können leicht zu aufgeblähtem und schwer zu wartendem Code führen.
Die Kernidee des dynamischen Proxys besteht darin, über das Proxy-Objekt indirekt auf das Originalobjekt zuzugreifen, ohne den Originalobjektcode zu ändern, und vor und nach dem Zugriff zusätzliche Vorgänge auszuführen.
Das Implementierungsprinzip des dynamischen Proxys basiert hauptsächlich auf dem Reflexionsmechanismus von Java. Wenn Sie dynamische Proxys verwenden, müssen Sie eine Schnittstelle oder eine Reihe von Schnittstellen definieren, die das Verhalten der Proxy-Klasse (Proxy-Objekt) definieren.Anschließend müssen Sie eine Implementierung schreibenInvocationHandler
Schnittstellenklasse. Diese Klasse enthält Logik, die vor und nach Methodenaufrufen für das Proxy-Objekt ausgeführt wird.beim AnrufenProxy.newProxyInstance()
Methode, übergeben Sie den Klassenlader der Schnittstelle, das Schnittstellenarray undInvocationHandler
Object: Java generiert dynamisch eine Proxy-Klasse, die zur Laufzeit die angegebene Schnittstelle implementiert, und delegiert Methodenaufrufe an dieseInvocationHandler
zu verarbeitende Objekte.Wenn eine Methode eines Proxy-Objekts aufgerufen wird, ruft sie tatsächlich aufInvocationHandler
Schnittstelleinvoke()
Methode, mit der Sie eine Vorverarbeitungslogik basierend auf dem Methodennamen, den Parametern und anderen Informationen ausführen und dann die entsprechende Methode des Proxy-Objekts durch Reflektion aufrufen können.
Als nächstes stellen wir zwei dynamische Proxys vor: JDK Proxy und CGLib
JDK Proxy verwendet den Reflexionsmechanismus von Java, um Proxy-Klassen dynamisch zu generieren. Speziell,Proxy
Klasse wird verwendetProxyGenerator
Klasse (obwohl diese Klasse keine öffentliche API ist, ist sie der Schlüssel zur Implementierung dynamischen Proxys innerhalb des JDK), um den Bytecode der Proxy-Klasse zu generieren und ihn in die JVM zu laden.Die generierte Proxy-Klasse erbt vonjava.lang.reflect.Proxy
Klasse und implementieren Sie die angegebene Schnittstelle.In der Methode der Proxy-Klasse wird es aufgerufenInvocationHandler
voninvoke
Methode: Leiten Sie den Methodenaufruf zur Verarbeitung an den Prozessor weiter.
Um die Leistung zu verbessern, bietet JDK Proxy außerdem einen Caching-Mechanismus zum Zwischenspeichern des Klassenobjekts der generierten Proxy-Klasse. Wenn Sie ein Proxy-Objekt desselben Typs erstellen müssen, können Sie auf diese Weise das Klassenobjekt der Proxy-Klasse direkt aus dem Cache abrufen, ohne es neu generieren zu müssen.Das Caching erfolgt überWeakCache
Es wird von der Klasse implementiert und verwendet schwache Referenzen auf Cache-Objekte, sodass nicht mehr verwendete Cache-Elemente automatisch bereinigt werden können, wenn die JVM eine Speicherbereinigung durchführt.
- public interface TargetInteface {
- void method1();
- void method2();
- int method3(Integer i);
- }
- public class Target implements TargetInteface {
- @Override
- public void method1() {
- System.out.println("method1 running ...");
- }
-
- @Override
- public void method2() {
- System.out.println("method2 running ...");
- }
-
- @Override
- public int method3(Integer i) {
- System.out.println("method3 running ...");
- return i;
- }
- }
InvocationHandler
Schnittstelle und neu schreibeninvoke
Methode.existiereninvoke
In der Methode können Sie benutzerdefinierte Logik wie Protokollierung, Berechtigungsprüfung usw. hinzufügen und die ursprüngliche Klassenmethode durch Reflektion aufrufen.Proxy.newProxyInstance
Methode, Übergabe des Klassenladers, des Schnittstellenarrays undInvocationHandler
Instanz zum dynamischen Generieren von Proxy-Objekten. Diese Methode gibt eine Proxy-Klasseninstanz zurück, die die angegebene Schnittstelle implementiert.- public class TargetProxy {
- public static <T> Object getTarget(T t) {
- //新构建了一个 新的 代理类的对象
- return Proxy.newProxyInstance(t.getClass().getClassLoader(), t.getClass().getInterfaces(), new InvocationHandler() {
- @Override
- public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
- // proxy就是目标对象t,method就是调用目标对象中方法,args就是调用目标对象中方法的参数。
- //比如说:代理对象.method1(),这时proxy就是目标类,method1就是method,args就是method1方法参数。
- System.out.println("执行方法前...");
- Object invoke = method.invoke(t, args);
- System.out.println("执行方法后...");
- return invoke;
- }
- });
- }
- }
InvocationHandler
voninvoke
Methode, in der die benutzerdefinierte Logik ausgeführt wird und anschließend die Methode der Originalklasse aufgerufen wird.- public class TargetUser {
-
- public static void main(String[] args) {
- TargetInteface target = (TargetInteface) TargetProxy.getTarget(new Target());
- target.method1();
- System.out.println("-----------------------------");
- target.method2();
- System.out.println("-----------------------------");
- System.out.println(target.method3(3));
- }
- }
Ergebnisausgabe:
Vor der Ausführung der Methode...
Methode1 wird ausgeführt ...
Nach Ausführung der Methode...
-----------------------------
Vor der Ausführung der Methode...
Methode 2 wird ausgeführt ...
Nach Ausführung der Methode...
-----------------------------
Vor der Ausführung der Methode...
Methode 3 wird ausgeführt ...
Nach Ausführung der Methode...
3
MethodInterceptor
Schnittstelle zum Definieren eines Methoden-Interceptors, der vor und nach dem Methodenaufruf des Proxy-Objekts benutzerdefinierte Logik ausführt, z. B. Vorverarbeitung, Nachverarbeitung, Ausnahmebehandlung usw.- import net.sf.cglib.proxy.Enhancer;
- import net.sf.cglib.proxy.MethodInterceptor;
- import net.sf.cglib.proxy.MethodProxy;
- import java.lang.reflect.Method;
- public class Target {
- public void method1() {
- System.out.println("method1 running ...");
- }
-
- public void method2() {
- System.out.println("method2 running ...");
- }
-
- public int method3(Integer i) {
- System.out.println("method3 running ...");
- return i;
- }
- }
MethodInterceptor
Schnittstellenklasse und Overrideintercept
Methode. Schreiben Sie die Proxy-Logik in diese Methode.Enhancer
Klasse zum Erstellen von Proxy-Objekten.Sie müssen die Proxy-Klasse festlegen (viasetSuperclass
Methode) und Rückrufe (viasetCallback
Die Methode legt die MethodInterceptor-Implementierungsklasse fest.- public class TargetProxy {
- public static <T> Object getProxy(T t) {
- Enhancer en = new Enhancer(); //帮我们生成代理对象
- en.setSuperclass(t.getClass());//设置要代理的目标类
- en.setCallback(new MethodInterceptor() {//代理要做什么
- @Override
- public Object intercept(Object object, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
- System.out.println("执行方法前。。。");
- //调用原有方法
- Object invoke = methodProxy.invokeSuper(object, args);
- // Object invoke = method.invoke(t, args);// 作用等同与上面。
- System.out.println("执行方法后。。。");
- return invoke;
- }
- });
- return en.create();
- }
- }
intercept
Proxy-Logik in Methoden.- public class TargetUser {
-
- public static void main(String[] args) {
- Target target = (Target) TargetProxy.getProxy(new Target());
- System.out.println(target.getClass().getName());
- target.method1();
- }
-
- }
Ergebnisausgabe:
com.heaboy.aopdemo.cglibproxy.ZielENHANCtRBjCGMICHBf9f41fb8
bevor die Methode ausgeführt wird. . .
Methode1 wird ausgeführt ...
Nach Ausführung der Methode. . .
Vorteil:
Mangel:
Proxy-Objekttyp:Der JDK-Proxy kann nur Klassen vertreten, die Schnittstellen implementieren, während CGLib gewöhnliche Klassen direkt vertreten kann.
Leistung: CGLib generiert zur Laufzeit Unterklassen von Proxy-Klassen und gilt im Allgemeinen als etwas leistungsstärker als JDK Proxy. In den meisten Szenarien ist dieser Leistungsunterschied jedoch nicht signifikant.
Zu verwendende Szenen: Wenn das Zielobjekt die Schnittstelle bereits implementiert, ist die Verwendung des JDK-Proxys eine einfache und unkomplizierte Wahl. Wenn Sie eine Klasse als Proxy verwenden müssen, die keine Schnittstelle implementiert, müssen Sie CGLib verwenden.
verlassen:Der JDK-Proxy erfordert keine zusätzlichen Abhängigkeiten, da er Teil der Java-Kernbibliothek ist, während CGLib das Hinzufügen der CGLib-Bibliothek als Projektabhängigkeit erfordert.
JDK Proxy ist eine Funktion, die in der Java-Sprache enthalten ist und nicht durch Laden von Klassen von Drittanbietern implementiert werden muss;
Java bietet stabile Unterstützung für JDK Proxy und wird JDK Proxy weiterhin aktualisieren und aktualisieren. Beispielsweise wurde die Leistung von JDK Proxy in der Java 8-Version im Vergleich zu früheren Versionen erheblich verbessert.
Der JDK-Proxy wird durch Interceptoren und Reflection implementiert.
Der JDK-Proxy kann nur Klassen vertreten, die Schnittstellen erben.
Der JDK-Proxy ist relativ einfach zu implementieren und aufzurufen.
CGLib ist ein von einem Drittanbieter bereitgestelltes Tool, das auf ASM basiert und eine relativ hohe Leistung aufweist.
CGLib muss nicht über eine Schnittstelle implementiert werden, sondern wird durch Implementierung einer Unterklasse aufgerufen.
Statischer Proxy: Statische Proxys werden während der Kompilierung bestimmt. Für jede Proxy-Klasse muss eine Proxy-Klasse geschrieben werden. Die Proxy-Klasse und die Proxy-Klasse implementieren dieselbe Schnittstelle oder erben dieselbe übergeordnete Klasse.
Die Proxy-Klasse eines statischen Proxys existiert zur Kompilierungszeit, sodass sie nur bestimmte Klassen vertreten kann, wenn das Programm ausgeführt wird, und nicht dynamisch entscheiden kann, welche Klassen vertreten werden sollen.
Der statische Proxy umschließt den Methodenaufruf des ursprünglichen Objekts und kann vor und nach dem Aufruf zusätzliche Logik hinzufügen. Die Proxy-Klasse muss jedoch im Voraus geschrieben werden, was die Codemenge erhöht.
Der statische Proxy gibt das Proxy-Objekt im Code explizit an und ist relativ intuitiv zu verwenden. Das Hinzufügen neuer Proxy-Klassen erfordert jedoch eine Neukompilierung.
Dynamischer Proxy: Der dynamische Proxy erstellt zur Laufzeit Proxy-Objekte, ohne vorher Proxy-Klassen zu schreiben. Verwenden Sie den Reflexionsmechanismus von Java, um Proxy-Klassen und Proxy-Objekte dynamisch zu generieren.
Dynamische Proxys basieren auf Schnittstellen und werden über die Klasse java.lang.reflect.Proxy und die Schnittstelle java.lang.reflect.InvocationHandler implementiert.
Dynamische Proxys können Klassen mehrerer Schnittstellen vertreten und dynamisch entscheiden, welche Klassen vertreten werden sollen. Zur Laufzeit können bei Bedarf Proxys für verschiedene Objekte generiert werden, was eine größere Flexibilität bietet.
Der dynamische Proxy muss nicht für jede Proxy-Klasse eine bestimmte Proxy-Klasse schreiben, was flexibler ist und Code spart.
Dynamische Agenten können vor und nach Methodenaufrufen für das Proxy-Objekt benutzerdefinierte Logik hinzufügen, z. B. Protokollierung, Transaktionsverwaltung usw. Der Nachteil des dynamischen Proxys besteht darin, dass das Generieren eines Proxy-Objekts zur Laufzeit im Vergleich zum statischen Proxy einen gewissen Leistungsaufwand erfordert.
Statische Proxys eignen sich für folgende Szenarien:
Wenn die Anzahl der Zielobjekte (Proxy-Objekte) begrenzt und bestimmt ist, kann ein statischer Proxy durch manuelles Schreiben von Proxy-Klassen implementiert werden. Statische Proxys erstellen zur Kompilierungszeit Proxy-Klassen, sodass sie zur Laufzeit eine bessere Leistung erzielen.
Statische Proxys kapseln das Zielobjekt und fügen zusätzliche Funktionen hinzu, ohne den Originalcode zu ändern. Daher werden statische Proxys häufig für übergreifende Anliegen wie Protokollierung und Transaktionsverwaltung verwendet.
Der dynamische Proxy eignet sich für die folgenden Szenarien:
Wenn die Anzahl der Zielobjekte unsicher ist oder nicht im Voraus bestimmt werden kann, können dynamische Proxys Proxy-Objekte bequemer generieren. Es generiert zur Laufzeit Proxy-Klassen und Proxy-Objekte und vermeidet so die mühsame Arbeit, mehrere Proxy-Klassen manuell zu schreiben.
Dynamische Proxys bieten die Flexibilität, das Proxy-Verhalten für Zielobjekte zur Laufzeit hinzuzufügen, zu entfernen oder zu ändern. Daher werden dynamische Proxys häufig in Anwendungsszenarien wie AOP (Aspektorientierte Programmierung) und RPC (Remote Procedure Call) eingesetzt.
Es ist zu beachten, dass die Leistung dynamischer Proxys möglicherweise etwas geringer ist als die statischer Proxys, da sie zur Laufzeit Proxy-Klassen und Proxy-Objekte über den Reflexionsmechanismus erstellen. Darüber hinaus können dynamische Proxys nur Zielobjekte vertreten, die die Schnittstelle implementieren, während für statische Proxys diese Einschränkung nicht gilt.
Zusammenfassend lässt sich sagen, dass statische Proxys für Szenarien geeignet sind, in denen die Anzahl der Zielobjekte begrenzt und sicher ist und eine Kapselung und zusätzliche Funktionen erfordern, während dynamische Proxys für Szenarien geeignet sind, in denen die Anzahl der Zielobjekte unsicher ist oder nicht im Voraus bestimmt werden kann. und das Proxy-Verhalten muss flexibel hinzugefügt, gelöscht oder geändert werden. Wählen Sie die geeignete Agenturmethode basierend auf den spezifischen Bedürfnissen und Umständen.
Spring AOP ist ein wichtiges Modul im Spring-Framework, das zur Implementierung aspektorientierter Programmierung verwendet wird.
Persönliche Programmierung Dies ist ein Programmiermodell, das es Programmierern ermöglicht, durch benutzerdefinierte Schnittpunkte zu modularisieren und Verhaltensweisen, die sich auf mehrere Klassen auswirken, in wiederverwendbaren Modulen zu kapseln. Beispiel: Wenn Sie beispielsweise für die Protokollausgabe kein AOP verwenden, müssen Sie die Protokollausgabeanweisungen in alle Klassen und Methoden einfügen. Mit AOP können Sie die Protokollausgabeanweisungen jedoch in ein wiederverwendbares Modul kapseln und in einem platzieren In einer Klasse wird die Protokollausgabe bei jeder Verwendung der Klasse automatisch abgeschlossen.
In der Idee der aspektorientierten Programmierung werden Funktionen in zwei Typen unterteilt
Kerngeschäft: Anmelden, Registrieren, Hinzufügen, Löschen, Ändern und Abfragen werden alle als Kerngeschäft bezeichnet
Peripheriefunktionen: Protokolle und Transaktionsverwaltung sind sekundäre Peripheriedienste.
Bei der aspektorientierten Programmierung werden Kerngeschäftsfunktionen und Peripheriefunktionen unabhängig voneinander entwickelt und sind nicht gekoppelt. Anschließend werden die Aspektfunktionen und Kerngeschäftsfunktionen miteinander „verwoben“, was als AOP bezeichnet wird.
AOP kann diejenigen umwandeln, die nicht mit dem Geschäft zusammenhängen,Es ist jedoch für die Logik oder Verantwortlichkeiten (wie Transaktionsverarbeitung, Protokollverwaltung, Berechtigungskontrolle usw.) gekapselt, die üblicherweise von Geschäftsmodulen aufgerufen werden.,einfach zuReduzieren Sie doppelten Code im System,Reduzieren Sie die Kopplung zwischen Modulen,UndFörderlich für zukünftige Skalierbarkeit und Wartbarkeit。
In AOP gibt es die folgenden Konzepte:
AspektJ: Aspect ist nur ein Konzept. Es gibt keine entsprechende Schnittstelle oder Klasse. Es handelt sich um einen Sammelnamen für Join Point, Advice und Pointcut.
Verbindungspunkt : Verbindungspunkt bezieht sich auf einen Punkt während der Programmausführung, z. B. Methodenaufruf, Ausnahmebehandlung usw. In Spring AOP werden nur Verbindungspunkte auf Methodenebene unterstützt.
Beratung : Benachrichtigung, also die übergreifende Logik in einem von uns definierten Aspekt, hat drei Arten: „um“, „vorher“ und „nachher“. In vielen AOP-Implementierungsframeworks fungiert Advice normalerweise als Interceptor und kann auch viele Interceptoren als Link enthalten, der um den Verbindungspunkt herum verarbeitet werden soll.
Punktschnitt: Pointcut, wird zum Abgleichen der in einem AspectJ enthaltenen Verbindungspunkte verwendet, die von Pointcut gefiltert werden müssen.
Einführung : Einführung, die es einem Aspekt ermöglicht, zu deklarieren, dass empfohlene Objekte alle zusätzlichen Schnittstellen implementieren, die sie tatsächlich nicht implementieren. Beispielsweise kann ein Proxy-Objekt als Proxy für zwei Zielklassen verwendet werden.
Weberei : Weben, jetzt, wo wir Verbindungspunkte, Schnittpunkte, Benachrichtigungen und Aspekte haben, wie können wir sie auf das Programm anwenden? Richtig, es ist Weben. Unter der Anleitung von Pointcuts wird die Benachrichtigungslogik in die Zielmethode eingefügt, sodass unsere Benachrichtigungslogik ausgeführt werden kann, wenn die Methode aufgerufen wird.
AOP-Proxy : AOP-Proxy bezieht sich auf das Objekt, das das Aspektprotokoll im AOP-Implementierungsframework implementiert. In Spring AOP gibt es zwei Arten von Proxys, nämlich den dynamischen JDK-Proxy und den dynamischen CGLIB-Proxy.
Zielobjekt: Das Zielobjekt ist das Objekt, das als Proxy fungiert.
Spring AOP wird basierend auf dem dynamischen JDK-Proxy und der Cglib-Förderung implementiert. Beide Proxy-Methoden sind Laufzeitmethoden und verfügen daher nicht über eine Verarbeitung zur Kompilierungszeit. Daher wird Spring über Java-Code implementiert.
Einige allgemeine Verhaltensweisen sind auf mehrere Klassen oder Objekte verteilt (z. B. Protokollierung, Transaktionsverwaltung, Berechtigungskontrolle, Begrenzung des Schnittstellenstroms, Schnittstellenleistung usw.). Diese Verhaltensweisen werden normalerweise aufgerufen Querschnittsthemen . Wenn wir diese Verhaltensweisen wiederholt in jeder Klasse oder jedem Objekt implementieren, führt dies zu redundantem, komplexem und schwer zu wartendem Code.
AOP kann übergreifende Anliegen (wie Protokollierung, Transaktionsverwaltung, Berechtigungskontrolle, Schnittstellenstrombegrenzung, Schnittstellenleistung usw.) umwandeln Kerngeschäftslogik (Kernanliegen, Kernanliegen) Trennen Sie sich vom Fokus, um eine Trennung der Anliegen zu erreichen.
Protokollierung: Passen Sie Protokollierungsanmerkungen an und verwenden Sie AOP, um die Protokollierung mit einer Codezeile zu erreichen.
Leistungsstatistik: Verwenden Sie AOP, um die Ausführungszeit der Methode vor und nach der Ausführung der Zielmethode zu zählen, um die Optimierung und Analyse zu erleichtern.
Transaktionsmanagement:@Transactional
Mithilfe von Anmerkungen kann Spring die Transaktionsverwaltung für uns durchführen, z. B. das Zurücksetzen von Ausnahmevorgängen, wodurch wiederholte Transaktionsverwaltungslogik eliminiert wird.@Transactional
Anmerkungen werden basierend auf AOP implementiert.
Berechtigungskontrolle: Verwenden Sie AOP, um festzustellen, ob der Benutzer über die erforderlichen Berechtigungen verfügt, bevor die Zielmethode ausgeführt wird. Wenn ja, führen Sie die Zielmethode aus, andernfalls wird sie nicht ausgeführt.Beispielsweise verwendet SpringSecurity@PreAuthorize
Sie können die Berechtigungsüberprüfung anpassen, indem Sie eine Codezeile kommentieren.
Schnittstellenstrombegrenzung: Verwenden Sie AOP, um die Anforderung durch bestimmte Strombegrenzungsalgorithmen und -implementierungen zu begrenzen, bevor die Zielmethode ausgeführt wird.
Cache-Verwaltung: Verwenden Sie AOP, um den Cache vor und nach der Ausführung der Zielmethode zu lesen und zu aktualisieren.
Zu den gängigen Implementierungsmethoden von AOP gehören dynamische Proxys, Bytecode-Operationen usw.
Spring AOP basiert auf einem dynamischen Proxy. Wenn das zu vertretende Objekt eine bestimmte Schnittstelle implementiert, wird Spring AOP verwendet JDK-Proxy, um ein Proxy-Objekt zu erstellen, das die Schnittstelle nicht implementiert, kann Spring AOP derzeit nicht als Proxy verwendet werden Cglib Generieren Sie eine Unterklasse des Proxy-Objekts, die als Proxy dient
existierenspring-aop.xml
Konfigurieren Sie verwandte Beans und Aspekte in der Konfigurationsdatei
- <?xml version="1.0" encoding="UTF-8"?>
- <beans xmlns="http://www.springframework.org/schema/beans"
- xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
- xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
-
- <bean id="target" class="com.xxhh.aopdemo.aop.Target"/>
-
- <bean id="targetAdvice" class="com.xxhh.aopdemo.aop.TargetAdvice"/>
-
- <bean id="targetProxy" class="org.springframework.aop.framework.ProxyFactoryBean">
- <property name="target" ref="target"/> <!--被代理的类-->
- <property name="interceptorNames" value="targetAdvice"/> <!--如果用多种增强方式,value的值使用逗号(,)分割-->
- <property name="proxyTargetClass" value="false"/> <!--如果设置为true,则创建基于类的代理(使用CGLIB);如果设置为false,则创建基于接口的代理(使用JDK动态代理)。-->
- <property name="interfaces" value="com.xxhh.aopdemo.aop.TargetInteface"/> <!--target实现的接口-->
- </bean>
- </beans>
- public interface TargetInteface {
- void method1();
- void method2();
- int method3(Integer i);
- }
- public class Target implements TargetInteface{
-
- /*
- * 需要增强的方法,连接点JoinPoint
- **/
- @Override
- public void method1() {
- System.out.println("method1 running ...");
- }
-
- @Override
- public void method2() {
- System.out.println("method2 running ...");
- }
-
- @Override
- public int method3(Integer i) {
- System.out.println("method3 running ...");
- return i;
- }
- }
- public class TargetAdvice implements MethodInterceptor, MethodBeforeAdvice, AfterReturningAdvice {
-
- /*
- * 通知/增强
- **/
- @Override
- public Object invoke(MethodInvocation methodInvocation) throws Throwable {
- System.out.println("前置环绕通知");
- Object proceed = methodInvocation.proceed();
- System.out.println("后置环绕通知");
- return proceed;
- }
-
- @Override
- public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {
- System.out.println("后置返回通知");
- }
-
- @Override
- public void before(Method method, Object[] args, Object target) throws Throwable {
- System.out.println("前置通知");
- }
- }
- public class AopTest {
- public static void main(String[] args) {
- ApplicationContext appCtx = new ClassPathXmlApplicationContext("spring-aop.xml");
- TargetInteface targetProxy = (TargetInteface) appCtx.getBean("targetProxy");
- targetProxy.method1();
- }
- }
Ausgabeergebnis:
Surround-Front-Benachrichtigung
Voranmeldung
Methode1 wird ausgeführt ...
Rücksendebenachrichtigung posten
Surround-Back-Benachrichtigung
existierenspring-confaop.xml
Konfigurieren Sie verwandte Beans und Aspekte in der Konfigurationsdatei
- <?xml version="1.0" encoding="UTF-8"?>
- <beans xmlns="http://www.springframework.org/schema/beans"
- xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
- xmlns:context="http://www.springframework.org/schema/context"
- xmlns:aop="http://www.springframework.org/schema/aop"
- xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">
-
- <!--先开启cglib代理,开启 exposeProxy = true,暴露代理对象-->
- <aop:aspectj-autoproxy proxy-target-class="true" expose-proxy="true"/>
- <!--扫包-->
- <context:component-scan base-package="com.xxhh.aopdemo.annotationaop"/>
-
- </beans>
- /*
- * 目标类
- **/
- public class Target {
-
- public void method1() {
- System.out.println("method1 running ...");
- }
-
- public void method2() {
- System.out.println("method2 running ...");
- }
-
- /*
- * 连接点JoinPoint
- **/
- public int method3(Integer i) {
- System.out.println("method3 running ...");
- // int i1 = 1 / i;
- return i;
- }
- }
- import org.aspectj.lang.ProceedingJoinPoint;
- /*
- * 切面类
- **/
- public class TargetAspect {
-
- /*
- * 前置通知
- **/
- public void before() {
- System.out.println("conf前置通知");
- }
-
- public void after() {
- System.out.println("conf后置通知");
- }
-
- public void afterReturning() {
- System.out.println("conf后置返回通知");
- }
-
- public void afterThrowing(Exception ex) throws Exception {
- // System.out.println("conf异常通知");
- // System.out.println(ex.getMessage());
- }
-
- public Object around(ProceedingJoinPoint pjp) throws Throwable {
- Object proceed = null;
- if (!"".equals("admin")) {
- System.out.println("conf环绕前置");
- proceed = pjp.proceed(pjp.getArgs());
- System.out.println("conf环绕后置");
- }
- return proceed;
- }
- }
- public class AopTest {
- public static void main(String[] args) {
- ApplicationContext appCtx = new ClassPathXmlApplicationContext("spring-confaop.xml");
- Target targetProxy = (Target) appCtx.getBean("target");
- System.out.println(targetProxy.method3(0));
- }
- }
Ausgabeergebnis:
conf-Vorabbenachrichtigung
conf-Surround-Präfix
Methode 3 wird ausgeführt ...
conf-Benachrichtigung nach der Rückgabe
conf-Surround-Beitrag
conf-Beitragsbenachrichtigung
0
- <?xml version="1.0" encoding="UTF-8"?>
- <beans xmlns="http://www.springframework.org/schema/beans"
- xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
- xmlns:context="http://www.springframework.org/schema/context"
- xmlns:aop="http://www.springframework.org/schema/aop"
- xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">
-
- <!--先开启cglib代理,开启 exposeProxy = true,暴露代理对象-->
- <aop:aspectj-autoproxy proxy-target-class="true" expose-proxy="true"/>
- <!--扫包-->
- <context:component-scan base-package="com.xxhh.aopdemo.annotationaop"/>
-
- </beans>
- @Target(ElementType.METHOD)
- @Retention(RetentionPolicy.RUNTIME)
- @Documented
- public @interface TestAnnotation{
- }
- /*
- * 切面类
- **/
- @Aspect
- @Component
- public class AnnotationAspect {
-
- // 定义一个切点:所有被RequestMapping注解修饰的方法会织入advice
- @Pointcut("@annotation(TestAnnotation)")
- private void advicePointcut() {}
-
- /*
- * 前置通知
- **/
- @Before("advicePointcut()")
- public void before() {
- System.out.println("annotation前置通知");
- }
-
- @After("advicePointcut()")
- public void after() {
- System.out.println("annotation后置通知");
- }
-
- @AfterReturning(pointcut = "advicePointcut()")
- public void afterReturning() {
- System.out.println("annotation后置返回通知");
- }
-
- @AfterThrowing(pointcut = "advicePointcut()", throwing = "ex")
- public void afterThrowing(Exception ex) throws Exception {
- System.out.println("annotation异常通知");
- System.out.println(ex.getMessage());
- }
-
- @Around("advicePointcut()")
- public Object around(ProceedingJoinPoint pjp) throws Throwable {
- Object proceed = null;
- if (!"".equals("admin")) {
- System.out.println("annotation环绕前置");
- proceed = pjp.proceed(pjp.getArgs());
- System.out.println("annotation环绕后置");
- }
- return proceed;
- }
- }
- @Controller
- public class TestController {
-
- @RequestMapping("/test.do")
- @ResponseBody
- public String testController() {
- TestController o = (TestController) AopContext.currentProxy();
- o.test();
- // System.out.println("tewt");
- return "ok";
- }
-
- @TestAnnotation
- public void test() {
- System.out.println("test running");
- }
-
- }