spring 第一篇(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文件才知道具体实现。

时间: 2024-10-12 01:18:12

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

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

应用切面(aspects) DI能够让你的软件组件间保持松耦合,而面向切面编程(AOP)能够让你捕获到在整个应用中可重用的组件功能.在软件系统中,AOP通常被定义为提升关注点分离的一个技术.系统由很多的组件组成,每个组件负责一部分的功能.但是这些组件往往除了核心功能外,还有些额外的责任.比如像日志,事务管理和安全这些系统服务会被引进到组件中.这些服务通常被称为横切关注点(cross-cutting-concerns),因为它们常常贯穿于多个组件中. 在多个组件中传播这些概念,你会引进两个复杂层面

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

如你所见,spring框架的核心是关注于如何使用DI.AOP和模板来让企业级java开发变得更简单.spring确实也是这样做的,所以很值得你去使用它.不过spring内容可能比你所能看到的要多很多.在spring框架中,你可能找到几种使java开发变得容易的方式.但是spring框架是构建在核心框架的的一个生态系统,将spring延伸到web service,REST,mobile和NoSQL领域. 首先,让我们分解下spring核新框架,看看它带来的价值.然后我们会将我们的视野集中在spri

可能是国内第一篇全面解读 Java 现状及趋势的文章

作者 | 张晓楠 Dragonwell JDK?最新版本 8.1.1-GA 发布,包括全新特性和更新! 导读:InfoQ 发布<2019 中国 Java 发展趋势报告>,反映 Java 在中国发展的独特性,同时也希望大家对 Java 有一个正确的认识. 2 个月前,InfoQ 英文站发布了一份<2019 Java 发展趋势报告>,从技术采用生命周期的角度,分析了 Java 这门 20 多年历史语言的发展现状.这份报告发布后,发生了几个我们没想到的问题:一是有些开发者对 Java 产

第一篇:Unity3d游戏开发之移动端完整包更新方案

由于Unity没法进行热更新,也就是局部更新,所以如果你在设计之前没有考虑好使用Lua,反射,或者其他方式实现热更新,但是又要面临游戏上线那么你可以考虑使用完整包更新,其实这对于游戏包不是很大的情况下,也是一个不错的选择,当然设计者在设计之初都想好了热更新那就宁当别论了. Unity3d 在移动端考虑Android和Ios平台的完整包更新 实现逻辑: 首先检查本地的版本和服务器的版本是否相同,如果相同则直接进入游戏,如果不同则下载服务器的最新文件,然后根据最新文件到资源服务器下载最新的安装包 我

APICloud可以让你开发变得很简单

QQ登录.分享怎么做?相信APICloud可以让你开发变得很简单.这里分享一个demo供大家参考. 1.使用模块,先打开它的开发文档http://www.apicloud.com/mod_detail/37360 添加模块至APIcloud项目 2. 编译自定义loader 3. 在腾讯开放平台管理中心创建应用,提交审核,拿到appkey和appId 4. 在项目代码中找到config.xml 添加代码 <font face="微软雅黑" size="3"&g

[翻译和注解]Kafka Streams简介: 让流处理变得更简单

Introducing Kafka Streams: Stream Processing Made Simple 这是Jay Kreps在三月写的一篇文章,用来介绍Kafka Streams.当时Kafka Streams还没有正式发布,所以具体的API和功能和0.10.0.0版(2016年6月发布)有所区别.但是Jay Krpes在这简文章里介绍了很多Kafka Streams在设计方面的考虑,还是很值得一看的. 以下的并不会完全按照原文翻译,因为那么搞太累了……这篇文件的确很长,而且Jay

jQuery Validate【为表单提供了强大的验证功能,让客户端表单验证变得更简单】

jQuery Validate jQuery Validate 插件为表单提供了强大的验证功能,让客户端表单验证变得更简单,同时提供了大量的定制选项,满足应用程序各种需求.该插件捆绑了一套有用的验证方法,包括 URL 和电子邮件验证,同时提供了一个用来编写用户自定义方法的 API.所有的捆绑方法默认使用英语作为错误信息,且已翻译成其他 37 种语言. 该插件是由 J?rn Zaefferer 编写和维护的,他是 jQuery 团队的一名成员,是 jQuery UI 团队的主要开发人员,是 QUn

深入解析 Kubebuilder:让编写 CRD 变得更简单

作者 | 刘洋(炎寻) 阿里云高级开发工程师 导读:自定义资源 CRD(Custom Resource Definition)可以扩展 Kubernetes API,掌握 CRD 是成为 Kubernetes 高级玩家的必备技能,本文将介绍 CRD 和 Controller 的概念,并对 CRD 编写框架 Kubebuilder 进行深入分析,让您真正理解并能快速开发 CRD. 概览 控制器模式与声明式 API 在正式介绍 Kubebuidler 之前,我们需要先了解下 K8s 底层实现大量使用

初学Java ssh之Spring 第一篇

之前虽然毕业前实习的工作是使用的C# .NET语言,但是,毕业后还是果断应聘Java.虽然自己对Java的理解不如C#深入,只是对基础知识比较熟悉,但还是义无返顾了··· 虽然应聘经历比较坎坷,但最终还是成功入职了. 刚入职,再加上经验不足,所以最近任务比较少,这样就有大把时间自学了,得抓紧时间积累知识啊,废话少说,步入正题. 对于java,自己的掌握仅限于基础知识,对于框架什么的几乎没有使用过,只大概知道ssh是企业常用的开发框架,struts+hibernate+spring是一套很不错的框