[摘录]从代理机制初探AOP

摘录地址:http://www.javaworld.com.tw/jute/post/print?bid=5&id=81236

1. 从代理机制初探 AOP  Copy to clipboard

Posted by: caterpillar

Posted on: 2004-11-24 11:10

 

 

AOP OOP 我觉得 AOP 观念其实不难,只是很容易被中文混淆, Aspect 在牛津字典中是这么解释的: particular part or feature of sth being considered.

 

Aspect 中文直译通常作「方面」,方面在中文通常作「角度」解译,我们常说「就这个方面来看」,其意是「就这个角度来看」,但在上面的英文解释中, aspect part 的意义,中文来说是「就这个部份来看」,「我」觉得 Aspect 在中文上应是取这个意思。

 

例如一个动作在执行过程中的某个「部份」是 AOP 所关注的,这个部份切入了执行过程,但本身不属于执行的最终目的,例如事务管理,执行的目的可能是储存数据,但事务管理切入了这个过程, AOP 关注于这个「切入面」,希望将其设计为通用,不介入对象设计的一个通用组件。

 

AOP 「我」比较接受的一个中文翻译是「切面导向」,这可以免除「方面」两字较具「表面」的意义, Aspect 是一个「面」没错,但是具有横切入过程的感觉。。。。。

 

总之,先看看下面的文章,我想应该比较了解吧。。。。。

 

==================================

 

AOP 的全名是 Aspect-oriented Programming Aspect 是程序设计时一个新的中心, AOP 并不取代 OOP ,两者各有各的角色,将职责各自分配自 Object Aspect ,会使得程序中各个组件的角色更为清楚。

 

这边先不直接探讨 AOP ,我们先从一个简单常见的例子来看看一个议题,这个例子是记录( log )动作,程序中很常需要为某些动作或事件作下记录,以便在事后检视或是作为除错时的信息,一个最简单的例子如下:

import java.util.logging.*;

                                                                               

public class HelloSpeaker {

    private Logger logger = Logger.getLogger(this.getClass().getName());

                                                                               

    public void hello(String name) {

        logger.log(Level.INFO, "hello method starts....");

                                                                               

        System.out.println("Hello, " + name);

                                                                               

        logger.log(Level.INFO, "hello method ends....");

    }

}

 

 

HelloSpeaker 在执行 hello() 方法时,我们希望能记录该方法已经执行及结束,最简单的作法就是如上在执行的前后加上记录动作,然而 Logger 介入了 HelloSpeaker 中,记录这个动作并不属于 HelloSpeaker ,这使得 HelloSpeaker 的职责加重。

 

想想如果程序中这种记录的动作到处都有需求,上面这种写法势必造成我们必须复制记录动作的程序代码,使得维护记录动作的困难度加大。如果不只有记录动作,有一些非对象本身职责的相关动作也混入了对象之中(例如权限检查、事务管理等等),会使得对象的负担更形加重,甚至混淆了对象的职责,对象本身的职责所占的程序代码,或许远小于这些与对象职责不相关动作的程序代码。

 

怎么办,用下面的方法或许好一些,我们先定义一个接口,然后实作该接口:

public interface IHello {

    public void hello(String name);

}

 

public class HelloSpeaker implements IHello {

    public void hello(String name) {

        System.out.println("Hello, " + name);

    }

}

 

 

接下来是重点了,我们实作一个代理对象 HelloProxy

import java.util.logging.*;

                                                                               

public class HelloProxy implements IHello {

    private Logger logger = Logger.getLogger(this.getClass().getName());

    private IHello helloObject;

                                                                               

    public HelloProxy(IHello helloObject) {

        this.helloObject = helloObject;

    }

                                                                               

     public void hello(String name) {

        logger.log(Level.INFO, "hello method starts....");

                                                                               

        helloObject.hello(name);

                                                                                

        logger.log(Level.INFO, "hello method ends....");

    }

}

 

执行时可以如此:

IHello helloProxy = new HelloProxy(new HelloSpeaker());

helloProxy.hello("Justin");

 

 

代理对象 HelloProxy 将代理真正的 HelloSpeaker 来执行 hello() ,并在其前后加上记录的动作,这使得我们的 HelloSpeaker 在撰写时不必介入记录动作, HelloSpeaker 可以专心于它的职责。

 

这是静态代理的基本范例,然而如您所看到的,代理对象的一个接口只服务于一种类型的对象,而且如果要代理的方法很多,我们势必要为每个方法进行代理,静态代理在程序规模稍大时就必定无法胜任。

 

Java JDK 1.3 之后加入协助开发动态代理功能的类别,我们不必为特定对象与方法撰写特定的代理,使用动态代理,可以使得一个 handler 服务于各个对象,首先,一个 handler 必须实现 java.lang.reflect.InvocationHandler

import java.util.logging.*;

import java.lang.reflect.*;

                                                                                       

public class LogHandler implements InvocationHandler {

    private Logger logger = Logger.getLogger(this.getClass().getName());

    private Object delegate;

                                                                                       

    public Object bind(Object delegate) {

        this.delegate = delegate;

        return Proxy.newProxyInstance(

                           delegate.getClass().getClassLoader(),

                           delegate.getClass().getInterfaces(),

                           this);

    }

                                                                                      

    public Object invoke(Object proxy, Method method, Object[] args)

                throws Throwable {

        Object result = null;

        try {

            logger.log(Level.INFO, "method stats..." + method);

            result = method.invoke(delegate, args);

            logger.log(Level.INFO, "method ends..." + method);

        } catch (Exception e){

            logger.log(Level.INFO, e.toString());

        }

        return result;

    }

}

 

InvocationHandler invoke() 方法会传入被代理对象的方法名称与执行参数实际上要执行的方法交由 method.invoke() ,并在其前后加上记录动作, method.invoke() 传回的对象是实际方法执行过后的回传结果。

 

动态代理必须宣告接口,实作该接口,例如:

public interface IHello {

    public void hello(String name);

}

 

public class HelloSpeaker implements IHello {

    public void hello(String name) {

        System.out.println("Hello, " + name);

    }

}

 

 

java.lang.reflect.Proxy newProxyInstance() 依要代理的对象、接口与 handler 产生一个代理对象,我们可以使用下面的方法来执行程序:

LogHandler logHandler  = new LogHandler();

IHello helloProxy = (IHello) logHandler.bind(new HelloSpeaker());

helloProxy.hello("Justin");

 

 

LogHandler 不在服务于特定对象与接口,而 HelloSpeaker 也不用插入任何有关于记录的动作,它不用意识到记录动作的存在。

 

讲了半天,我们回到 AOP 的议题上,这个例子与 AOP 有何关系?

 

如上面的例子中示范的, HelloSpeaker 本来必须插入记录动作,这使得 HelloSpeaker 的职责加重,并混淆其原来的角色,为此,我们使用代理将记录的动作提取出来,以厘清记录动作与 HelloSpeaker 的职责与角色。

 

在这里,记录这个动作是我们所关注的, AOP 中的 Aspect 所指的就是像记录这类的动作,我们将这些动作(或特定职责)视为关注的中心,将其设计为通用、不介入特定对象、职责清楚的组件,这就是所谓的「 Aspect 导向」(比较好的一个中文翻法是「切面导向」,下一个主题再说明),就如同「对象导向」一样,每个对象仅代表一个实际的个体,将对象视为关注的中心,将其设计为通用、职责清楚的组件。

 

这边先以代理机制作为一个开端初步探讨 AOP ,如您所看到的, AOP OOP 并不抵触,两者的合作使得程序更为清晰, Object Aspect 职责不互相混淆,而且都更具重用性,下一个主题我们再来深入 AOP 的一些细节。

 



欢迎大家访问我的个人网站 萌萌的IT人

posted on 2006-04-24 11:16 见酒就晕 阅读(113) 评论(0)  编辑  收藏 所属分类: J2EE文章


只有注册用户登录后才能发表评论。


网站导航:
 
<2024年5月>
2829301234
567891011
12131415161718
19202122232425
2627282930311
2345678

导航

统计

常用链接

留言簿(3)

我参与的团队

随笔分类

随笔档案

文章分类

文章档案

收藏夹

BLOG

FRIENDS

LIFE

搜索

最新评论

阅读排行榜

评论排行榜