You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

配置使用CDI的Java库与CDI框架:如何避免内部Bean被外部应用的CDI容器扫描到

Great question—this is a super common headache when building shared CDI frameworks without module boundaries. Let's walk through solutions for both your scenarios step by step:

1. Configuring a CDI-based Java Library to Exclude External Beans

The approach depends on which CDI implementation you're using (Weld, OpenWebBeans, etc.), but here are the most common methods:

For Weld (WildFly, JBoss, etc.)

  • Declarative exclusion via beans.xml:
    Add <exclude> tags inside the <scan> section to target specific packages or classes:
    <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">
        <scan>
            <!-- Exclude an entire package -->
            <exclude name="com.example.external.*"/>
            <!-- Exclude a single class -->
            <exclude name="com.example.UnwantedExternalBean"/>
        </scan>
    </beans>
    
  • Programmatic exclusion via CDI Extension:
    Write a custom extension to veto unwanted beans during the discovery phase:
    public class ExternalBeanExclusionExtension implements Extension {
        void processBean(@Observes ProcessBean<?> processBean) {
            Bean<?> bean = processBean.getBean();
            // Veto beans from external packages
            if (bean.getBeanClass().getPackageName().startsWith("com.example.external")) {
                processBean.veto();
            }
        }
    }
    
    Don't forget to register the extension in META-INF/services/javax.enterprise.inject.spi.Extension by adding the full class name of your extension.

For OpenWebBeans (Apache TomEE, etc.)

  • Declarative exclusion via beans.xml:
    Use <excludedPackages> or <excludedClasses> to filter out unwanted beans:
    <beans xmlns="http://xmlns.jcp.org/xml/ns/javaee" version="2.0">
        <scan>
            <excludedPackages>com.example.external</excludedPackages>
            <excludedClasses>com.example.UnwantedExternalBean</excludedClasses>
        </scan>
    </beans>
    
2. Solution for the Shared Framework Scenario (App A + Libs L1/L2)

Your goal is to prevent App A's CDI container from scanning L1/L2's internal beans, while letting L1/L2 access those beans via the same container. Since you can't use module-info.java, here's the most robust approach:

This method ensures internal beans are only registered by their respective libraries, and App A's container won't discover them automatically:

  1. Mark internal beans with @Vetoed:
    Add the @Vetoed annotation to L1/L2's internal beans. This tells CDI to ignore them during default discovery:

    @Vetoed
    public class L1InternalBean {
        // Internal logic here
    }
    
  2. Write library-specific CDI extensions:
    Create an extension for L1 (and another for L2) that manually registers the internal beans during the BeforeBeanDiscovery phase:

    public class L1InternalBeanExtension implements Extension {
        void beforeBeanDiscovery(@Observes BeforeBeanDiscovery bbd, BeanManager bm) {
            // Register L1's internal bean with the container
            AnnotatedType<L1InternalBean> annotatedType = bm.createAnnotatedType(L1InternalBean.class);
            bbd.addAnnotatedType(annotatedType, "L1InternalBean");
            
            // If you need to add qualifiers or scope programmatically, you can modify the AnnotatedType here
        }
    }
    
  3. Register the extensions:
    For each library, create a file named javax.enterprise.inject.spi.Extension in META-INF/services/, and add the full class name of the extension (e.g., com.example.l1.L1InternalBeanExtension).

Why this works:

  • App A's CDI container won't scan L1/L2's internal beans because they're marked @Vetoed.
  • L1/L2's extensions explicitly register their internal beans, so the libraries can still @Inject them normally.
  • The beans remain public but are effectively hidden from App A's code (unless someone intentionally uses reflection, which is outside standard CDI usage).

Alternative: Package Scoping + beans.xml Filters

If you can organize L1/L2's internal beans into dedicated internal packages (e.g., com.example.l1.internal), you can use package filters:

  1. In App A's beans.xml, exclude the internal packages:
    <beans xmlns="http://xmlns.jcp.org/xml/ns/javaee" version="2.0">
        <scan>
            <exclude name="com.example.l1.internal.*"/>
            <exclude name="com.example.l2.internal.*"/>
        </scan>
    </beans>
    
  2. In L1/L2's beans.xml, include their own internal packages:
    <!-- L1's beans.xml -->
    <beans xmlns="http://xmlns.jcp.org/xml/ns/javaee" version="2.0">
        <scan>
            <include name="com.example.l1.internal.*"/>
        </scan>
    </beans>
    

Note: This relies on your CDI implementation merging scan configurations correctly, so test thoroughly with your setup.

Alternative: Programmatic Bean Lookup

If L1/L2 don't need to @Inject internal beans (and can fetch them manually), you can skip container scanning entirely:

public class L1Service {
    @Inject
    BeanManager beanManager;

    public L1InternalBean getInternalBean() {
        Set<Bean<?>> beans = beanManager.getBeans(L1InternalBean.class);
        if (beans.isEmpty()) {
            // Fallback to manual instantiation if needed
            return new L1InternalBean();
        }
        Bean<L1InternalBean> bean = (Bean<L1InternalBean>) beans.iterator().next();
        CreationalContext<L1InternalBean> ctx = beanManager.createCreationalContext(bean);
        return bean.create(ctx);
    }
}

This avoids scanning configuration but requires manual bean management.


内容的提问来源于stack exchange,提问作者Byebye

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.29 10:53:11