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

如何在不依赖实现细节的情况下确定Bean所属的Bean Archive?

确定CDI Bean所属的Bean Archive(不依赖实现细节)

由于CDI规范本身未暴露Bean Archive的API,要实现这个需求可以从类的来源定位或自定义元数据标记入手,以下是几种可行方案:

方案一:通过CDI扩展+类的资源定位识别

利用CDI扩展的ProcessBean事件,获取Bean对应的类,再通过类的ProtectionDomain或资源路径定位其所属的Jar(即Bean Archive)。

实现步骤:

  1. 创建CDI扩展类,监听ProcessBean事件:
public class BeanArchiveTrackingExtension implements Extension {

    private final Map<Bean<?>, String> beanToArchiveMap = new ConcurrentHashMap<>();

    <T> void observeProcessBean(@Observes ProcessBean<T> event) {
        Bean<T> bean = event.getBean();
        Class<?> beanClass = bean.getBeanClass();
        // 获取类所属的Jar路径
        String archivePath = getArchivePath(beanClass);
        if (archivePath != null) {
            beanToArchiveMap.put(bean, archivePath);
        }
    }

    private String getArchivePath(Class<?> clazz) {
        ProtectionDomain domain = clazz.getProtectionDomain();
        CodeSource codeSource = domain.getCodeSource();
        if (codeSource != null) {
            URL location = codeSource.getLocation();
            if (location != null) {
                // 提取Jar文件名或完整路径,根据需求调整
                return location.getFile();
            }
        }
        // 备选:通过类的资源路径推断
        URL classResource = clazz.getResource(clazz.getSimpleName() + ".class");
        if (classResource != null) {
            String resourcePath = classResource.toString();
            // 截取Jar路径部分,格式通常为 jar:file:/xxx/yyy.jar!/com/example/MyClass.class
            if (resourcePath.startsWith("jar:")) {
                return resourcePath.substring(4, resourcePath.indexOf("!"));
            }
        }
        return null;
    }

    // 提供外部获取Bean所属Archive的方法
    public String getArchiveForBean(Bean<?> bean) {
        return beanToArchiveMap.get(bean);
    }
}
  1. 在beans.xml中注册该扩展(或通过自动扫描,取决于CDI版本)。
  2. 后续需要查询时,注入该扩展并调用getArchiveForBean方法。

注意事项:

  • 部分容器环境下,CodeSource可能返回null,需依赖资源路径的备选方案。
  • 模块化环境(如JPMS)中,路径格式可能略有不同,需根据实际情况调整截取逻辑。

方案二:自定义Archive标记注解+扩展扫描

如果您能控制所有Bean Archive,可以给每个Archive添加一个专属的标记类,通过扩展扫描这些标记类来关联Bean与Archive。

实现步骤:

  1. 定义自定义标记注解:
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface BeanArchive {
    String name(); // 标识Archive的名称
}
  1. 在每个Bean Archive中创建一个空类并添加该注解,例如:
// 在archive-1.jar中
@BeanArchive(name = "user-service-archive")
public class UserServiceArchiveMarker {}

// 在archive-2.jar中
@BeanArchive(name = "order-service-archive")
public class OrderServiceArchiveMarker {}
  1. 创建CDI扩展,扫描所有标记类并记录类加载器与Archive名称的映射:
public class BeanArchiveTrackingExtension implements Extension {

    private final Map<ClassLoader, String> classLoaderToArchiveMap = new ConcurrentHashMap<>();
    private final Map<Bean<?>, String> beanToArchiveMap = new ConcurrentHashMap<>();

    void observeProcessAnnotatedType(@Observes ProcessAnnotatedType<?> event) {
        AnnotatedType<?> annotatedType = event.getAnnotatedType();
        BeanArchive archiveAnnotation = annotatedType.getAnnotation(BeanArchive.class);
        if (archiveAnnotation != null) {
            ClassLoader classLoader = annotatedType.getJavaClass().getClassLoader();
            classLoaderToArchiveMap.put(classLoader, archiveAnnotation.name());
        }
    }

    <T> void observeProcessBean(@Observes ProcessBean<T> event) {
        Bean<T> bean = event.getBean();
        ClassLoader beanClassLoader = bean.getBeanClass().getClassLoader();
        String archiveName = classLoaderToArchiveMap.get(beanClassLoader);
        if (archiveName != null) {
            beanToArchiveMap.put(bean, archiveName);
        }
    }

    public String getArchiveForBean(Bean<?> bean) {
        return beanToArchiveMap.get(bean);
    }
}
  1. 同样注册扩展后,即可通过扩展查询Bean所属的Archive名称。

优势:

  • 相比路径定位,名称更直观,不受环境路径变化影响。
  • 完全可控,适合您能管理所有Bean Archive的场景。

方案三:利用Bean的自定义元数据关联

如果允许给Bean添加额外元数据,可以在Bean类上添加自定义注解,直接标记其所属Archive:

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface FromArchive {
    String value();
}

然后在Bean类上标注:

@FromArchive("user-service-archive")
@ApplicationScoped
public class UserService {}

之后通过CDI的Bean.getBeanClass()获取类,再读取该注解的值即可。

局限性:

  • 需要手动给每个Bean添加注解,工作量较大,适合Bean数量较少的场景。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.03 06:32:18