Spring AOP面向切面编程(代理模式,装饰模式简述)

转载自:http://bolide74.iteye.com/blog/1049935

这一章我们将开始剖析Spring框架最为重要的AOP(Aspect Oriented Programming)面向切面编程。可以说Spring的精华就在于AOP了。 
所谓AOP,就是相对于OOP(Object Oriented Programming)面向对象编程的说法,有些人喜欢叫面向切面编程,有些人喜欢叫做面向方面,事实上这两个都是指同一个东西,只是叫法不同。 
我们传统的编程都是面向对象,就是说每个类都有它实际的意义。而面向切面略有不同,它在面向对象的基础上扩展了一下,它编程的时候不是先考虑的一个具体对象(比如用户类),而是先考虑的对象的行为或者功能。这个不是编程方法的不同,而是编程思维的转变。 

理论性的东西还是放一边,我们用实际的机器人案例来慢慢理解这个概念。 
为了突出重点我们这里重写了ISpeak: 

Java代码   收藏代码
  1. package com.iteye.bolide74.impl;  
  2.   
  3. public interface ISpeaker {  
  4.     public void say(String msg);  
  5. }  


接着是实现这个接口的机器人类: 

Java代码   收藏代码
  1. package com.iteye.bolide74.action;  
  2.   
  3. import com.iteye.bolide74.impl.ISpeaker;  
  4.   
  5. public class Robot implements ISpeaker {  
  6.     public String name;  
  7.   
  8.     public String getName() {  
  9.         return name;  
  10.     }  
  11.   
  12.     public void setName(String name) {  
  13.         this.name = name;  
  14.     }  
  15.   
  16.     public Robot(String name) {  
  17.         this.name = name;  
  18.     }  
  19.   
  20.     @Override  
  21.     public void say(String msg) {  
  22.         System.out.println("到达邻居家,对邻居说:" + msg + ",我是" + this.name);  
  23.     }  
  24. }  


那么现在我们要实现跟很多个不同的邻居(不同的应用代码)打招呼,只需要下面的应用代码了: 

Java代码   收藏代码
  1. package com.iteye.bolide74.tester;  
  2.   
  3. import com.iteye.bolide74.action.Robot;  
  4. import com.iteye.bolide74.impl.ISpeaker;  
  5.   
  6. public class Tester {  
  7.     public static void main(String[] args) {  
  8.         ISpeaker speaker = new Robot("Wall-E");  
  9.         speaker.say("你好");  
  10.     }  
  11. }  



以上是实现最简单功能的方法。现在我们要慢慢增加功能了,假设Wall-E在出发打招呼之前要先拿一个礼物,然后打完招呼以后把礼物递给邻居,该怎么实现呢? 

Java代码   收藏代码
  1. public void getGift() {  
  2.     System.out.println("获取了一个礼物");  
  3. }  
  4. public void giveGift(){  
  5.     System.out.println("赠予一个礼物");  
  6. }  



最傻的办法就是每次在应用代码里调用speaker.say()方法的前后都调用get/give方法,小程序还好,要是大型程序的话人都要写傻掉。 
那么能不能直接把get/give方法写在Robot类的say()方法里呢?这样好处是每次调用say()的时候都能实现get/give功能,但是坏处也是这个:万一有个别邻居是不需要给礼物的呢?怎么办? 
那有没有更加灵活的办法呢?当然有! 

代理模式: 
我们先新建一个SpeakerProxy代理类: 

Java代码   收藏代码
  1. package com.iteye.bolide74.action;  
  2.   
  3. import com.iteye.bolide74.impl.ISpeaker;  
  4.   
  5. public class SpeakerProxy implements ISpeaker {  
  6.     ISpeaker speaker;  
  7.   
  8.     public SpeakerProxy(ISpeaker speaker) {  
  9.         super();  
  10.         this.speaker = speaker;  
  11.     }  
  12.   
  13.     @Override  
  14.     public void say(String msg) {  
  15.         getGift();  
  16.         speaker.say(msg);  
  17.         giveGift();  
  18.     }  
  19.   
  20.     public void getGift() {  
  21.         System.out.println("获取了一个礼物");  
  22.     }  
  23.     public void giveGift(){  
  24.         System.out.println("赠予一个礼物");  
  25.     }  
  26. }  


这个SpeakerProxy类实现了ISpeaker接口,然后又有一个靠构造函数传入的ISpeaker类型的成员属性。 
而这个SpeakerProxy类的say()方法就有点意思了,它不是重写了自己的say()方法,而是调用了ISpeaker类型的speaker成员属性的say()方法,然后再这个say()方法前后嵌入get/give方法。 
让我们看看这个SpeakerProxy类是怎么在应用代码使用: 

Java代码   收藏代码
  1. package com.iteye.bolide74.tester;  
  2.   
  3. import com.iteye.bolide74.action.Robot;  
  4. import com.iteye.bolide74.action.SpeakerProxy;  
  5. import com.iteye.bolide74.impl.ISpeaker;  
  6.   
  7. public class SpeakerProxyTester {  
  8.     public static void main(String[] arg0) {  
  9.         // 没有带礼物的机器人:  
  10.         ISpeaker noGiftSpeaker = new Robot("空手来的Wall-E");  
  11.         noGiftSpeaker.say("你好");  
  12.         System.out.println();  
  13.         // 带了礼物的机器人  
  14.         ISpeaker speaker = new SpeakerProxy(new Robot("有礼而来的Wall-E"));  
  15.         speaker.say("你好");  
  16.     }  
  17. }  


输出结果: 

引用
到达邻居家,对邻居说:你好,我是空手来的Wall-E 

获取了一个礼物 
到达邻居家,对邻居说:你好,我是有礼而来的Wall-E 
赠予一个礼物



这样的话我们就达到了灵活性:不需要礼物的时候直接新建一个机器人;而需要礼物的时候我们就new一个SpeakerProxy(代理器),然后新建一个机器人丢进去,我们只要调用这个代理器就行了,这个代理器内部会给机器人一个礼物然后命令机器人去打招呼送礼物。 


扩展阅读:装饰模式: 

引用
        装饰模式与AOP关系不大,但是它的实现方法与代理模式很像,不乘此机会一起介绍有点可惜,如果已经了解了装饰模式的可以直接跳过。


假设我们的机器人要送的礼物种类比较多,有红包、水果、花等等。而给不同的邻居打招呼送礼的时候都会送不同组合的礼物,比如:红包+水果、红包+花、水果+花、全部都送、单独送其中一样。 
那么这种功能该怎么实现呢?每种组合写一个get/give方法吗?如果只有简单3种组合可能忍忍也就算了,但是如果以后礼物种类越来越多,那这个组合数量是成爆发性增长的,显然不合适。 
那么这时候就该轮到装饰模式登场了: 

Java代码   收藏代码
  1. package com.iteye.bolide74.action;  
  2.   
  3. import com.iteye.bolide74.impl.ISpeaker;  
  4.   
  5. public abstract class SpeakerGiftDecorator implements ISpeaker {  
  6.     ISpeaker speaker;  
  7.   
  8.     public SpeakerGiftDecorator(ISpeaker speaker) {  
  9.         super();  
  10.         this.speaker = speaker;  
  11.     }  
  12.   
  13.     public abstract void say(String msg);  
  14.   
  15.     public abstract void getGift();  
  16.   
  17.     public abstract void giveGift();  
  18. }  


这是一个礼物的装饰器类,你可以理解为各种礼物的组装器。它的写法跟代理模式的代理类很像,不同的就是它是一个抽象类。它是所有种类的礼物的父类。 
接下来就写一个继承了这个SpeakerGiftDecorator类的礼物之类: 

Java代码   收藏代码
  1. package com.iteye.bolide74.action;  
  2.   
  3. import com.iteye.bolide74.impl.ISpeaker;  
  4. //礼物的一种:一束花  
  5. public class Flower extends SpeakerGiftDecorator {  
  6.   
  7.     public Flower(ISpeaker speaker) {  
  8.         super(speaker);  
  9.     }  
  10.   
  11.     @Override  
  12.     public void say(String msg) {  
  13.         getGift();  
  14.         this.speaker.say(msg);  
  15.         giveGift();  
  16.     }  
  17.   
  18.     @Override  
  19.     public void getGift() {  
  20.         System.out.println("获取了一束花");  
  21.     }  
  22.   
  23.     @Override  
  24.     public void giveGift() {  
  25.         System.out.println("赠予一束花");  
  26.     }  
  27.   
  28. }  


以上面的Flower子类类推来写出另外的Money类和Fruit类,区别仅在于get/give方法的内容不同,这里就不浪费篇幅了。 

看起来代码似乎很简单,没什么出奇的,但是当应用代码使用的时候就能体现出它强大的地方了: 

Java代码   收藏代码
  1. package com.iteye.bolide74.tester;  
  2.   
  3. import com.iteye.bolide74.action.Flower;  
  4. import com.iteye.bolide74.action.Fruit;  
  5. import com.iteye.bolide74.action.Money;  
  6. import com.iteye.bolide74.action.Robot;  
  7. import com.iteye.bolide74.impl.ISpeaker;  
  8.   
  9. public class SpeakerGiftDecoratorTester {  
  10.     public static void main(String[] args) {  
  11.         // 一种礼物:  
  12.         ISpeaker speaker = new Flower(new Robot("我是带花来的Wall-E"));  
  13.         speaker.say("Hello");  
  14.         System.out.println();  
  15.         // 两种礼物:  
  16.         speaker = new Money(new Flower(new Robot("我是带了花和红包的Wall-E")));  
  17.         speaker.say("Hello");  
  18.         System.out.println();  
  19.         // 另外两种礼物:  
  20.         speaker = new Fruit(new Money(new Robot("我是带了水果和钱的Wall-E")));  
  21.         speaker.say("Hello");  
  22.         System.out.println();  
  23.         // 三种礼物:  
  24.         speaker = new Fruit(new Money(new Flower(new Robot("我是三种礼物都带的Wall-E"))));  
  25.         speaker.say("Hello");  
  26.     }  
  27. }  


可以看到,speaker引用的是一个一层一层嵌套的类,要多带一种礼物那就多嵌套一层,这样就能尽可能的实现了代码的重用,而且应用起来也简单明了。 


输出结果: 

引用
获取了一束花 
到达邻居家,对邻居说:Hello,我是我是带花来的Wall-E 
赠予一束花 

获取了一个红包 
获取了一束花 
到达邻居家,对邻居说:Hello,我是我是带了花和红包的Wall-E 
赠予一束花 
赠予一个红包 

获取了一袋水果 
获取了一个红包 
到达邻居家,对邻居说:Hello,我是我是带了水果和钱的Wall-E 
赠予一个红包 
赠予一袋水果 

获取了一袋水果 
获取了一个红包 
获取了一束花 
到达邻居家,对邻居说:Hello,我是我是三种礼物都带的Wall-E 
赠予一束花 
赠予一个红包 
赠予一袋水果 

猜你喜欢

转载自xvshell.iteye.com/blog/2363443