4

我正在尝试将 aop.xml 的配置外部化,因此我删除aop.xmlMETA-INF它并使其在服务器中可供系统管理员手动配置。

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

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

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

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

当调用以下行时:success = registerAspects(weaver, loader, definitions);

捕获并记录异常。

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

异常在 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;
    }

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

4

3 回答 3

5

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

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

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

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

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

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

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

  • 重新 jar 并准备好您的新代理。

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

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

使用风险自负 :) 我希望这会有所帮助

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;
    }
}
于 2013-08-18T21:09:30.490 回答
4

如果您需要使用代理方法将类加载器排除在编织之外的功能,则可以使用开发人员构建提供新的命令行开关-Daj.weaving.loadersToSkip来执行此操作。该主题正在AspectJ 用户邮件列表的一个线程中讨论。该功能可能会在 AspectJ 1.7.4 中使用,但在 1.7.3 中尚不可用。

更新:

功能确实进入了 AspectJ 1.7.4,尽管它没有在发行说明中明确提及,但在该版本的已解决问题下列出。

于 2013-08-20T07:29:43.543 回答
0

我最终做的是将错误消息的日志级别从错误更改为调试,因为我不认为这是错误(至少在我的情况下)。在这种情况下,当我启用 DEBUG 级别时,我仍然可以看到错误。所以我修改了下面的源文件并重建了我的 aspectjweaver-1.7.1.jar

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) {
    //(CHANGE 1) trace.error("register definition failed", ex);
    trace.debug( "register definition failed" + ex.getMessage());
    success = false;
    // (CHANGE 2) warn("register definition failed", (ex instanceof AbortException) ? null : ex);
    debug("register definition failed" + ((ex instanceof AbortException) ? null : ex));
}
于 2013-08-20T10:48:01.237 回答