Spring可以做很多事情,它为企业级开发提供了丰富的功能,但是这些功能的底层都依赖于它的两个核心特性,也就是依赖注入(dependency injection,DI)面向切面编程(aspect-oriented programming,AOP)。

一、依赖注入

1.构造注入

考虑下面程序展现的Knight类:

package sia.knights;public class DamselRescuingKnight implements Knight {private RescueDamselQuest quest;public DamselRescuingKnight() {//将DamselRescuingKnight和RescueDamselQuest紧耦合this.quest = new RescueDamselQuest();}public void embarkOnQuest() {quest.embark();}
}

可以看到,DamselRescuingKnight在它的构造函数中自行创建了RescueDamselQuest。这使得DamselRescuingKnight紧密地和RescueDamselQuest耦合到一起了,因此极大地限制了这个骑士执行探险的能力。如果一个少女需要救援,这个骑士能够召之即来。但是如果一条恶龙需要杀掉,或者让一个圆桌滚起来,那么这个骑士就爱莫能助了。

我们再定义一个Knight类:

package sia.knights;public class BraveKnight implements Knight {private Quest quest;//通过构造函数将Quest注入进来public BraveKnight(Quest quest) {this.quest = quest;}public void embarkOnQuest() {quest.embark();}
}

我们可以看到不同于之前的DamselRescuingKnightBraveKnight 没有自行创建探险任务,而是在构造的时候把探险任务作为构造器参数传入。这是依赖注入的方式之一,即构造器注入(constructor injection)

更重要的是,传入的探险类型是Quest,也就是所有探险任务都必须实现一个接口。所以,BraveKnight 能够响应RescueDamselQuest(少女救援请求)、SlayDragonQuest(屠杀恶龙请求)、MakeRoundTableRounderQuest(圆桌滚起来请求)等任意的Quest实现。

2.依赖注入举例

BraveKnight.java:

package sia.knights;public class BraveKnight implements Knight {private Quest quest;//通过构造函数将Quest注入进来public BraveKnight(Quest quest) {this.quest = quest;}public void embarkOnQuest() {quest.embark();}
}

SlayDragonQuest.java:

package sia.knights;import java.io.PrintStream;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!");}
}

knight.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/beanshttp://www.springframework.org/schema/beans/spring-beans.xsd"><bean id="knight" class="sia.knights.BraveKnight"><!--注入Quest bean--><constructor-arg ref="quest" />
</bean><!--创建SlayDragonQuest-->
<bean id="quest" class="sia.knights.SlayDragonQuest"><constructor-arg value="#{T(System).out}" />
</bean></beans>

KnightConfig.java:

package sia.knights.config;import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;import sia.knights.BraveKnight;
import sia.knights.Knight;
import sia.knights.Quest;
import sia.knights.SlayDragonQuest;@Configuration
public class KnightConfig {@Beanpublic Knight knight() {return new BraveKnight(quest());}@Beanpublic Quest quest() {return new SlayDragonQuest(System.out);}}

KnightMain.java:

package sia.knights;import org.springframework.context.support.ClassPathXmlApplicationContext;public class KnightMain {public static void main(String[] args) throws Exception {//加载Spring上下文ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("META-INF/spring/knight.xml");//获取knight beanKnight knight = context.getBean(Knight.class);//使用knightknight.embarkOnQuest();context.close();}
}

流程梳理如下图: 

二、应用切面

1.切面概述

DI能够让相互协作的软件组件保持松散耦合,而面向切面编程(aspect-oriented programming, AOP)允许你把遍布应用各处的功能分离出来形成可重用的组件。

诸如日志、事务管理和安全这样的系统服务经常融入到自身具有核心业务逻辑的组件中去,这些系统服务通常被称为横切关注点,因为它们会跨越系统的多个组件。如下图,在整个系统内,关注点(例如日志和安全)的调用经常散布到各个模块中,而这些关注点并不是模块的核心业务。

AOP能使这些服务(例如日志、安全、事务等)模块化,并以声明的方式将它们应用到它们需要影响的组件中去。所造成的结果就是这些组件会具有更高的内聚性并且会更加关注自身的业务,完全不需要了解涉及系统服务所带来复杂性。如下图,我们可以把切面想象为覆盖在很多组件之上的一个外壳。借助AOP,可以使用各种功能层去包裹核心业务层。这是一个非常强大的理念,可以将安全、事务和日志关注点与核心业务逻辑相分离。

2.切面举例

2.1 通常做法

每一个人熟知骑士的故事,主要原因是吟游诗人(Minstrel)记载了骑士的事迹并将其进行传唱。我们定义一个吟游诗人类(Minstrel):

public class Minstrel {private PrintStream stream;public Minstrel(PrintStream stream) {this.stream = stream;}//探险之前调用public void singBeforeQuest() {stream.println("Fa la la, the knight is so brave!");}//探险之后调用public void singAfterQuest() {stream.println("Tee hee hee, the brave knight did embark on a quest!");}
}

定义一个使用了游吟诗人(Minstrel)的骑士类(BraveKnight):

public class BraveKnight implements Knight {private Quest quest;private Minstrel minstrel;//通过构造函数将Quest注入进来public BraveKnight(Quest quest, Minstrel minstrel) {this.quest = quest;this.minstrel = minstrel;}public void embarkOnQuest() {//embark()之前调用minstrel.singBeforeQuest();quest.embark();//embark()之后调用minstrel.singAfterQuest();}
}

这样可以达到吟游诗人(Minstrel)歌颂骑士(BraveKnight)的效果,但是这样做似乎有一些问题——管理吟游诗人(Minstrel)真的是骑士(BraveKnight)职责范围内的工作吗?在我们看来,吟游诗人(Minstrel)歌颂骑士(BraveKnight)是自己分内的事情,根本不需要骑士(BraveKnight)去命令他这么做。为什么骑士(BraveKnight)还需要提醒吟游诗人(Minstrel)去做他份内的事情呢?并且因为骑士(BraveKnight)知道吟游诗人(Minstrel),所以就必须把吟游诗人(Minstrel)注入到骑士类(BraveKnight)中,这不但会使骑士类(BraveKnight)代码复杂化,同时还会引发是否需要单独建立一个不需要吟游诗人(Minstrel)的骑士类。

2.2 切面做法

定义一个纯净的骑士类(BraveKnight):

public class BraveKnight implements Knight {private Quest quest;//通过构造函数将Quest注入进来public BraveKnight(Quest quest) {this.quest = quest;}public void embarkOnQuest() {quest.embark();}
}

将Minstrel抽象为一个切面,在Spring配置文件中声明它:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns:aop="http://www.springframework.org/schema/aop"xsi:schemaLocation="http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsdhttp://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"><bean id="knight" class="sia.knights.BraveKnight"><constructor-arg ref="quest" /></bean><bean id="quest" class="sia.knights.SlayDragonQuest"><constructor-arg value="#{T(System).out}" /></bean><bean id="minstrel" class="sia.knights.Minstrel"><constructor-arg value="#{T(System).out}" /></bean><aop:config><aop:aspect ref="minstrel"><!--定义切点--><aop:pointcut id="embark" expression="execution(* *.embarkOnQuest(..))"/><!--声明前置通知-->  <aop:before pointcut-ref="embark" method="singBeforeQuest"/><!--声明后置通知--><aop:after pointcut-ref="embark" method="singAfterQuest"/></aop:aspect></aop:config>
</beans>

定义切面步骤:

1)在<aop:aspect>元素中引入对应的bean

2)在<aop:pointcut>定义一个切点

3)在<aop:before>中定义前置通知(before advice),即在调用embarkOnQuest()方法调用MinstrelsingBeforeQuest()方法。

4)在<aop:after>中定义后置通知(after advice),即在调用embarkOnQuest()方法调用MinstrelsingAfterQuest()方法。

这样既可以达到吟游诗人(Minstrel)歌颂骑士(BraveKnight)的效果,又同时解决了代码复杂化的问题。

Spring DI和AOP简介(一)相关推荐

  1. Spring AOP 简介以及简单用法

    Spring AOP 简介以及简单用法 如果你去面试java开发, 那么Spring的AOP和DI几乎是必问的问题. 那么AOP是什么呢? 一. AOP 所谓Aop就是 Aspect-Oriented ...

  2. Spring框架零基础学习(一):IOC|DI、AOP

    文章目录 一.IDEA创建Spring项目 二.Spring: IOC和DI 三.Spring: AOP 参考链接: HOW2J.CN:Spring idea创建一个spring项目 一.IDEA创建 ...

  3. 深入理解Spring IOC和AOP

    文章目录 1. 什么是 Spring 框架? 1.1Spring简介 1.2Spring的好处 1.3Spring体系结构 2.理解Spring IoC 2.1IoC简介 2.2IoC的好处 2.3依 ...

  4. Spring(IOC+AOP)

    Spring 容器(可以管理所有的组件(类))框架:        核心关注:IOC和AOP: Test:Spring的单元测试模块: spring-test-4.0.0.RELEASE Core C ...

  5. Java Spring DI之旅

    做过.NET的人很多都用过Microsoft Enterprise Library,里面有一个Dependency injection工具Unity,我们可以使用它来实现依赖注入:什么是依赖注入呢?我 ...

  6. Spring学习之AOP(面向切面编程)

    动态代理 代理模式是常用的java设计模式,他的特征是代理类与委托类有同样的接口,代理类主要负责为委托类预处理消息.过滤消息.把消息转发给委托类,以及事后处理消息等.代理类与委托类之间通常会存在关联关 ...

  7. Spring (Bean, IoC, AOP, SpringMVC)

    Spring - Bean, IoC, AOP, SpringMVC Spring 1. 核心容器 1.1 Spring-beans 1.1.1 Bean 的配置 1.1.1.1 自动装配 1.1.1 ...

  8. Spring Boot使用AOP在控制台打印请求、响应信息

    AOP称为面向切面编程,在程序开发中主要用来解决一些系统层面上的问题,比如日志,事务,权限等. AOP简介 AOP全称Aspect Oriented Programming,面向切面,AOP主要实现的 ...

  9. Spring 2 和 JPA 简介

    Spring 2 和 JPA 简介 用 Eclipse 和 DB2 Express-C 研究 Spring 2 框架和 Java 持久性 API 第 1 页,共 16 页 对本教程的评价 帮助我们改进 ...

  10. 一步一步手绘Spring DI运行时序图(Spring 自动装配之依赖注入)

    相关内容: 架构师系列内容:架构师学习笔记(持续更新) 一步一步手绘Spring IOC运行时序图一(Spring 核心容器 IOC初始化过程) 一步一步手绘Spring IOC运行时序图二(基于XM ...

最新文章

  1. Nat. Commun. 速递:虚拟颅内脑电与神经动力学模型
  2. Halcon_灰度直方图和特征直方图的使用
  3. PEInfo编程思路讲解03 - 工具篇03|解密系列
  4. String比较? 用==判断两个字符串,有时为true有时为false
  5. 如何在Chrome开发者工具console里手动调用focus方法给元素设置focus
  6. 网页关闭(解决window.close在火狐下不兼容问题)
  7. 客户端渲染换为服务器端渲染_服务器与客户端渲染(AngularJS与服务器端MVC)
  8. C#调用SQL中的存储过程中有output参数,存储过程执行过程中返回信息
  9. unity 日志级别_【Unity】通用的Debugger日志模块
  10. linux 编译安装apache
  11. python连载第11篇 if 语句
  12. 数 AI 人物还看今朝!CCAI 2017 人工智能青年论坛即将启航
  13. 如果使用Node.js不存在该如何创建目录?
  14. node,js的基础认识(一)
  15. ipad的正确使用方法视频,ipad的正确使用方法图解
  16. 计算机检索系统常用的运算符,计算机信息检索过程中常用的检索表达式 计算机信息检索系统.doc...
  17. 借助echarts制作酷炫3d地球动画
  18. 叔叔不约---匿名聊天网 聊天图片爬虫抓取
  19. matlab 产生高斯噪声和高斯白噪声方法
  20. 繁体批量转换工具:支持繁体字和简体本地化互转

热门文章

  1. 7款英文语法检查工具推荐
  2. 微信html代码怎么换行,微信小程序文本如何换行
  3. jQuery DOM操作 实现本地表格查询
  4. 手机,电脑都能用的,整人,恶搞代码连接,“你不会百度一下吗”教你用百度
  5. SuperMap iDesktop许可模块介绍
  6. java实现日历打印项目
  7. 汤姆猫炫跑鸿蒙,汤姆猫炫跑游戏怎么操作-玩法规则一览
  8. java 查找英语单词相似度,用于单词匹配,法1
  9. 【研报】医美行业产业投资宝典:颜值新经济,美丽无止境——附下载链接
  10. 十大api接口平台(接口商)