Por que meus aspectos são executados em sua configuração original e não quando embalado como um frasco separado e chamado a partir de outro lugar?

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

  •  13-09-2019
  •  | 
  •  

Pergunta

Eu sou um novato para aspectj ...

Eu escrevi o seguinte aspecto que se pretende adicionar o registo de chamadas de função do tipo public * doSomething*(..). Se a minha classe principal é parte do mesmo projeto de tecelagem do aspecto é realizado sem uma falha e as executa o código. Se eu arrumar o código tecida em uma jarra e chamá-lo de outro projeto eclipse - o conselho não é executado. Outro cenário é a embalagem se o aspecto (.aj), apenas em um frasco separado e acrescentando que jar para o "Caminho Aspect" no eclipse, isso permite eclipse para tecer o aspecto corretamente. A coisa é que eu preciso envolvê-lo em uma jarra e chamar o código de outro lugar. Isso não quer trabalhar (Não surpreendentemente presumo ...) Por quê?

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);
    }
}

A classe que é suposto ser avisados:

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");

    }

}

Obrigado a todos!

Foi útil?

Solução

Eu não tentei usando aspectj no desenvolvimento de plugins, então pode haver algumas coisas adicionais. Mas é aqui algumas coisas que você precisa fazer para garantir a meta é tecida corretamente em tempo de compilação e pode ser executado.

  • O plug-in que está sendo tecida precisa ter uma dependência no plug-in contendo o aspecto
  • As necessidades aspecto a ser nos pacotes exportados no classpath
  • O alvo plugin precisa ter aspectjrt no classpath para que ele possa lidar com os aspectos
  • As necessidades do compilador AspectJ para ser usado para tecer o alvo quando ele é compilado.

Update, eu fui incapaz de reproduzir o seu problema (ou seja, ele funciona bem na minha caixa). Para replicar a situação Eu criei um projeto AspectJ com o arquivo Logging.aj único no diretório de origem. I exportados isso como um arquivo jar (chamado logging.jar) para a raiz de outro projeto (o outro projeto também estabeleceu como um projeto AspectJ que contém a classe "principal"). Eu, então, modificou o Caminho aspecto do projeto "main" para incluir o logging.jar e os aspectos eo conselho foi tecida a cada doSomethingAa () e doSomethingB () chamada de método.

O único problema que eu encontrei com o seu código era que suas chamadas de métodos estáticos são para "A" em vez de "Main".

Aqui é a entrada do arquivo .classpath do projeto principal:

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

Eu tentei várias permutações, e as únicas maneiras que eu posso fazê-lo não trabalho são removendo a natureza AspectJ ou remover o frasco do caminho de construção.

Existem outros fatores que podem estar afetando o seu espaço de trabalho que você omitido?


Um outro ponto sobre o seu aspecto de registro que eu encontrei em um projeto similar; Separar antes e depois de o conselho irá resultar em casos joinpoint sendo criado duas vezes para cada chamada de método, isso pode causar um problema para a coleta de lixo se o seu tipo de registro tece uma série de métodos. Em vez disso você pode considerar o uso em torno de aconselhamento para registrar tanto a entrada e saída, isso também faz com que seja mais fácil adicionar em qualquer registro de tempo de execução do método, se você decidir mais tarde.


Update: Com base em seus comentários, eu adicionei um terceiro projeto (aj_client) para o meu espaço de trabalho e passou pelas seguintes etapas:

  1. Modificado Logging.aj fazer chamadas System.out, descartando problemas de configuração log4j
    • aj_logging exportado (projeto AspectJ contendo Logging.aj) para logging.jar
    • logging.jar adicionado ao caminho Aspecto da aj_target
    • aj_target exportado (projeto AspectJ contendo Main.java) para target.jar
    • criou uma nova classe (Client.java) no projeto aj_client (que não tem natureza AspectJ).
    • target.jar acrescentou, logging.jar (e log4j.jar) para o Caminho de Construção Java de aj_client e ele correu.

Client.java contém um único método:

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

Quando executado, este falha com um 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

Para resolver isso, eu modifiquei a .classpath de aj_client por isso tem aspectjrt sobre ele (adicionando manualmente Runtime Library AspectJ recipiente classpath ao .classpath) e reran, as executa e saídas de programa as declarações de registro:

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

O arquivo .classpath para olhares aj_client assim:

<?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>

Eu também tentei apontando para o meu aspectjrt no meu repositório Maven e Eclipse plugin, com o mesmo resultado (as mensagens de log foram output), ou seja, substituir:

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

com

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

ou

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

Depois de ter provado que o código de registro é tecida, voltei e mudou Logging.aj usar getLog (). Info () chama novamente, e encontrou as declarações de registo já não são de saída. Para remediar esta situação eu adicionei um arquivo de configuração log4j.xml (apenas especificando o 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>

Isto resultou no seguinte resultado:

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 Você precisa ter cuidado para garantir que você have limpo, construído, e exportado logging.jar antes da limpeza, construção e exportação target.jar, em seguida, limpar o projeto do cliente. Se você estragar a ordem em tudo você vai ter conteúdo incompatíveis.


Resumo

Assim parece, desde que seus clientes referências do projeto um "target.jar" que foi construído com AspectJ (por isso o Logging.aj foi tecido), e você tem um aspectjrt.jar em seu classpath e log4j você tenha configurado corretamente o registro será emitido.

Você pode especificar a dependência aspectjrt por qualquer adicionando o recipiente classpath, ou especificando o caminho para um aspectjrt.jar compatíveis

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top