zoukankan      html  css  js  c++  java
  • javax.inject中@Inject、@Named、@Qualifier和@Provider用法

    javax.inject

      包 javax.inject 指定了获取对象的一种方法,该方法与构造器、工厂以及服务定位器(例如 JNDI))这些传统方法相比可以获得更好的可重用性、可测试性以及可维护性。此方法的处理过程就是大家熟知的依赖注入,它对于大多数应用是非常有价值的。

      javax.inject包里的几个类:

      在我们的程序中,很多类型依赖于其他类型。例如,一个 Stopwatch 可能依赖于一个 TimeSource。一些类型被另一个类型依赖,我们就把这些类型叫做这个类型的依赖(物)。在运行时查找一个依赖实例的过程叫做解析依赖。如果找不到依赖的实例,那我们称该依赖为不能满足的,并导致应用运行失败。

          在不使用依赖注入时,对象的依赖解析有几种方式。最常见的就是通过编写直接调用构造器的代码完成:

           class Stopwatch {
               final TimeSource timeSource;
               Stopwatch () {
                   timeSource = new TimeSource(…);
               }
    
               void start() { … }
               long stop() { … }
           }

    如果需要更有弹性一点,那么我们可以通过工厂或服务定位器实现:

           class Stopwatch {
               final TimeSource timeSource;
               Stopwatch () {
                   timeSource = DefaultTimeSource.getInstance();
               }
    
               void start() { … }
               long stop() { … }
           }

     在使用这些传统方式进行依赖解析时,程序员必须做出适当权衡。构造器非常简洁,但却有一些限制(对象生存期,对象复用)。工厂确实解耦了客户与实现,但却需要样本式的代码。服务定位器更进一步地解耦了客户与实现,但却降低了编译时的类型安全。并且,这三个方式都不适合进行单元测试。例如,当程序员使用工厂时,该工厂的每一个产品都必须模拟出来,测试完后还要得记得清理:

           void testStopwatch() {
               TimeSource original = DefaultTimeSource.getInstance();
               DefaultTimeSource.setInstance(new MockTimeSource());
               try {
                   // Now, we can actually test Stopwatch.
                   Stopwatch sw = new Stopwatch();
                   //
               } finally {
                   DefaultTimeSource.setInstance(original);
               }
           }

     现实中,要模拟工厂将导致更多的样本式代码。测试模拟出的产品并清理它们在依赖多的情况下很快就控制不了了。更糟的是,程序员必须精确地预测未来到底需要多少这样的弹性,并为他做的“弹性选择”负责。如果程序员开始时选择了构造器方式,但后来需要一个更有弹性的方式,那他就不得不替换所有调用构造器的代码。如果程序员一开始过于谨慎地选择了工厂方式,结果可能导致要编写很多额外的样本式代码,引入了不必要的复杂度,潜在的问题比比皆是。

     依赖注入就是为了解决这些问题。代替程序员调用构造器或工厂,一个称作依赖注入器的工具将把依赖传递给对象:

           class Stopwatch {
               final TimeSource timeSource;
               @Inject Stopwatch(TimeSource timeSource) {
                   this.TimeSource = timeSource;
               }
               void start() { … }
               long stop() { … }
           }

     注入器将更进一步地传递依赖给其他的依赖,直到它构造出整个对象图。例如,假设一个程序员需要注入器创建一个 StopwatchWidget 实例:

     /** GUI for a Stopwatch */
        class StopwatchWidget {
    
            @Inject StopwatchWidget(Stopwatch sw) { … }
    
            …
    
        }

         注入器可能会:

    1. 查找一个 TimeSource 实例

    2. 使用找到的 TimeSource 实例构造一个 Stopwatch

    3. 使用构造的 Stopwatch 实例构造一个 StopwatchWidget

         这使得代码保持干净,使得程序员感到使用依赖(物)的基础设施非常容易。

         现在,在单元测试中,程序员可以直接构造对象(不使用注入器)并将该对象以模拟依赖的方式直接传入待测对象的构造中。程序员再也不需要为每一次测试都配置并清理工厂或服务定位器。这大大简化了我们的单元测试:

       void testStopwatch() {
               Stopwatch sw = new Stopwatch(new MockTimeSource());
               //
           }

          完全降低了单元测试的复杂度,降低的复杂程度与待测对象的数目及其依赖成正比。

           包 javax.inject 为使用这样的轻便类提供了依赖注入注解,但没有引入依赖配置方式。依赖配置方式取决于注入器的实现。程序员只需要标注了构造器、方法或字段来说明它们的可注入性(上面的例子就是构造器注入)。依赖注入器通过这些注解来识别一个类的依赖,并在运行时注入这些依赖。此外,注入器要能够在构建时验证所有的依赖是否满足。相比之下,服务定位器在构建时是不能检测到依赖不满足情况的,直到运行时才能发现。

          注入器实现有很多形式。一个注入器可以通过 XML、注解、DSL(领域规约语言),或是普通 Java代码来进行配置。注入器实现可以使用反射或代码生成。使用编译时代码生成的注入器甚至可能没有它自己的运行时描述。而其他注入器实现无论在编译时还是运行时可能都不使用代码生成。一个“容器”,其实可以把它定义为一个注入器,不过包 javax.inject不涉及非常大概念,旨在最小化注入器实现的限制。

        @Inject支持构造函数、方法和字段注解,也可能使用于静态实例成员。可注解成员可以是任意修饰符(private,package-private,protected,public)。注入顺序:构造函数、字段,然后是方法。父类的字段和方法注入优先于子类的字段和方法,同一类中的字段和方法是没有顺序的。

        @Inject注解的构造函数可以是无参或多个参数的构造函数。@Inject每个类中最多注解一个构造函数。

        在字段注解:

    用@Inject注解
    字段不能是final的
    拥有一个合法的名称
        在方法上注解:

    用@Inject注解
    不能是抽象方法
    不能声明自身参数类型
    可以有返回结果
    拥有一个合法的名称
    可以有0个或多个参数
            @Inject MethodModirers ResultType Identifier(FormalParameterList ) Throws MethodBody

        [上述翻译:inject的doc文档,翻译不好敬请谅解]

        构造函数注解:

    @Inject
    public House(Person owner) {
        System.out.println("---这是房屋构造函数---");
        this.owner = owner;
    }
        字段注解:
    @Inject private Person owner;
        方法注解:
    @Inject
    public void setOwner(Person owner) {
        this.owner = owner;
    }
        @Inject注解和Spring的@Autoware注解都是根据类型对其进行自动装配。
        SpringUtil类:

    public class SpringUtil {
        private static ApplicationContext context = null;
        public static ApplicationContext getApplicationContext() {
            if (context == null) {
                context = new ClassPathXmlApplicationContext("spring.xml");
            }
            return context;
        }
     
        public static ApplicationContext getApplicationContext(String path) {
            return new ClassPathXmlApplicationContext(path);
        }
     
        public static ApplicationContext getAnnotationConfigApplicationContext(String basePackages) {
            return new AnnotationConfigApplicationContext(basePackages);
        }
    }
    Person类:
    import javax.inject.Named;
     
    @Named
    public class Person {
        private String name;
     
        public Person() {
            System.out.println("---这是人的构造函数---");
        }
     
        public String getName() {
            return name;
        }
     
        public void setName(String name) {
            this.name = name;
        }
    }
    House类:
    @Named
    public class House {
        @Inject private Person owner;
        public House() {
            System.out.println("---这是房屋构造函数---");
        }
     
        public Person getOwner() {
            return owner;
        }
     
        public void setOwner(Person owner) {
            this.owner = owner;
        }
    }
    测试类:
    public class Test {
        public static void main(String[] args) {
            ApplicationContext context = SpringUtil.getApplicationContext(
                    "test/spring/inject/bean-inject.xml");
            House house = (House)context.getBean("house");
            Person p = house.getOwner();
            p.setName("张三");
            System.out.println(house.getOwner().getName());
        }
    }
    输出结果:
    ---这是房屋构造函数---
    ---这是人的构造函数---
    张三

        上述例子在Spring3.1下测试成功,在Spring3.1下,每个构造函数只初始化一次及默认的单例形式,个人感觉如果脱离Spring环境应该每次用都会实例化新的对象,当然根据实现的jar包不同而不同,要不javax.inject下的@Singleton注解就没有什么用途了。

    @Named

        @Named和Spring的@Component功能相同。@Named可以有值,如果没有值生成的Bean名称默认和类名相同。

        例如:

    @Named public class Person
        该bean的名称就是person。
    @Named("p") public class Person
        如果指定名称,那么就是指定的名称喽。
    @Qualifier

        任何人都可以定义一个新的修饰语,一个qualifier注解应该满足如下条件:

    定义的注解类有@Qualifier,@Retention(RUNTIME)和@Documented。
    可以有属性
    可以是公共API的一部分
    可以用@Target注解限定使用范围
        下面是Qualifier的例子:

    Genre注解类:

    @Documented
    @Retention(RetentionPolicy.RUNTIME)
    @Qualifier
    @Target(value = {ElementType.FIELD, ElementType.PARAMETER, ElementType.TYPE})
    public @interface Genre {
        User user() default User.STUDENT;
        public enum User {STUDENT, TEACHER}
    }
    用户接口:(对个数进行统计)
    public interface IUserDAO {
        int count();
    }
    StudentDAO:
    @Named
    @Genre(user = User.STUDENT)
    public class StudentDAO implements IUserDAO{
        @Override
        public int count() {
            System.out.println("----StudentDAO----");
            return 0;
        }
     
    }
    TeacherDAO:
    @Named
    @Genre(user = User.TEACHER)
    public class TeacherDAO implements IUserDAO {
     
        @Override
        public int count() {
            System.out.println("--TeacherDAO--");
            return 0;
        }
    }
    UserDAOProcessor:
    @Named
    public class UserDAOProcessor {
        /*对TeacherDAO类的注入,如果对StudentDAO类注入应该是:@Genre(user = User.STUDENT)或@Genre,因为@Genre默认的是STUDENT*/
        @Inject
        private @Genre(user = User.TEACHER) IUserDAO userDAO;
     
        public int count() {
            return userDAO.count();
        }
     
        public IUserDAO getUserDAO() {
            return userDAO;
        }
     
        public void setUserDAO(IUserDAO userDAO) {
            this.userDAO = userDAO;
        }
    }


    测试类:

    public class Test {
        public static void main(String[] args) {
            ApplicationContext context = SpringUtil.getApplicationContext(
                    "test/spring/inject/bean-inject.xml");
            UserDAOProcessor processor = (UserDAOProcessor)context.getBean("userDAOProcessor");
            System.out.println(processor.count());
        }
    }
    输出结果:
    --TeacherDAO--
    0

        个人对@Qualifier的理解:

    和Spring的@Qualifier大致相同
    单独用@Inject无法满足对接口的注入,无法找到哪个具体类,所以用@Qualifier来确定注入的具体类
    用到@Qualifier的注解中可以有值、无值和用枚举类型
    @Singleton

        使用该注解标记该类只创建一次,不能被继承。一般在类上用该注解。

  • 相关阅读:
    VBA 的编写与执行
    C# eBook
    【转】Winfrom datagridview 打印
    jquery循序渐渐1
    C# 数据库备份及还原
    Asp.net调用RAR压缩 解压文件
    SQL Server 2005下的分页SQL
    优秀文档收藏
    动态传入“表名,字段名,字段类型,默认值”四个字符串,根据新的字段名称和类型来创表表结构
    一句话搞定生日提示
  • 原文地址:https://www.cnblogs.com/duanxz/p/3634370.html
Copyright © 2011-2022 走看看