草庐IT

java - AspectJ 加载时间编织器未检测到所有类

coder 2023-08-30 原文

我在“aspectj”模式下使用 Spring 的声明式事务(@Transactional 注释)。它在大多数情况下都能正常工作,但在某些情况下却不能。我们可以称它为 Lang(因为这就是它的实际名称)。

我已经能够将问题精确定位到加载时间编织器。通过在 aop.xml 中打开调试和详细日志记录,它会列出所有正在编织的类。日志中确实根本没有提到有问题的类 Lang

然后我在Lang的顶部打了一个断点,导致Eclipse在加载Lang类时挂起线程。这个断点是在 LTW 编织其他类时命中的!所以我猜它要么尝试编织 Lang 并失败并且不输出它,要么其他一些类有一个引用强制它在实际获取之前加载 Lang有机会编织它。

但是我不确定如何继续调试它,因为我无法以较小的规模重现它。关于如何继续的任何建议?


更新:也欢迎提供其他线索。例如,LTW 实际上是如何运作的?似乎发生了很多魔法。是否有任何选项可以从 LTW 获得更多调试输出?我目前有:

<weaver options="-XnoInline -Xreweavable -verbose -debug -showWeaveInfo">

我忘了之前提到它:spring-agent 被用来允许 LTW,即 InstrumentationLoadTimeWeaver


根据 Andy Clement 的建议,我决定检查 AspectJ 转换器是否曾经通过类(class)。我在 ClassPreProcessorAgent.transform(..) 中放置了一个断点,看起来 Lang 类甚至从未到达该方法,尽管它是由与其他类(Jetty 的 WebAppClassLoader 的实例)。

然后我继续在 InstrumentationLoadTimeWeaver$FilteringClassFileTransformer.transform(..) 中放置一个断点。 Lang 甚至连那个都没有命中。而且我认为应该为所有 加载的类调用该方法,而不管它们使用的是什么类加载器。这开始看起来像:

  1. 我的调试有问题。可能 Lang 在 Eclipse 报告加载时未加载
  2. Java 错误?牵强附会,但我想它确实发生了。

下一条线索:我打开了 -verbose:class 并且看起来好像 Lang is 被过早地加载了——可能在转换器加载之前添加到仪器。奇怪的是,我的 Eclipse 断点没有捕捉到这个加载。

这意味着 Spring 是新的嫌疑人。 ConfigurationClassPostProcessor 中似乎有一些处理加载类以检查它们。这可能与我的问题有关。


ConfigurationClassBeanDefinitionReader 中的这些行导致读取 Lang 类:

else if (metadata.isAnnotated(Component.class.getName()) ||
        metadata.hasAnnotatedMethods(Bean.class.getName())) {
    beanDef.setAttribute(CONFIGURATION_CLASS_ATTRIBUTE, CONFIGURATION_CLASS_LITE);
    return true;
}

特别是,metadata.hasAnnotatedMethods() 调用类上的getDeclaredMethods(),加载该类中所有方法的所有参数类。我猜这可能不是问题的结束,因为我认为这些类应该被卸载。 JVM 是否会出于不可知的原因缓存类实例?

最佳答案

好的,我已经解决了这个问题。本质上,它是与某些自定义扩展相关的 Spring 问题。如果有人遇到类似的事情,我会尝试逐步解释发生了什么。

首先,我们的项目中有一个自定义的BeanDefintionParser。此类具有以下定义:

private static class ControllerBeanDefinitionParser extends AbstractSingleBeanDefinitionParser {

    protected Class<?> getBeanClass(Element element) {
        try {
            return Class.forName(element.getAttribute("class"));
        } catch (ClassNotFoundException e) {
            throw new RuntimeException("Class " + element.getAttribute("class") + "not found.", e);
        }
    }

// code to parse XML omitted for brevity

}

现在,问题发生在所有 bean 定义都被读取并且 BeanDefinitionRegistryPostProcessor 开始启动之后。在这个阶段,一个名为 ConfigurationClassPostProcessor 的类开始查看所有 bean 定义, 搜索用 @Configuration 注释或具有 @Bean 方法的 bean 类。

在为bean 读取注解的过程中,它使用了AnnotationMetadata 接口(interface)。对于大多数常规 bean,使用一个名为 AnnotationMetadataVisitor 的子类。但是,在解析 bean 定义时,如果您重写了 getBeanClass() 方法以返回类实例,就像我们所做的那样,则会使用 StandardAnnotationMetadata 实例。当 StandardAnnotationMetadata.hasAnnotatedMethods(..) 被调用时,它会调用 Class.getDeclaredMethods(),这反过来会导致类加载器加载所有用作该类参数的类.以这种方式加载的类没有正确卸载,因此永远不会编织,因为这发生在 AspectJ 转换器注册之前。

现在,我的问题是我有这样一个类:

public class Something {
    private Lang lang;
    public void setLang(Lang lang) {
        this.lang = lang;
    }
}

然后,我有一个 Something 类的 bean,它使用我们的自定义 ControllerBeanDefinitionParser 进行了解析。这触发了错误的注解检测过程,进而触发了意外的类加载,这意味着 AspectJ 永远没有机会编织 Lang

解决方案是不覆盖 getBeanClass(..),而是覆盖 getBeanClassName(..),根据文档,这是更可取的:

private static class ControllerBeanDefinitionParser extends AbstractSingleBeanDefinitionParser {

    protected String getBeanClassName(Element element) {
        return element.getAttribute("class");
    }

// code to parse XML omitted for brevity

}

今天的教训:不要重写 getBeanClass 除非你是认真的。实际上,除非您知道自己在做什么,否则不要尝试编写自己的 BeanDefinitionParser。

鳍。

关于java - AspectJ 加载时间编织器未检测到所有类,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/3619519/

有关java - AspectJ 加载时间编织器未检测到所有类的更多相关文章

  1. ruby - 如何以所有可能的方式将字符串拆分为长度最多为 3 的连续子字符串? - 2

    我试图获取一个长度在1到10之间的字符串,并输出将字符串分解为大小为1、2或3的连续子字符串的所有可能方式。例如:输入:123456将整数分割成单个字符,然后继续查找组合。该代码将返回以下所有数组。[1,2,3,4,5,6][12,3,4,5,6][1,23,4,5,6][1,2,34,5,6][1,2,3,45,6][1,2,3,4,56][12,34,5,6][12,3,45,6][12,3,4,56][1,23,45,6][1,2,34,56][1,23,4,56][12,34,56][123,4,5,6][1,234,5,6][1,2,345,6][1,2,3,456][123

  2. ruby - 如何在续集中重新加载表模式? - 2

    鉴于我有以下迁移:Sequel.migrationdoupdoalter_table:usersdoadd_column:is_admin,:default=>falseend#SequelrunsaDESCRIBEtablestatement,whenthemodelisloaded.#Atthispoint,itdoesnotknowthatusershaveais_adminflag.#Soitfails.@user=User.find(:email=>"admin@fancy-startup.example")@user.is_admin=true@user.save!ende

  3. java - 等价于 Java 中的 Ruby Hash - 2

    我真的很习惯使用Ruby编写以下代码:my_hash={}my_hash['test']=1Java中对应的数据结构是什么? 最佳答案 HashMapmap=newHashMap();map.put("test",1);我假设? 关于java-等价于Java中的RubyHash,我们在StackOverflow上找到一个类似的问题: https://stackoverflow.com/questions/22737685/

  4. ruby-on-rails - 跳过状态机方法的所有验证 - 2

    当我的预订模型通过rake任务在状态机上转换时,我试图找出如何跳过对ActiveRecord对象的特定实例的验证。我想在reservation.close时跳过所有验证!叫做。希望调用reservation.close!(:validate=>false)之类的东西。仅供引用,我们正在使用https://github.com/pluginaweek/state_machine用于状态机。这是我的预订模型的示例。classReservation["requested","negotiating","approved"])}state_machine:initial=>'requested

  5. ruby - RuntimeError(自动加载常量 Apps 多线程时检测到循环依赖 - 2

    我收到这个错误:RuntimeError(自动加载常量Apps时检测到循环依赖当我使用多线程时。下面是我的代码。为什么会这样?我尝试多线程的原因是因为我正在编写一个HTML抓取应用程序。对Nokogiri::HTML(open())的调用是一个同步阻塞调用,需要1秒才能返回,我有100,000多个页面要访问,所以我试图运行多个线程来解决这个问题。有更好的方法吗?classToolsController0)app.website=array.join(',')putsapp.websiteelseapp.website="NONE"endapp.saveapps=Apps.order("

  6. ruby - Nokogiri 剥离所有属性 - 2

    我有这个html标记:我想得到这个:我如何使用Nokogiri做到这一点? 最佳答案 require'nokogiri'doc=Nokogiri::HTML('')您可以通过xpath删除所有属性:doc.xpath('//@*').remove或者,如果您需要做一些更复杂的事情,有时使用以下方法遍历所有元素会更容易:doc.traversedo|node|node.keys.eachdo|attribute|node.deleteattributeendend 关于ruby-Nokog

  7. ruby-on-rails - Ruby 检查日期时间是否为 iso8601 并保存 - 2

    我需要检查DateTime是否采用有效的ISO8601格式。喜欢:#iso8601?我检查了ruby​​是否有特定方法,但没有找到。目前我正在使用date.iso8601==date来检查这个。有什么好的方法吗?编辑解释我的环境,并改变问题的范围。因此,我的项目将使用jsapiFullCalendar,这就是我需要iso8601字符串格式的原因。我想知道更好或正确的方法是什么,以正确的格式将日期保存在数据库中,或者让ActiveRecord完成它们的工作并在我需要时间信息时对其进行操作。 最佳答案 我不太明白你的问题。我假设您想检查

  8. ruby - 获取模块中定义的所有常量的值 - 2

    我想获取模块中定义的所有常量的值:moduleLettersA='apple'.freezeB='boy'.freezeendconstants给了我常量的名字:Letters.constants(false)#=>[:A,:B]如何获取它们的值的数组,即["apple","boy"]? 最佳答案 为了做到这一点,请使用mapLetters.constants(false).map&Letters.method(:const_get)这将返回["a","b"]第二种方式:Letters.constants(false).map{|c

  9. ruby-on-rails - 将 Ruby 中的日期/时间格式化为 YYYY-MM-DD HH :MM:SS - 2

    这个问题在这里已经有了答案:Railsformattingdate(4个答案)关闭4年前。我想格式化Time.Now函数以显示YYYY-MM-DDHH:MM:SS而不是:“2018-03-0909:47:19+0000”该函数需要放在时间中.现在功能。require‘roo’require‘roo-xls’require‘byebug’file_name=ARGV.first||“Template.xlsx”excel_file=Roo::Spreadsheet.open(“./#{file_name}“,extension::xlsx)xml=Nokogiri::XML::Build

  10. ruby - 查找字符串中的内容类型(数字、日期、时间、字符串等) - 2

    我正在尝试解析一个CSV文件并使用SQL命令自动为其创建一个表。CSV中的第一行给出了列标题。但我需要推断每个列的类型。Ruby中是否有任何函数可以找到每个字段中内容的类型。例如,CSV行:"12012","Test","1233.22","12:21:22","10/10/2009"应该产生像这样的类型['integer','string','float','time','date']谢谢! 最佳答案 require'time'defto_something(str)if(num=Integer(str)rescueFloat(s

随机推荐