【问题标题】:Instrumenting with Javassist entity classes used by Hibernate使用 Hibernate 使用的 Javassist 实体类进行检测
【发布时间】:2023-03-08 17:43:01
【问题描述】:

我开发了一个库,它在给定抽象类中的某些注释的情况下在运行时生成抽象类的特化类。 所以我的库使用有点像:

X x = newInstance(X.class) //X is an abstract class

该库使用 Javassist 在运行时生成一个子类,该子类将由 newInstance 方法实例化。

据我了解,Hibernate 还利用 Javassist 来检测运行时实体类(如果不是这样,请有人纠正我)。

我的问题是是否有可能使这两个框架一起工作?我的意思是,我可以告诉 Hibernate 每次它需要一个实体类(一个抽象类)的实例时,它应该使用我的库中的特定工厂方法吗?

我不知道 Hibernate 是否通过在实体类的运行时生成子类(假设 Hibernate 也需要检测它们)来支持 Courtains。如果是这种情况,将某些类的工厂传递给 Hibernate 的解决方案将不起作用。 在那种情况下,Hibernate 是否支持使用抽象实体类?我的意思是,应该可以使用抽象(或接口?)实体类,并以某种方式告诉 Hibernate,当需要使用这些抽象实体类之一时,它应该检测哪个是正确的具体类。

但另一个复杂的问题是,在编译时不存在专门化抽象实体类的具体类。

【问题讨论】:

    标签: java hibernate javassist


    【解决方案1】:

    在前往您的答案之前:

    • 是的,您是正确的,Hibernate 当前使用 Javassist(过去使用 GCLib,但已弃用)在运行时检测类。
    • Hibernate 确实会在运行时创建子类,这些子类可以为您的持久实体提供代理。

    简答

    遗憾的是,我认为您无法将 Hibernate 配置为使用您自己的工厂。有关详细信息,我邀请您阅读长答案部分。

    长答案

    据我所知,目前,Hibernate 4.x 仅支持 Javassist 作为其字节码操作提供程序。虽然,它曾经允许您在 3.x 版本中在 GClib 和 Javassist 之间切换。回到那些版本,您可以通过配置名为hibernate.bytecode.provider 的休眠全局设置来修改要使用的工厂。

    此设置不再显示在 Hibernate 4.1 documentation 中,但您仍然可以在 optional configuration properties 下的 Hibernate 3.2 文档中找到有关它的信息。

    作为一名开发人员,我知道有时我们是一些棘手的家伙,仅仅因为某些东西不在文档中并不意味着它一定不在代码中 :-) 所以,我认为 也许如果该设置仍然存在,我们可以尝试利用它来做你想做的事情(尽管以不受支持的方式)。

    出于好奇,因为我的机器中有 Hibernate 4.0.1 代码(但请注意这不是最新的),我做了一些挖掘......而且惊喜,惊喜财产仍然存在!在跟踪使用过的参考资料(感谢 Eclipse)后,我最终进入了 org.hibernate.cfg.Environment 类(code for version 4.2.0.CR2),在那里我找到了以下代码(我的版本和 4.2.0CR2 中的代码相同):

     public static BytecodeProvider buildBytecodeProvider(Properties properties) {
        String provider = ConfigurationHelper.getString( BYTECODE_PROVIDER, properties, "javassist" );
        LOG.bytecodeProvider( provider );
        return buildBytecodeProvider( provider );
    }
    
    private static BytecodeProvider buildBytecodeProvider(String providerName) {
        if ( "javassist".equals( providerName ) ) {
            return new org.hibernate.bytecode.internal.javassist.BytecodeProviderImpl();
        }
    
        LOG.unknownBytecodeProvider( providerName );
        return new org.hibernate.bytecode.internal.javassist.BytecodeProviderImpl();
    }
    

    到目前为止,我可以说它是您的代理工厂的 Javassist 实现,并且不会有标准的方法来更改它。


    疯子旁注

    我要说的是纯粹的疯狂,它不应该被考虑到生产代码中,但只有在它真的可以工作/学术/让事情发生变化的情况下。

    • 您可以尝试扩展您自己的框架来检测类,以便不仅添加满足您需要的字节码,还添加hibernate 需要的字节码——我想说这就像将您的操作与org.hibernate.bytecode.internal.javassist.BytecodeProviderImpl 操作合并。
    • 然后你可以将你的扩展类重命名为BytecodeProviderImpl 把它放在同一个包org.hibernate.bytecode.internal.javassist 中,最后把它放在类路径中的某个位置,类加载器会在jar 中的那个之前找到它(或者可能使用自定义类加载器)

    然后你就可以享受聆听 Hibernate、你的框架和可能整个 JVM 惊慌失措的尖叫,不知道该做什么,或者它可以工作......

    锤子黑客>

    无论如何,如果您有时间并愿意尝试,请告诉我是否成功。


    更新

    在 cmets 部分讨论了一下之后,我有了使用自定义 EntityPersister 的想法。因为我对此不太确定,所以我用谷歌搜索了一下,看看我是否能找到一些可以告诉我我的想法是否可行的东西。

    我在 Stackoverflow 中发现了一个似乎是 pretty similar to yours 的问题,这比找出我的直觉是否正确要好。可悲的是,那里没有公认的答案。

    但该问题的第一个答案提供了与我的想法类似的链接。引用 Pascal Thivent:

    一个自定义的 EntityPersister 实现(你可以register for a particular entity during Hibernate initialization using a custom Configuration

    这个例子确实是针对 Grails 中的 Hibernate 的,但在纯 Java 中几乎是一样的:

     public void registerCustomEntityPersister(Configuration configuration) {
         final Iterator<PersistentClass> classesIterator = configuration.getClassMappings();
     while (classesIterator.hasNext()) {
        final PersistentClass persistentClass = classesIterator.next();
            if (checkIfIsOneTheClassesThatMatters(persistentClass)) {
              persistentClass.etEntityPersisterClass(CustomEntityPersister.class); 
            }
     }
    

    尽管这看起来可行,但看起来工作量太大了,因为实现 EntityPersister 看起来并不那么简单……太多的事情。您可以尝试扩展 Hibernate 使用的默认一个(我真的不知道是哪一个)并尝试覆盖 getProxy() 方法以返回您的检测类之一。

    很抱歉,如果它仍然不是答案,但遗憾的是我不是 Hibernate 专家,我通常只是开箱即用,实际上由于 javassist 标签,我发现了你的问题,发现它很有趣。

    我希望至少我给了你可以帮助你的信息。

    【讨论】:

    • 大声笑,喜欢听 Hibernate 的部分,我的框架和整个 JVM 都在恐慌中尖叫 :) 。我知道使用工厂方法似乎不是一种选择。但是,Hibernate 中根本不支持抽象实体类(或实体接口)?我的意思是,它应该是一种将实体类声明为抽象的方式,并且我们可以通过某种方式告诉 Hibernate 在需要实例化这些抽象实体类时它应该检测哪些具体类。
    • 阅读您的评论,我开始认为也许可以通过将抽象类/接口与@MappedSuperclass 注释映射然后创建自定义EntityPersister (docs.jboss.org/hibernate/orm/3.6/javadocs/org/hibernate/…) 用于您将决定加载哪个类的接口。但不确定...
    • @sergio:很抱歉在最后一条评论中忘记提及您,我认为您不会收到其他通知。
    • 嗨@pabrantes,感谢您的反馈!我需要时间来处理和理解您的链接(几个世纪前与 Hibernate 一起工作过,我已经忘记了很多)。无论如何,它看起来有点过于复杂(但这并不意外)。我还根据我们的讨论更新了我的问题。
    • @Sergio:很抱歉,我现在无法用这个想法更新我的问题。但是给我几个小时(4 或 5 小时),我会更新我的问题,提供关于我刚才告诉你的更好的信息。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-03-01
    • 1970-01-01
    • 2015-06-14
    • 1970-01-01
    相关资源
    最近更新 更多