了解 CDI 中类型安全的必要性 [英] Understanding the necessity of type Safety in CDI

查看:25
本文介绍了了解 CDI 中类型安全的必要性的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

首先我要澄清一下,这篇文章不是要批评 CDI,而是要发现 CDI 设计背后的思想和假设,这将对设计任何使用 CDI 的 Web 应用程序产生明显影响.

First I should clarify that this post is not intended to criticize CDI, but to discover the thinking and assumptions behind the design of CDI and that will have obvious influence on designing any web app, which uses CDI.

CDI(Java EE 6)最显着的特性之一是类型安全.Jboss Seam 字体不安全.它使用名称来限定要注入的任何实例.如下图:

One of the most distinguished feature of CDI (of Java EE 6) is type safety. Jboss Seam was not safe in type. It uses name to qualify any instance to inject. Like bellow:

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

   @Name("yourBean")
   public class YourBean implements Bean {
     ...
   }

在注入 MyBean 时,可以这样做:

While injecting MyBean one can do this:

   @In
   private Bean myBean; //myBean is injected

   @In
   private Bean yourBean; //yourBean is injected  

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

And earlier versions of Spring (before 3.0), this type of injection happened like bellow:

在bean配置文件中定义bean即可:

Just define the beans in bean configuration file:

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

   <bean id="yourBean" class="com.example.common.YourBean">
       ...
   </bean>

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

And use named qualifier, deciding which one to use:

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

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

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

But now in CDI, First you need to define a custom Qualifier annotation for any specific type of object. Then use that annotation for qualifying that object. At the end of the day, when you look at your source code, you see that, you wasted considerable amount of time to write lots of custom annotations for dependency injection. Java community is moving towards annotations, leaving XML based configurations (verbose XML) behind. Is there anything that would convince anyone to think this (type safety with custom annotations) not as verbose annotations, but as an excellent and distinguished feature of CDI?

点,推送到突出显示

  1. 如果我为每个服务或 dao(每个接口)的类型安全使用自定义限定符,那么对于大型应用程序,例如具有 1000 个或更多服务或多个实现的 dao 类,这将是混乱的.那么对于大型应用程序,使用类型安全注入是否可行?
  2. 如果上述问题的答案是否",那么使用类型安全的意义何在?
  3. 即使为类型安全编写注释是可行的,但在大型应用程序中,仅仅避免详细的 xml 配置是否真的值得付出努力?
  4. 实际上我什么时候需要类型安全而不是 bean 名称限定符?
  1. If I use custom qualifier for type safety per service or dao (per interface), then for a large sized application like having 1000 or more service or dao classes with multiple implementations, it will be messy. Then for large sized applications, Is that feasible to use type safe injection?
  2. If the answer of the above question is "No" then, what is the point to use type safety?
  3. Even if it is feasible to write annotations for type safety, in large applications, is it really worth the effort for just avoiding verbose xml configuration?
  4. When actually I need type safety instead of bean name qualifier?

对以上几点的简短讨论

  1. 真正需要类型安全注入的情况并不多,特别是当你有一个接口的实现时,你应该使用 @Name 作为限定符.所以是的,在大型应用程序中,在实际需要时使用类型安全是可行的.
  2. 当然,类型安全是 CDI 的显着特征之一,在接受的答案中,有一个非详尽的列表,列出了您可能选择使用类型安全的原因.
  3. 由于您是一名聪明的程序员,并且您确切地知道何时使用类型安全,因此在真正需要时绝对值得付出努力.
  4. 已接受答案的大部分内容都在讨论,我们什么时候需要类型安全和这篇文章 对理解也很有帮助.
  1. There are not too many cases when you actually need type safe injection, specially when you have one implementation of an interface, you should use @Name as the qualifier. So yes, in a large sized application it is feasible to use type safety when it is actually needed.
  2. Ofcourse type safety is one of the distinguished feature of CDI and in the accepted answer there is a non-exhaustive list of reasons why you may chose to use type safety.
  3. As you are an intelligent programmer and you know precisely when to use type safety, so definitely it worth the effort, when really needed.
  4. Most of the parts of the accepted answer really talks, when do we need type safety and this article is also very helpful to understand.

感谢,祝您编码愉快!

推荐答案

CDI 是否冗长?是否需要限定符?

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

  1. First off, you don't need qualifiers when you only have one implementation of an interface.
  2. If you have multiple implementations of an interface, then ask yourself - do you need to differentiate between them after deployment?

  • 如果答案是否定的,则考虑使用替代方案.
  • 如果答案是肯定的,那么您仍然不需要限定符.原因如下:

  • If the answer is no, then consider using alternatives.
  • If the answer is yes, then you still don't need qualifiers. Here's why:

使用您自己的示例:

public class MyBean implements Bean {
    ...
}

public class YourBean implements Bean {
    ...
}

然后,您只需:

@Inject MyBean bean;

@Inject YourBean bean;

如果你不喜欢你的实例变量是一个具体的类型,而是希望看到一个接口,而不是这样做:

If you don't like your instance variables to be of a concrete type and would rather see an interface, than do this:

private Bean bean;

@Inject
public void setBean(MyBean bean) {
    this.bean = bean;
}

private Bean bean;

@Inject
public void setBean(YourBean bean) {
    this.bean = bean;
}

在上述所有情况下,它完全没有限定符,绝对类型安全,而且绝对不冗长.

In all the above cases it's completely qualifier free, absolutely type-safe, and definitely not verbose.

那么,详细说明最后一点 - 开发人员是否需要选择合适的实现,还是会出现问题?

Then, to elaborate on the last point - does the developer need to chose the appropriate implementation or can the choice be made problematically?

  • 如果开发人员要选择,则按照上述 2 中的说明进行操作.
  • 如果选择有问题,那么使用生产者:

  • If the the developer will be choosing, then do as described above in 2.
  • If the the choice can be made problematically, then use a producer:

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

仍然没有限定符,类型安全,并且可能仍然不冗长(自动化的交易选择).

Still qualifier free, type-safe, and maybe still not verbose (trade choice for automation).

请参阅这篇文章,对这一点进行了很好的扩展以及如何使用InjectionPoint API.

See this article for an excellent expansion of this point and ideas on how to use the InjectionPoint API.

是否需要限定符?

我可以看到在上述示例之后出现的这个问题.答案是肯定的,以下是您选择使用它们的非详尽原因列表:

Are qualifiers needed at all?

I can see this question arising after the above examples. The answer is yes and here's a non-exhaustive list of reasons why you may chose to use them:

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

  • In one of the examples above I mentioned injecting specific implementations of an interface in order to avoid using qualifiers. That is entirely fine when the code is internal and internal developers will know which is which. But what if the code is a library or framework and you don't want to expose any particular implementation in the public API? Define some qualifiers then and document them well. How is this different than verbose XML? Even though you as the library writer may be doing just as much verbose work, your users will not have to. Instead, they will just write one word above an injection point and be happy you didn't make them write any XML - I personally would be very very very happy. :)
  • In the producer example above, you may be able to cover most cases but not all with the logic in the producer method. Or maybe you just want the ability to override that logic at any particular injection point. Then, keep the producer, make a qualifier and annotate some specific implementation with it. Then use the qualifier when you don't want the producer logic to run.
  • Imagine a situation where you have multiple interfaces and multiple implementations. Particular implementations may have common distinguishable traits and those traits are common to all of your interfaces. As an example, lets take the Java Colections Framework, specifically the List, Set, and Map interfaces. Each of those have multiple implementations, but there are common traits across all or some interfaces. For example linked nodes (fast iteration) - think LinkedList, LinkedHashSet, LinkedHashMap; sorted (ordering) - think TreeSet, TreeMap; hash-table based (fast insertion / removal / contains) - think HashSet, LinkedHashSet, HashMap, LinkedHashMap; concurent; random access; etc. Now, you could define the @Linked, @Sorted, and @Hashannotations. Then inject:

@Inject @Linked @Hash private Map map;
@Inject @Sorted private Map map;
@Inject @Hash private Set set;
@Inject @Hash private Set set;

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

Now, is it worth doing this for a collections framework? I wouldn't do it, but I have a case similar to what I describe here in my current project at work (sorry, can't discuss).

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

And finally, you could use qualifiers to pass parameters to producers in conjunction with @Nonbinding. Continuing with the collections framework above, define:

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

这样,您可以将所需的容量和负载因子传递给生产者,返回任何基于哈希表的内容:

This way, you could pass in a desired capacity and load factor to the producer returning anything hash table based like so:

@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;

我希望这能回答您的问题.这肯定是我喜欢 CDI 的部分原因.

I hope this answers your question. It sure is part of why I love CDI.

这篇关于了解 CDI 中类型安全的必要性的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆