Condivisione della tecnologia

Spiegazione dettagliata del proxy: proxy statico, proxy dinamico, implementazione Spring AOP

2024-07-12

한어Русский языкEnglishFrançaisIndonesianSanskrit日本語DeutschPortuguêsΕλληνικάespañolItalianoSuomalainenLatina

1. Introduzione dell'agente

Il proxy si riferisce a una modalità in cui un oggetto A può avere lo stesso comportamento di B trattenendo un altro oggetto B. Per aprire il protocollo al mondo esterno, B spesso implementa un'interfaccia e anche A implementerà l'interfaccia. Ma B è la classe di implementazione "reale", mentre A è più "virtuale". Prende in prestito i metodi di B per implementare i metodi dell'interfaccia. Sebbene A sia uno "pseudo esercito", può potenziare B e fare altre cose prima e dopo aver richiamato il metodo di B. Spring AOP utilizza proxy dinamici per completare la "tessitura" dinamica del codice.

I vantaggi dell'utilizzo di un proxy vanno oltre. Se un progetto si basa sull'interfaccia fornita da un altro progetto, ma l'interfaccia dell'altro progetto è instabile e il protocollo viene modificato spesso, è possibile utilizzare un proxy è necessario modificare solo il proxy, non uno per uno. In questo senso, possiamo farlo per tutte le interfacce che si adattano al mondo esterno per impedire che il codice esterno invada il nostro codice. Questa si chiama programmazione difensiva. Potrebbero esserci molte altre applicazioni per il proxy.

Nell'esempio precedente, la classe A è codificata per contenere B, che è il proxy statico di B. Se l'oggetto del proxy A è incerto, si tratta di un proxy dinamico. Attualmente esistono due implementazioni comuni di proxy dinamico, proxy dinamico jdk e proxy dinamico cglib.

2. Procura statica

Il proxy statico è un modello di progettazione e un tipo di modello proxy. In un proxy statico, la classe proxy è stata definita prima dell'esecuzione del programma e la relazione tra la classe proxy e la classe proxy viene determinata in fase di compilazione. Ciò significa che sia la classe proxy che la classe proxy implementano la stessa interfaccia o ereditano la stessa classe genitore. La classe proxy contiene internamente un'istanza della classe proxy e chiama i metodi della classe proxy nel proprio metodo allo stesso tempo , può aggiungere alcune delle tue operazioni prima e dopo la chiamata, come la registrazione, il controllo delle autorizzazioni, l'elaborazione delle transazioni, ecc.

Il proxy statico ha tre componenti: interfaccia astratta, classe proxy e classe proxy. I suoi esempi di implementazione sono i seguenti:

1) Definire l'interfaccia astratta

  1. public interface TargetInteface {
  2. void method1();
  3. void method2();
  4. int method3(Integer i);
  5. }

2) Definire la classe proxy

  1. public class TargetProxy implements TargetInteface {
  2. private Target target =new Target();
  3. @Override
  4. public void method1() {
  5. System.out.println("执行方法前...");
  6. target.method1();
  7. System.out.println("执行方法后...");
  8. }
  9. @Override
  10. public void method2() {
  11. System.out.println("执行方法前...");
  12. target.method2();
  13. System.out.println("执行方法后...");
  14. }
  15. @Override
  16. public int method3(Integer i) {
  17. System.out.println("执行方法前...");
  18. int method3 = target.method3(i);
  19. System.out.println("执行方法后...");
  20. return method3;
  21. }
  22. }

3) Definire la classe proxy

  1. public class Target implements TargetInteface {
  2. @Override
  3. public void method1() {
  4. System.out.println(" Target method1 running ...");
  5. }
  6. @Override
  7. public void method2() {
  8. System.out.println("Target method2 running ...");
  9. }
  10. @Override
  11. public int method3(Integer i) {
  12. System.out.println("Target method3 running ...");
  13. return i;
  14. }
  15. }

4) Definire il cliente e visualizzare i risultati dell'esecuzione

  1. public class TargetUser {
  2. public static void main(String[] args) {
  3. TargetInteface target = new TargetProxy();
  4. target.method1();
  5. System.out.println("-----------------------------");
  6. target.method2();
  7. System.out.println("-----------------------------");
  8. System.out.println(target.method3(3));
  9. }
  10. }

Output dei risultati:

Prima di eseguire il metodo...
Metodo di destinazione 1 in esecuzione ...
Dopo aver eseguito il metodo...
-----------------------------
Prima di eseguire il metodo...
Metodo di destinazione 2 in esecuzione ...
Dopo aver eseguito il metodo...
-----------------------------
Prima di eseguire il metodo...
Metodo di destinazione 3 in esecuzione ...
Dopo aver eseguito il metodo...
3

Non è difficile vedere dall'implementazione degli agenti statici che i vantaggi degli agenti statici sono la semplicità di implementazione e la facile comprensione. Ma anche i suoi difetti sono evidenti, ovvero ogni volta che è necessario aggiungere funzionalità proxy a una nuova classe, è necessario creare manualmente una nuova classe proxy, il che porterà ad un forte aumento del numero di classi e ad un aumento dei costi di manutenzione. . Allo stesso tempo, il grado di accoppiamento tra la classe proxy e la classe proxy è troppo elevato Quando i metodi vengono aggiunti, eliminati o modificati nella classe proxy, è necessario aggiungere, eliminare o modificare anche i metodi corrispondenti nella classe proxy. , che migliora i costi di manutenzione del codice. Un altro problema è che quando l'oggetto proxy esegue il proxy delle classi di implementazione di più interfacce di destinazione, devono esserci metodi diversi nelle più classi di implementazione, poiché l'oggetto proxy deve implementare la stessa interfaccia dell'oggetto di destinazione (in realtà una relazione di inclusione). essere scritti Numerosi metodi possono facilmente portare a codice voluminoso e difficile da mantenere.

3. Procura dinamica

L'idea centrale del proxy dinamico è accedere indirettamente all'oggetto originale tramite l'oggetto proxy senza modificare il codice dell'oggetto originale ed eseguire operazioni aggiuntive prima e dopo l'accesso.

Il principio di implementazione del proxy dinamico si basa principalmente sul meccanismo di riflessione di Java. Quando si utilizzano proxy dinamici, è necessario definire un'interfaccia o un insieme di interfacce che definiscono il comportamento della classe proxy (oggetto proxy).Quindi, è necessario scrivere un'implementazioneInvocationHandler Classe di interfaccia, questa classe contiene la logica che viene eseguita prima e dopo le chiamate al metodo sull'oggetto proxy.quando si chiamaProxy.newProxyInstance()metodo, passare nel caricatore di classi dell'interfaccia, l'array dell'interfaccia eInvocationHandlerOggetto, Java genererà dinamicamente una classe proxy che implementa l'interfaccia specificata in fase di esecuzione e delegherà le chiamate al metodoInvocationHandler oggetti da elaborare.Quando viene chiamato un metodo di un oggetto proxy, in realtà chiamaInvocationHandlerInterfacciainvoke()Metodo, in cui è possibile eseguire una logica di preelaborazione basata sul nome del metodo, parametri e altre informazioni, quindi chiamare il metodo corrispondente dell'oggetto proxy tramite riflessione.

Successivamente, introduciamo due proxy dinamici: JDK Proxy e CGLib

1) Proxy JDK

① Meccanismo interno del proxy JDK

JDK Proxy utilizza il meccanismo di riflessione di Java per generare dinamicamente classi proxy. Nello specifico,Proxyla classe utilizzeràProxyGenerator class (sebbene questa classe non sia un'API pubblica, è la chiave per implementare il proxy dinamico all'interno del JDK) per generare il bytecode della classe proxy e caricarlo nella JVM.La classe proxy generata erediterà dajava.lang.reflect.Proxy classe e implementare l'interfaccia specificata.Nel metodo della classe proxy, verrà chiamatoInvocationHandlerDiinvokeMetodo, inoltra la chiamata del metodo al processore per l'elaborazione.

Inoltre, al fine di migliorare le prestazioni, JDK Proxy fornisce anche un meccanismo di memorizzazione nella cache per memorizzare nella cache l'oggetto Class della classe proxy generata. In questo modo, quando è necessario creare un oggetto proxy dello stesso tipo, è possibile ottenere direttamente dalla cache l'oggetto Class della classe proxy senza rigenerarla.La memorizzazione nella cache viene eseguita tramiteWeakCacheImplementato dalla classe, utilizza riferimenti deboli per memorizzare nella cache gli oggetti in modo che gli elementi della cache che non vengono più utilizzati possano essere automaticamente ripuliti quando la JVM esegue la garbage collection.

② Passaggi di implementazione del proxy JDK

  • Definire interfacce e classi proxy: Innanzitutto definire una o più interfacce, che verranno implementate dalla classe proxy.
  1. public interface TargetInteface {
  2. void method1();
  3. void method2();
  4. int method3(Integer i);
  5. }
  1. public class Target implements TargetInteface {
  2. @Override
  3. public void method1() {
  4. System.out.println("method1 running ...");
  5. }
  6. @Override
  7. public void method2() {
  8. System.out.println("method2 running ...");
  9. }
  10. @Override
  11. public int method3(Integer i) {
  12. System.out.println("method3 running ...");
  13. return i;
  14. }
  15. }
  • CreaInvocationHandler:compiereInvocationHandlerinterfaccia e riscrivereinvoke metodo.esistereinvokeNel metodo è possibile aggiungere logica personalizzata, ad esempio registrazione, controllo delle autorizzazioni e così via, e chiamare il metodo della classe originale tramite la riflessione.
  • Genera oggetto proxy:trasferimentoProxy.newProxyInstancemetodo, passando nel caricatore di classi, array di interfacce eInvocationHandler Istanza per generare dinamicamente oggetti proxy. Questo metodo restituisce un'istanza della classe proxy che implementa l'interfaccia specificata.
  1. public class TargetProxy {
  2. public static <T> Object getTarget(T t) {
  3. //新构建了一个 新的 代理类的对象
  4. return Proxy.newProxyInstance(t.getClass().getClassLoader(), t.getClass().getInterfaces(), new InvocationHandler() {
  5. @Override
  6. public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
  7. // proxy就是目标对象t,method就是调用目标对象中方法,args就是调用目标对象中方法的参数。
  8. //比如说:代理对象.method1(),这时proxy就是目标类,method1就是method,args就是method1方法参数。
  9. System.out.println("执行方法前...");
  10. Object invoke = method.invoke(t, args);
  11. System.out.println("执行方法后...");
  12. return invoke;
  13. }
  14. });
  15. }
  16. }
  • Utilizza oggetti proxy: Quando un metodo viene chiamato tramite un oggetto proxy, viene effettivamente chiamatoInvocationHandlerDiinvokeMetodo, in cui viene eseguita la logica personalizzata e quindi viene chiamato il metodo della classe originale.
  1. public class TargetUser {
  2. public static void main(String[] args) {
  3. TargetInteface target = (TargetInteface) TargetProxy.getTarget(new Target());
  4. target.method1();
  5. System.out.println("-----------------------------");
  6. target.method2();
  7. System.out.println("-----------------------------");
  8. System.out.println(target.method3(3));
  9. }
  10. }

Output dei risultati:
Prima di eseguire il metodo...
metodo1 in esecuzione ...
Dopo aver eseguito il metodo...
-----------------------------
Prima di eseguire il metodo...
metodo2 in esecuzione ...
Dopo aver eseguito il metodo...
-----------------------------
Prima di eseguire il metodo...
metodo3 in esecuzione ...
Dopo aver eseguito il metodo...
3

③ Funzionalità del proxy JDK

  1. Procuratore dell'interfaccia: JDK Proxy può eseguire il proxy solo delle classi che implementano le interfacce e non può eseguire il proxy delle classi ordinarie che non implementano le interfacce.
  2. Generato dinamicamente: la classe proxy viene generata dinamicamente in fase di esecuzione e gli sviluppatori non devono scrivere manualmente il codice per la classe proxy.
  3. Elevata flessibilità: è possibile aggiungere funzionalità o logica aggiuntive alla classe originale senza modificare il codice della classe originale.
  4. Considerazioni sulle prestazioni: A causa del coinvolgimento della riflessione e della generazione di classi dinamiche, le prestazioni del proxy JDK potrebbero essere leggermente inferiori a quelle dei proxy statici o delle chiamate dirette ai metodi della classe originale.

2)CGLib

① Il principio fondamentale del proxy dinamico CGLib

  1. Operazioni di bytecode : CGLib utilizza ASM (un framework di manipolazione del bytecode piccolo e veloce) per generare dinamicamente nuove classi Java (solitamente sottoclassi della classe di destinazione). Queste classi appena generate ereditano dalla classe di destinazione e inseriscono la logica proxy quando viene chiamato il metodo.
  2. Intercettazione del metodo : La funzione principale di CGLib è implementare l'intercettazione a livello di metodo.Gli sviluppatori implementanoMethodInterceptorinterfaccia per definire un intercettore di metodo, che eseguirà la logica personalizzata prima e dopo la chiamata al metodo dell'oggetto proxy, come preelaborazione, postelaborazione, gestione delle eccezioni, ecc.
  3. Meccanismo FastClass : Per migliorare le prestazioni, CGLib adotta il meccanismo FastClass. FastClass indicizza i metodi della classe di destinazione e accede al metodo di destinazione direttamente tramite l'indice durante la chiamata. Questo metodo è molto più veloce della riflessione Java.

② Fasi di implementazione del proxy dinamico CGLib

  • Presentazione delle dipendenze CGLib:Introdurre la dipendenza Maven o Gradle di CGLib nel progetto.
  1. import net.sf.cglib.proxy.Enhancer;
  2. import net.sf.cglib.proxy.MethodInterceptor;
  3. import net.sf.cglib.proxy.MethodProxy;
  4. import java.lang.reflect.Method;
  • Definire la classe di destinazione: definisce la classe di destinazione che deve essere sottoposta a proxy.
  1. public class Target {
  2. public void method1() {
  3. System.out.println("method1 running ...");
  4. }
  5. public void method2() {
  6. System.out.println("method2 running ...");
  7. }
  8. public int method3(Integer i) {
  9. System.out.println("method3 running ...");
  10. return i;
  11. }
  12. }
  • Implementare l'interfaccia MethodInterceptor:Crea un'implementazioneMethodInterceptorclasse di interfaccia e overrideintercept metodo. Scrivi la logica del proxy in questo metodo.
  • Crea oggetto proxy: utilizzare quello fornito da CGLibEnhancer classe per creare oggetti proxy.È necessario impostare la classe proxy (tramitesetSuperclassmetodo) e callback (viasetCallbackMethod imposta la classe di implementazione MethodInterceptor).
  1. public class TargetProxy {
  2. public static <T> Object getProxy(T t) {
  3. Enhancer en = new Enhancer(); //帮我们生成代理对象
  4. en.setSuperclass(t.getClass());//设置要代理的目标类
  5. en.setCallback(new MethodInterceptor() {//代理要做什么
  6. @Override
  7. public Object intercept(Object object, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
  8. System.out.println("执行方法前。。。");
  9. //调用原有方法
  10. Object invoke = methodProxy.invokeSuper(object, args);
  11. // Object invoke = method.invoke(t, args);// 作用等同与上面。
  12. System.out.println("执行方法后。。。");
  13. return invoke;
  14. }
  15. });
  16. return en.create();
  17. }
  18. }
  • Utilizza oggetti proxy: attivato quando il metodo della classe di destinazione viene chiamato tramite l'oggetto proxyinterceptLogica proxy nei metodi.
  1. public class TargetUser {
  2. public static void main(String[] args) {
  3. Target target = (Target) TargetProxy.getProxy(new Target());
  4. System.out.println(target.getClass().getName());
  5. target.method1();
  6. }
  7. }

Output dei risultati:

com.heaboy.aopdemo.cglibproxy.TargetENHUNNCeRBeCGLIOBilf9f41fb8
prima di eseguire il metodo. . .
metodo1 in esecuzione ...
Dopo aver eseguito il metodo. . .

③ Scenari applicabili del proxy dinamico CGLib

  1. È necessario eseguire il proxy delle classi che non implementano interfacce: Quando la classe di destinazione non implementa alcuna interfaccia, CGLib può essere utilizzata come proxy.
  2. Requisiti ad alte prestazioni: negli scenari con requisiti di prestazioni elevate, se il proxy dinamico JDK non è in grado di soddisfare le esigenze, è possibile prendere in considerazione l'utilizzo di CGLib.
  3. Implementazione del quadro AOP: Nei framework di programmazione orientati agli aspetti, come Spring AOP, quando è necessario eseguire il proxy di una classe che non implementa un'interfaccia, CGLib viene solitamente utilizzata come implementazione sottostante.

④ Vantaggi e svantaggi del proxy dinamico CGLib

vantaggio:

  1. Elevata flessibilità: Può proxy classi che non implementano interfacce, ampliando l'ambito di applicazione del proxy.
  2. Prestazioni migliori: Attraverso il meccanismo FastClass, l'efficienza della chiamata è superiore rispetto al meccanismo di riflessione del proxy dinamico JDK.
  3. Potente: supporta l'aggiunta dinamica di funzionalità o logica aggiuntive alle classi di destinazione in fase di esecuzione senza modificare il codice della classe originale.

discordanza:

  1. Overhead dell'operazione bytecode: La generazione dinamica del bytecode e il caricamento nella JVM comportano un certo sovraccarico delle prestazioni.
  2. Impossibile eseguire il proxy delle classi e dei metodi finali: Poiché CGLib implementa il proxy ereditando la classe di destinazione, non può proxy classi e metodi modificati in modo finale.
  3. Più complesso da usare: Rispetto al proxy dinamico JDK, CGLib è più complesso da utilizzare e necessita di introdurre dipendenze aggiuntive e gestire i problemi di generazione del bytecode.

3)Proxy JDK contro CGLib

Tipo di oggetto proxy:JDK Proxy può eseguire il proxy solo delle classi che implementano le interfacce; mentre CGLib può eseguire il proxy direttamente delle classi ordinarie.

prestazione: CGLib genera sottoclassi di classi proxy in fase di esecuzione ed è generalmente considerato con prestazioni leggermente migliori rispetto a JDK Proxy. Nella maggior parte degli scenari, tuttavia, questa differenza di prestazioni non è significativa.

scene da utilizzare: Se l'oggetto di destinazione implementa già l'interfaccia, utilizzare JDK Proxy è una scelta semplice e diretta. Se è necessario eseguire il proxy di una classe che non implementa un'interfaccia, è necessario utilizzare CGLib.

fare affidamento:JDK Proxy non richiede dipendenze aggiuntive perché fa parte della libreria principale Java mentre CGLib richiede l'aggiunta della libreria CGLib come dipendenza del progetto.

JDK Proxy è una funzione fornita con il linguaggio Java e non necessita di essere implementata caricando classi di terze parti;

Java fornisce un supporto stabile per JDK Proxy e continuerà ad aggiornare e aggiornare JDK Proxy. Ad esempio, le prestazioni di JDK Proxy nella versione Java 8 sono state notevolmente migliorate rispetto alle versioni precedenti;

JDK Proxy è implementato tramite intercettori e riflessione;

JDK Proxy può proxy solo per le classi che ereditano le interfacce;

JDK Proxy è relativamente semplice da implementare e chiamare;

CGLib è uno strumento fornito da terze parti, implementato sulla base di ASM e ha prestazioni relativamente elevate;

CGLib non necessita di essere implementato tramite un'interfaccia, viene chiamato implementando una sottoclasse.

4. Proxy statico VS proxy dinamico

la differenza

Proxy statico: I proxy statici vengono determinati durante la compilazione. È necessario scrivere una classe proxy per ciascuna classe proxy. La classe proxy e la classe proxy implementano la stessa interfaccia o ereditano la stessa classe genitore.

La classe proxy di un proxy statico esiste in fase di compilazione, quindi può proxy solo classi specifiche quando il programma è in esecuzione e non può decidere dinamicamente quali classi proxy.

Il proxy statico racchiude la chiamata al metodo dell'oggetto originale e può aggiungere ulteriore logica prima e dopo la chiamata, ma la classe proxy deve essere scritta in anticipo, il che aumenterà la quantità di codice.

Il proxy statico specifica esplicitamente l'oggetto proxy nel codice ed è relativamente intuitivo da utilizzare, ma l'aggiunta di nuove classi proxy richiede la ricompilazione.

Procura dinamica: Il proxy dinamico crea oggetti proxy in fase di esecuzione senza scrivere in anticipo le classi proxy. Utilizza il meccanismo di riflessione di Java per generare dinamicamente classi proxy e oggetti proxy.

I proxy dinamici si basano su interfacce e sono implementati tramite la classe java.lang.reflect.Proxy e l'interfaccia java.lang.reflect.InvocationHandler.

Il proxy dinamico può eseguire il proxy di classi di più interfacce e decidere dinamicamente quali classi proxy. In fase di esecuzione, è possibile generare proxy per diversi oggetti in base alle necessità, garantendo una maggiore flessibilità.

Il proxy dinamico non ha bisogno di scrivere una classe proxy specifica per ciascuna classe proxy, il che è più flessibile e consente di risparmiare codice.

Gli agenti dinamici possono aggiungere logica personalizzata prima e dopo le chiamate ai metodi sull'oggetto proxy, come la registrazione, la gestione delle transazioni, ecc. Lo svantaggio del proxy dinamico è che, rispetto al proxy statico, la generazione di oggetti proxy in fase di esecuzione richiede un certo sovraccarico delle prestazioni.

Scena applicabile

I proxy statici sono adatti per i seguenti scenari:

Quando il numero di oggetti di destinazione (oggetti proxy) è limitato e determinato, il proxy statico può essere implementato scrivendo manualmente le classi proxy. I proxy statici creano classi proxy in fase di compilazione, quindi hanno prestazioni migliori in fase di esecuzione.

I proxy statici incapsulano l'oggetto di destinazione e aggiungono funzioni aggiuntive senza modificare il codice originale. Ciò rende i proxy statici spesso utilizzati per questioni trasversali come la registrazione e la gestione delle transazioni.

Il proxy dinamico è adatto per i seguenti scenari:

Quando il numero di oggetti target è incerto o non può essere determinato in anticipo, i proxy dinamici possono generare in modo più conveniente oggetti proxy. Genera classi proxy e oggetti proxy in fase di esecuzione, evitando il noioso lavoro di scrivere manualmente più classi proxy.

I proxy dinamici forniscono la flessibilità necessaria per aggiungere, rimuovere o modificare il comportamento del proxy per gli oggetti di destinazione in fase di runtime. Ciò rende i proxy dinamici spesso utilizzati in scenari applicativi come AOP (programmazione orientata agli aspetti) e RPC (chiamata di procedura remota).

Va notato che poiché i proxy dinamici creano classi proxy e oggetti proxy attraverso il meccanismo di riflessione in fase di esecuzione, le loro prestazioni potrebbero essere leggermente inferiori a quelle dei proxy statici. Inoltre, i proxy dinamici possono proxy solo oggetti di destinazione che implementano l'interfaccia, mentre i proxy statici non hanno questa restrizione.

In sintesi, i proxy statici sono adatti a scenari in cui il numero di oggetti target è limitato e certo e richiedono incapsulamento e funzioni aggiuntive mentre i proxy dinamici sono adatti a scenari in cui il numero di oggetti target è incerto o non può essere determinato in anticipo; e il comportamento del proxy deve essere aggiunto, eliminato o modificato in modo flessibile. Scegli il metodo di agenzia appropriato in base alle esigenze e alle circostanze specifiche.

5. Implementazione del proxy in SpringAOP

1) Introduzione a SpringAOP

Parla della comprensione di AOP

Spring AOP è un modulo importante nel framework Spring, utilizzato per implementare la programmazione orientata agli aspetti.

Programmazione faccia a faccia , si tratta di un modello di programmazione che consente ai programmatori di modularizzare attraverso punti trasversali personalizzati e incapsulare comportamenti che influenzano più classi in moduli riutilizzabili. Esempio: ad esempio, l'output del log, se non si utilizza AOP, è necessario inserire le istruzioni di output del log in tutte le classi e i metodi. Tuttavia, con AOP, è possibile incapsulare le istruzioni di output del log in un modulo riutilizzabile e inserirle in un file modo dichiarativo. In una classe, l'output del log viene completato automaticamente ogni volta che viene utilizzata la classe.

Nell'idea della programmazione orientata agli aspetti, le funzioni sono divise in due tipi

  • Attività principale: Login, registrazione, aggiunta, cancellazione, modifica e interrogazione sono tutti definiti attività principali

  • Funzioni periferiche: I registri e la gestione delle transazioni sono servizi periferici secondari.

Nella programmazione orientata agli aspetti, le funzioni aziendali principali e le funzioni periferiche vengono sviluppate in modo indipendente e le due non sono accoppiate. Quindi le funzioni aziendali e le funzioni aziendali principali vengono "intrecciate" insieme, il che si chiama AOP.

AOP può convertire coloro che non sono legati all'azienda,Ma è incapsulato per la logica o le responsabilità (come l'elaborazione delle transazioni, la gestione dei log, il controllo dei permessi, ecc.) comunemente chiamate dai moduli aziendali.,facile daRidurre il codice duplicato nel sistemaRidurre l'accoppiamento tra i moduli,EFavorevole alla futura scalabilità e manutenibilità

Nell'AOP sono presenti i seguenti concetti:

  • AspettoJ: Aspect è solo un concetto. Non esiste un'interfaccia o una classe specifica corrispondente. È un nome collettivo per Join point, Advice e Pointcut.

  • Punto di unione : Il punto di connessione si riferisce a un punto durante l'esecuzione del programma, come l'invocazione del metodo, la gestione delle eccezioni, ecc. In Spring AOP sono supportati solo i punti di join a livello di metodo.

  • Consiglio : La notifica, cioè la logica trasversale in un aspetto che definiamo, ha tre tipologie: "intorno", "prima" e "dopo". In molti framework di implementazione AOP, Advice solitamente agisce come un interceptor e può anche contenere molti interceptor come collegamento da elaborare attorno al punto di join.

  • Taglio a punti: Pointcut, utilizzato per abbinare i punti di join. Quali punti di join contenuti in un AspectJ devono essere filtrati da Pointcut.

  • introduzione : Introduzione, che consente ad un aspetto di dichiarare che gli oggetti avvisati implementano eventuali interfacce aggiuntive che in realtà non implementano. Ad esempio, un oggetto proxy può essere utilizzato per eseguire il proxy di due classi di destinazione.

  • Tessitura : Tessitura, ora che abbiamo punti di connessione, punti di taglio, notifiche e aspetti, come applicarli al programma? Esatto, si sta intrecciando. Sotto la guida di pointcuts, la logica di notifica viene inserita nel metodo di destinazione, in modo che la nostra logica di notifica possa essere eseguita quando viene chiamato il metodo.

  • Procuratore AOP : Il proxy AOP si riferisce all'oggetto che implementa il protocollo dell'aspetto nel framework di implementazione AOP. Esistono due tipi di proxy in Spring AOP, ovvero il proxy dinamico JDK e il proxy dinamico CGLIB.

  • Oggetto di destinazione: L'oggetto target è l'oggetto proxy.

Spring AOP è implementato in base al proxy dinamico JDK e alla promozione Cglib. Entrambi i metodi proxy sono metodi runtime, quindi non hanno un'elaborazione in fase di compilazione, quindi Spring è implementato tramite codice Java.

Quale problema risolve l'AOP?

Alcuni comportamenti comuni sparsi tra più classi o oggetti (come registrazione, gestione delle transazioni, controllo dei permessi, limitazione della corrente dell'interfaccia, alimentazione dell'interfaccia, ecc.), questi comportamenti sono solitamente chiamati preoccupazioni trasversali . Se implementiamo questi comportamenti ripetutamente in ogni classe o oggetto, ciò porterà a un codice ridondante, complesso e difficile da mantenere.

L'AOP può trasformare preoccupazioni trasversali (come la registrazione, la gestione delle transazioni, il controllo dei permessi, la limitazione della corrente dell'interfaccia, la potenza dell'interfaccia, ecc.) da Logica aziendale principale (preoccupazioni principali, preoccupazioni principali) Separarsi dal focus per ottenere la separazione delle preoccupazioni.

Scenari applicativi AOP

  • Registrazione: personalizza le annotazioni di registrazione e utilizza AOP per ottenere la registrazione con una riga di codice.

  • Statistiche sulle prestazioni: utilizzare AOP per contare il tempo di esecuzione del metodo prima e dopo l'esecuzione del metodo di destinazione per facilitare l'ottimizzazione e l'analisi.

  • Gestione delle transazioni:@Transactional Le annotazioni consentono a Spring di eseguire la gestione delle transazioni per noi, come il rollback delle operazioni di eccezione, eliminando la logica di gestione delle transazioni ripetute.@TransactionalLe annotazioni vengono implementate in base all'AOP.

  • Controllo delle autorizzazioni: utilizzare AOP per determinare se l'utente dispone delle autorizzazioni richieste prima di eseguire il metodo di destinazione. In tal caso, eseguire il metodo di destinazione, altrimenti non verrà eseguito.Ad esempio, SpringSecurity utilizza@PreAuthorize Puoi personalizzare la verifica dell'autorizzazione commentando una riga di codice.

  • Limitazione della corrente dell'interfaccia: utilizzare AOP per limitare la richiesta tramite specifici algoritmi e implementazioni di limitazione della corrente prima dell'esecuzione del metodo di destinazione.

  • Gestione della cache: utilizzare AOP per leggere e aggiornare la cache prima e dopo l'esecuzione del metodo di destinazione.

Come viene implementata l'AOP

I metodi di implementazione comuni di AOP includono proxy dinamico, operazione bytecode, ecc.

Spring AOP si basa sul proxy dinamico. Se l'oggetto da proxy implementa una determinata interfaccia, verrà utilizzato Spring AOP Proxy JDK, per creare un oggetto proxy. Per gli oggetti che non implementano l'interfaccia, non è possibile utilizzare il proxy JDK. Al momento, verrà utilizzato Spring AOP Cglib Genera una sottoclasse dell'oggetto proxy che funge da proxy

2) Implementare SpringAOP basato sul proxy dinamico JDK Proxy

① Configura SpringAOP

esisterespring-aop.xmlConfigurare i bean e gli aspetti correlati nel file di configurazione

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <beans xmlns="http://www.springframework.org/schema/beans"
  3. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  4. 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">
  5. <bean id="target" class="com.xxhh.aopdemo.aop.Target"/>
  6. <bean id="targetAdvice" class="com.xxhh.aopdemo.aop.TargetAdvice"/>
  7. <bean id="targetProxy" class="org.springframework.aop.framework.ProxyFactoryBean">
  8. <property name="target" ref="target"/> <!--被代理的类-->
  9. <property name="interceptorNames" value="targetAdvice"/> <!--如果用多种增强方式,value的值使用逗号(,)分割-->
  10. <property name="proxyTargetClass" value="false"/> <!--如果设置为true,则创建基于类的代理(使用CGLIB);如果设置为false,则创建基于接口的代理(使用JDK动态代理)。-->
  11. <property name="interfaces" value="com.xxhh.aopdemo.aop.TargetInteface"/> <!--target实现的接口-->
  12. </bean>
  13. </beans>

② Definire l'interfaccia astratta

  1. public interface TargetInteface {
  2. void method1();
  3. void method2();
  4. int method3(Integer i);
  5. }

③ Definire la classe proxy

  1. public class Target implements TargetInteface{
  2. /*
  3. * 需要增强的方法,连接点JoinPoint
  4. **/
  5. @Override
  6. public void method1() {
  7. System.out.println("method1 running ...");
  8. }
  9. @Override
  10. public void method2() {
  11. System.out.println("method2 running ...");
  12. }
  13. @Override
  14. public int method3(Integer i) {
  15. System.out.println("method3 running ...");
  16. return i;
  17. }
  18. }

④ Definisci classe proxy (metodo di miglioramento)

  1. public class TargetAdvice implements MethodInterceptor, MethodBeforeAdvice, AfterReturningAdvice {
  2. /*
  3. * 通知/增强
  4. **/
  5. @Override
  6. public Object invoke(MethodInvocation methodInvocation) throws Throwable {
  7. System.out.println("前置环绕通知");
  8. Object proceed = methodInvocation.proceed();
  9. System.out.println("后置环绕通知");
  10. return proceed;
  11. }
  12. @Override
  13. public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {
  14. System.out.println("后置返回通知");
  15. }
  16. @Override
  17. public void before(Method method, Object[] args, Object target) throws Throwable {
  18. System.out.println("前置通知");
  19. }
  20. }

⑤ Prova

  1. public class AopTest {
  2. public static void main(String[] args) {
  3. ApplicationContext appCtx = new ClassPathXmlApplicationContext("spring-aop.xml");
  4. TargetInteface targetProxy = (TargetInteface) appCtx.getBean("targetProxy");
  5. targetProxy.method1();
  6. }
  7. }

Risultato dell'output:

Notifica frontale surround
Pre-notifica
metodo1 in esecuzione ...
notifica di reso post
Notifica surround posteriore

3) Implementare SpringAOP basato sul proxy dinamico CGLib

① Configura SpringAOP

esisterespring-confaop.xmlConfigurare i bean e gli aspetti correlati nel file di configurazione

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <beans xmlns="http://www.springframework.org/schema/beans"
  3. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  4. xmlns:context="http://www.springframework.org/schema/context"
  5. xmlns:aop="http://www.springframework.org/schema/aop"
  6. 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">
  7. <!--先开启cglib代理,开启 exposeProxy = true,暴露代理对象-->
  8. <aop:aspectj-autoproxy proxy-target-class="true" expose-proxy="true"/>
  9. <!--扫包-->
  10. <context:component-scan base-package="com.xxhh.aopdemo.annotationaop"/>
  11. </beans>

② Definire la classe proxy

  1. /*
  2. * 目标类
  3. **/
  4. public class Target {
  5. public void method1() {
  6. System.out.println("method1 running ...");
  7. }
  8. public void method2() {
  9. System.out.println("method2 running ...");
  10. }
  11. /*
  12. * 连接点JoinPoint
  13. **/
  14. public int method3(Integer i) {
  15. System.out.println("method3 running ...");
  16. // int i1 = 1 / i;
  17. return i;
  18. }
  19. }

③ Definisci la classe proxy (classe aspetto)

  1. import org.aspectj.lang.ProceedingJoinPoint;
  2. /*
  3. * 切面类
  4. **/
  5. public class TargetAspect {
  6. /*
  7. * 前置通知
  8. **/
  9. public void before() {
  10. System.out.println("conf前置通知");
  11. }
  12. public void after() {
  13. System.out.println("conf后置通知");
  14. }
  15. public void afterReturning() {
  16. System.out.println("conf后置返回通知");
  17. }
  18. public void afterThrowing(Exception ex) throws Exception {
  19. // System.out.println("conf异常通知");
  20. // System.out.println(ex.getMessage());
  21. }
  22. public Object around(ProceedingJoinPoint pjp) throws Throwable {
  23. Object proceed = null;
  24. if (!"".equals("admin")) {
  25. System.out.println("conf环绕前置");
  26. proceed = pjp.proceed(pjp.getArgs());
  27. System.out.println("conf环绕后置");
  28. }
  29. return proceed;
  30. }
  31. }

④ Prova

  1. public class AopTest {
  2. public static void main(String[] args) {
  3. ApplicationContext appCtx = new ClassPathXmlApplicationContext("spring-confaop.xml");
  4. Target targetProxy = (Target) appCtx.getBean("target");
  5. System.out.println(targetProxy.method3(0));
  6. }
  7. }

Risultato dell'output:

conf pre-notifica
conf prefisso surround
metodo3 in esecuzione ...
conf notifica post-restituzione
conf surround post
conf notifica post

4) Implementare SpringAOP basato sul proxy dinamico dell'annotazione

① Configura SpringAOP

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <beans xmlns="http://www.springframework.org/schema/beans"
  3. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  4. xmlns:context="http://www.springframework.org/schema/context"
  5. xmlns:aop="http://www.springframework.org/schema/aop"
  6. 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">
  7. <!--先开启cglib代理,开启 exposeProxy = true,暴露代理对象-->
  8. <aop:aspectj-autoproxy proxy-target-class="true" expose-proxy="true"/>
  9. <!--扫包-->
  10. <context:component-scan base-package="com.xxhh.aopdemo.annotationaop"/>
  11. </beans>

② Definire le annotazioni

  1. @Target(ElementType.METHOD)
  2. @Retention(RetentionPolicy.RUNTIME)
  3. @Documented
  4. public @interface TestAnnotation{
  5. }

③ Definire le classi di aspetto

  1. /*
  2. * 切面类
  3. **/
  4. @Aspect
  5. @Component
  6. public class AnnotationAspect {
  7. // 定义一个切点:所有被RequestMapping注解修饰的方法会织入advice
  8. @Pointcut("@annotation(TestAnnotation)")
  9. private void advicePointcut() {}
  10. /*
  11. * 前置通知
  12. **/
  13. @Before("advicePointcut()")
  14. public void before() {
  15. System.out.println("annotation前置通知");
  16. }
  17. @After("advicePointcut()")
  18. public void after() {
  19. System.out.println("annotation后置通知");
  20. }
  21. @AfterReturning(pointcut = "advicePointcut()")
  22. public void afterReturning() {
  23. System.out.println("annotation后置返回通知");
  24. }
  25. @AfterThrowing(pointcut = "advicePointcut()", throwing = "ex")
  26. public void afterThrowing(Exception ex) throws Exception {
  27. System.out.println("annotation异常通知");
  28. System.out.println(ex.getMessage());
  29. }
  30. @Around("advicePointcut()")
  31. public Object around(ProceedingJoinPoint pjp) throws Throwable {
  32. Object proceed = null;
  33. if (!"".equals("admin")) {
  34. System.out.println("annotation环绕前置");
  35. proceed = pjp.proceed(pjp.getArgs());
  36. System.out.println("annotation环绕后置");
  37. }
  38. return proceed;
  39. }
  40. }

④ Il controller aggiunge annotazioni

  1. @Controller
  2. public class TestController {
  3. @RequestMapping("/test.do")
  4. @ResponseBody
  5. public String testController() {
  6. TestController o = (TestController) AopContext.currentProxy();
  7. o.test();
  8. // System.out.println("tewt");
  9. return "ok";
  10. }
  11. @TestAnnotation
  12. public void test() {
  13. System.out.println("test running");
  14. }
  15. }

⑤ Prova