应用切面(aspects)
DI能够让你的软件组件间保持松耦合,而面向切面编程(AOP)能够让你捕获到在整个应用中可重用的组件功能。在软件系统中,AOP通常被定义为提升关注点分离的一个技术。系统由很多的组件组成,每个组件负责一部分的功能。但是这些组件往往除了核心功能外,还有些额外的责任。比如像日志,事务管理和安全这些系统服务会被引进到组件中。这些服务通常被称为横切关注点(cross-cutting-concerns),因为它们常常贯穿于多个组件中。
在多个组件中传播这些概念,你会引进两个复杂层面到你的代码中
- 实现系统级概念代码将会在多个组件中重复出现,这意味着如果你需要改变这些概念的话,你必须修改多个组件的内容。即使你将这些概念抽象到一个独立的方法中来使它成为一个单一的方法,但是方法的调用还是会出现在多个地方。
- 你的组件现在充斥着和核心代码无关的功能。一个添加它的条目到地址簿中方法应当只关心如何添加而不是安全和事务问题
下图展示的它的复杂度。左边的业务对象和右边的系统服务紧密的关联在一起。
AOP能够让这些服务现在模块化并且声明的使用它们到组件中来消除这些影响。这样使组件更有粘合性,使它们只关注于自己具体的概念,完全忽略任何涉及到的系统服务。简而言之,切面确保POJOS保持简单。
你可以将切面设想为覆盖了很多组件的毛毯,如下图所示。一个应用由多个实现业务功能的模块组成。使用AOP,你可以使用功能层来覆盖你的核心应用。这些层可以通过声明的方式来贯穿到你的应用中而不用你核心代码知道它们的存在。这是一个强大的概念,因为它将应用核心业务和系统服务独立开来。
下面我们来看个具体的例子,现在是个骑士的英勇的故事,故事如何流传下来呢,当然是通过歌手的歌声来传递
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是个简单的类并有两个方法。singBeforeQuest()方法在骑士开始探索的之前被调用,而singAfterQuest()方法应当在骑士探索结束后调用。在这两个用例中,Minstrel通过在构造器中注入的PrintSteam来歌颂骑士的事迹。
现在你要做的就是将这些带人到你的代码中----你只需要注入BraveKnight,让我们微调下BraveKnight去使用Minstrel。下面展示如何将他们合并在一起
public class BraveKnight implements Knight { private Quest quest; private Minstrel minstrel; public BraveKnight(Quest quest, Minstrel minstrel) { this.quest = quest; this.minstrel = minstrel; } public void embarkOnQuest() throws QuestException { minstrel.singBeforeQuest(); quest.embark(); minstrel.singAfterQuest(); } }
它应该能够运行,你要做的就是回到你的配置文件中让后声明一个Minstrel bean并且注入到BraveKnight的构造器中。但是等等...