Perché i miei aspetti sono eseguite nel loro contesto originale e non quando confezionate in un barattolo separato e chiamato da un'altra parte?

StackOverflow https://stackoverflow.com/questions/1265487

  •  13-09-2019
  •  | 
  •  

Domanda

Sono un novizio per AspectJ ...

ho scritto il seguente aspetto che ha lo scopo di aggiungere la registrazione di chiamate di funzione di tipo public * doSomething*(..). Se la mia classe principale è parte dello stesso progetto la tessitura dell'aspetto viene eseguita senza un problema tecnico e il codice viene eseguito. Se I pack il codice tessuta in un vaso e lo chiamo da un altro progetto Eclipse - il consiglio non viene eseguito. Un altro scenario è di imballaggio fino all'aspetto (.aj) soltanto in un vaso separato e aggiungendo quel vaso al "Aspect Path" in eclisse, ciò consente eclisse per tessere all'aspetto correttamente. Il fatto è che ho bisogno di avvolgere questo in un vaso e chiamare il codice da altrove. Che non funziona o (Non sorprende che presumo ...) Perché?

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.reflect.CodeSignature;
import org.apache.log4j.Logger;

public aspect Logging {
    pointcut allPublic(): !cflow(call(public void main(..))) && (call(public * doSomething*(..)));

    private static final Logger log = Logger.getLogger("Logging.aspect");

    @SuppressWarnings({"unchecked", "unused"})
    private void printParameters(JoinPoint jp) {
        CodeSignature methodSignature = (CodeSignature) jp.getSignature();
        String methodName = methodSignature.getName();
        Object[] paramNames = methodSignature.getParameterNames();
        Class[] paramTypes = (Class[])methodSignature.getParameterTypes();
        Object[] paramObjects = jp.getArgs();
        StringBuffer infoMsg = new StringBuffer();

        infoMsg.append("Entering function: " + methodName);
        if (paramNames != null && paramNames.length > 0){
            if (paramNames.length == 1){
                infoMsg.append(" with input parameter: ["+ paramNames[1]+ "] = [" + paramObjects[1] + "]");
            }
            else {
                infoMsg.append(" with input parameters: ");
            }
            for (int i = 1; i < paramNames.length; i++) {
                infoMsg.append(" [" + paramTypes[i].getName() + " " + paramNames[i]+ "] = [" + paramObjects[i] + "]");
            }
        }
        else {
            infoMsg.append(" NONE");
        }
       log.info(infoMsg.toString());

    }

    @SuppressWarnings("unused")
    private void printExit(JoinPoint jp) {
        log.info("Exit function: " + jp.getSignature().toString());
    }

    before() : allPublic() {
        printParameters (thisJoinPoint);
    }

    after() : allPublic() {
        printExit(thisJoinPoint);
    }
}

La classe che dovrebbe essere informati:

public class Main {

    private static final Logger log = Logger.getLogger("A.class");

    public static void doSomethingAa(int number, String message, Map<String, String> map){
        log.debug("A");
    } 

    public static void doSomethingB(int id, String name){
        log.debug("B");
    }

    public static void main(String[] args){
        Map<String, String> map1 = new TreeMap<String, String>();
        Map<String, String> map2 = new TreeMap<String, String>();

        map1.put("FirstKey", "FirstValue");
        map1.put("SecondKey", "SecondValue");

        map2.put("Tal", "Guy");
        map2.put("Happy", "Birthday");

        A.doSomethingAa(17, "Tal", map1);
        A.doSomethingAa(35, "Guy", map2); 

        A.doSomethingB(12, "TalG");
        A.doSomethingB(40, "GuyG");

        System.out.println("Finished running main");

    }

}

Grazie a tutti!

È stato utile?

Soluzione

Non ho provato ad utilizzare aspectj nello sviluppo di plug-in, quindi ci potrebbe essere un paio di cose aggiuntive. Ma ecco un paio di cose che dovete fare per garantire l'obiettivo è tessuto in modo corretto al momento della compilazione e possono essere eseguiti.

  • Il plugin che viene tessuta necessità di avere una dipendenza del plugin contenente l'aspetto
  • L'aspetto deve essere nei pacchetti esportati nel classpath
  • Il plugin di destinazione deve avere aspectjrt nel classpath in modo che possa gestire gli aspetti
  • Il compilatore aspectj deve essere utilizzato a tessere il bersaglio quando viene compilato.

Update, sono stato in grado di riprodurre il problema (vale a dire che funziona bene sulla mia macchina). Per replicare la situazione che ho creato un progetto AspectJ con il file Logging.aj singolo nella directory di origine. Ho esportato che come un file jar (chiamato logging.jar) per radice di un altro progetto (l'altro progetto impostato anche come un progetto AspectJ che contiene la classe "principale"). Ho poi modificato il percorso aspetto del progetto "principale" per includere il logging.jar e gli aspetti e la consulenza è stata tessuta a ciascuna doSomethingAa () e doSomethingB () chiamata di metodo.

L'unico problema che ho trovato con il tuo codice è stato che le chiamate di metodo statico sono per "A" piuttosto che "principale".

Ecco la voce dal file di .classpath del progetto principale:

<classpathentry kind="lib" path="logging.jar">
  <attributes>
    <attribute name="org.eclipse.ajdt.aspectpath"
        value="org.eclipse.ajdt.aspectpath"/>
  </attributes>
</classpathentry>

Ho provato varie permutazioni, e gli unici modi che posso ottenere per non di lavoro sono rimuovendo la natura AspectJ o rimuovere il barattolo dal percorso di generazione.

Ci sono altri fattori che possono influenzare il vostro spazio di lavoro che hai omesso?


Un altro punto sul vostro aspetto registrazione che ho trovato in un progetto simile; Separati prima e dopo consiglio si tradurrà in casi joinpoint stati creati due volte per ogni chiamata di metodo, questo può causare un problema per la garbage collection se il tipo di registrazione tesse un sacco di metodi. Invece si potrebbe considerare l'utilizzo intorno consulenza per accedere sia l'ingresso e l'uscita, questo rende anche più facile aggiungere in qualsiasi momento la registrazione metodo di esecuzione se si decide di seguito.


Aggiornamento: In base alle vostre osservazioni, ho aggiunto un terzo progetto (aj_client) al mio lavoro e sono andato attraverso le seguenti fasi:

  1. Modificato Logging.aj fare System.out chiama, escludendo problemi di configurazione log4j
    • aj_logging esportato (progetto AspectJ contenente Logging.aj) per logging.jar
    • aggiunto logging.jar al Sentiero Aspetto della aj_target
    • aj_target esportato (progetto AspectJ contenente Main.java) per target.jar
    • Creata una nuova classe (Client.java) nel progetto aj_client (che non ha la natura AspectJ).
    • aggiunto target.jar, logging.jar (e log4j.jar) al costruire il percorso Java di aj_client e corse.

Client.java contiene un solo metodo:

public static void main(String[] args) {
    Main.main(args);
}

Quando viene eseguito, questo viene a mancare con un NoClassDefFoundError:

Exception in thread "main" java.lang.NoClassDefFoundError: org/aspectj/lang/Signature
at Client.main(Client.java:6)
Caused by: java.lang.ClassNotFoundException: org.aspectj.lang.Signature

Per risolvere questo, ho modificato il .classpath di aj_client così è aspectjrt su di esso (aggiungendo manualmente il AspectJ libreria runtime classpath contenitore al .classpath) e reran, il programma esegue le uscite le dichiarazioni di registrazione:

Entering function: doSomethingAa with input parameters:  [java.lang.String message] = [Tal] [java.util.Map map] = [{FirstKey=FirstValue, SecondKey=SecondValue}]
log4j:WARN No appenders could be found for logger (A.class).
log4j:WARN Please initialize the log4j system properly.
Exit function: void target.Main.doSomethingAa(int, String, Map)
Entering function: doSomethingAa with input parameters:  [java.lang.String message] = [Guy] [java.util.Map map] = [{Happy=Birthday, Tal=Guy}]
Exit function: void target.Main.doSomethingAa(int, String, Map)
Entering function: doSomethingB with input parameters:  [java.lang.String name] = [TalG]
Exit function: void target.Main.doSomethingB(int, String)
Entering function: doSomethingB with input parameters:  [java.lang.String name] = [GuyG]
Exit function: void target.Main.doSomethingB(int, String)
Finished running main

Il file .classpath per aj_client assomiglia a questo:

<?xml version="1.0" encoding="UTF-8"?>
<classpath>
    <classpathentry kind="src" path="src/main/java"/>
    <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
    <classpathentry kind="con" path="org.eclipse.ajdt.core.ASPECTJRT_CONTAINER"/>
    <!-- the other jars for the logging and target projects -->
    <classpathentry kind="lib" path="/aj_target/target.jar"/>
    <classpathentry kind="lib" path="/aj_target/log4j-1.2.14.jar"/>
    <classpathentry kind="lib" path="/aj_target/logging.jar"/>
    <classpathentry kind="output" path="target/classes"/>
</classpath>

Ho provato anche indicando il mio aspectjrt nel mio repository Maven e plug-in di Eclipse, con lo stesso risultato (i messaggi di registrazione erano di uscita), vale a dire la sostituzione:

<classpathentry kind="con" path="org.eclipse.ajdt.core.ASPECTJRT_CONTAINER"/>

con

<!--aspectjrt from Maven repository-->
<classpathentry kind="lib" path="C:/maven-2.2.0/repo/aspectj/aspectjrt/1.5.3/aspectjrt-1.5.3.jar"/>

o

<!--aspectjrt from Eclipse plugin -->
<classpathentry kind="lib" path="C:/eclipse-3.5/eclipse/plugins/org.aspectj.runtime_1.6.5.20090618034232/aspectjrt.jar"/>

Dopo aver dimostrato che il codice di registrazione è tessuto, sono tornato e ha cambiato Logging.aj utilizzare getLog (). Info () chiama ancora una volta, e ho trovato le dichiarazioni di registrazione sono uscita non sono più. Per ovviare a questo ho aggiunto un file di configurazione log4j.xml (solo specificando l'appender root)

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE log4j:configuration SYSTEM "log4j.dtd">

<log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/">
  <appender name="console" class="org.apache.log4j.ConsoleAppender"> 
    <param name="Target" value="System.out"/> 
    <layout class="org.apache.log4j.PatternLayout"> 
      <param name="ConversionPattern" value="%-5p %c{1} - %m%n"/> 
    </layout> 
  </appender> 

  <root> 
    <priority value ="debug" /> 
    <appender-ref ref="console" /> 
  </root>

</log4j:configuration>

Il risultato è il seguente output:

DEBUG class - A
INFO  Logging - Exit function: void target.Main.doSomethingAa(int, String, Map)
INFO  Logging - Entering function: doSomethingB with input parameters:  [java.lang.String name] = [TalG]
DEBUG class - B
INFO  Logging - Exit function: void target.Main.doSomethingB(int, String)
INFO  Logging - Entering function: doSomethingB with input parameters:  [java.lang.String name] = [GuyG]
DEBUG class - B
INFO  Logging - Exit function: void target.Main.doSomethingB(int, String)
Finished running main

Nota È necessario essere attenti a garantire si have pulito, costruito, ed esportato logging.jar prima della pulizia, costruzione, e l'esportazione target.jar, quindi pulire il progetto client. Se si letame l'ordine a tutti si otterrà contenuti non corrispondenti.


Riepilogo

Così sembra a patto che il progetto client fa riferimento a un "target.jar" che è stato costruito con AspectJ (in modo che il Logging.aj è stata tessuta), e si dispone di un aspectjrt.jar sul tuo classpath e è stato configurato correttamente log4j la registrazione viene emesso.

È possibile specificare la dipendenza aspectjrt sia aggiungendo il contenitore percorso di classe, o specificando il percorso di un aspectjrt.jar compatibile

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top