为什么我的方面在其原始设置中执行,而不是打包为单独的 jar 并从其他地方调用时执行?
我是方面的新手...
我编写了以下方面,旨在向 public * doSomething*(..)
类型的函数调用添加日志记录。 如果我的主类是同一项目的一部分,则方面的编织将顺利执行并且代码将执行。 如果我将编织的代码打包到一个 jar 中并从另一个 eclipse 项目调用它 - 该建议不会被执行。 另一种情况是将切面(.aj)仅打包到一个单独的 jar 中,并将该 jar 添加到 eclipse 中的“切面路径”中,这使得 eclipse 能够正确地编织切面。 问题是我需要将其包装到一个 jar 中并从其他地方调用代码。 这也不起作用(毫不奇怪,我认为......)为什么?
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);
}
}
应该建议的班级:
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");
}
}
谢谢大家!
I am a newbie to aspectj...
I have written the following aspect which is intended to add logging to function calls of type public * doSomething*(..)
. If my main class is part of the same project the weaving of the aspect is performed without a glitch and the code executes. If I pack up the weaved code into a jar and call it from another eclipse project - the advice is not executed. Another scenario is packing up the aspect (.aj) only into a separate jar and adding that jar to the "Aspect Path" in eclipse, this enables eclipse to weave the aspect properly. Thing is I need to wrap this up into a jar and call the code from elsewhere. That doesn't work either (Not surprisingly I presume...) Why?
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);
}
}
The class which is supposed to be advised:
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");
}
}
Thanks all!
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
我还没有尝试在插件开发中使用aspectj,所以可能还有一些额外的事情。 但您需要执行以下一些操作,以确保目标在编译时正确编织并可以运行。
更新,我无法重现您的问题(即它在我的盒子上运行良好)。 为了复制这种情况,我在源目录中使用单个 Logging.aj 文件创建了一个 AspectJ 项目。 我将其作为 jar 文件(称为logging.jar)导出到另一个项目的根目录(另一个项目也设置为包含“Main”类的 AspectJ 项目)。 然后,我修改了“主”项目的方面路径以包含logging.jar和方面,并且将建议编织到每个doSomethingAa()和doSomethingB()方法调用中。
我发现您的代码的唯一问题是您的静态方法调用是针对“A”而不是“Main”。
以下是主项目 .classpath 文件中的条目:
我尝试了各种排列,而使其无法工作的唯一方法是删除 AspectJ 性质或从构建中删除 jar小路。
是否还有其他可能影响您工作空间但被您忽略的因素?
我在类似项目中发现的关于日志记录方面的另一点; 单独的前后建议将导致每次方法调用都会创建两次 JoinPoint 实例,如果您的日志记录类型编织了很多方法,这可能会导致垃圾收集问题。 相反,您可以考虑使用周围建议来记录进入和退出,这也使得在以后决定添加任何方法执行时间日志记录时变得更容易。
更新:根据您的评论,我在工作区中添加了第三个项目 (aj_client) 并执行了以下步骤:
Client.java 包含一个方法:
运行时,此方法失败,并出现 NoClassDefFoundError:
为了解决此问题,我修改了 aj_client 的 .classpath,使其上有aspectjrt(通过手动添加 AspectJ 运行时库 类路径容器到.classpath)并重新运行,程序执行并输出日志记录语句:
aj_client 的.classpath 文件如下所示:
我还尝试在 Maven 存储库和 Eclipse 插件中指向我的aspectjrt,得到相同的结果(日志记录messages were output),即替换:
with
or
证明了日志记录代码已编织完毕,我返回并更改 Logging.aj 以再次使用 getLog().info() 调用,发现不再输出日志记录语句。 为了解决这个问题,我添加了一个 log4j.xml 配置文件(仅指定根附加程序),
这导致了以下输出:
注意 在清理、构建和导出之前,您需要小心确保已清理、构建和导出logging.jar导出target.jar,然后清理客户端项目。 如果你完全搞乱了顺序,你就会得到不匹配的内容。
总结
因此,只要您的客户端项目引用了使用 AspectJ 构建的“target.jar”(因此 Logging.aj 已被编织),并且您就有了类路径上的aspectjrt.jar并且您已正确配置log4j,日志记录将被输出。
您可以通过添加类路径容器或指定兼容的aspectjrt.jar 的路径来指定aspectjrt 依赖项
I've not tried using aspectj in plugin development, so there might be a few additional things. But here's a few things you need to do to ensure the target is woven correctly at compile time and can be run.
Update, I've been unable to reproduce your problem (i.e. it works fine on my box). To replicate the situation I created an AspectJ project with the single Logging.aj file in the source directory. I exported that as a jar file (called logging.jar) to another project's root (the other project also set up as an AspectJ project containing the "Main" class). I then modified the Aspect Path of the "main" project to include the logging.jar and the aspects and the advice was woven to each doSomethingAa() and doSomethingB() method call.
The only issue I found with your code was that your static method calls are for "A" rather than "Main".
Here is the entry from the main project's .classpath file:
I've tried various permutations, and the only ways I can get it to not work are by removing the AspectJ nature or removing the jar from the build path.
Are there any other factors that may be affecting your workspace that you've omitted?
One other point about your logging aspect that I found in a similar project; Separate before and after advice will result in JoinPoint instances being created twice for every method call, this can cause a problem for garbage collection if your logging type weaves a lot of methods. Instead you could consider using around advice to log both the entry and exit, this also makes it easier to add in any method execution time logging if you decide to later.
Update: Based on your comments, I added a third project (aj_client) to my workspace and went through the following steps:
Client.java contains a single method:
When run, this fails with a NoClassDefFoundError:
To address this, I modified the .classpath of aj_client so it has aspectjrt on it (by manually adding the AspectJ Runtime Library classpath container to the .classpath) and reran, the program executes and outputs the logging statements:
The .classpath file for aj_client looks like this:
I also tried pointing to my aspectjrt in my Maven repository and Eclipse plugin, with the same result (the logging messages were output), i.e. replace:
with
or
Having proven that the logging code is woven, I went back and changed Logging.aj to use getLog().info() calls again, and found the logging statements are no longer output. To remedy this I added a log4j.xml configuration file (just specifying the root appender)
This resulted in the following output:
Note You need to be careful to ensure you have cleaned, built, and exported logging.jar before cleaning, building, and exporting target.jar, then clean the client project. If you muck up the order at all you'll get mismatched content.
Summary
So it appears as long as your client project references a "target.jar" that was built with AspectJ (so the Logging.aj was woven), and you have an aspectjrt.jar on your classpath and you have configured log4j correctly the logging will be output.
You can specify the aspectjrt dependency by either adding the classpath container, or by specifying the path to a compatible aspectjrt.jar