我正试图将aop.xml的配置外部化,因此我从META-INF
中删除了aop.xml
,并使其在服务器中可供系统管理员手动配置。
当我尝试使用使用外部aop.xml时
-Dorg.aspectj.weaver.loadtime.configuration="file:D:Workspacetomcat7sharedlibaop.xml"
我得到java.lang.RuntimeException: Cannot register non aspect: aspectclass....
,主要是因为当时AppClassLoader
还没有加载aj磁带。下次它尝试从WebAppClassLoader
注册方面时(在加载所有类之后),它运行良好,但我仍然会在第一次尝试注册时记录异常。
异常被捕获并记录在ClassLoaderWeavingAdaptor.java
的307行。
当调用以下行时:成功=registerSpects(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;
}
当方面尚未加载时,我如何防止classLoader将错误记录到控制台。我想对源文件中记录异常的行进行注释,并重建aspectjweaver jar文件,但我正在寻找一个更好的解决方案,而不修改aspectj源。
我不确定是否有简单的方法来解决您的问题。正如我所说,我以前从未使用过AspectJ,但我认为这是编织者的错误行为。
问题描述:在引导过程中,代理尝试将编织其他不仅应用于WebAppClassLoader
,而且应用于整个类加载器链(每个类加载器一次),即:sun.misc.Launcher$AppClassLoader
、sun.misc.Launcher$ExtClassLoader
、org.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
至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
)
这些类是原始代理的类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;
}
}
如果您需要将类加载器排除在使用代理方法编织之外的功能,那么有一个开发人员构建可以提供一个新的命令行开关-Daj.weaving.loadersToSkip
来实现这一点。这个主题是在AspectJ用户邮件列表的一个线程上讨论的。该功能可能会进入AspectJ 1.7.4,但在1.7.3中还不可用。
更新:
该功能确实进入了AspectJ 1.7.4,尽管在发行说明中没有明确提到,但在该版本的已解决问题下列出了该功能。
我最终所做的是将错误消息的日志级别从error更改为DEBUG,因为我不认为这是error(至少在我的情况下)。在这种情况下,当我启用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));
}