ServiceMix中部署多个Hibernate Envers Bundle是否可行及问题咨询
我之前在OSGi容器(尤其是ServiceMix这类)里踩过类似Hibernate Envers多Bundle冲突的坑,你遇到的「单独部署正常、同时部署就挂一个」的问题,大概率是类加载隔离问题或者Envers全局资源竞争导致的,下面给你拆解可能的原因和对应的解决办法:
OSGi每个Bundle有独立的类加载器,但如果你的多个业务Bundle都把Hibernate、Envers的依赖打包进自己的Jar里,就会出现同一类被多个类加载器重复加载的情况——这会导致Envers内部的核心组件(比如AuditReaderFactory、EntityManagerFactory的审计绑定)状态混乱,进而引发初始化失败。
解决办法:统一用OSGi化的共享依赖
- 替换为OSGi兼容的Hibernate/Envers包:普通的Maven中央仓库的Hibernate Jar大多没有OSGi元数据,要改用JBoss发布的OSGi化版本(比如
org.hibernate:hibernate-core-osgi:4.3.6.Final、org.hibernate:hibernate-envers-osgi:4.3.6.Final)。 - 把依赖部署为共享Bundle:
- 将这些OSGi化的Jar放到ServiceMix的
system/org/hibernate对应目录下 - 通过Karaf命令行安装为共享Bundle:
bundle:install -s mvn:org.hibernate/hibernate-core-osgi/4.3.6.Final,同理安装Envers包
- 将这些OSGi化的Jar放到ServiceMix的
- 修改业务Bundle的依赖配置:在每个业务Bundle的
MANIFEST.MF里通过Import-Package引入需要的包,不要把Hibernate/Envers打包进业务Jar:Import-Package: org.hibernate.envers;version="[4.3.6,4.4)", org.hibernate;version="[4.3.6,4.4)", javax.persistence;version="[2.0,3.0)"
Envers会自动给EntityManagerFactory注册审计监听器,但如果多个Bundle的EntityManagerFactory初始化时共享了底层的Hibernate组件(哪怕你用的是独立数据源),就会出现监听器被覆盖或注册失败的情况。
解决办法:给每个Bundle配置独立的审计监听器
在每个Bundle的蓝图文件里,显式给EntityManagerFactory绑定专属的Envers监听器,不要依赖自动注册:
<!-- 定义专属的审计监听器 --> <bean id="auditEventListener" class="org.hibernate.envers.event.AuditEventListener"/> <!-- 配置EntityManagerFactory时手动指定监听器 --> <bean id="entityManagerFactory" class="org.hibernate.jpa.HibernateEntityManagerFactory" factory-method="createEntityManagerFactory"> <argument value="your-persistence-unit"/> <argument> <map> <!-- 数据源配置 --> <entry key="hibernate.connection.datasource" value-ref="yourDataSource"/> <!-- 显式绑定Envers监听器 --> <entry key="hibernate.ejb.event.post-insert" value="org.hibernate.envers.event.AuditEventListener"/> <entry key="hibernate.ejb.event.post-update" value="org.hibernate.envers.event.AuditEventListener"/> <entry key="hibernate.ejb.event.post-delete" value="org.hibernate.envers.event.AuditEventListener"/> <entry key="hibernate.ejb.event.pre-collection-update" value="org.hibernate.envers.event.AuditEventListener"/> <entry key="hibernate.ejb.event.pre-collection-remove" value="org.hibernate.envers.event.AuditEventListener"/> <entry key="hibernate.ejb.event.post-collection-recreate" value="org.hibernate.envers.event.AuditEventListener"/> <!-- 其他Hibernate配置 --> </map> </argument> </bean>
如果上面的方法没解决,还可以检查这两点:
- 数据库连接池资源:确保数据库的最大连接数足够两个Bundle使用,同时在蓝图的数据源配置里调大连接池参数(比如
maxActive) - Bundle启动顺序:在ServiceMix里给两个Bundle设置不同的启动级别,让它们错开初始化时间。比如给其中一个Bundle设置启动级别为
80,另一个为85,避免同时抢占资源:bundle:start-level 你的BundleID 85
最后提醒下,一定要看故障Bundle的日志——如果是类加载问题,日志会出现ClassNotFoundException、NoSuchMethodError或者「类重复定义」的提示;如果是监听器问题,会有EntityManagerFactory初始化失败的堆栈信息,这些日志是精准定位问题的关键。
内容的提问来源于stack exchange,提问作者Charlie Horne

