Spring快速入门基础

1、Spring简介

       Spring框架是由于软件开发的复杂性而创建的。Spring使用的是基本的JavaBean(POJO,Plain Ordinary Java Object,简单的Java对象)来完成以前只可能由EJB完成的事情。Spring是一个轻量级控制反转(IoC)和面向切面(AOP)的容器框架。Spring可以做很多事情,它为企业级开发提供给了丰富的功能,但是这些功能的底层都依赖于它的两个核心特性,也就是依赖注入(dependency injection, DI) 和面向切面编程(aspect-oriented programming, AOP)。

注:

       JavaBean:一种JAVA语言写成的可重用组件,它可以被Applet、Servlet、SP等Java应用程序调用.也可以可视化地被Java开发工具使用。它包含属性(Properties)、方法(Methods)、事件(Events)等特性。

       EJ8(Enterprise Java Beans):企业Java Beans。基于分布式事务处理的企业级应用程序的组件。EJB是用于开发和部署多层结构的、分布式的、面向对象的Java应用系统的跨平台的构件体系结构。

 

2、特点

       轻量:从大小与开销两方面而言Spring都是轻量的。完整的Spring框架可以在一个大小只有1MB多的JAR文件里发布。并且Spring所需的处理开销也是微不足道的。此外,Spring是非侵入式的:典型地,Spring应用中的对象不依赖于Spring的特定类。

        控制反转:Spring通过一种称作控制反转(IoC)的技术促进了松耦合。当应用了IoC,一个对象依赖的其它对象会通过被动的方式传递进来,而不是这个对象自己创建或者查找依赖对象。你可以认为IoC与JNDI相反——不是对象从容器中查找依赖,而是容器在对象初始化时不等对象请求就主动将依赖传递给它。

       面向切面:Spring提供了面向切面编程的丰富支持,允许通过分离应用的业务逻辑与系统级服务(例如审计(auditing)和事务(transaction)管理)进行内聚性的开发。应用对象只实现它们应该做的——完成业务逻辑——仅此而已。它们并不负责(甚至是意识)其它的系统级关注点,例如日志或事务支持。

       容器:Spring包含并管理应用对象的配置和生命周期,在这个意义上它是一种容器,你可以配置你的每个bean如何被创建——基于一个可配置原型(prototype),你的bean可以创建一个单独的实例或者每次需要时都生成一个新的实例——以及它们是如何相互关联的。然而,Spring不应该被混同于传统的重量级的EJB容器,它们经常是庞大与笨重的,难以使用。

       框架:Spring可以将简单的组件配置、组合成为复杂的应用。在Spring中,应用对象被声明式地组合,典型地是在一个XML文件里。Spring也提供了很多基础功能(事务管理、持久化框架集成等等),将应用逻辑的开发留给了你。

 

3、为了降低Java开发的复杂性,Spring采取了以下4种关键策略:

  • 基于POJO的轻量级和最小侵入性编程;
  • 通过依赖注入和面向接口实现松耦合;
  • 基于切面和惯例进行声明式编程;
  • 通过切面和模板减少样板式代码。

注:

      最小侵入性编程:侵入性编程是指,框架通过强迫应用继承它们的类或实现它们的接口从而导致应用与框架绑死。Spring竭力避免因自身的AP而弄乱你的应用代码。Spring不会强迫你实现Spring规范的接口或继承Spring规范的类,相反,在基于Spring构建的应用中,它的类通常没有任何痕迹表明你使用了Spring,即最小侵入性编程。最坏的场景是,一个类或许会使用Spring注解,但它依旧是POJO。

       依赖注入(DI):通过DI,对象的依赖关系将由系统中负责协调各对象的第三方组件在创建对象的时候进行设定。对象无需自行创建或管理它们的依赖关系,依赖关系将被自动注入到需要它们的对象当中去。

       装配(wiring):创建应用组件之间协作的行为通常称为装配(wiring) 。Spring有多种装配bean的方式,采用XML是很常见的一种装配方式。Spring还支持使用Java来描述配置。

 

4、依赖注入和控制反转

(1)依赖注入和控制反转

       Rod Johnson是第一个高度重视以配置文件来管理Java实例的协作关系的人,他给这种方式起了一个名字:控制反转(Inverse of Control,IoC)。后来Martine Fowler为这种方式起了另一个名称:依赖注入(Dependency Injection),因此不管是依赖注入,还是控制反转,其含义完全相同。当某个Java对象(调用者)需要调用另一个Java对象(被依赖对象)的方法时,在传统模式下通常有两种做法:

  • 原始做法: 调用者主动创建被依赖对象,然后再调用被依赖对象的方法。
  • 简单工厂模式: 调用者先找到被依赖对象的工厂,然后主动通过工厂去获取被依赖对象,最后再调用被依赖对象的方法。

       注意上面的主动二字,这必然会导致调用者与被依赖对象实现类的硬编码耦合,非常不利于项目升级的维护。使用Spring框架之后,调用者无需主动获取被依赖对象,调用者只要被动接受Spring容器为调用者的成员变量赋值即可,由此可见,使用Spring后,调用者获取被依赖对象的方式由原来的主动获取,变成了被动接受——所以Rod Johnson称之为控制反转

       另外从Spring容器的角度来看,Spring容器负责将被依赖对象赋值给调用者的成员变量——相当于为调用者注入它依赖的实例,因此Martine Fowler称之为依赖注入

(2)两种注入方式

设值注入

       设值注入是指IoC容器通过成员变量的setter方法来注入被依赖对象。这种注入方式简单、直观,因而在Spring的依赖注入里大量使用。

构造注入

       利用构造器来设置依赖关系的方式,被称为构造注入。通俗来说,就是驱动Spring在底层以反射方式执行带指定参数的构造器,当执行带参数的构造器时,就可利用构造器参数对成员变量执行初始化——这就是构造注入的本质。

两种注入方式的对比

设值注入有如下优点:

  • 与传统的JavaBean的写法更相似,程序开发人员更容易理解、接受。通过setter方法设定依赖关系显得更加直观、自然。
  • 对于复杂的依赖关系,如果采用构造注入,会导致构造器过于臃肿,难以阅读。Spring在创建Bean实例时,需要同时实例化其依赖的全部实例,因而导致性能下降。而使用设值注入,则能避免这些问题。
  • 尤其在某些成员变量可选的情况下,多参数的构造器更加笨重。

构造注入优势如下:

  • 构造注入可以在构造器中决定依赖关系的注入顺序,优先依赖的优先注入。
  • 对于依赖关系无需变化的Bean,构造注入更有用处。因为没有setter方法,所有的依赖关系全部在构造器内设定,无须担心后续的代码对依赖关系产生破坏。
  • 依赖关系只能在构造器中设定,则只有组件的创建者才能改变组件的依赖关系,对组件的调用者而言,组件内部的依赖关系完全透明,更符合高内聚的原则。

注意:

       建议采用设值注入为主,构造注入为辅的注入策略。对于依赖关系无须变化的注入,尽量采用构造注入;而其他依赖关系的注入,则考虑采用设值注入。

 

5、Spring的核心机制——管理Bean

       程序主要是通过Spring容器来访问容器中的Bean,ApplicationContext是Spring容器最常用的接口,该接口有如下两个实现类:

  • ClassPathXmlApplicationContext: 从类加载路径下搜索配置文件,并根据配置文件来创建Spring容器。
  • FileSystemXmlApplicationContext: 从文件系统的相对路径或绝对路径下去搜索配置文件,并根据配置文件来创建Spring容器。

 

6、Spring框架的组成结构图

(1)Spring核心容器

        容器是Spring框架最核心的部分,它管理着Spring应用中bean的创建、配置和管理。在该模块中,包括了Spring beanI厂,它为Spring提供了DI的功能。基于bean工厂,我们还会发现有多种Spring应用上下文的实现,每一种都提供了配置Spring的不同方式。

       除了bean工厂和应用上下文,该模块也提供了许多企业服务,例如E-mail、JNDI访问、EJB集成和调度。所有的Spring模块都构建于核心容器之上。当你配置应用时,其实你隐式地使用了这些类。

(2)Spring的AOP模块

       在AOP模块中,Spring对面向切面编程提供了丰富的支持。这个模块是Spring应用系统中开发切面的基础。与DI样,AOP可以帮助应用对象解耦。借助于AOP,可以将遍布系统的关注点(例如事务和安全)从它们所应用的对象中解耦出来。

(3)数据访问与集成

       使用JDBC编写代码通常会导致大量的样板式代码,例如获得数据库连接、创建语句、处理结果集到最后关闭数据库连接。Spring的JDBC和DAO (Data Access Object)模块抽象了这些样板式代码,使我们的数据库代码变得简单明了,还可以避免因为关闭数据库资源失败而引发的问题。该模块在多种数据库服务的错误信息之上构建了一个语义丰富的异常层,以后我们再也不需要解释那些隐晦专有的SQL错误信息了!

       对于那些更喜欢ORM (Object-Relational Mapping)工具而不愿意直接使用JDBC的开发者,Spring提 供了ORM模块。Spring的ORM模块建立在对DAO的支持之上,并为多个ORM框架提供了一种 构建DAO的简便方式。Spring没有 尝试去创建自己的ORM解决方案,而是对许多流行的ORM框架进行了集成,包括Hibernate、 Java Persistermce API、Java Data Object和iBATIS SQL Maps。Spring的事 务管理支持所有的ORM框架以及JDBC.

      本模块同样包含了在JMS (Java Message Service)之上构建的Spring抽象层,它会使用消息以异步的方式与其他应用集成。从Spring 3.0开始,本模块还包含对象到XML映射的特性,它最初是Spring Web Service项目的一部分。

       除此之外,本模块会使用SpringAOP模块为Spring应用中的对象提供事务管理服务。

(4)Web与远程调用

       MVC (Model-View-Controller) 模式是一种普遍被接受的构建Web应用的方法,它可以帮助用户将界面逻辑与应用逻辑分离。Java从来不缺少MVC框架,Apache 的Struts、JSF 、WebWork和Tapestry都是 可选的最流行的MVC框架。

       虽然Spring能够与多种流行的MVC框架进行集成,但它的Web和远程调用模块自带了一个强大的MVC框架,有助于在Web层提升应用的松耦合水平。

        除了面向用户的Web应用,该模块还提供了多种构建与其他应用交互的远程调用方案。Spring远程调用功能集成了RMI (Remote MethodInvocation)、Hessian、 Burlap、 JAX-WS,同时Spring还自带了一个远程调用框架: HTTP invoker。Spring还提供了暴露和使用REST API的良好支持。

(5)Instrumentation

       Spring的Instrumentation模块提供了为JVM添加代理(agent) 的功能。具体来讲,它为Tomcat提供了一个织入代理,能够为Tomcat传递类文件,就像这些文件是被类加载器加载的一-样。

(6)测试

       鉴于开发者自测的重要性,Spring提 供了测试模块以致力于Spring应用的测试。通过该模块,你会发现Spring为使用JNDI、SerMet和Portlet编 写单元测试提供了一系列的mock对象实现。对于集成测试,该模块为加载Spring应用上下文中的bean集合以及与Spring上下文中的bean进行交互提供了支持。

 

7、Spring Portfolio

       当谈论Spring时,其实它远远超出我们的想象。事实上,Spring远不 是Spring框架所下载的那些。如果仅仅停留在核心的Spring框架层面,我们将错过Spring Portfolio所提供的巨额财富。整个Spring Portolio包括多个构建于核心Spring框架之上的框架和类库。概括地讲,整个SpringPortfolio几乎为每一个领域的Java开 发都提供了Spring编程模型。

  • Spring Web Flow建立于Spring MVC框架之上,它为基于流程的会话式Web应用(可以想一下购物车或者向导功能)提供了支持。
  • Spring Web Service:虽然核心的Spring框架提供了将Spring bean以声明的方式发布为Web Service的功能,但是这些服务是基于一个具有争议性的架构(拙劣的契约后置模型)之上而构建的。这些服务的契约由bean的接口来决定。Spring Web Service提供了契约优先的Web Service模型,服务的实现都是为了满足服务的契约而编写的。
  • Spring Security安全对于许多应用都是一个非常关键的切面。利用Spring AOP,Spring Security为Spring应用提供了声明式的安全机制。
  • Spring Integration许多企业级应用都需要与其他应用进行交互。SpringIntegration提供了多种通用应用集成模式的Spring声明式风格实现。
  • Spring Batch当我们需要对数据进行大量操作时,没有任何技术可以比批处理更胜任这种场景。如果需要开发一个批处理应用,你可以通过Spring Batch,使用Spring强大的面向POJO的编程模型。
  • Spring DataSpringData使得在Spring中使用任何数据库都变得非常容易。尽管关系型数据库统治企业级应用多年,但是现代化的应用正在认识到并不是所有的数据都适合放在一张表中 的行和列中。-一种新的数据库种类,通常被称之为NoSQL数据库2),提供了使用数据的新方法,这些方法会比传统的关系型数据库更为合适。不管你使用文档数据库,如MongoDB, 图数据库,如Neo4j, 还是传统的关系型数据库,Spring Data都为持久化提供了- -种简单的编程模型。这包括为多种数据库类型提供了一种自动化的Repository机制,它负贵为你创建Repository的实现。
  • Spring Social社交网络是互联网领域中新兴的- -种 潮流,越来越多的应用正在融入社交网络网站,例如Facebook或者Twitter.如果对此感兴趣,你可以了解一下Spring Social,这是Spring的-一个社交网络扩展模块。不过,Spring Social并不仅仅是tweet和好友。尽管名字是这样,但Spring Socia更多的是关注连接(comect),而不是社交(social) 。它能够帮助你通过REST API连接Spring应用,其中有些Spring应用可能原本并没有任何社交方面的功能目标。
  • Spring Mobile移动应用是另一个引人瞩目的软件开发领域。智能手机和平板设备已成为许多用户首选的客户端。Spring Mobile是Spring MVC新的扩展模块,用于支持移动Web应用开发。
  • Spring for Android与Spring Mobile相关的是Spring Android项目。这个新项目,旨在通过Spring框架为开发基于Android设备的本地应用提供某些简单的支持。最初,这个项目提供了Spring RestTemplate的一个可以用于Android应用之中的版本。它还能与Spring Social协作,使得原生应用可以通过REST API进行社交网络的连接。
  • Spring BootSpring极大地简化了众多的编程任务,减少甚至消除了很多样板式代码,如果没有Spring的话, 在日常工作中你不得不编写这样的样板代码。Spring Boot是一个崭新的令人兴奋的项目,它以Spring的视角, 致力于简化Spring本身。Spring Boot大量依赖于自动配置技术,它能够消除大部分(在很多场景中,甚至是全部) Spring配置。 它还提供了多个Starter项目,不管你使用Maven还是Gradle,这都能减少Spring工程构建文件的大小。

 

8、实例

例1:普通依赖关系导致紧耦合

public interface Knight {
    void embarkOnQuest();
}
public class DamselRescuingKnight implements Knight {
    private RescueDamselQuest quest;

    public DamselRescuingKnight() {
        this.quest = new RescueDamselQuest();   // 与RescueDamselQuest紧耦合
    }

    public void embarkOnQuest() {
        quest.embark();
    }
}

       可以看到,DamselRescuingKnight 在它的构造函数中自行创建了Rescue DamselQuest. 这使得DamselRescuingKnight 紧密地和RescueDamselQuest耦合到了一起。

       通过DI,对象的依赖关系将由系统中负责协调各对象的第三方组件在创建对象的时候进行设定。对象无需自行创建或管理它们的依赖关系。

例2:依赖注入

public class BraveKnight implements Knight {
    private Quest quest;

    public BraveKnight(Quest quest) {   // Quest被注入(构造器注入)
        this.quest = quest;
    }

    public void embarkOnQuest() {
        quest.embark();
    }
}

       这里的要点是BraveKnight没有与任何特定的Quest实现发生耦合。对它来说,被要求挑战的探险任务只要实现了Quest接口,那么具体是哪种类型的探险就无关紧要了。这就是DI所带来的最大收益——松耦合。如果一个对象只通过接口(而不是具体实现或初始化过程)来表明依赖关系,那么这种依赖就能够在对象本身毫不知情的情况下,用不同的具体实现进行替换。

例3:SlayDragonQuest堤要注入到BraveKnight中的Quest实现

public class SlayDragonQuest implements Quest {
    private PrintStream stream;

    public SlayDragonQuest(PrintStream stream) {
        this.stream = stream;
    }

    public SlayDragonQuest(String s) {

    }

    public void embark() {
        stream.println("Embarking on quest to slay the dragon!");
    }
}

       我们可以看到,SlayDragonQuest实现了 Quest接口,这样它就适合注入到BraveKnight中去了。这里最大的问题在于,我们该如何将SlayDragonQuest交给BraveKnight呢?又如何将PrintStream交给SlayDragonQuest呢?

例4:使用Spring将SlayDragonQuest注入到BraveKnight中

<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">

<!--注入Quest-->
<bean id="knight" class="main.java.sia.knights.BraveKnight">
    <constructor-arg ref="quest" />
</bean>

<bean id="quest" class="main.java.sia.knights.SlayDragonQuest">
    <constructor-arg value="#{T(System).out}" />
</bean>

</beans>

knights.xml,该配置文件将BraveKnight、SlayDragonQues t和PrintStream装配到了一起。

例5:Spring提供了基于Java的配置,可作为XML的替代方案

package main.java.sia.knights.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import main.java.sia.knights.BraveKnight;
import main.java.sia.knights.Knight;
import main.java.sia.knights.Quest;
import main.java.sia.knights.SlayDragonQuest;

@Configuration
public class KnightConfig {
    @Bean
    public Knight knight() {
        return new BraveKnight(quest());
    }

    @Bean
    public Quest quest() {
        return new SlayDragonQuest(System.out);
    }
}

       不管你使用的是基于XML的配置还是基于Java的配置,DI所带来的收益都是相同的。尽管BraveKnight依赖于Quest,但是它并不知道传递给它的是什么类型的Quest,也不知道这个Quest来自哪里。与之类似,SlayDragonQuest依赖于PrintStream, 但是在编码时它并不需要知道这个PrintStream是什么样子的。只有Spring通过它的配置,能够了解这些组成部分是如何装配起来的。这样的话,就可以在不改变所依赖的类的情况下,修改依赖关系。

如何工作:

       Spring通过应用上下文(Application Contex)装载bean的定义并把它们组装起来。Spring应用 上下文全权负责对象的创建和组装。Spring自带了多种应用上下文的实现,它们之间主要的区别仅仅在于如何加载配置。

       因为knights. xml中的bean是使用XML文件进行配置的,所以选择ClassPathXmlApp1 icationContext作为应用上下文相对是比较合适的。该类加载位于应用程序类路径下的一个或多个XML配置文件。

例:

package main.java.sia.knights;

import org.springframework.context.support.
        ClassPathXmlApplicationContext;

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

输出:

       这里的main()方法基于knights.xml文件创建了Spring应用上下文。随后它调用该应用上下文获取一个ID 为knight的bean。

原创文章 99 获赞 68 访问量 3万+

猜你喜欢

转载自blog.csdn.net/King_weng/article/details/105478844