AspectJ:尝试使用外部 aop.xml 文件时出现类加载问题 [英] AspectJ: ClassLoading issue when trying to use external aop.xml file

查看:25
本文介绍了AspectJ:尝试使用外部 aop.xml 文件时出现类加载问题的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试将 aop.xml 的配置外部化,因此我从 META-INF 中删除了 aop.xml 并使其在服务器中可用,以便通过以下方式进行手动配置系统管理员.

当我尝试使用外部 aop.xml 时

-Dorg.aspectj.weaver.loadtime.configuration="file:D:\Workspace\tomcat7\shared\lib\aop.xml"

我得到 java.lang.RuntimeException: Cannot register non aspect: aspectclass.... 主要是因为当时 AppClassLoader 还没有加载 aj 案例.下次它尝试从 WebAppClassLoader 注册方面(在加载所有类之后)时,它工作正常,但我仍然收到第一次尝试注册时记录的异常.>

异常被捕获并记录在 ClassLoaderWeavingAdaptor.java 第 307 行.

当以下行被调用时:成功 = registerAspects(weaver, loader, definition);

捕获并记录异常.

 试试 {registerOptions(weaver, loader, definition);registerAspectExclude(weaver, loader, definition);registerAspectInclude(weaver, loader, definition);成功 = registerAspects(weaver, loader, definition);registerIncludeExclude(weaver, loader, definition);registerDump(weaver, loader, definition);} 捕捉(异常前){trace.error("注册定义失败", ex);成功=假;警告(注册定义失败",(ex instanceof AbortException)?null:ex);}

在 BcelWeaver.java 中的以下行中准确抛出异常

if (type.isAspect()) {......} 别的 {//FIXME AV - 更好地警告 aop.xml 中没有这样的方面RuntimeException ex = new RuntimeException("无法注册非方面:" + type.getName() + " , " + aspectName);如果(trace.isTraceEnabled()){trace.exit("addLibraryAspect", ex);}扔前;}

当方面尚未加载时,如何防止 classLoader 将错误记录到控制台.我想评论从源文件记录异常的行并重建aspectjweaver jar文件,但正在寻找更好的解决方案而不修改aspectj源.

解决方案

我不确定是否有简单的方法可以解决您的问题.正如我所说,我之前没有与 AspectJ 合作过,但我相信这是编织者的错误行为.

问题描述: 在启动期间,代理尝试将其他编织应用到 WebAppClassLoader 和整个类加载器链(每个类加载器一次),即:sun.misc.Launcher$AppClassLoadersun.misc.Launcher$ExtClassLoaderorg.apache.catalina.loader.StandardClassLoader(tomcat 的类加载器).当您使用 META-INF/aop.xml 方法时,它会禁用上述类加载器的编织,因为配置文件不可用"(如果您启用 verbose 模式,您可以在控制台中查看这些消息).当您使用文件配置方法时,一个配置可用于链中的所有类加载器.因为它确实找到了一个配置文件,所以代理解析定义,它没有找到方面的类并显示错误.

奇怪的是,如配置文档中所述 如果您使用 WeavingURLClassLoader 方法进行加载时编织,"...它还允许用户通过类加载器明确限制可以编织哪些类".所以这实际上是类加载器方法可以具有但代理方法没有的功能(!).(不幸的是我无法使用这种方法)

好(和坏)消息:好消息是您可以轻松创建自己的代理,该代理将忽略上述类加载器的编织.坏消息是限制每个类加载器的编织是不够的,因为如果您在同一服务器中有其他应用程序,Tomcat 仍将使用 WebAppClassLoader 加载它们,因此您仍然会收到这些应用程序的错误消息.(在这种情况下,也许您也可以扩展下面的类来过滤包/类).

您可以在下面找到修改后的代理的两个类.要使用它们,您需要执行以下操作:

  • aspectjweaver.jar 解压到一个文件夹
  • org/aspectj/weaver/loadtime 下创建一个新文件夹 filter 以匹配包名称,并在编译后将两个新类放在那里.
  • 编辑 META-INF/MANIFEST.MF 文件并更改行

    Premain-Class: org.aspectj.weaver.loadtime.Agent to
    Premain-Class: org.aspectj.weaver.loadtime.filter.FilterAgent

  • 重新打包,你的新代理就准备好了.

  • 启动 JVM 时,您现在可以传递一个新的系统属性,其中包含要忽略的类加载器的逗号分隔列表,即 -Dorg.aspectj.weaver.loadtime.filter=sun.misc.Launcher$AppClassLoader,sun.misc.Launcher$ExtClassLoader,org.apache.catalina.loader.StandardClassLoader(我已经设置了 CATALINA_OPTS 来做到这一点).

这些类是原始代理的类AgentClassPreProcessorAgentAdapter 的修改副本.我添加的唯一代码是解析上述系统属性(如果存在)并忽略对我们不感兴趣的类加载器的调用的部分.

使用风险自负 :) 希望对您有所帮助

package org.aspectj.weaver.loadtime.filter;导入 java.lang.instrument.ClassFileTransformer;导入 java.lang.instrument.Instrumentation;公共类过滤器代理{私有静态仪器 s_instrumentation;//使用我们自己的 ClassFileTransformer 版本来过滤掉选定的类加载器私有静态 ClassFileTransformer s_transformer = new ClassPreprocessorFilteredAdapter();/*** JSR-163 preMain Agent 入口方法** @param 选项* @param 检测*/公共静态无效premain(字符串选项,仪器仪表){/* 处理重复代理 */如果(s_instrumentation != null){返回;}s_instrumentation = 仪表;s_instrumentation.addTransformer(s_transformer);}公共静态仪器 getInstrumentation() {如果(s_instrumentation == null){throw new UnsupportedOperationException("Java 5 不是用 preMain -javaagent for AspectJ 启动的");}返回 s_instrumentation;}}//---------------------------------------------------------------------包 org.aspectj.weaver.loadtime.filter;导入 java.lang.instrument.ClassFileTransformer;导入 java.lang.instrument.IllegalClassFormatException;导入 java.security.ProtectionDomain;导入 java.util.HashMap;导入 java.util.Map;导入 org.aspectj.weaver.loadtime.Aj;导入 org.aspectj.weaver.loadtime.ClassPreProcessor;公共类 ClassPreprocessorFilteredAdapter 实现 ClassFileTransformer {/***混凝土预处理器.*/私有静态 ClassPreProcessor s_preProcessor;私有静态映射忽略ClassloaderNames = new HashMap();静止的 {尝试 {s_preProcessor = new Aj();s_preProcessor.initialize();String ignoreLoaders = System.getProperty("org.aspectj.weaver.loadtime.filter", "");if (ignoredLoaders.length() > 0) {String[] loaders = ignoreLoaders.split(",");for (String s : 装载机) {s = s.trim();忽略ClassloaderNames.put(s, s);System.out.println("---> 将过滤掉类加载器:" + s);}}} 捕获(异常 e){throw new ExceptionInInitializerError("无法初始化 JSR163 预处理器,因为:" + e.toString());}}/*** 调用编织器来修改一些输入字节集.** @param loader 定义类加载器* @param className 正在加载的类名* @param classBeingRedefined 在尝试热交换时设置* @param protectionDomain 正在加载的类的保护域* @param bytes 传入的字节(在编织之前)* @return 编织的字节*/@覆盖public byte[] transform(ClassLoader loader, String className, Class<?> classBeingRedefined, ProtectionDomain protectionDomain, byte[] bytes) 抛出 IllegalClassFormatException {如果(classBeingRedefined != null){System.err.println("INFO: (Enh120375): AspectJ 试图重新编织 '" + className + "'");}String loaderName = loader.getClass().getName();如果 (shouldIgnoreClassLoader(loaderName)) {返回字节;}return s_preProcessor.preProcess(className, bytes, loader, protectionDomain);}私有布尔值 shouldIgnoreClassLoader(String loaderName) {布尔结果 = 假;String ignoreLoader = ignoreClassloaderNames.get(loaderName);if (ignoredLoader != null) {结果=真;//如果加载器名称存在于地图中,我们将忽略编织}返回结果;}}

I m trying to externalize the configuration of aop.xml so I removed the aop.xml from META-INF and made it available in the server for manual configuration by sys admins.

When I try to use an external aop.xml using

-Dorg.aspectj.weaver.loadtime.configuration="file:D:\Workspace\tomcat7\shared\lib\aop.xml"

I get java.lang.RuntimeException: Cannot register non aspect: aspectclass.... mainly because the aj casses are not loaded by AppClassLoader yet at that time. And the next time it tries to register the aspects from the WebAppClassLoader ( after all the classes are loaded), it works fine, but i still get the exceptions logged from the 1st attempt to register it.

The exception is caught and logged at ClassLoaderWeavingAdaptor.java line 307.

when the following line is called: success = registerAspects(weaver, loader, definitions);

the exception is caught and logged.

    try {
        registerOptions(weaver, loader, definitions);
        registerAspectExclude(weaver, loader, definitions);
        registerAspectInclude(weaver, loader, definitions);
        success = registerAspects(weaver, loader, definitions);
        registerIncludeExclude(weaver, loader, definitions);
        registerDump(weaver, loader, definitions);
    } catch (Exception ex) {
        trace.error("register definition failed", ex);
        success = false;
        warn("register definition failed", (ex instanceof AbortException) ? null : ex);
    }

the exception is thrown excactly in the following line in BcelWeaver.java

if (type.isAspect()) {
      ......
} else {
        // FIXME AV - better warning upon no such aspect from aop.xml
        RuntimeException ex = new RuntimeException("Cannot register non aspect: " + type.getName() + " , " + aspectName);
        if (trace.isTraceEnabled()) {
            trace.exit("addLibraryAspect", ex);
        }
        throw ex;
    }

How can I prevent the classLoader from logging the error to the console, when the aspects are not loaded yet. I was thinking of commenting the line that logs the exception from the source file and rebuilding the aspectjweaver jar file, but was looking for a better solution without modifying the aspectj source.

解决方案

I am not sure that there is an easy way out of your problem. As I said I haven't worked with AspectJ before but I believe this is a mis-behaviour of the weaver.

Problem description: During boot the agent tries to apply weaving other not only to the WebAppClassLoader but to the whole classloader chain (once per classloader) i.e. to: sun.misc.Launcher$AppClassLoader, sun.misc.Launcher$ExtClassLoader, org.apache.catalina.loader.StandardClassLoader (the tomcat's classloader). When you use the META-INF/aop.xml approach it disables weaving for the above classloaders because "a configuration file is not available" (if you enable verbose mode you can see those messages in console). When you use the file configuration approach, a configuration is available for all the classloaders in the chain. Since it does find a configuration file, the agent parses the definitions, it does not find the aspects' class and shows the error.

The weird thing is that, as described in the configuration documentation if you use the WeavingURLClassLoader approach for load time weaving, "... it also allows the user to explicitly restrict by class loader which classes can be woven". So this is actually a feature (!) that the classloader approach can have but the agent approach doesn't. (Unfortunately I was not able to use this approach)

The good (and the bad) news: The good news is that you can easily create your own agent that will ignore the weaving for the aforementioned classloaders. The bad news is that restricting weaving per classloader is not enough because if you have other applications in the same server, Tomcat would still use the WebAppClassLoader to load them so you would still get error messages for those applications. (Perhaps you could extend the classes below to filter packages/classes as well, in that case).

Below you can find two class for the modified agent. To use them you would need to do the following:

  • Un-jar the aspectjweaver.jar to a folder
  • Under org/aspectj/weaver/loadtime create a new folder filter to match the package name and put there the two new classes after you compile them.
  • Edit the META-INF/MANIFEST.MF file and change the line

    Premain-Class: org.aspectj.weaver.loadtime.Agent to
    Premain-Class: org.aspectj.weaver.loadtime.filter.FilterAgent

  • Re-jar and you have your new agent ready.

  • When starting the JVM you can now pass a new system property with a comma separated list of the classloaders you would like to ignore i.e. -Dorg.aspectj.weaver.loadtime.filter=sun.misc.Launcher$AppClassLoader,sun.misc.Launcher$ExtClassLoader,org.apache.catalina.loader.StandardClassLoader ( I have set CATALINA_OPTS to do that).

The classes are a modified copy of the original agent's classes Agent and ClassPreProcessorAgentAdapter. The only code I have added is the part that parses the above system property if it exists and to ignore calls for the classloaders we are not interested in.

Use at your own risk :) I hope that helps

package org.aspectj.weaver.loadtime.filter;

import java.lang.instrument.ClassFileTransformer;
import java.lang.instrument.Instrumentation;

public class FilterAgent {

    private static Instrumentation s_instrumentation;

    // Use our own version of ClassFileTransformer that would filter out selected classloaders 
    private static ClassFileTransformer s_transformer = new ClassPreprocessorFilteredAdapter();

    /**
     * JSR-163 preMain Agent entry method
     *
     * @param options
     * @param instrumentation
     */
    public static void premain(String options, Instrumentation instrumentation) {
        /* Handle duplicate agents */
        if (s_instrumentation != null) {
            return;
        }
        s_instrumentation = instrumentation;
        s_instrumentation.addTransformer(s_transformer);
    }

    public static Instrumentation getInstrumentation() {
        if (s_instrumentation == null) {
            throw new UnsupportedOperationException("Java 5 was not started with preMain -javaagent for AspectJ");
        }
        return s_instrumentation;
    }
}
//-----------------------------------------------------------------------------------
package org.aspectj.weaver.loadtime.filter;

import java.lang.instrument.ClassFileTransformer;
import java.lang.instrument.IllegalClassFormatException;
import java.security.ProtectionDomain;
import java.util.HashMap;
import java.util.Map;

import org.aspectj.weaver.loadtime.Aj;
import org.aspectj.weaver.loadtime.ClassPreProcessor;


public class ClassPreprocessorFilteredAdapter implements ClassFileTransformer {

    /**
     * Concrete preprocessor.
     */
    private static ClassPreProcessor s_preProcessor;

    private static Map<String, String> ignoredClassloaderNames = new HashMap<String, String>();

    static {
        try {
            s_preProcessor = new Aj();
            s_preProcessor.initialize();


            String ignoredLoaders = System.getProperty("org.aspectj.weaver.loadtime.filter", "");
            if (ignoredLoaders.length() > 0) {
                String[] loaders = ignoredLoaders.split(",");

                for (String s : loaders) {
                    s = s.trim();
                    ignoredClassloaderNames.put(s, s);
                    System.out.println("---> Will filtered out classloader: " + s);
                }
            }

        } catch (Exception e) {
            throw new ExceptionInInitializerError("could not initialize JSR163 preprocessor due to: " + e.toString());
        }
    }

    /**
     * Invokes the weaver to modify some set of input bytes.
     * 
     * @param loader the defining class loader
     * @param className the name of class being loaded
     * @param classBeingRedefined is set when hotswap is being attempted
     * @param protectionDomain the protection domain for the class being loaded
     * @param bytes the incoming bytes (before weaving)
     * @return the woven bytes
     */
    @Override
    public byte[] transform(ClassLoader loader, String className, Class<?> classBeingRedefined, ProtectionDomain protectionDomain, byte[] bytes) throws IllegalClassFormatException {
        if (classBeingRedefined != null) {
            System.err.println("INFO: (Enh120375):  AspectJ attempting reweave of '" + className + "'");
        }

        String loaderName = loader.getClass().getName();
        if (shouldIgnoreClassLoader(loaderName)) {
            return bytes;
        }
        return s_preProcessor.preProcess(className, bytes, loader, protectionDomain);
    }

    private boolean shouldIgnoreClassLoader(String loaderName) {
        boolean result = false;
        String ignoredLoader = ignoredClassloaderNames.get(loaderName);
        if (ignoredLoader != null) {
            result = true;    // if the loader name exists in the map we will ignore weaving
        }
        return result;
    }
}

这篇关于AspectJ:尝试使用外部 aop.xml 文件时出现类加载问题的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆