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

如何避免JavaEE中重复编写多个@Produces方法?

解决JavaEE中批量注册@Produces类的方案

要实现新增Dao类时无需手动编写@Produces方法,可通过**CDI可移植扩展(Portable Extension)**结合自定义注解来自动扫描并注册生产者,以下是具体实现步骤:

1. 定义自定义注解标记目标Dao类

创建一个限定符注解,用于标记需要自动生成生产者的Dao接口:

import javax.inject.Qualifier;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;

import static java.lang.annotation.ElementType.TYPE;
import static java.lang.annotation.RetentionPolicy.RUNTIME;

@Qualifier
@Target(TYPE)
@Retention(RUNTIME)
public @interface JdbiDao {
}

2. 实现CDI扩展类

编写CDI扩展,在容器启动时扫描所有带@JdbiDao的类,并动态注册生产者Bean:

import javax.enterprise.context.ApplicationScoped;
import javax.enterprise.event.Observes;
import javax.enterprise.inject.spi.*;
import javax.inject.Inject;
import org.jdbi.v3.core.Jdbi;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;

public class JdbiDaoExtension implements Extension {
    private final Map<Class<?>, Bean<?>> daoBeanMap = new HashMap<>();

    // 扫描并收集所有带@JdbiDao的类
    <T> void processAnnotatedType(@Observes ProcessAnnotatedType<T> pat) {
        if (pat.getAnnotatedType().isAnnotationPresent(JdbiDao.class)) {
            Class<T> daoClass = pat.getAnnotatedType().getJavaClass();
            daoBeanMap.put(daoClass, new DaoProducerBean<>(daoClass));
            pat.veto(); // 让CDI忽略原始类,使用我们自定义的Bean
        }
    }

    // 在Bean发现阶段完成后注册自定义生产者
    void afterBeanDiscovery(@Observes AfterBeanDiscovery abd) {
        daoBeanMap.values().forEach(abd::addBean);
    }

    // 自定义生产者Bean实现
    private static class DaoProducerBean<T> implements Bean<T> {
        private final Class<T> daoClass;

        public DaoProducerBean(Class<T> daoClass) {
            this.daoClass = daoClass;
        }

        @Override
        public T create(CreationalContext<T> ctx) {
            // 获取已注入的Jdbi实例(需确保Jdbi已被注册为CDI Bean)
            Jdbi jdbi = CDI.current().select(Jdbi.class).get();
            return jdbi.onDemand(daoClass);
        }

        @Override
        public void destroy(T instance, CreationalContext<T> ctx) {
            // JDBI的onDemand代理无需手动销毁,留空即可
        }

        @Override
        public Set<Type> getTypes() {
            return Collections.singleton(daoClass);
        }

        @Override
        public Set<Annotation> getQualifiers() {
            return Collections.singleton(new JdbiDaoImpl());
        }

        @Override
        public Class<? extends Annotation> getScope() {
            return ApplicationScoped.class; // 可根据需求改为Dependent或其他作用域
        }

        @Override
        public String getName() {
            return daoClass.getSimpleName();
        }

        @Override
        public Set<Class<? extends Annotation>> getStereotypes() {
            return Collections.emptySet();
        }

        @Override
        public boolean isAlternative() {
            return false;
        }

        @Override
        public boolean isNullable() {
            return false;
        }

        @Override
        public Set<InjectionPoint> getInjectionPoints() {
            return Collections.emptySet();
        }
    }

    // 实现@JdbiDao注解的实例类
    private static class JdbiDaoImpl implements JdbiDao {
        @Override
        public Class<? extends Annotation> annotationType() {
            return JdbiDao.class;
        }
    }
}

3. 配置CDI扩展

在项目的META-INF/services/javax.enterprise.inject.spi.Extension文件中添加扩展类的全限定名,让CDI容器加载该扩展:

com.yourpackage.JdbiDaoExtension

(替换com.yourpackage为实际的包路径)

4. 注册Jdbi为CDI Bean

确保Jdbi实例可被CDI注入,可通过生产者类实现:

import javax.enterprise.context.ApplicationScoped;
import javax.enterprise.inject.Produces;
import org.jdbi.v3.core.Jdbi;

@ApplicationScoped
public class JdbiProducer {
    @Produces
    public Jdbi createJdbi() {
        // 初始化Jdbi,替换为你的数据库连接配置
        return Jdbi.create("jdbc:mysql://localhost:3306/your_db", "username", "password");
    }
}

5. 使用方式

给需要自动注册的Dao接口添加@JdbiDao注解:

@JdbiDao
public interface DaoX {
    // Dao方法定义
}

@JdbiDao
public interface DaoY {
    // Dao方法定义
}

之后就可以直接通过@Inject注入这些Dao,无需手动编写@Produces方法:

@Inject
private DaoX daoX;

@Inject
private DaoY daoY;

内容的提问来源于stack exchange,提问作者Matej Delinčák

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.13 15:52:37