理解AOP之"实现"
在理解AOP之"初识"一文中我从概念上介绍了什么是AOP和一个典型的应用场景,在这篇文章中我将介绍AOP的具体实现。
AOP实际上是一个GoF的Proxy模式的应用和扩展。
继续使用上文中的代码
public interface Closeable {
void close();
}
public class FileSaver implements Closeable{
public void close() {
// close file output stream
}
}
public class UserDao implements Closeable{
public void close() {
// close db connection
}
}
实现一:静态代理(Proxy)
public class CloseableResourceProxy implements Closeable {
private final Closeable target;
private final Logger logger = new Logger();
public CloseableResourceProxy(Closeable target) {
this.target = target;
}
public void close() {
// try-catch exception and write into error log
try {
target.close();
} catch (Exception e) {
logger.error(e.getMessage());
}
}
}
上面实现的这个Proxy类实际上就是一个对于Closeable的"异常处理-错误日志"的业务横切面。对于其他Closeable的实现类,不直接调用自己的close方法去关闭资源,而是创建一个Proxy并将实现类作为Proxy的构造方法参数传递进去,由Proxy去做关闭操作,达到统一进行日志处理的目的。
这段代码实际上就是AOP的雏形。但是,你可能会发现一些问题,静态代理的方式会有比较大的局限性。
1、要实现代理方式,必须要定义接口。
2、被代理的对象必须实现同一个接口。
假设新引入一个类:
public class Socket implements ISocket{
public boolean start() {
//start socket and bind to a ip address and port
}
public boolean stop() {
// stop socket
}
}
如果我们想要为start和stop方法去做异常处理-错误日志的话,CloseableResourceProxy类是无法重用的,我们必须写一个Proxy类继承ISocket接口。但是对于2个Proxy类来说,try-catch和错误日志的逻辑没什么不同,这样在2个类中就存在了重复的代码(甚至对于start和stop方法来说try-catch和日志错误的逻辑都相同的,在start和stop的代理方法内也会存在duplicated code)。所以静态代理类并不是一种AOP很理想的实现。
实现二: 动态代理
JDK 1.5开始提供了InvocationHandler接口可以允许我们去做一个动态代理
public class DynamicProxy implements InvocationHandler {
private Object target;
public Object delegate(Object target) {
this.target = target;
return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces() , this);
}
@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
Object result = null;
System.out.println("Pre-aspect process for " + this.target.getClass().getName() + "@" + method.getName());
try {
result = method.invoke(this.target, args);
} catch (Exception e) {
System.out.println("Error log");
}
return result;
}
}
使用的时候
Closeable fileSaver = new DynamicProxy().delegate(new FileSaver()); ISocket socket= new DynamicProxy().delegate(new Socket()); fileSaver.close(); socket.start(); socket.stop();
运行结果为:
Pre-aspect process for org.stardust.javaquick.aop.FileSaver@close
Pre-aspect process for org.stardust.javaquick.aop.Socket@start
Pre-aspect process for org.stardust.javaquick.aop.Socket@stop
由上面的例子可以看出,相比静态代理来说动态代理可以在不同接口间或者相同接口的不同方法间重用。大大增加了代码的复用率,减少了重复的相似类。
但是注意,被代理的类必须实现一个接口。Proxy.newProxyInstance方法的定义如下
public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)
其第二个参数是被代理对象的接口集合。newProxyInstance的返回对象只能被转换成这个集合中的接口。
实现三:CGLib代理
CGLib (Code Generation Library)是一个强大的,高性能,高质量的Code生成类库,它可以在运行期扩展Java类与实现Java接口。通过CGLib实现的代理类允许被代理的对象不用实现任何接口。如下列代码:
public class CGLibProxy implements MethodInterceptor{
public Object delegate(Object target) {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(target.getClass());
enhancer.setCallback(this);
return enhancer.create();
}
@Override
public Object intercept(Object proxy, Method method, Object[] args,
MethodProxy methodProxy) throws Throwable {
Object result = null;
try {
result = methodProxy.invokeSuper(proxy, args);
} catch (Exception e) {
System.out.println("Error log");
}
return result;
}
}
调用代码:
CGLibProxy cgProxy = new CGLibProxy(); FileSaver saver= (FileSaver)cgProxy.delegate(new FileSaver()); saver.close();
注意CGLibProxy的代理对象转型到一个类而不是接口。
以上是AOP的几种实现方法,Spring对于AOP的支持也是基于以上几种方式来实现的,不过更加的复杂而已。
浙公网安备 33010602011771号