1.释放POJOS能量

  传统开发中是如何束缚POJOS呢,如果你开发过java很长时间,那你一定有接触过EJB的开发。那时候开发一个小小的功能都要扩展框架的类或者实现其接口。所以你很容易在早期的Struts,WebWork,Taperstry等框架中看到侵入到你应用中的框架代码。

  spring尽可能避免在你的应用中充满它的API.spring从来都不强迫你实现具体的spring接口或者扩展一个具体的spring类。替代的是,在基于spring开发的应用中经常没有指示说明你正在使用spring。最糟的情况是,类中使用spring注解,不过它还是一个POJO。下面是一个POJO实例

public class HelloWorldBean {
public String sayHello() {
return "Hello World";
}
}

  如你所见,这是一个简单的,普通的java类 ---- 一个POJO。没有任何指示可以看出它是spring的一个组件。spring的非侵略性编程模型意味着这个类在spring应用和非spring应用中表现出的功能是一样的。

  尽管它们的表现形式很简单,但POJO却是很强大的。spring使它们变得强大的一种方式是通过DI来组装。下面我们来看下DI如何让应用中的对象彼此间保持解耦的。

2.依赖注入

  依赖注入这个短语听起来可能很吓人,好像一个复杂编程技术或者设计模式概念那样神秘。但是事实正好相反,DI不像它听起来的那么复杂。通过在你的项目中应用DI,你会发现你的代码会变得非常的简单,容易理解和测试。

  DI如何工作?

  任何一个重要的应用(比起hello world复杂的多)都是由两个或者更多的类进行协作来执行一系列业务逻辑。传统上,每个对象都有责任去获取它所依赖的对象。这样就会导致高度耦合并且很难去进行代码测试。下面是一个例子

public class DamselRescuingKnight implements Knight {
private RescueDamselQuest quest;
public DamselRescuingKnight() {
this.quest = new RescueDamselQuest();
}
public void embarkOnQuest() {
quest.embark();
}
}

  如你所见,在DamselRescuingKnight构造器中创建了quest,一个RescueDamselQuest,这导致了DamselRescuingKnight和RescueDamselQuest紧紧的耦合在一起并且限制了quest-embarking的操作。更糟糕的是,你很难去为这个类写单元测试。在这样的一个测试中,你希望能够在embarkOnQuest()方法被调用的时候能够评估quest's embark()方法被调用。显然这里没有明显的方法来实现那个。很不幸,DamselRescuingKnight会变得不可测试。

  耦合是一个双头怪物。一方面,紧耦合代码很难去测试,很难去复用和理解,它是打鼹鼠游戏的典型展现(修复一个会导致一个或者更多的bug出现)。另一方面,一定量的耦合也是必要的。完全解耦的代码什么也做不了。为了能够实现有用的功能,类必须以某种方法知道其他类的一些情况。耦合是必须的但是应当小心管理。

  使用DI,对象的依赖的创建将会由第三方来协调处理。对象将不再期待创建或者获取它们的依赖,如图1.1所示,依赖将会被注入到需要的对象上。

  下面我们来看一下BraveKnight例子

public class BraveKnight implements Knight {
private Quest quest;
public BraveKnight(Quest quest) {
this.quest = quest;
}
public void embarkOnQuest() {
quest.embark();
}
}

  如你所见,BraveKnight,不像DamselResucingKnight那样,没有创建它自己的quest。取而代之,将quest作为构成参数传进来。这种类型的DI我们称为构造器注入。

  而且,quest是作为一个接口,而不是一个具体的实现。这样所有实现quest的接口都可以用在这里。BraveKnight关键点是不和任何Quest具体实现耦合。它不关心哪个类型的quest的embark被调用。只要它实现Quest接口就行。这就是DI最重要的好处 ---- 解耦。

  在测试期间最常见的做法是将依赖包装成一个mock实现。你可能由于紧耦合不能充分测试DamselResucingKnight,但是却能很容易通过一个mock实现来测试BraveKnight。如下所示

public class BraveKnightTest {
@Test
public void knightShouldEmbarkOnQuest() {
Quest mockQuest = mock(Quest.class);
BraveKnight knight = new BraveKnight(mockQuest);
knight.embarkOnQuest();
verify(mockQuest, times(1)).embark();
}
}

  这里使用Mockito框架来mock对象,伴随着mock对象,你创建一个新的BraveKnight实例,通过构造器来注入到mock Quest。随后调用embarkOnQuest()方法,询问Mockito去验证mock Quest's embark()方法是否调用一次。下面是一个具体Quest实现

public class SlayDragonQuest implements Quest {
private PrintStream stream;
public SlayDragonQuest(PrintStream stream) {
this.stream = stream;
}
public void embark() {
stream.println("Embarking on quest to slay the dragon!");
}
}

  如你所见,SlayDragonQuest是Quest具体实现,但是你可能注意到在Quest的构造器中还有个PrintStream对象,你可能会有疑问,如何将SlayDragonQuest给BraveKnight?还有PrintStream如何给SlayDragonQuest?

  创建相关应用组件的行为通常被叫做装配(wiring)。在spring中,这里有很多种装配的方式,但是最常见方法是通过XML。下面就是一个xml代码

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="knight" class="com.springinaction.knights.BraveKnight">
  <constructor-arg ref="quest" />
</bean>
<bean id="quest" class="com.springinaction.knights.SlayDragonQuest">
  <constructor-arg value="#{T(System).out}" />
</bean>
</beans>

  在这里,BraveKnight和SlayDragonQuest被声明为Spring的bean。在BraveKnight bean的例子中,将一个SlayDragonQuest bean的引用作为构造器参数传到BraveKnight构造器中。同时,SlayDragonQuest bean声明使用Spring表达式语言来传递System.out到SlayDragonQuest’s构造器中。

  如果XML配置不合适你的胃口,你可能会喜欢使用java来达到相同的目的

  

@Configuration
public class KnightConfig {
@Bean
public Knight knight() {
return new BraveKnight(quest());
}
@Bean
public Quest quest() {
return new SlayDragonQuest(System.out);
}
}

  这段代码和上面的XML配置等价。这个例子简单的展示了spring中如何装配bean的。你现在不用太担心这些细节,在以后的章节中将会深入来了解这些配置。现在一切都准备好了,只差如何运行这个应用。

  在Spring应用中,application context主要用来加载bean定义并且将它们装配在一起。Spring  application context全部的责任就是用来创建和装配对象,并以此来构造应用。Spring自带有几种application context实现,主要不同在于如何加载它的配置。

  当使用XML文件来声明beans时候,一个恰当的application context选择可能是ClassPathXmlApplicationContext。这个Spring context实现从应用的类路径中一个或者多个XML文件来加载Spring context。下面是个具体例子

public class KnightMain {

    public static void main(String[] args) throws Exception {
ClassPathXmlApplicationContext context =
new ClassPathXmlApplicationContext(
"META-INF/spring/knight.xml");
Knight knight = context.getBean(Knight.class);
knight.embarkOnQuest();
context.close();
}
}

  在这个main()方法中通过knight.xml中来加载Spring application context。然后将application context当作一个工厂来获取ID为knight的bean。最后从一个引用为Knight的对象来调用embarkOnQuest方法。注意到这个类根本不知道Quest的具体实现,只有knights.xml文件才知道具体实现。

spring 第一篇(1-1):让java开发变得更简单(上)的更多相关文章

  1. spring 第一篇(1-1):让java开发变得更简单(下)转

    spring 第一篇(1-1):让java开发变得更简单(下) 这个波主虽然只发了几篇,但是写的很好 上面一篇文章写的很好,其中提及到了Spring的jdbcTemplate,templet方式我之前 ...

  2. spring 第一篇(1-1):让java开发变得更简单(下)

    切面(aspects)应用 DI能够让你的软件组件间保持松耦合,而面向切面编程(AOP)能够让你捕获到在整个应用中可重用的组件功能.在软件系统中,AOP通常被定义为提升关注点分离的一个技术.系统由很多 ...

  3. GVIM与模板——让FPGA开发变得更简单

    还在使用FPGA开发环境自带的代码编辑器?还在逐个字母敲击冗长重复的代码?明德扬至简设计法让你快速提高代码编写效率!利用GVIM这一高效的编辑工具并添加自定义模板,通过简短的脚本命令即可自动生成所有常 ...

  4. .net敏捷开发框架 力软敏捷开发(learun) 让开发变的更简单

    版本:6.1.6.2 体验地址:www.fishcmonkey.com 联系QQ:6539471

  5. .net敏捷开发框架 力软(learun) 让开发变的更简单

    版本:6.1.6.2 体验地址:www.fishcmonkey.com 联系QQ:6539471

  6. spring 第一篇(1-3):鸟瞰spring蓝图

    如你所见,spring框架的核心是关注于如何使用DI.AOP和模板来让企业级java开发变得更简单.spring确实也是这样做的,所以很值得你去使用它.不过spring内容可能比你所能看到的要多很多. ...

  7. 第一篇:微信公众平台开发实战Java版之了解微信公众平台基础知识以及资料准备

    相信很多人或多或少听说了微信公众平台的火热.但是开发还是有一点门槛,鉴于挺多朋友问我怎么开发,问多了,自己平时也进行以下总结.所以下面给大家分享一下我的经验: 微信公众号是什么? 官网的介绍:再小的个 ...

  8. JVM学习第一篇思考:一个Java代码是怎么运行起来的-上篇

    JVM学习第一篇思考:一个Java代码是怎么运行起来的-上篇 作为一个使用Java语言开发的程序员,我们都知道,要想运行Java程序至少需要安装JRE(安装JDK也没问题).我们也知道我们Java程序 ...

  9. 初学Java ssh之Spring 第一篇

    之前虽然毕业前实习的工作是使用的C# .NET语言,但是,毕业后还是果断应聘Java.虽然自己对Java的理解不如C#深入,只是对基础知识比较熟悉,但还是义无返顾了··· 虽然应聘经历比较坎坷,但最终 ...

随机推荐

  1. 调用上一个页面的js方法

    点击商品分类,弹出下框 点击确定,将选中的类别的name和唯一的code返回到上个页面 function save(){ var ids = getIdSelections(); jp.get(&qu ...

  2. python语言实现阶乘的两种方法---递归和迭代

    阶乘的递归实现,代码如下: def factorial(n): if n==1: return 1 else: return n*factorial(n-1) number = int(input(& ...

  3. 关于java的关键字 transient

    我们都知道一个对象只要实现了Serilizable接口,这个对象就可以被序列化,Java的这种序列化模式为开发者提供了很多便利,我们可以不必关系具体序列化的过程,只要这个类实现了Serilizable ...

  4. ajax请求不能下载文件(转载)

    最近在做文件下载,后台写了个控制层,直接走进去应该就可以下载文件,各种文件图片,excel等 但是起初老是下载失败,并且弹出下面的乱码: 前台请求代码: $('#fileexcel').unbind( ...

  5. [Android Traffic] 看无线电波如何影响网络操作]

    转载自: http://blog.csdn.net/kesenhoo/article/details/7391031 Optimizing Downloads for Efficient Networ ...

  6. XSS-Proxy

    关于XSS(cross site scripting),相信对此有过研究的人已经感受到了它的“魅力”,权威机构也公布了最近的安全数据,xss已经上升为第二大网络安全隐患: 于此我想通过此文浅析一下xs ...

  7. javascript快速入门8--值,类型与类型转换

    原始值和引用值 在ECMAScript中,变量可以存放两种类型的值,即原始值和引用值. 原始值(primitive value)是存储在栈(stack)中的简单数据段,也就是说,它们的值直接存储在变量 ...

  8. Netty源码分析之NioEventLoop(转)

    原文:http://www.jianshu.com/p/9acf36f7e025 上一章节中,我们分析了Netty服务的启动过程,本章节分析Netty的NioEventLoop是如工作的. NioEv ...

  9. PHPer 应聘见闻

    关于我自己 我,很普通的一个开发,88年出生在皖南山区.从小学到高中毕业都没想过自己会从事软件开发,高考的误打误撞,被某普通二本院校收编.大学浑浑噩噩,对软件开发也没多大的兴趣,11年毕业后来杭,面试 ...

  10. Java8:纠结的默认方法

    [编程导论(Java)·4.3Java接口] 在[0.3.1 Java简单介绍]中,有这么一段话:"请注意:Java并不是作为教学语言设计的.世界各地的大学在讲授Java的过程中均遇到一些教 ...