这一章我们将开始剖析Spring框架最为重要的AOP(Aspect Oriented Programming)面向切面编程。可以说Spring的精华就在于AOP了。
所谓AOP,就是相对于OOP(Object Oriented Programming)面向对象编程的说法,有些人喜欢叫面向切面编程,有些人喜欢叫做面向方面,事实上这两个都是指同一个东西,只是叫法不同。
我们传统的编程都是面向对象,就是说每个类都有它实际的意义。而面向切面略有不同,它在面向对象的基础上扩展了一下,它编程的时候不是先考虑的一个具体对象(比如用户类),而是先考虑的对象的行为或者功能。这个不是编程方法的不同,而是编程思维的转变。
理论性的东西还是放一边,我们用实际的机器人案例来慢慢理解这个概念。
为了突出重点我们这里重写了ISpeak:
package com.iteye.bolide74.impl;
public interface ISpeaker {
public void say(String msg);
}
接着是实现这个接口的机器人类:
package com.iteye.bolide74.action;
import com.iteye.bolide74.impl.ISpeaker;
public class Robot implements ISpeaker {
public String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Robot(String name) {
this.name = name;
}
@Override
public void say(String msg) {
System.out.println("到达邻居家,对邻居说:" + msg + ",我是" + this.name);
}
}
那么现在我们要实现跟很多个不同的邻居(不同的应用代码)打招呼,只需要下面的应用代码了:
package com.iteye.bolide74.tester;
import com.iteye.bolide74.action.Robot;
import com.iteye.bolide74.impl.ISpeaker;
public class Tester {
public static void main(String[] args) {
ISpeaker speaker = new Robot("Wall-E");
speaker.say("你好");
}
}
以上是实现最简单功能的方法。现在我们要慢慢增加功能了,假设Wall-E在出发打招呼之前要先拿一个礼物,然后打完招呼以后把礼物递给邻居,该怎么实现呢?
public void getGift() {
System.out.println("获取了一个礼物");
}
public void giveGift(){
System.out.println("赠予一个礼物");
}
最傻的办法就是每次在应用代码里调用speaker.say()方法的前后都调用get/give方法,小程序还好,要是大型程序的话人都要写傻掉。
那么能不能直接把get/give方法写在Robot类的say()方法里呢?这样好处是每次调用say()的时候都能实现get/give功能,但是坏处也是这个:万一有个别邻居是不需要给礼物的呢?怎么办?
那有没有更加灵活的办法呢?当然有!
代理模式:
我们先新建一个SpeakerProxy代理类:
package com.iteye.bolide74.action;
import com.iteye.bolide74.impl.ISpeaker;
public class SpeakerProxy implements ISpeaker {
ISpeaker speaker;
public SpeakerProxy(ISpeaker speaker) {
super();
this.speaker = speaker;
}
@Override
public void say(String msg) {
getGift();
speaker.say(msg);
giveGift();
}
public void getGift() {
System.out.println("获取了一个礼物");
}
public void giveGift(){
System.out.println("赠予一个礼物");
}
}
这个SpeakerProxy类实现了ISpeaker接口,然后又有一个靠构造函数传入的ISpeaker类型的成员属性。
而这个SpeakerProxy类的say()方法就有点意思了,它不是重写了自己的say()方法,而是调用了ISpeaker类型的speaker成员属性的say()方法,然后再这个say()方法前后嵌入get/give方法。
让我们看看这个SpeakerProxy类是怎么在应用代码使用:
package com.iteye.bolide74.tester;
import com.iteye.bolide74.action.Robot;
import com.iteye.bolide74.action.SpeakerProxy;
import com.iteye.bolide74.impl.ISpeaker;
public class SpeakerProxyTester {
public static void main(String[] arg0) {
// 没有带礼物的机器人:
ISpeaker noGiftSpeaker = new Robot("空手来的Wall-E");
noGiftSpeaker.say("你好");
System.out.println();
// 带了礼物的机器人
ISpeaker speaker = new SpeakerProxy(new Robot("有礼而来的Wall-E"));
speaker.say("你好");
}
}
输出结果:
引用
到达邻居家,对邻居说:你好,我是空手来的Wall-E
获取了一个礼物
到达邻居家,对邻居说:你好,我是有礼而来的Wall-E
赠予一个礼物
这样的话我们就达到了灵活性:不需要礼物的时候直接新建一个机器人;而需要礼物的时候我们就new一个SpeakerProxy(代理器),然后新建一个机器人丢进去,我们只要调用这个代理器就行了,这个代理器内部会给机器人一个礼物然后命令机器人去打招呼送礼物。
扩展阅读:装饰模式:
引用
装饰模式与AOP关系不大,但是它的实现方法与代理模式很像,不乘此机会一起介绍有点可惜,如果已经了解了装饰模式的可以直接跳过。
假设我们的机器人要送的礼物种类比较多,有红包、水果、花等等。而给不同的邻居打招呼送礼的时候都会送不同组合的礼物,比如:红包+水果、红包+花、水果+花、全部都送、单独送其中一样。
那么这种功能该怎么实现呢?每种组合写一个get/give方法吗?如果只有简单3种组合可能忍忍也就算了,但是如果以后礼物种类越来越多,那这个组合数量是成爆发性增长的,显然不合适。
那么这时候就该轮到装饰模式登场了:
package com.iteye.bolide74.action;
import com.iteye.bolide74.impl.ISpeaker;
public abstract class SpeakerGiftDecorator implements ISpeaker {
ISpeaker speaker;
public SpeakerGiftDecorator(ISpeaker speaker) {
super();
this.speaker = speaker;
}
public abstract void say(String msg);
public abstract void getGift();
public abstract void giveGift();
}
这是一个礼物的装饰器类,你可以理解为各种礼物的组装器。它的写法跟代理模式的代理类很像,不同的就是它是一个抽象类。它是所有种类的礼物的父类。
接下来就写一个继承了这个SpeakerGiftDecorator类的礼物之类:
package com.iteye.bolide74.action;
import com.iteye.bolide74.impl.ISpeaker;
//礼物的一种:一束花
public class Flower extends SpeakerGiftDecorator {
public Flower(ISpeaker speaker) {
super(speaker);
}
@Override
public void say(String msg) {
getGift();
this.speaker.say(msg);
giveGift();
}
@Override
public void getGift() {
System.out.println("获取了一束花");
}
@Override
public void giveGift() {
System.out.println("赠予一束花");
}
}
以上面的Flower子类类推来写出另外的Money类和Fruit类,区别仅在于get/give方法的内容不同,这里就不浪费篇幅了。
看起来代码似乎很简单,没什么出奇的,但是当应用代码使用的时候就能体现出它强大的地方了:
package com.iteye.bolide74.tester;
import com.iteye.bolide74.action.Flower;
import com.iteye.bolide74.action.Fruit;
import com.iteye.bolide74.action.Money;
import com.iteye.bolide74.action.Robot;
import com.iteye.bolide74.impl.ISpeaker;
public class SpeakerGiftDecoratorTester {
public static void main(String[] args) {
// 一种礼物:
ISpeaker speaker = new Flower(new Robot("我是带花来的Wall-E"));
speaker.say("Hello");
System.out.println();
// 两种礼物:
speaker = new Money(new Flower(new Robot("我是带了花和红包的Wall-E")));
speaker.say("Hello");
System.out.println();
// 另外两种礼物:
speaker = new Fruit(new Money(new Robot("我是带了水果和钱的Wall-E")));
speaker.say("Hello");
System.out.println();
// 三种礼物:
speaker = new Fruit(new Money(new Flower(new Robot("我是三种礼物都带的Wall-E"))));
speaker.say("Hello");
}
}
可以看到,speaker引用的是一个一层一层嵌套的类,要多带一种礼物那就多嵌套一层,这样就能尽可能的实现了代码的重用,而且应用起来也简单明了。
输出结果:
引用
获取了一束花
到达邻居家,对邻居说:Hello,我是我是带花来的Wall-E
赠予一束花
获取了一个红包
获取了一束花
到达邻居家,对邻居说:Hello,我是我是带了花和红包的Wall-E
赠予一束花
赠予一个红包
获取了一袋水果
获取了一个红包
到达邻居家,对邻居说:Hello,我是我是带了水果和钱的Wall-E
赠予一个红包
赠予一袋水果
获取了一袋水果
获取了一个红包
获取了一束花
到达邻居家,对邻居说:Hello,我是我是三种礼物都带的Wall-E
赠予一束花
赠予一个红包
赠予一袋水果
我的围脖:
http://t.qq.com/bolide74
下一篇:Spring温故知新(六)AOP面向切面编程 <2>
http://bolide74.iteye.com/blog/1007828
上一篇:Spring温故知新(五)Spring的Bean和IoC 容器
http://bolide74.iteye.com/blog/1004086
分享到:
相关推荐
Spring框架的核心特性之一就是AOP(Aspect Oriented Programming,面向切面编程)。AOP提供了一种模块化和声明式的方式来处理系统中的横切关注点,如日志、事务管理、权限检查等。在OOP(面向对象编程)中,这些关注...
HTML5引入了许多新元素和功能,如`<article>`、`<section>`、`<header>`、`<footer>`用于更好地组织内容,`<video>`和`<audio>`支持多媒体播放,`<canvas>`提供绘图功能,`<svg>`用于矢量图形。 十、HTML与CSS、...
2. **HTML标签**:HTML通过一系列标签来定义页面内容,如文本内容(`<p>`)、标题(`<h1>`至`<h6>`)、超链接(`<a>`)、图像(`<img>`)、列表(`<ul>`和`<ol>`)等。每个标签都有其特定的作用和用法。 3. **属性*...
4. **HTML5新特性**:HTML5引入了许多新元素,如`<header>`,`<footer>`,`<section>`,`<article>`,以及`<canvas>`用于图形绘制,`<video>`和`<audio>`支持多媒体内容。 5. **响应式设计**:在Pineapple-juice中...
此外,还会涉及表格(`<table>`)、图像(`<img>`)、列表(`<ul>`、`<ol>`、`<li>`)等常见元素的使用方法。 其次,测试用例是检验HTML知识掌握程度的有效工具。通过编写和运行这些案例,你可以实践HTML语法,理解...
1. 元素与标签:HTML由一系列的元素组成,每个元素都有对应的标签,如`<p>`用于段落,`<h1>`到`<h6>`用于标题。 2. 属性:元素可以拥有属性,比如`src`用于指定图像或脚本的路径,`href`用于链接目标。 3. 结构:...
3. "spring01quickstart.zip":这是Spring框架的基础快速启动教程,可能涉及到Spring的核心概念,如IoC容器、bean的生命周期、AOP(面向切面编程)等。这部分可能会介绍如何创建第一个Spring应用,包括引入Spring库...
首先,Spring的核心组件包括Spring Core和Spring Beans,它们提供了依赖注入(DI)和面向切面编程(AOP)的基础。依赖注入使得对象之间的关系在运行时动态配置,降低了代码间的耦合度;面向切面编程则允许我们分离...
在书中,你会了解到HTML的基本标签,如`<html>`、`<head>`、`<body>`,以及如何使用`<p>`创建段落,`<a>`创建链接,`<img>`插入图片等。此外,还会有关于表格、列表、框架、表单等复杂元素的介绍,帮助你理解如何...
<li><a href="#">和平精英</a></li> <li><a href="#">刺激战场</a></li> </li> <p>Hello</p> <p class="hello">魔兽世界</p> <p>Hello</p> </div> ``` 如果要选中所有在`div`内的`p`元素并将其颜色设为深粉红...
1. **基础语法**:在HTML项目中,学习者需要掌握基本的HTML标签,如`<html>`、`<head>`、`<body>`、`<p>`、`<a>`等,以及如何使用它们构建网页结构。 2. **页面布局**:通过使用`<div>`、`<span>`等元素,结合CSS...
备注:内容大部分从网上复制,代码为自己...<1>.比较相邻的元素。如果第一个比第二个大,就交换它们两个;<2>.对每一对相邻元素作同样的工作,从开始第一对到结尾的最后一对,这样在最后的元素应该会是最大的数;<3>
4. **集合**:了解ArrayList、List<T>、Dictionary<TKey, TValue>等集合类型,以及LINQ(Language Integrated Query)的使用。 5. **事件和委托**:在ASP.NET中,事件和委托是页面交互和控件通信的重要机制。 6. *...
此外,字典(Dictionary<TKey, TValue>)和集合(HashSet<T>)等数据结构也可能出现在实例中,帮助我们理解数据组织和操作。 文件I/O和网络编程也是C#的重要部分。源代码可能涵盖如何读写文件,使用Stream、...
cout << sizeof(a) << " " << sizeof(b) << " " << sizeof(c); return 0; } ``` - **样例输出**:`2 4 8` 通过本次课程的学习,学生们不仅能够加强对C++基础概念的理解,还能够掌握如何运用这些概念解决实际...
在这个项目中,你会接触到基本标签如`<head>`、`<body>`、`<p>`、`<a>`,以及更复杂元素如表格、图像、链接等。每个项目都会让你实践不同场景下的HTML应用。 2. CSS样式设计:CSS(Cascading Style Sheets)负责...
2. **面向对象编程**:C++的核心是面向对象,实例可能会讲解类与对象的概念,封装、继承和多态性。通过创建和操作自定义对象,你可以深入理解这些核心概念。 3. **标准库的使用**:C++标准库提供了许多功能强大的...
1. **脚本元素**:`<% %>` 用于编写Java代码,可以是声明、表达式或脚本块。 2. **指令元素**:如`<%@ page %>`, `<%@ include %>`, `<%@ taglib %>`, 用于设置页面属性、引入其他页面或定义标签库。 3. **动作元素*...
**VB6.0编程入门详解** Visual Basic 6.0(简称VB6)是微软公司推出...无论你是编程新手还是希望温故知新,这本书都将是你宝贵的参考资料。通过学习,你可以创建自己的Windows应用程序,实现各种功能,满足实际需求。