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

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

事务之五:Spring @Transactional工作原理的更多相关文章

  1. Java:Spring @Transactional工作原理

    本文将深入研究Spring的事务管理.主要介绍@Transactional在底层是如何工作的.之后的文章将介绍: propagation(事务传播)和isolation(隔离性)等属性的使用 事务使用 ...

  2. 25.Spring @Transactional工作原理

    转自:http://www.importnew.com/12300.html 本文将深入研究Spring的事务管理.主要介绍@Transactional在底层是如何工作的.之后的文章将介绍: prop ...

  3. Spring MVC工作原理(好用版)

    Spring MVC工作原理 参考: SpringMVC工作原理 - 平凡希 - 博客园https://www.cnblogs.com/xiaoxi/p/6164383.html SpringMVC的 ...

  4. Spring Session工作原理

    本文首发于 vivo互联网技术 微信公众号 https://mp.weixin.qq.com/s/KCOFv0nRuymkX79-RZi9eg 作者:张正林 目录:1.引入背景2.使用方法3.工作流程 ...

  5. Struts1、Struts2、Hibernate、Spring框架工作原理介绍

    Struts1工作原理 Struts1工作原理图 1.初始化:struts框架的总控制器ActionServlet是一个Servlet,它在web.xml中配置成自动启动的Servlet,在启动时总控 ...

  6. Spring MVC工作原理及源码解析(三) HandlerMapping和HandlerAdapter实现原理及源码解析

    1.HandlerMapping实现原理及源码解析 在前面讲解Spring MVC工作流程的时候我们说过,前端控制器收到请求后会调⽤处理器映射器(HandlerMapping),处理器映射器根据请求U ...

  7. Spring MVC工作原理及源码解析(一) MVC原理介绍、与IOC容器整合原理

    MVC原理介绍 Spring MVC原理图 上图是Spring MVC工作原理图(图片来自网上搜索),根据上图,我们可以得知Spring MVC的工作流程如下: 1.用户(客户端,即浏览器)发送请求至 ...

  8. “Ceph浅析”系列之五——Ceph的工作原理及流程

    本文将对Ceph的工作原理和若干关键工作流程进行扼要介绍.如前所述,由于Ceph的功能实现本质上依托于RADOS,因而,此处的介绍事实上也是针对RADOS进行.对于上层的部分,特别是RADOS GW和 ...

  9. Spring MVC工作原理 及注解说明

    SpringMVC框架介绍 1) spring MVC属于SpringFrameWork的后续产品,已经融合在Spring Web Flow里面. Spring 框架提供了构建 Web 应用程序的全功 ...

随机推荐

  1. Swift学习笔记十一:方法

         方法是与某些特定类型相关联的功能/函数.在Swift中,结构体和枚举能够定义方法:其实这是Swift与C/Objective-C的主要差别之中的一个. 在Objective-C中,类是唯一能 ...

  2. Struts2之ModelDriven的使用

    http://www.cnblogs.com/luoyanli/archive/2012/11/20/2778361.html 我们可以根据Action属性的不同将它分为两类:Field-Driven ...

  3. mysql 修改表名的方法:sql语句

    在使用mysql时,经常遇到表名不符合规范或标准,但是表里已经有大量的数据了,如何保留数据,只更改表名呢? 可以通过建一个相同的表结构的表,把原来的数据导入到新表中,但是这样视乎很麻烦. 能否简单使用 ...

  4. 小程序getApp() 被删除坑

    在一个非page的js文件内使用getApp,当前台切到后台的时候,定义的var app = getApp()被删除了 如:新建一个app-libs.js start: function() { va ...

  5. 3.26课·········window.document对象

    1.Window.document对象 一.找到元素:    docunment.getElementById("id"):根据id找,最多找一个:    var a =docun ...

  6. HDU - 1598 find the most comfortable road 【最小生成树】

    题目链接 http://acm.hdu.edu.cn/showproblem.php?pid=1598 思路 用kruskal 算法 将边排序后 跑 kruskal 然后依次将最小边删除 再去跑 kr ...

  7. Data Structure Graph: cycle in a directed graph

    geeks上的解答复杂了些,用回溯就行了 #include <iostream> #include <vector> #include <algorithm> #i ...

  8. 【leetnode刷题笔记】Maximum Depth of binary tree

    Given a binary tree, find its maximum depth. The maximum depth is the number of nodes along the long ...

  9. vRA Customizing error

    toolsDeployPkg.log An error occurred while customizing VM vwbjvuqtest0751. For details reference the ...

  10. 国内ADSL用户的带宽一般都是1M、2M、3M的,理论上的下载速度分别是128K/S、256K/S、384K/S。

    国内ADSL用户的带宽一般都是1M.2M.3M的,理论上的下载速度分别是128K/S.256K/S.384K/S. 1024/8===128      2048/8==256