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