摘录地址: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
的一些细节。