了解CDI中类型安全的必要性

Saz*_*man 9 jsf dependency-injection seam java-ee cdi

首先,我要澄清一下,这篇文章并不是要批评CDI,而是要发现CDI设计背后的思路和假设,这对设计任何使用CDI的网络应用程序都有明显的影响.

CDI(Java EE 6)最显着的特征之一是类型安全.Jboss Seam在类型上并不安全.它使用name来限定要注入的任何实例.如下:

   @Name("myBean")
   public class MyBean implements Bean {
     ...
   }

   @Name("yourBean")
   public class YourBean implements Bean {
     ...
   }
Run Code Online (Sandbox Code Playgroud)

注入MyBean时,可以这样做:

   @In
   private Bean myBean; //myBean is injected

   @In
   private Bean yourBean; //yourBean is injected  
Run Code Online (Sandbox Code Playgroud)

早期版本的Spring(3.0之前),这种类型的注入发生在下面:

只需在bean配置文件中定义bean:

   <bean id="myBean" class="com.example.common.MyBean">
       ...
   </bean>

   <bean id="yourBean" class="com.example.common.YourBean">
       ...
   </bean>
Run Code Online (Sandbox Code Playgroud)

并使用命名限定符,决定使用哪一个:

   @Autowired
   @Qualifier("myBean")
   private Bean bean;

   @Autowired
   @Qualifier("yourBean")
   private Bean bean; 
Run Code Online (Sandbox Code Playgroud)

但现在在CDI中,首先需要Qualifier为任何特定类型的对象定义自定义注释.然后使用该注释来限定该对象.在一天结束时,当您查看源代码时,您会发现,您浪费了大量时间为依赖注入编写大量自定义注释.Java社区正朝着注释方向发展,将基于XML的配置(冗长的XML)留在后面.有没有什么可以说服任何人认为这(使用自定义注释键入安全性)不是冗长的注释,而是作为CDI的优秀和杰出特征?

编辑:

积分,推动突出显示

  1. 如果我使用自定义限定符来实现每个服务的类型安全性或dao(每个接口),那么对于大型应用程序(如具有1000个或更多服务或具有多个实现的dao类),它将是混乱的.那么对于大型应用,使用类型安全注入是否可行?
  2. 如果上述问题的答案为"否",那么使用类型安全有什么意义呢?
  3. 即使为类型安全编写注释是可行的,但在大型应用程序中,仅仅为了避免冗长的xml配置真的值得付出努力吗?
  4. 实际上我需要类型安全而不是bean名称限定符?

关于以上几点的简短讨论

  1. 当你真正需要类型安全注入时,没有太多的情况,特别是当你有一个接口的实现时,你应该@Name用作限定符.所以,是的,在大型应用程序中,在实际需要时使用类型安全是可行的.
  2. 当然类型安全是CDI的一个显着特征,在接受的答案中,有一个非详尽的原因列表,您可以选择使用类型安全.
  3. 由于您是一名智能程序员,并且您确切知道何时使用类型安全,所以当真正需要时,绝对值得付出努力.
  4. 接受的答案的大多数部分都是真正的说法,我们什么时候需要类型安全,这篇文章也非常有助于理解.

谢谢,快乐的编码!

rdc*_*rng 17

CDI是否详细?是否需要资格赛?

  1. 首先,当您只有一个接口实现时,您不需要限定符.
  2. 如果您有多个接口实现,那么问问自己 - 部署后是否需要区分它们?

  3. 然后,详细说明最后一点 - 开发人员是否需要选择适当的实现,或者选择是否有问题?

    • 如果开发人员将选择,则按照上面2中所述进行操作.
    • 如果选择可能有问题,那么使用生产者:

      @Produces
      public Bean obtainTheAppropriateBean(InjectionPoint ip) {
          if (meetsConditionA(ip)) {
              return getBeanImplA();
          } else if (meetsConditionB(ip)) {
              return getBeanImplB();
          } else if (...) {
              ...
          } else {
              return getDefaultBeanImpl();
          }
      }
      
      Run Code Online (Sandbox Code Playgroud)

      仍然没有资格,类型安全,可能仍然没有冗长(自动化的贸易选择).

    有关如何使用InjectionPoint API 的这一观点和想法,请参阅文章.

是否需要资格赛?

在上面的例子之后,我可以看到这个问题.答案是肯定的,这里列出了您可能选择使用它们的非详尽清单:

  • 在上面的一个例子中,我提到了注入接口的特定实现,以避免使用限定符.当代码是内部代码并且内部开发人员知道哪个是哪个时,这完全没问题.但是,如果代码是库或框架并且您不希望在公共API中公开任何特定实现,该怎么办?然后定义一些限定符并将其记录下来.这与冗长的XML有何不同?即使您作为图书馆作家可能正在做同样多的冗长工作,您的用户也不必这么做.相反,他们只会在注入点之上写一个单词,并且很高兴你没有让他们写任何XML - 我个人会非常非常高兴.:)
  • 在上面的生成器示例中,您可能能够覆盖大多数情况,但不能覆盖生成器方法中的逻辑.或者您可能只想在任何特定注入点覆盖该逻辑.然后,保留生产者,制作限定符并使用它注释一些特定的实现.然后在不希望生成器逻辑运行时使用限定符.
  • 想象一下,您有多个接口和多个实现的情况.特定实现可能具有共同的可区分特征,并且这些特征对于所有接口都是通用的.举个例子,让我们看看Java Colections Framework,特别是List,Set和Map接口.每个都有多个实现,但是所有或某些接口都有共同的特征.例如链接节点(快速迭代) - 想想LinkedList,LinkedHashSet,LinkedHashMap; 排序(排序) - 想想TreeSet,TreeMap; 基于哈希表(快速插入/删除/包含) - 想想HashSet,LinkedHashSet,HashMap,LinkedHashMap; concurent; 随机访问; 等等.现在,你可以定义的@Linked,@Sorted@Hash注释.然后注入:

    @Inject @Linked @Hash private Map map;
    @Inject @Sorted private Map map;
    @Inject @Hash private Set set;
    @Inject @Hash private Set set;
    
    Run Code Online (Sandbox Code Playgroud)

    现在,是否值得为集合框架执行此操作?我不会这样做,但我有一个类似于我在当前工作项目中所描述的情况(抱歉,无法讨论).

  • 最后,您可以使用限定符将参数传递给生成器@Nonbinding.继续上面的集合框架,定义:

    @Qualifier
    @Documented
    @Retention(RetentionPolicy.RUNTIME)
    public @interface Hash {
        @Nonbinding int capacity() default 16;
        @Nonbinding float loadFactor() default 0.75f;
    }
    
    Run Code Online (Sandbox Code Playgroud)

    这样,您可以将所需的容量和加载因子传递给生成器,返回任何哈希表,如下所示:

    @Inject @Hash(capacity = 256, loadFactor = 0.85f) private Set set;
    @Inject @Hash private Set set;
    @Inject @Hash(capacity = 8, loadFactor = 0.65f) private Map map;
    
    Run Code Online (Sandbox Code Playgroud)

我希望这回答了你的问题.这肯定是我爱CDI的部分原因.