D 的个人博客

全职做开源,自由职业者

  menu

Drop 简介

Drop 简介

Drop 简介

 

转载请保留作者信息:

作者:88250

时间:2009 年 11 月 30 日

     Drop
是一个开源的 JSR-330(Dependency Injection for Java)参考实现,已通过 JSR-330 TCK 测试。该项目旨在构建一个符合 JSR-330 的最小化实现,使得新手能够更快了解 Java 依赖注入实现原理以及了解 Java 依赖注入标准规范。Drop 使用的是 Java 反射来进行依赖注入,没有使用代码生成来实现依赖注入。


  1. Drop 简介
    1    <ol class="writely-toc-subheading writely-toc-none" style="MARGIN-LEFT:0pt">
    
  2. Drop 注入器配置
    1      </li>
    
  3. 注入点检查
    1        <ol class="writely-toc-subheading writely-toc-none" style="MARGIN-LEFT:0pt">
    
  4. 注入点合法性检查
    1          </li>
    
  5. 注入点依赖满足检查
    1          </li>
    
1      </li>
  • 依赖注入
    1        <ol class="writely-toc-subheading writely-toc-none" style="MARGIN-LEFT:0pt">
    
  • 注入顺序
    1          </li>
    
  • 注入实现
    1          </li>
    
    1      </li>
    
  • 中英术语对照表
    1      </li>
    
    1  </li>
    

  • Drop 注入器配置

         Drop 提供两种方式配置组件(Bean):
    1. Java 注解
      1  例如:<br />
      2
      3  <br />
      4
      5  <span style="FONT-FAMILY:Courier New">@Named(&quot;helloSpeaker&quot;) </span>
      


      1  <span style="FONT-FAMILY:Courier New">@Hello // 限定器</span>
      


      1  <span style="FONT-FAMILY:Courier New">@SessionScoped // 作用域</span>
      


      1  <span style="FONT-FAMILY:Courier New">public class HelloSpeaker {</span>
      


      1  <span style="FONT-FAMILY:Courier New">&nbsp;&nbsp;&nbsp; public String say() {</span>
      


      1  <span style="FONT-FAMILY:Courier New">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return &quot;Hello!&quot;;</span>
      


      1  <span style="FONT-FAMILY:Courier New">&nbsp;&nbsp;&nbsp; }</span>
      


      1  <span style="FONT-FAMILY:Courier New">}</span>
      2
      3  
      4</li>
      
    2. Builder-style APIs
      1  <span style="FONT-FAMILY:Courier New">Configurer.createBean(concreteClass&lt;T&gt;) : Bean&lt;T&gt;</span>
      


      1  <span style="FONT-FAMILY:Courier New">Bean.&lt;T&gt;named(stringName) : Bean&lt;T&gt;</span>
      


      1  <span style="FONT-FAMILY:Courier New">Bean.&lt;T&gt;scoped(scopeAnnotation) : Bean&lt;T&gt;</span>
      


      1  <span style="FONT-FAMILY:Courier New">Bean.&lt;T&gt;qualified(qualifierAnnotation, qualifierAnnotation...) : Bean&lt;T&gt;</span>
      2
      3</li>
      

    1例如:<br />
    2
    3<br />
    4
    5final Configurer configurer = injector.getConfigurer();<br />
    
        configurer.createBean(HelloSpeaker.class).
                    named("helloSpeaker").

                    scoped(new DefaultScopedLiteral()).

                    qualified(new HelloLiteral());

            

            在 JSR-330 中,Named 也是一种 Qualifier,所以上述例子也可以这样写:


    configurer.createBean(HelloSpeaker.class).
    1<span style="FONT-FAMILY:Courier New">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span style="text-decoration: line-through;">named(&quot;helloSpeaker&quot;).</span>
    

                    scoped(new DefaultScopedLiteral()).

                   
    qualified
    (new NamedLiteral("helloSpeaker"),
    new HelloLiteral());


        

    注入点检查

         Drop 使用类型安全的依赖解析算法,类型安全指的就是:
    1.  
      1. 不使用文本内容进行组件依赖配置
      2. 依赖类型 + 限定器 = 类型安全

         在所有组件配置完成后,Drop 将对其进行注入点检查。


    1. 注入点
      1    注入点描述了需要进行依赖注入(标注了 @Inject)的元素信息。依赖注入有三个方法:字段注入,构造器注入,方法注入。据此,注入点可以分为两类,字段注入点,参数注入点。
      2  </div>
      
    2. 需要的类型
      1    需要的类型指的是被注入实例的的类型。该信息包含在注入点描述中。
      2  </div>
      
    3. 需要的限定器
      1    需要的限定器指的是注入点中描述的限定器。当注入点同时存在需要的限定器与需要的类型时,依赖注入容器可以进行类型安全的依赖查找与注入。
      2  </div>
      
    4. 依赖不满足
      1    该情况指的是依赖注入容器在进行注入点依赖查找时未从容器中找到满足注入点条件的情况。
      2  </div>
      
    5. 依赖混淆
      1    该情况指的是依赖注入容器在进行注入点依赖查找时从容器中查找到多于一个满足注入点条件的情况。
      2  </div>
      
    6. 依赖检查
      1  依赖检查指的就是检查依赖注入器中的所有注入点是否合法以及是被否满足,发生在容器配置后。如果满足,则注入器可以进行依赖注入,如果不满足,则抛出检查异常。
      2</li>
      
         例如,形如 @Inject Speaker nightSpeaker 的注入点,其需要的类型为:Speaker;需要的限定器为空集。形如 @Inject @Night Speaker nightSpeaker 的注入点,其需要的类型为:Speaker;需要的限定器为 @Night。该注入点为合法注入点,但是否能满足依赖还需要根据注入器的配置进行检查。
    1<br />
    

    注入点合法性检查

         一个合法可注入的构造器:
    1<ol>
    
  • 被 @Inject 标注。
  • 接受 0 个或多个参数,所有参数都作为参数注入点。
  • 在同一个类上只能存在一个可注入的构造器(只能有一个构造器被 @Inject 标注),如果一个类有且仅有一个默认构造器(public 修饰,无参数),不管是否标注 @Inject,注入器都将调用该默认构造器。
  • 不能是带类型参数的构造器。例如:<T>  TypeName(T t) 不能进行注入。
  • 1<br />
    2
    3&nbsp;&nbsp;&nbsp;&nbsp; 一个合法可注入的字段:<br />
    4
    5<ol>
    
  • 被 @Inject 标注。
  • 不能是 final 的。
  • 不能是类型变量。例如 T field 不能进行注入。
  • 1<br />
    2
    3&nbsp;&nbsp;&nbsp;&nbsp; 一个合法可注入的方法:<br />
    4
    5<ol>
    
  • 被 @Inject 标注。
  • 不能是 abstract 的。
  • 接受 0 个或多个参数,所有参数都作为参数注入点。
  • 不能是带类型参数的方法。例如:<T> void method(T t) 不能进行注入。
  • 1<br />
    2
    3&nbsp;&nbsp;&nbsp;&nbsp; Drop 目前还没有全部实现所有上述条件的检查。<br />
    

    注入点依赖满足检查

         JSR-330 规范将 @Named 定义为了一个限定器,并且有一个 String 类型的属性。这样,除了使用类型安全的依赖解析算法,也可以使用命名解析算法。下面,我们通过一个具体的例子来看看如何对注入点进行依赖满足检查。
     
    @Retention(RetentionPolicy.RUNTIME)
    @Target({ElementType.TYPE, ElementType.FIELD})
    @Qualifier
    public @interface Night {}
     
    public interface Speaker {
        String say();
    }
     
    public interface DarkernessSpeaker extends Speaker {}
     
    @Named("nightSpeaker")
    @Night
    public class NightSpeaker implements DarknessSpeaker {
        @Override
        public String say() {
            return "Night!";       
        }
    }
     
    public class SpeakerService {
        @Inject
        private Speaker nightSpeaker1;
     
        @Inject
        private DarknessSpeaker nightSpeaker2;
     
        @Inject
        private NightSpeaker nightSpeaker3;
     
        @Inject
        @Night
        private Speaker nightSpeaker4;
     
        @Inject
        @Named("nightSpeaker")
        private Speaker nightSpeaker5;
        
        @Inject
        @Named("nightSpeaker")
        @Night
        private Speaker nightSpeaker6;
    }
    1</div>
    

         我们逐个分析 SpeakerService 类的字段注入点依赖检查。注意,在整个依赖注入容器中,目前有且仅有一个 Speaker 接口的实现,即 NightSpeaker。因此,对于:
    1. @Inject private Speaker nightSpeaker1;
      1  该字段是可依赖解析的,不存在依赖不满足或依赖混淆情况。注入器可以根据接口 Speaker 查找到其唯一的实现 NightSpeaker。
      2</li>
      
    2. @Inject private DarknessSpeaker nightSpeaker2;
      1  该字段是可以依赖解析的,不存在依赖不满足或依赖混淆情况。注入器可以根据抽象类 DarknessSpeaker 查找到其唯一的实现 NightSpeaker。
      2</li>
      
    3. @Inject private NightSpeaker nightSpeaker3;
      1  该字段是可以依赖解析的,不存在依赖不满足或依赖混淆情况。注入器可以根据具体类 DarknessSpeaker 查找到其唯一的实现 NightSpeaker。
      2</li>
      
    4. @Inject @Night private Speaker nightSpeaker4;
      1  该字段是可以依赖解析的,不存在依赖不满足或依赖混淆情况。注入器可以根据接口 Speaker 与限定器 @Night 查找到其唯一的实现 NightSpeaker。
      2</li>
      
    5. @Inject @Named("nightSpeaker") private Speaker nightSpeaker4;
      1  该字段是可以依赖解析的,不存在依赖不满足或依赖混淆情况。注入器可以根据接口 Speaker 与限定器 @Named(&quot;nightSpeaker&quot;) 查找到其唯一的实现 NightSpeaker。
      2</li>
      
    6. @Inject @Named("nightSpeaker") @Night private Speaker nightSpeaker4;
      1  该字段是可以依赖解析的,不存在依赖不满足或依赖混淆情况。注入器可以根据接口 Speaker 与限定器集 {@Named(&quot;nightSpeaker&quot;), @Night} 查找到其唯一的实现 NightSpeaker。
      2</li>
      

         现在,我们添加一个 Speaker 实现类:


    public class HelloSpeaker implements Speaker {
             @Override
             public String say() {
                 return "Night!";       
        }
    1<span style="FONT-FAMILY:Courier New">}</span>
    

    1<br />
    
         此时,对于 SpeakerService 类的字段注入点 @Inject private Speaker nightSpeaker1; 来说,该注入点是不可依赖解析的,存在依赖混淆情况。注入器在根据接口 Speaker 查找时,将找到两个实现:NightSpeaker 与 HelloSpeaker。注入器无法判别该用哪一个类的实例进行依赖注入。

    依赖注入

    注入顺序

         JSR-330 规定了依赖注入的顺序,如下(忽略了静态成员注入):
    1. 构造器注入
    2. 父类字段注入
    3. 父类方法注入
    4. 子类字段注入
    5. 子类方法注入

        

    注入实现

        算法实现了遵循 JSR-330 的依赖注入顺序:

        SimpleBean.java:


        @Override

        protected void resolveDependencies(final Object reference)

                throws Exception {

            final Class<?> superclass = reference.getClass().getSuperclass();

            resolveSuperclassFieldDependencies(reference, superclass);

            resolveSuperclassMethodDependencies(reference, superclass);

            resolveCurrentclassFieldDependencies(reference);

            resolveCurrentclassMethodDependencies(reference);

        }


          具体实现为 Java 类层次递归实现,请参考源码

    中英术语对照表

    英文
    1        </strong>
    2
    3      </td>
    
    中文
    1        </strong>
    2
    3      </td>
    
    Annotation 注解
    Qulifier 限定器
    Scope
    1      </td>
    
    作用域
    1      </td>
    
    Bean
    1      </td>
    
    组件
    1      </td>
    
    Injection Point
    1      </td>
    
    注入点
    1      </td>
    
    Required Type
    1      </td>
    
    需要的类型
    1      </td>
    
    Required Qualifiers
    1      </td>
    
    需要的限定器
    1      </td>
    

    1<br />
    2
    3<a href="http://docs.google.com/View?id=ddrm6c35_1143hf63xgkq">well-formed</a>
    

    1</div>