如何在多模块CDI应用中隔离EntityManager生产者并解决依赖歧义?
场景回顾
存在三个Bean归档模块X、Y、Z,X和Y均依赖Z模块。Z模块内的SomeClassImpl通过构造方法注入EntityManager,但EntityManager本身并非CDI Bean。X和Y各自定义了不同的EntityManager生产者,且这些生产者对所有模块可见,导致Z模块注入EntityManager时出现依赖歧义,抛出WELD-001408部署异常。另外无法修改Z模块,且希望避免使用自定义作用域。
可行解决方案
1. 为单个生产者保留默认限定符
Z模块注入EntityManager时未指定限定符,默认会匹配带@Default的Bean。可以让其中一个模块(比如X)的生产者保留默认行为,另一个模块(Y)的生产者使用自定义限定符,这样Z会自动匹配默认生产者:
// X模块的生产者(作为默认,@Default可省略,CDI默认会添加) @Produces public EntityManager createXEntityManager() { // 创建X对应的EntityManager实例 } // Y模块的生产者,使用自定义限定符 @Produces @YEntityManager // 自定义限定符注解,需自行定义 public EntityManager createYEntityManager() { // 创建Y对应的EntityManager实例 }
Y模块内部使用时,只需在注入点加上@YEntityManager即可,Z模块则自动匹配X的生产者。
2. 用@Alternative隔离生产者
在其中一个模块的生产者上添加@Alternative注解,再通过该模块的beans.xml激活这个替代类,这样只有当前模块会识别该生产者,Z模块只会看到另一个未标记的生产者:
// Y模块的生产者 @Produces @Alternative public EntityManager createYEntityManager() { // 创建Y对应的EntityManager实例 }
在Y模块的META-INF/beans.xml中激活替代类:
<beans xmlns="http://xmlns.jcp.org/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/beans_2_0.xsd" version="2.0"> <alternatives> <class>com.y.producers.YEntityManagerProducer</class> </alternatives> </beans>
3. 调整Bean Archive可见性限制生产者范围
若使用Java EE 8+或Jakarta EE 8+,可通过模块的beans.xml限制生产者仅对当前模块可见,让Z模块只能识别对应依赖模块的生产者:
在X和Y模块的META-INF/beans.xml中添加可见性配置:
<beans xmlns="http://xmlns.jcp.org/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/beans_2_0.xsd" version="2.0"> <visibility> <package>com.x.producers</package> <!-- 替换为生产者所在包 --> <exclude>true</exclude> </visibility> </beans>
也可设置bean-discovery-mode="annotated",确保生产者仅在当前模块的注解扫描范围内,其他模块无法识别。
4. 用@Priority指定生产者优先级
如果两个生产者都需要对外可见,可通过@Priority注解设置优先级,CDI会优先选择优先级更高的生产者注入Z模块:
// X模块的生产者,设置更高优先级 @Produces @Priority(100) public EntityManager createXEntityManager() { // 创建X对应的EntityManager实例 } // Y模块的生产者,优先级较低 @Produces @Priority(50) public EntityManager createYEntityManager() { // 创建Y对应的EntityManager实例 }
内容的提问来源于stack exchange,提问作者Astonvish32

