为AbstractPopulator子类指定依赖注入:自定义@Inject next字段需求
next字段注入的解决方案 看起来你是想给每个继承自AbstractPopulator的子类配置专属的next依赖对吧?这个场景其实很常见——默认的依赖注入会因为类型匹配的问题,没法区分不同的ITemplatePopulator实例,导致注入的不是你想要的那个。下面给你几个实用的解决思路:
方案1:自定义限定符注解(通用型,适配CDI/Spring)
这种方式最灵活,能清晰标记每个专属的依赖实例。
首先创建一个自定义的限定符注解,用来区分不同的next实例:
import javax.inject.Qualifier; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; @Qualifier @Retention(RetentionPolicy.RUNTIME) public @interface PopulatorNext { String value(); // 用value来标识不同的实例 }
接下来,给每个子类的next字段加上这个限定符,指定要注入的实例标识:
比如ImportsPopulator需要注入FieldsPopulator作为它的next:
public class ImportsPopulator extends AbstractPopulator { // 覆盖父类的注入,指定专属的next实例 @Inject @PopulatorNext("fields") private ITemplatePopulator next; // 子类自己的业务逻辑... }
然后给对应的next实现类加上相同标识的限定符,让DI容器能匹配上:
@PopulatorNext("fields") public class FieldsPopulator extends AbstractPopulator { // 同样给这个子类的next指定专属标识 @Inject @PopulatorNext("leftHandSide") private ITemplatePopulator next; // 业务逻辑... } @PopulatorNext("leftHandSide") public class LeftHandSidePopulator extends AbstractPopulator { // 这个子类如果不需要next,可以不用注入,或者注入空实现 // @Inject // private ITemplatePopulator next; // 业务逻辑... }
这样DI容器就能根据限定符的标识,精准给每个子类注入对应的next实例了。
方案2:用现成的@Named注解(快速实现)
如果不想自定义注解,可以用DI框架自带的@Named注解来标记实例:
在子类中指定要注入的实例名称:
public class ImportsPopulator extends AbstractPopulator { @Inject @Named("fieldsPopulator") private ITemplatePopulator next; }
然后给对应的实现类命名:
@Named("fieldsPopulator") public class FieldsPopulator extends AbstractPopulator { @Inject @Named("leftHandSidePopulator") private ITemplatePopulator next; } @Named("leftHandSidePopulator") public class LeftHandSidePopulator extends AbstractPopulator { // 业务逻辑... }
这个方案更快捷,适合不想新增自定义注解的场景。
方案3:构造函数注入(推荐最佳实践)
字段注入虽然方便,但在这种需要精准控制依赖的场景下,构造函数注入更清晰,也更利于测试。你可以修改抽象类,把next作为构造参数传递:
public abstract class AbstractPopulator implements ITemplatePopulator { protected final ITemplatePopulator next; // 抽象类的构造函数,子类必须传递对应的next protected AbstractPopulator(ITemplatePopulator next) { this.next = next; } }
然后每个子类通过构造函数传入自己的专属next,DI容器会自动解析构造参数:
// ImportsPopulator注入FieldsPopulator作为next public class ImportsPopulator extends AbstractPopulator { @Inject public ImportsPopulator(@Named("fieldsPopulator") ITemplatePopulator next) { super(next); } } // FieldsPopulator注入LeftHandSidePopulator作为next public class FieldsPopulator extends AbstractPopulator { @Inject public FieldsPopulator(@Named("leftHandSidePopulator") ITemplatePopulator next) { super(next); } } // LeftHandSidePopulator不需要next,构造时传null或空实现 public class LeftHandSidePopulator extends AbstractPopulator { @Inject public LeftHandSidePopulator() { super(null); } }
构造函数注入的好处很明显:
- 依赖关系在对象创建时就确定,避免了空指针风险
- 单元测试时可以手动传入Mock的
next实例,非常方便 - 符合依赖注入的最佳实践,避免了字段注入的隐蔽性
额外小提示
如果是在Spring环境下,还可以用@Qualifier配合@Component的value属性来标记实例,效果和@Named类似。另外要确保所有的ITemplatePopulator实现类都被DI容器扫描到——比如加上@Component(Spring)或者@Named(CDI)注解,让容器管理这些实例。
内容的提问来源于stack exchange,提问作者lapots

