開始理解起來可能比較費勁,但我們可以通過一個通俗說明來這樣理解AOP的概念,就是使用AOP可以不用修改原有的代碼,而可以追加新的功能。
比如,我們用AOP實現了用戶登陸(判斷ID與密碼是否正確)功能,現在我們要求在用戶登陸時用LOG記錄用戶登陸的情況。一般的做法是直接修改已有的登陸邏輯代碼,但使用AOP,可以不用修改原有的代碼而完成此功能。
本文試圖通過使用Java本身的動態代理功能,來實現一個具有簡單的AOP功能的容器。從而幫助大家對AOP有個大概的認識。
Java動態代理功能
首先,我們簡單介紹一下Java動態代理功能。JAVA的動態代理功能主要通過java.lang.reflect.Proxy類與java.lang.reflect.InvocationHandler接口完成,這里正是通過它們實現一個簡單的AOP容器的。其實,像JBoss AOP等其他動態AOP框架也都是通過Proxy和InvocationHandler來實現的。
- Java從JDK1.3開始提供動態代理(Java Proxy)功能。
- 所謂動態代理,即通過代理類:Proxy的代理,接口和實現類之間可以不直接發生聯系,而可以在運行期(Runtime)實現動態關聯。
- AOP(Aspect Oriented Programming):面向切面編程,其中的一種實現方法便是用Proxy來實現的。
- Java Proxy只能代理接口,不能代理類。
- Java Proxy功能主要通過java.lang.reflect.Proxy類與java.lang.reflect.InvocationHandler接口實現。
- java.lang.reflect.Proxy (代理類) > ProxyInterface(被代理的接口) > InvocationHandler(關聯類)> Class(可以在InvocationHandler中被調用)。它們之間的關系可以用下面的流程圖來表示:
動態代理類:Proxy 被代理的接口 InvocationHandler實現類 代理類
實際上的調用關系可以用下面的流程圖來表示:
Proxy.newProxyInstance
XxxxInterface xx = $ProxyN(N=0,1,2…)
XxxxInterface.calledMethod
$ProxyN.calledMethod
InvocationHandler.invoke
method.invoke(obj, args)
obj.calledMethod
- Proxy. newProxyInstance的參數:必須傳送以下3個參數給Proxy. newProxyInstance方法:ClassLoader,Class[],InvocationHandler。其中參數2為被代理的接口 Class,參數3為實現InvocationHandler接口的實例。
- 可以通過Proxy. newProxyInstance方法得到被代理的接口的一個實例(instance),該實例由newProxyInstance方法動態生成,并實現了該接口。
- 當程序顯示調用接口的方法時,其時是調用該實例的方法,此方法又會調用與該實例相關聯InvocationHandler的invoke方法。
- 這樣我們可以在InvocationHandler.invoke方法里調用某些處理邏輯或真正的邏輯處理實現類。
用Java Proxy實現AOP容器
下面我們使用Java Proxy來實現一個簡單的AOP容器。
文件列表:
文件名 |
說明 |
AopInvocationHandlerImpl.java |
該類實現了java.lang.reflect.InvocationHandler接口,我們通過它記錄LOG信息 |
AopContainer.java |
簡單的AOP容器,通過它把IDoBusiness與AopInvocationHandlerImpl關聯起來 |
IDoBusiness.java |
邏輯處理接口 |
DoBusiness.java |
邏輯處理實現類 |
TestAop.java |
測試類 |
簡單的AOP容器:
AopContainer.java
package com.test.aop.framework;
import java.lang.reflect.Proxy;
/**
* A Simple AOP Container
*
*/
public class AopContainer {
public static <T> T getBean(Class<T> interfaceClazz, final T obj) {
assert interfaceClazz.isInterface();
return (T) Proxy.newProxyInstance(interfaceClazz.getClassLoader(),
new Class[] { interfaceClazz }, new AopInvocationHandlerImpl(obj));
}
public static Object getBean(final Object obj) {
return Proxy.newProxyInstance(obj.getClass().getClassLoader(),
obj.getClass().getInterfaces(), new AopInvocationHandlerImpl(obj));
}
}
第一個getBean方法通過2個參數(第一個參數為被代理的接口,第二個參數為被代理的類實例)
第二個getBean方法只有一個參數,就是類的實例。該類必須實現1個以上的接口。本文的例子并沒有使用到該方法,所以這里順便介紹一下它的使用方法。比如有一個類HelloWorld實現了接口IHelloWorld1和IHelloWorld2,那么可以通過
HelloWorld helloWorld = new HelloWorld();
IHelloWorld1 helloWorld1 = (IHelloWorld1)AopContainer.getBean(helloWorld);
//或
IHelloWorld2 helloWorld2 = (IHelloWorld2)AopContainer.getBean(helloWorld);
調用。當然很多時候都不會直接用new HelloWorld()生成HelloWorld實例,這里為了簡便,就直接用new生成HelloWorld實例了。
實現InvocationHandler接口的中間類:
AopInvocationHandlerImpl.java
package com.test.aop.framework;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
public class AopInvocationHandlerImpl implements InvocationHandler {
private Object bizPojo;
public AopInvocationHandlerImpl(Object bizPojo) {
this.bizPojo = bizPojo;
}
/*
* (non - Javadoc)
*
* @see java.lang.reflect.InvocationHandler#invoke(java.lang.Object,
* java.lang.reflect.Method, java.lang.Object[])
*/
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
Object o = null;
try {
System.out.println("Start:" + method.getName());
o = method.invoke(bizPojo, args);
System.out.println("End:" + method.getName());
} catch (Exception e) {
e.printStackTrace();
System.out.println("Exception Occured!" + e.getMessage());
// excetpion handling.
}
return o;
}
}
AopInvocationHandlerImpl.invoke方法的第一個參數為代理類,在我們這個例子里為java.lang.reflect.Proxy類的一個實例。第二個參數Method,為被代理的接口的方法調用(實際上是自動生成代理類的方法調用),第三個方法為方法調用的參數。
我們通過在AopInvocationHandlerImpl.invoke方法里的method.invoke(bizPojo, args)來調用bizPojo類的與被代理接口的同名方法。這里,bizPojo必須實現了被代理的接口。
在我們的例子里,我們在實際上被調用的業務邏輯方法的前后輸出了日志信息。
實際上的邏輯處理類。該類實現了被代理的接口:IDoBusiness。
DoBusiness.java
package com.test.aop.framework;
/**
* A business class
*
*/
public class DoBusiness implements IDoBusiness {
public void printNothing() {
System.out.println("Just Say Hello!");
}
public void throwException() {
throw new RuntimeException("throw Exception from DoBusiness.throwException()");
}
}
被代理的接口定義:
IDoBusiness.java
package com.test.aop.framework;
/**
* interface for business logic process
*
*/
public interface IDoBusiness {
public void printNothing();
public void throwException();
}
測試類:
TestAop.java
package com.test.aop.framework;
/**
* Test AOP
*
*/
public class TestAop {
/**
* @param args
*/
public static void main(String[] args) {
DoBusiness doBusiness = new DoBusiness();
IDoBusiness idoBusiness = AopContainer.getBean(IDoBusiness.class, doBusiness);
idoBusiness.printNothing();
idoBusiness.throwException();
}
}
總結:
本文通過Java Proxy實現了一個簡單地AOP容器。也簡單地展示了AOP的基本實現原理,實際上可以以此為基礎實現一個功能完善的AOP容器。