【问题标题】:How to inject objects of the same class with different scopes?如何注入具有不同范围的同一类的对象?
【发布时间】:2026-01-21 10:30:01
【问题描述】:

就简单性和正确性而言,注入不同作用域的同一类对象的最佳方法是什么?

在 servlet 中,我希望注入具有不同范围的同一类的对象。 还不知道要不要用jsf。

  • 简单性:为每个作用域制作一个Qualifier和一个生产者方法太多了;在beans.xml中做一个接口,两个类,添加替代也太多了;使用 Address#isCurrent() 方法没有意义。
  • 正确性:JSR299, 3.11 说:不建议使用 @Named 作为注入点限定符。 仍然不知道为什么。
    尽管在注入点使用 @Named@ApplicationScoped@RequestScoped 但不是 @SessionScoped。请参阅下面的命名 sn-p

在春天很容易:
Spring sn-p

<bean id="currentAddress" class="xxx.Address" scope="session" />
<bean id="newAddress" class="xxx.Address" scope="request" />
<bean id="servlet" class="xxx.MyServlet">
 <property name="currentAddress" ref="currentAddress" />
 <property name="newAddress" ref="newAddress" />
</bean>


命名为 sn-p

/* Address class */
@Produces @RequestScoped @Named(value="request")
 public Address getNewAddress(){
 return new Address();
}

@Produces @SessionScoped @Named(value="application")
 public Address getCurrentAddress(){
 return new Address();
}
/* Servlet */
@Inject @RequestScoped @Named("request")  private Address newAddress;
@Inject @ApplicationScoped @Named("application") private Address currentAddress;

【问题讨论】:

    标签: java java-ee-6 cdi jboss-weld jsr299


    【解决方案1】:

    包含此建议的原因与人们更喜欢枚举而不是任意字符串的常量的原因相同,因为它不是类型安全的。您可以很容易地输入错误的类名,它会在运行时编译正常并失败。之所以包含该建议,是因为在大多数情况下,当您能够在编译时强制执行这些约束时,@named 会使您的应用程序变得不必要地脆弱。

    这里有一个good article 概述了原因:

    处理这种情况的首选方法是使用带有枚举值的@Qualifiers。查看标题为“String Qualifiers are Legacy”和“The Right Way”的部分,了解处理此问题的步骤。

    【讨论】:

    • 非常感谢您的回答。我可以看到不鼓励使用@Named("string") 的原因。在阅读了您建议的文章后,我提出了一个我更喜欢的新想法:两种方法只有一个限定符。我稍后会尝试。
    • 我编辑了我已经注意到@Qualifier 注释的帖子作为处理此问题的首选方式。
    • 谢谢,我刚刚用 sn-p 添加了答案
    【解决方案2】:

    感谢@nsfyn55 指出good article,在阅读了“正确的方法”部分后,我想出了我认为在简单性和正确性方面实现它的最佳方法。

    所以我只使用一个接口作为限定符注释。

    /* Qualifier annotation */
    @Qualifier
    @Retention(RUNTIME)
    @Target({FIELD,METHOD})
    public @interface Scope {
    
     Type value();
    
     enum Type { REQUEST, SESSION, APPLICATION };
    }
    
    
    /* Address class */
    @Produces @Scope(REQUEST) @RequestScoped
     public Address request() {
     return new Address();
    }
    
    @Produces @Scope(SESSION) @SessionScoped
     public Address session() {
     return new Address();
    }
    
    /* Servlet */
    @Inject @Scope(REQUEST)
    private Address newAddress;
    
    @Inject @Scope(SESSION)
    private Address currentAddress;
    

    【讨论】: