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. TZOJ 5396: 集五福过大年

    描述 又是一年春来到,伴随着春节,支付宝的“集五福”活动又开始了,五福分别是“爱国福”.“富强福”.“和谐福”.“友善福”和“敬业福”,五张不同的福卡可以合成一张“五福到”,crq也扫了不少福,这么多 ...

  2. 在xcode6中使用矢量图(iPhone6置配UI)

    转载出处:http://blog.xoneday.com ios应用程序是一个图像主导的产品.在开发一个应用程序时,你需要各种尺寸的图标,你需要为每个图像文件制作一个@1x尺寸和一个@2x尺寸.这样你 ...

  3. Android UI 统一修改Button控件的样式,以及其它系统控件的默认样式

    先介绍下修改原理:首先打开位于android.widget包下面的Button.java文件,这里有一句关键的代码如下: public Button(Context context, Attribut ...

  4. Linux下#!/usr/bin/env bash和#!/usr/bin/bash、#!/bin/bash的比较

    #!/usr/bin/env bash #在不同的系统上提供了一些灵活性. #!/usr/bin/bash #将对给定的可执行文件系统进行显式控制. 通过/usr/bin/env运行程序,用户不需要去 ...

  5. CentOS 6.9使用Setup配置网络(解决dhcp模式插入网线不自动获取IP的问题)

    说明:dhcp模式插入网线不自动获取IP是因为网卡没有激活,造成这种原因的,应该是安装系统时没有插入网线造成的. 解决方法: 修改网卡配置文件 vim /etc/sysconfig/network-s ...

  6. HTTP Basic 验证客户端 C#实现笔记

    HTTP Basic 验证客户端的原理:把HTTP头重的ContentType设置为:application/x-www-form-urlencoded如果HTTP头没有Authorization,那 ...

  7. Android 中 Environment.getExternalStorageDirectory()无效

    我们在处理缓存的时候,并不是每次都会在应用私有存储空间那里保存,很多时候是需要用到ExternalStorage.我们平时一般都是用Environment.getExternalStorageDire ...

  8. 监控Coherence成员的加入和离开集群事件

    对server事件的监控主要是实现MemberListener类,对Cache事件的监控主要通过MapListener 参考代码 package coherencetest; import com.t ...

  9. 异常值监测的方法 Tukey test

    参考: https://www.zhihu.com/question/38066650

  10. JavaScript破解验证码

    现在,Megaupload站点提供的CAPTCHA在上述代码面前已经败下阵来,说实话,这里的验证码设计的不不太好.但更有趣的是: 1.HTML 5中的Canvas应用程序接口getImageData可 ...