当前位置:   article > 正文

java事物原理_事务之五:Spring @Transactional工作原理

java @traditional事物

javax.inject

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

javax.inject包里的几个类:

65d1a256db9bfa0dd2ad8685a0b99e93.png

在我们的程序中,很多类型依赖于其他类型。例如,一个 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) { … }

}

注入器可能会:

查找一个 TimeSource 实例

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

使用构造的 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

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

Java:Spring @Transactional工作原理

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

25.Spring @Transactional工作原理

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

Spring MVC工作原理(好用版)

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

Spring Session工作原理

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

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

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

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

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

Spring MVC工作原理 及注解说明

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

深入理解Spring IOC工作原理

为什么会出现spring,spring出现解决了什么问题? 1.分析普通多层架构存在的问题 JSP->Servlet->Service->Dao 层与层之间的依赖很强,属于耦合而且是 ...

Spring的工作原理核心组件和应用

Spring框架 Spring 是管理多个java类的容器框架,注意是类不管理接口. Spring 的主要功能 Ioc 反转控制和 DI 依赖注入. 注入的方式可以是构造函数赋值也可以是 set方法赋 ...

随机推荐

paper 132:图像去噪算法:NL-Means和BM3D

这篇文章写的非常好,确定要~认真~慎重~的转载了,具体请关注本文编辑作者:http://wenhuix.github.io/research/denoise.html   我不会告诉你这里的代码都是f ...

javascript:window.history.go(-1)

history是你浏览过的网页的url(简单的说就是网址)的集合,也就是你的浏览器里的那个历史记录.它在js里是一个内置对象,就跟document一样,它有自己的方法,go就是其中一个. 这个方法的参 ...

Regular Expression Matching leetcode

递归方法运行时间过长.考虑使用动态规划的方法. 代码如下: bool isMatch(string s, string p) { int i,j; int m=s.size(); int n=p.si ...

安装xubuntu时遇到的一些问题

1  下载地址 http://www.linuxdown.net/ 2  选择虚拟机 VirtualBox 3  安装步骤 http://www.cnblogs.com/zhcncn/p/398730 ...

js8月-4号,,思想

1.js使用观察者模式,做异步编程.面向事件编程. 2.事件执行 (1)用户触发事件(2)定时执行 作业:选项卡,导航栏.

笔记-动画篇-layout动画初体验

约束动画的文章要比预计的迟迟来临,最大的原因是没有找到我认为的足够好的动画来讲解约束动画 —— 当然了,这并不是因为约束动画太难.相反,因为约束动画实在太简单了,反而没有足够多的简单动画素材让我选用. ...

读写应用程序数据-NSUserDefault、对象归档(NSKeyedArchiver)、文件操作

ios中数据持久化存储方式一般有5种:NSUserDefault.对象归档(NSKeyedArchiver).文件操作.数据库存储(SQLite3).CoreData. 1.NSUserDefault ...

vim 学习笔记

vim介绍:一款编辑器,另外一般linux系统会自带,所以一般linux下日志.配置文件等 纯文本文件的修改编辑等通过vim操作 学会的好处:1 方便操作linux下日志.配置文件等纯文本文件 2 功 ...

iOS开发之使程序在后台运行

方法一(此方法不太可靠): 开启程序后台运行: [application beginBackgroundTaskWithExpirationHandler:^{ //后台运行过期后会调用此block内 ...

初学HTML5,你要懂得哪些?

很多人问过我这个问题,想要做HTML5页面你要懂得哪些知识?而问这个问题的人基本上都是刚听说过或刚接触HTML5,处在迷茫的阶段,他们往往会被一些网上炫酷页面所吸引,然后自己也想学习HTML5,能通过 ...

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/繁依Fanyi0/article/detail/225452
推荐阅读
相关标签
  

闽ICP备14008679号