spring 第一篇(1-1):让java开发变得更简单(上)
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开发变得更简单(上)的更多相关文章
- spring 第一篇(1-1):让java开发变得更简单(下)转
		spring 第一篇(1-1):让java开发变得更简单(下) 这个波主虽然只发了几篇,但是写的很好 上面一篇文章写的很好,其中提及到了Spring的jdbcTemplate,templet方式我之前 ... 
- spring 第一篇(1-1):让java开发变得更简单(下)
		切面(aspects)应用 DI能够让你的软件组件间保持松耦合,而面向切面编程(AOP)能够让你捕获到在整个应用中可重用的组件功能.在软件系统中,AOP通常被定义为提升关注点分离的一个技术.系统由很多 ... 
- GVIM与模板——让FPGA开发变得更简单
		还在使用FPGA开发环境自带的代码编辑器?还在逐个字母敲击冗长重复的代码?明德扬至简设计法让你快速提高代码编写效率!利用GVIM这一高效的编辑工具并添加自定义模板,通过简短的脚本命令即可自动生成所有常 ... 
- .net敏捷开发框架 力软敏捷开发(learun) 让开发变的更简单
		版本:6.1.6.2 体验地址:www.fishcmonkey.com 联系QQ:6539471 
- .net敏捷开发框架 力软(learun) 让开发变的更简单
		版本:6.1.6.2 体验地址:www.fishcmonkey.com 联系QQ:6539471 
- spring 第一篇(1-3):鸟瞰spring蓝图
		如你所见,spring框架的核心是关注于如何使用DI.AOP和模板来让企业级java开发变得更简单.spring确实也是这样做的,所以很值得你去使用它.不过spring内容可能比你所能看到的要多很多. ... 
- 第一篇:微信公众平台开发实战Java版之了解微信公众平台基础知识以及资料准备
		相信很多人或多或少听说了微信公众平台的火热.但是开发还是有一点门槛,鉴于挺多朋友问我怎么开发,问多了,自己平时也进行以下总结.所以下面给大家分享一下我的经验: 微信公众号是什么? 官网的介绍:再小的个 ... 
- JVM学习第一篇思考:一个Java代码是怎么运行起来的-上篇
		JVM学习第一篇思考:一个Java代码是怎么运行起来的-上篇 作为一个使用Java语言开发的程序员,我们都知道,要想运行Java程序至少需要安装JRE(安装JDK也没问题).我们也知道我们Java程序 ... 
- 初学Java ssh之Spring 第一篇
		之前虽然毕业前实习的工作是使用的C# .NET语言,但是,毕业后还是果断应聘Java.虽然自己对Java的理解不如C#深入,只是对基础知识比较熟悉,但还是义无返顾了··· 虽然应聘经历比较坎坷,但最终 ... 
随机推荐
- iOS 读取Json 代码
			保存一下iOS 读取Json的代码,留着以后Copy用,哈哈. NSString* path = [[NSBundle mainBundle] pathForResource: @"Sand ... 
- HashMap源码-使用说明部分
			/* * Implementation notes. * 使用说明 * * This map usually acts as a binned (bucketed) hash table, but * ... 
- O2S.Components.PDFRender4NET.dll 图片空白BUG问题
			在用网上的2.0.1.0破解版的时候,会出现莫名的错误,大多数pdf转成图片都是Ok的,但是有部分转出来是空白,因为我这里要涉及大量的pdf转图片,不允许出现错误. 后来经过实验发现是这个版本存在bu ... 
- VB6的UTF8编码解码
			'UTF-8编码 Public Function UTF8Encode(ByVal szInput As String) As String Dim wch As String D ... 
- Solr6.6.0 用 SimplePostTool与界面dataimport索引方式区别
			通过测试发现用SimplePostTool与solr界面dataimport索引数据的结果有如下区别: 1.SimplePostTool索引数据对结构化数据文件索引比较合适,比如csv/json/xm ... 
- Github:在Github上创建自己的代码仓库
			Github是一个分布式版本控制系统,最初由Linux之父Linus Torvalds编写,在云时代的今天已经成为了开发者管理代码和发现已有代码的最常用工具之一,下面我们将开始git之旅. 注册Git ... 
- Mycat探索之旅(5)----常用的分片规则
			分片枚举 通过在配置文件中配置可能的枚举id,自己配置分片,本规则适用于特定的场景,比如有些业务需要按照省份或区县来做保存, 而全国省份区县固定的,这类业务使用本条规则,配置如下: <table ... 
- centos 6.5 下使用nginx 反向代理 多个tornado
			centos 6.5 nginx 1.4.4 /etc/nginx/conf.d 下创建tornado.conf upstream tornado { server 127.0.0.1:8887; s ... 
- struts2入门示例(hello world)
			1. 环境搭建 按照之前的文章配置好myeclipse的jdk和tomcat,并新建一个web项目后,可开始动手配置与struts2相关的地方了.首先去struts的官网下载好最新的struts2代码 ... 
- 个人或者企业怎么进行app开发?开发一款APP应用大概须要多少钱?
			App开发.是指专注于手机应用软件开发与服务. App是application的缩写,通常专指手机上的应用软件,或称手机client.另外眼下有非常多在线app开发平台.当然移动互联网时代是全民的移动 ... 
