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

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

问题描述

首先,我应该澄清一点,这篇文章并不是为了批评CDI,而是发现CDI设计背后的思考和假设,这将对设计任何使用Web应用程序的Web应用程序产生明显的影响CDI。


CDI(Java EE 6)最为显着的功能之一是类型安全。 Jboss Seam在类型上不安全。它使用名称来限定要注入的任何实例。如下:

  @Name(myBean)
public class MyBean实现Bean {
..
}

@Name(yourBean)
public class YourBean实现Bean {
...
}

注意MyBean时可以执行以下操作:

  @In 
私人Bean myBean; // myBean被注入

@In
private Bean yourBean; // yourBean被注入

早期版本的Spring(3.0之前),这种类型的注入类似于bellow:



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

  < bean id =myBeanclass =com.example.common.MyBean> 
...
< / bean>

< bean id =yourBeanclass =com.example.common.YourBean>
...
< / bean>

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

  @Autowired 
@Qualifier(myBean)
私人Bean bean;

@Autowired
@Qualifier(yourBean)
private Bean bean;

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



编辑:



突出显示


  1. 如果我使用自定义限定符来确定每个服务或dao(每个界面)的类型,那么一个大型的应用程序,如拥有1000个或更多的服务或多个实现的dao类,这将是凌乱的。那么对于大型应用程序来说,使用类型安全注射是否可行?

  2. 如果上述问题的答案是否,那么使用类型安全性是什么? li>
  3. 即使可以为类型安全写入注释,在大型应用程序中,是否真的值得为避免 verbose xml 配置而努力?

  4. 实际上我需要类型安全而不是bean名称限定符?

以上点


  1. 当您确实需要输入安全注射时,没有太多的情况,特别是当您有一个接口的实现,你应该使用 @Name 作为限定符。那么是的,在一个大型的应用程序中,实际上需要使用类型安全是可行的。

  2. 当然,安全性是CDI的特色之一,在接受的答案中您可以选择使用类型安全性的非穷尽列表。

  3. 由于您是一名聪明的程序员,您确切知道何时使用类型安全,所以绝对值得的努力,真正需要的时候。

  4. 被接受的答案的大部分内容真的在谈论,何时需要类型安全和这篇文章也很有帮助。

谢谢快乐的编码! p>

CDI是否详细?是否需要限定词?




  1. 首先,当您只有一个界面实现时,您不需要限定词。

  2. 如果您有多个接口实现,请问自己 - 部署后需要区分它们吗?




    • 如果答案为否,请考虑使用替代方案。

    • 如果答案为是,则您仍然不需要限定词。这就是为什么:



      使用自己的例子:

        public class MyBean实现Bean {
      ...
      }

      public class YourBean实现Bean {
      ...
      }

      然后,您只需执行以下操作:

        @Inject MyBean bean; 

        @Inject YourBean bean; 

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

       私人豆豆; 

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



      <$ p私人豆豆$ p>

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

      在上述所有情况下,它完全是免费的,绝对类型安全,绝对不是冗长的。



  3. 然后,详细说明最后一点 - 开发人员需要选择适当的实施或可以选择有问题吗?




    • 如果开发人员将选择,请按照上述2。 li>
    • 如果选择有问题,请使用制作人:

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

      仍然限定符免费,类型安全,也许仍然不详细(自动化的交易选择)。




    请参阅这篇的文章,以极大的扩展这一点,以及如何使用 InjectionPoint API。




需要限定符?



我可以看到上述例子后出现这个问题。答案是肯定的,这里是您可以选择使用它们的非常详尽的列表:




  • 在上述其中一个示例中提到注入接口的特定实现以避免使用限定符。这是完全正确的,当代码是内部的,内部开发人员会知道哪个是哪个。但是,如果代码是库或框架,而不想在公共API中公开任何特定的实现呢?然后定义一些限定符,并记录好它们。这与XML不同吗?尽管您作为图书馆作家可能正在做同样多的详细工作,您的用户将不必。相反,他们只会在注入点之上写一个字,并且高兴,你没有让他们写任何XML - 我个人将非常非常高兴。 :)

  • 在上面的生产者示例中,您可能可以覆盖大多数情况,但不能全部使用生产者方法中的逻辑。或者也许你只是想要在任何特定的注入点覆盖该逻辑的能力。然后,保留制作者,制作一个限定符,并用它注释一些具体的实现。然后,当您不希望生产者逻辑运行时,使用限定符。

  • 想象一下,您有多个接口和多个实现的情况。特定实现可能具有共同的可区分特征,并且这些特征对于所有接口都是通用的。例如,让我们来看看Java Colections框架,特别是List,Set和Map接口。每个都有多个实现,但是在所有或某些接口中都有常见的特征。例如链接节点(快速迭代) - 认为LinkedList,LinkedHashSet,LinkedHashMap;排序(排序) - 认为TreeSet,TreeMap;基于哈希表(快速插入/删除/包含) - 认为HashSet,LinkedHashSet,HashMap,LinkedHashMap; concurent;随机访问;现在,您可以定义 @Linked @Sorted @Hash 注释。然后注意:

      @Inject @Linked @Hash私人地图; 
    @Inject @Sorted私人地图;
    @Inject @Hash private Set set;
    @Inject @Hash private Set set;

    现在,是否值得为集合框架?我不会这样做,但我有一个类似于我在现在的工作项目中描述的情况(抱歉,不能讨论)。


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

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

    这样,您可以将所需的容量和负载因子传递给生产者返回任何散列表,如下所示:

      @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的一部分。


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.

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 {
     ...
   }

While injecting MyBean one can do this:

   @In
   private Bean myBean; //myBean is injected

   @In
   private Bean yourBean; //yourBean is injected  

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

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; 

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?

Edit:

Points, pushed to be highlighted

  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?

Short discussion on the above points

  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.

Thanks and happy coding!

解决方案

Is CDI verbose? Are qualifiers needed?

  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:

      To use your own example:

      public class MyBean implements Bean {
          ...
      }
      
      public class YourBean implements Bean {
          ...
      }
      

      Then, you simply do:

      @Inject MyBean bean;
      

      or

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

      or

      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.

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

    • 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).

    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:

  • 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).

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

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

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

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