闲言碎语-逐步了解Spring

WHY

在诞生之初,创建Spring的主要目的是用来替代更加重量级的企业级Java技术,尤其是EJB。相对于EJB来说,Spring提供了更加轻量级和简单的编程模型。

WHAT

Spring是一个开源框架,最早由RodJohnson创建,Spring是为了解决企业级应用开发的复杂性而创建的,使用Spring可以让简单的JavaBean实现之前只有EJB才能完成的事情。Spring不仅仅限于服务端的开发,任何Java应用都能在简单性、可测试性和松耦合等方面从Spring中获益。

如今Spring在移动开发、社交API集成、NoSQL数据库、云计算及大数据方面都在涉足。随着时间的推移EJB也采用了依赖注入(DependencyInjection,DI)和面向切面编程(Aspect-OrientedProgramming,AOP)的理念。总之,Spring最根本的使命就是简化Java开发

HOW

为了降低Java开发的复杂性,Spring采取了4钟关键策略

基于POJO的轻量级和最小侵入性编程通过依赖注入和面向接口实现松耦合

基于切面和惯例进行声明式编程通过切面和模板减少样式代码

POJO潜能

很多框架通过强迫应用继承它们的类或实现它们的接口从而导致应用与框架绑定在一起,这就是侵入性编程,导致无法复用代码块。Spring竭力避免因自身的API而弄乱你的应用代码,在基于Spring构建的应用程序中,他的类通常没有任何痕迹表明你使用了Spring

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

以上的实例代码表示一个很简单普通的Java类(POJO),你看不出来他是一个Spring组件,Spring的非侵入式编程体现在这个类在Spring应用和非Spring应用中都可以发挥作用。仅仅这么一段代码并没有能实际体现Spring功能,还需要后面的知识。
依赖注入(将自身依赖的类注入的自身)

依赖注入这个此在Spring中并不是这么高大上,尽管现在已经演变成一项复杂的编程技巧或者设计模式理念。在Spring中可以这么理解,注入依赖。一个具有实际意义的应用都需要多个类进行相互协作来完成特定的业务逻辑。传统的做法是每个对象负责管理与自己有关的对象(这个有关的对象就是Spring中表述的所依赖的对象)的引用,不过这将会导致高度耦合和代码难以测试

考虑如下代码

/**这个拗口的类名是作者为了拟合一个模拟的场景特地取名
 * 这个类表示营救少女的骑士
 * Created by Wung on 2016/8/25.
 */
public class DamselRescuingKnight implements Knight{
	private RescueDamselQuest quest;
	/**
   * 在它的构造函数中自行创建了RescueDamselQuest
   * 这使得DamselRescuingKnight和在它的构造函数中自行创建了RescueDamselQuest耦合在了一起
   */
	public DamselRescuingKnight(){
		this.quest = new RescueDamselQuest();
	}
	public void embarkOnQuest(){
		quest.embark();
	}
}

耦合具有两面性,一方面,紧密耦合的代码难以测试难以复用难以理解,并且会出现"打地鼠"式的BUG。另一方面,一定程度的耦合又是必要的,不同的类必须以适当的方式进行交互。

问题出现了,那么Spring是如何解决的呢

在Spring中,通过依赖注入(DI),对象之间的依赖关系由系统中负责协调各对象的第三方组件在创建对象的时候进行设定。也就是说对象只需要管理自己内部的属性而无需自行创建或者管理它们的依赖关系,依赖关系会被自动注入到需要它们的对象当中去。

/**
 * 这个骑士可以执行各种冒险任务而不在仅仅是之前那个营救少女的任务
 * Created by Wung on 2016/8/25.
 */
public class BraveKnight implements Knight{
	private Quest quest;
	/**
   * BraveKnight没有自行创建冒险类型,而是在构造的时候把冒险任务作为参数传入
   * 这是依赖注入的方式之一:构造器注入
   */
	public BraveKnight(Quest quest){
		this.quest = quest;
	}
	public void embarkOnQuest(){
		quest.embark();
	}
}

BraveKnight没有与任何特定的Quest实现发生耦合,只要某个任务实现了Quest接口,那么具体是哪种类型的冒险就无所谓了,这就打到了DI的目的——松耦合

如果一个对象只通过接口来表明依赖关系,那么这种依赖关系就能够在对象本身毫不知情的情况下用不同的具体实现来进行替代。

那么现在来进行实际意义的注入

对于上面那块代码我们来将一个具有具体实现的冒险任务注入到勇敢骑士中去

/**屠龙的冒险任务(这个作者好中二阿)
 * Created by Wung on 2016/8/25.
 */
public class SlayDragonQuest implements Quest{
	private PrintStream stream;
	public SlayDragonQuest(PrintStream stream){
		this.stream = stream;
	}
	public void embark() {
		stream.print("slay the dragon");
	}
}

那么现在问题来了,在SlayDragonQuest中如何注入它所依赖的PrintStream对象,在BraveKnight中如何注入它所依赖的Quest对象。前面提到的Spring会将这些依赖进行集中管理,创建应用组件之间协作的行为通常称为装配(wiring)也就是注入。Spring提供了多种装配的方式在后面会更加详细的介绍,此处简单介绍基于XML的装配和基于Java注解的装配

<!--
    这就是一个装配的过程,将SlayDragonQuest声明为一个bean,取名为"quest"
    因为是构造器注入所以使用constructor-arg属性 value表示注入的值
    那么这个过程解决了之前的问题,将PrintStream对象注入到SlayDragonQuest对象中
  -->
  <bean class="impl.SlayDragonQuest" id="quest">
    <constructor-arg value="#{T(System).out}">
  </constructor-arg></bean>

  <!--
    这个过程同上,BraveKnight声明为一个bean取名为"knight"(不一定使用到这个名字)
    然后BraveKnight的构造器参数要求为Quest类型此时传入了另外一个bean的引用
    就是上面这个名字"quest"的bean的引用,此时也完成了对BraveKnight的构造器注入
   -->
  <bean class="impl.BraveKnight" id="knight">
    <constructor-arg ref="quest">
  </constructor-arg></bean>

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

/**基于Java的配置文件实现对象的装配
 * Created by Wung on 2016/8/26.
 */
@Configuration
public class KnightConfig {

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

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

效果是相同的,具体的解释在下一章中详细描述。现在再来回顾一下,一直说Spring会自动管理对象之间的依赖关系,那么这种管理者是什么。答案是Application Context(应用上下文),它是Spring的一种容器,可以装载bean的定义并将它们组装起来。Spring应用上下文全权负责对象的创建和组装。实际上这个Context有好多中实现他们之间的区别仅仅是加载配置的方式不同,下面来看一种加载配置的方式

public class KnightMain {

  public static void main(String[] args){
    AnnotationConfigApplicationContext context = new
        AnnotationConfigApplicationContext(KnightConfig.class);
    //从配置文件中就可以获取到bean的定义了
    Knight knight = context.getBean(Knight.class);
    knight.embarkOnQuest();
    context.close();
  }

}

应用切面

DI能够让相互协作的软件组件保持松耦合,而面向切面编程允许你把遍布应用各处的功能分离出来形成可重用的组件,更详细的说,它是促使软件系统实现关注点奋力的一项技术。什么是关注点呢,诸如日志、事务管理、安全管理这样的系统服务经常需要融入到其他自身具有业务逻辑的组件中去,那么这些系统服务通常就被称为横切关注点,因为它们会在多个地方被重用,跨越系统的多个组件。简单来说,就是你把需要重用的经常会服务与各种其他组件的组件抽离出来,但是抽离出来如何使用呢,其实就是在用的时候将方法插入到需要使用的地方。但是根据这个"切面"的术语,应该表述为将重用的组件抽离出来作为一个切面,在需要使用的时候将切面横切进组件。所以这样就做到了核心应用不需要知道这些切面的存在,切面也不会将业务逻辑融合在核心应用中。

/**一个歌手类用来歌颂骑士也就是服务于骑士类
 * Created by Wung on 2016/8/26.
 */
public class Minstrel {

  private PrintStream stream;

  public Minstrel(PrintStream stream){
    this.stream = stream;
  }
  //在冒险之前执行
  public void singBeforeQuest(){
    stream.print("Begin");
  }
  //在冒险之后执行
  public void singAfterQuest(){
    stream.print("End");
  }

}
public class BraveKnight implements Knight{
	private Quest quest;
	private Minstrel minstrel;
	/**
   * BraveKnight没有自行创建冒险类型,而是在构造的时候把冒险任务作为参数传入
   * 这是依赖注入的方式之一:构造器注入
   */
	//  public BraveKnight(Quest quest){
	//    this.quest = quest;
	//  }
	public BraveKnight(Quest quest, Minstrel minstrel){
		this.quest = quest;
		this.minstrel = minstrel;
	}
	public void embarkOnQuest(){
		minstrel.singBeforeQuest();
		quest.embark();
		minstrel.singAfterQuest();
	}
}

这个时候这个勇敢的骑士开始执行,但是他发现在他的职责中不仅仅是冒险了,现在竟然还要管理这个歌手要为他歌颂,然而这本身并不应该属于这个类应该管理的。所以应用切面的思想,我们需要将这个歌手的歌颂行为抽离出来成为一个切面,在骑士冒险之前这个切面将会横切进去执行singBeforeQuest方法在冒险之后执行singAfterQuest方法。那么这样是不是就实现了骑士中不需要歌颂的代码,歌手也不存在与骑士对象中,他将不仅仅赞颂骑士,也可以赞颂任何人只要别人使用这个切面切入即可

  <!-- 意思就是将上述id为minstrel的bean配置为切面实际上就是把歌手配置为切面 -->
  aspect ref="minstrel">
    <!--
      定义切入点,也就是在哪里会使用切面
      expression="execution(* *.embarkOnQuest(..))
      是一种AspectJ的切点表达式语言后面会深入
      此处的意思是会在embarkOnQuest()方法处切入
      下面那个分别为前置通知和后置通知在切入点之前和之后执行
    -->
  </aop:after></aop:before></aop:pointcut>
</aop:</aop:config>

现在的情况是Minstrel仍然是独立的一个POJO,Spring的上下文已经将他变成了一个切面了。最重要的是此时骑士完全不知道这个切面的存在,这只是一个小小的栗子,实际上可以做很多重要的事情。

使用模板消除样式代码

有这样一种情况,当我们使用JDBC访问数据库查询数据的时候,完整的流程需要建立连接、创建语句对象、处理结果集、查询、关闭各种连接,此外还需要各种异常的捕获,然后对于各种场景的查询都需要如此费心费力的重复。JDBC还不仅仅是唯一这样会有大量的样式代码的情况,Spring旨在通过模板封装来消除样式代码,比如Spring的jdbcTemplate。

容纳你的Bean

在基于Spring的应用中,你的应用对象生存于Spring容器,容器负责创建对象装配对象并且管理他们的生命周期。那什么是Spring的容器呢,容器并不是只有一种,Spring自带了多个容器实现。分为两类:bean工厂,是最简单的容器提供基本的DI支持。应用上下文比较高级提供应用框架级别的服务。大多数情况下应用上下文更加受欢迎。

应用上下文也分为多种,显著的区别的是加载配置的方式不同

Spring的各种功能

总结

Spring是一个可以简化开发的框架技术,核心内容是DI和AOP。

以上就是本文关于闲言碎语-逐步了解Spring的全部内容,希望对大家有所帮助。感兴趣的朋友可以继续参阅本站:

SpringMVC入门实例

Spring中利用配置文件和@value注入属性值代码详解

Spring集成Redis详解代码示例

如有不足之处,欢迎留言指出。

(0)

相关推荐

  • 零基础入门学习——Spring Boot注解(一)

    声明bean的注解: @Component组件,没有明确角色的bean @Service,在业务逻辑层(service)中使用 @Repository,在数据访问层(dao)中使用 @Controller,在展现层中使用 @Configuration声明配置类 实体类无需添加注解,因为并不需要"注入"实体类 指定Bean的作用域的注解: @Scope("prototype") 默认值为singleton 可选值prototype.request.session.gl

  • Spring Boot入门(web+freemarker)

    1.配置maven文件pom.xml <?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apach

  • Java Spring开发环境搭建及简单入门示例教程

    本文实例讲述了Java Spring开发环境搭建及简单入门示例.分享给大家供大家参考,具体如下: 前言 虽然之前用过Spring,但是今天试着去搭建依然遇到了困难,而且上网找教程,很多写的是在web里使用Spring MVC的示例,官方文档里的getting start一开始就讲原理去了(可能打开的方法不对).没办法,好不容易实验成功了,记下来免得自己以后麻烦. 添加依赖包 进入spring官网,切换到projects下点击 spring framework.官网上写的是以maven依赖的形式写

  • Spring Batch入门教程篇

    SpringBatch介绍: SpringBatch 是一个大数据量的并行处理框架.通常用于数据的离线迁移,和数据处理,⽀持事务.并发.流程.监控.纵向和横向扩展,提供统⼀的接⼝管理和任务管理;SpringBatch是SpringSource和埃森哲为了统一业界并行处理标准为广大开发者提供方便开发的一套框架. 官方地址:github.com/spring-projects/spring-batch SpringBatch 本身提供了重试,异常处理,跳过,重启.任务处理统计,资源管理等特性,这些特

  • Spring boot学习教程之快速入门篇

    前言 首先来说一下为什么使用 Spring Boot,之前我用的后端 WEB 开发框架一直都是 PlayFramework的 1.2.7 版本(目前已经停止更新), 不得不说这个框架非常好用,但是由于 Play2.x 版本和 Play1.x 版本差别巨大,并且不兼容,所以现在面临着选择新的框架的问题,问了下身边的朋友,发现他们都在用 Spring ,然而我发现 Spring 的话,经常要配置各种东西,习惯了 Play 的简单明了的配置方式,确实有些不习惯 Spring ,这个时候发现了 Spri

  • 基于Spring MVC 简介及入门小例子(推荐)

    一.什么是 Spring MVC Spring MVC 属于 SpringFrameWork 的后续产品,已经融合在 Spring Web Flow 里面,是一个强大灵活的 Web 框架.Spring MVC 提供了一个 DispatcherServlet 作为前端控制器来分配请求.通过策略接口,Spring 框架是高度可配置的.Spring MVC 还包含多种视图技术,如 Java Server Pages(JSP).Velocity.Tiles.iText 和 POI 等.Spring MV

  • 闲言碎语-逐步了解Spring

    WHY 在诞生之初,创建Spring的主要目的是用来替代更加重量级的企业级Java技术,尤其是EJB.相对于EJB来说,Spring提供了更加轻量级和简单的编程模型. WHAT Spring是一个开源框架,最早由RodJohnson创建,Spring是为了解决企业级应用开发的复杂性而创建的,使用Spring可以让简单的JavaBean实现之前只有EJB才能完成的事情.Spring不仅仅限于服务端的开发,任何Java应用都能在简单性.可测试性和松耦合等方面从Spring中获益. 如今Spring在

  • spring boot的maven配置依赖详解

    本文介绍了spring boot的maven配置依赖详解,分享给大家,具体如下: 我们通过引用spring-boot-starter-parent,添加spring-boot-starter-web 可以实现web项目的功能,当然不使用spring-boot-start-web,通过自己添加的依赖包也可以实现,但是需要一个个添加,费时费力,而且可能产生版本依赖冲突.我们来看下springboot的依赖配置: 利用pom的继承,一处声明,处处使用.在最顶级的spring-boot-dependen

  • 详解Spring Boot配置排序依赖技巧

    本文主要介绍了Spring Boot配置排序依赖技巧,分享给大家,具体如下: Spring Boot - 被错误使用的注解 我自己曾经在 Spring Boot 中集成通用 Mapper 时,写过下面的代码: @Configuration @AutoConfigureAfter(MyBatisConfig.class) public class MyBatisMapperScannerConfig { //其他 } 这种用法我参考的 mybatis-spring-boot-starter. 由于

  • spring boot创建项目包依赖问题的解决

    今天捣腾了spring boot,按照官网案例,缺发现本地无论包依赖出现问题,并且无法启动,一整天在踩maven的坑,记录下这个血的教训. 1.spring-core依赖包问题 运行application,发现缺少依赖的spring-core包: 但是spring boot的包都是通过parent的starter引入的,通过mvn denpendency:tree查看项目的jar依赖信息:  发现spring-core依赖包是存在的,但是为什么运行的时候回报错找不到类. 倒腾了一个下午试过各种方

  • Spring获取ApplicationContext对象工具类的实现方法

     Spring获取ApplicationContext对象工具类的实现方法 (1)实现的工具类: package com.util; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; final public class ApplicationContextUtil { private s

  • 详解Spring的核心机制依赖注入

    详解Spring的核心机制依赖注入 对于一般的Java项目,他们都或多或少有一种依赖型的关系,也就是由一些互相协作的对象构成的.Spring把这种互相协作的关系称为依赖关系.如A组件调用B组件的方法,可称A组件依赖于B组件,依赖注入让Spring的Bean以配置文件组织在一起,而不是以硬编码的方式耦合在一起 一.理解依赖注入 依赖注入(Dependency Injection) = 控制反转(Inversion ofControl,IoC):当某个Java实例(调用者)需另一个Java实例(被调

  • Spring依赖注入的三种方式实例详解

    Spring依赖注入(DI)的三种方式,分别为: 1. 接口注入 2. Setter方法注入 3. 构造方法注入 下面介绍一下这三种依赖注入在Spring中是怎么样实现的. 首先我们需要以下几个类: 接口 Logic.java 接口实现类 LogicImpl.java 一个处理类 LoginAction.java 还有一个测试类 TestMain.java Logic.java如下: package com.spring.test.di; public interface Logic { pub

  • Angular.js前台传list数组由后台spring MVC接收数组示例代码

    前言 本文主要给大家介绍了关于Angular.js前台传list数组由后台spring MVC接收数组的相关内容,分享出来供大家参考学习,下面话不多说,来一起看看详细的介绍吧. 在开发中有时候需要在前台自定义对象,然后把对象封装在list中,在传送到后台,这样的思想也比较合理,直接来看示例代码: 1. 前台代码 $scope.saveScore = function () { $scope.userScoreList = new Array();//自定义数组 angular.forEach (

  • AngularJS整合Springmvc、Spring、Mybatis搭建开发环境

    最近想学习AngularJS的使用,网上搜了一圈后,折腾了半天解决bug后,成功使用AngularJS整合Springmvc.Spring.Mybatis搭建了一个开发环境.(这里Spring使用的版本是4.0.6,Mybatis版本是3.2.5,AngularJS的版本是1.0.3) 第一步:创建一Maven项目,在pom.xml下添加需要的包 <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="

  • docker连接spring boot和mysql容器方法介绍

    在之前使用docker部署运行了Spring Boot的小例子,但是没有使用数据库.在这一篇中,介绍docker如何启动mysql容器,以及如何将Spring Boot容器与mysql容器连接起来运行. docker基本命令 首先熟悉一下在操作过程中常用的docker基本命令: docker images:列出所有docker镜像 docker ps:列出所有运行中的容器,-a参数可以列出所有容器,包括停止的 docker stop container_id:停止容器 docker start

随机推荐