Java編程中使用動(dòng)態(tài)代理實(shí)現(xiàn)AOP功能
一、Java編程使用的背景
也不能算是使用的背景,最多只能算是一個(gè)在什么條件下面我想到了使用動(dòng)態(tài)代理實(shí)現(xiàn)AOP的攔截功能):因?yàn)樵陧?xiàng)目中程序的結(jié)構(gòu)是使用SOAP調(diào)用JNI,因此在SOAP服務(wù)端里面沒(méi)有任何實(shí)現(xiàn)代碼,僅僅是new一個(gè)JNI的對(duì)象,然后調(diào)用JNI對(duì)應(yīng)的方法。但是在調(diào)用JNI方法之前需要對(duì)傳進(jìn)JNI的JavaBean進(jìn)行初始化,而且還需要記錄日志。而SOAP服務(wù)端的代碼是通過(guò)ant自動(dòng)生成的,需要對(duì)他進(jìn)行手工的修改,在修改過(guò)程中發(fā)現(xiàn)每一個(gè)方法里面都是相同的:記錄進(jìn)入方法的日志、初始化JavaBean和記錄退出方法的日志,這寫(xiě)東西都是通過(guò)拷貝粘貼來(lái)完成的,想到如果以后再加一個(gè)什么功能的時(shí)候又得每一個(gè)方法進(jìn)行拷貝粘貼,而且方法的數(shù)量還不少,所以覺(jué)得這樣來(lái)實(shí)現(xiàn)是不科學(xué)的。示例代碼如下:
- public class SOAP{
- private JniInterface jni = null;
- private Log log = 。。。;
- public SOAP(){
- jni=new JniClass();
- }
- /**方法A**/
- public JavaBeanA aMethod(JavaBeanA javaBeanA){
- log.debug("進(jìn)入A方法");
- //初始化JavaBean
- Init(javaBeanA);
- //調(diào)用JNI對(duì)應(yīng)的方法
- JavaBeanA result = jni.aMethod(javaBeanA);
- log.debug("退出A方法");
- return result;
- }
- ……………………………………
- ……………………………………
- 等等,很多這樣的方法
- ……………………………………
- ……………………………………
- }
從示例代碼里面可以看出,除了調(diào)用JNI對(duì)應(yīng)的方法不同之外,其他的都是相同的代碼,把所有的東西進(jìn)行拷貝復(fù)制是不合理的。每當(dāng)對(duì)SOAP進(jìn)行修改,就必須將所有的方法重新拷貝粘貼。為了省去拷貝粘貼這一工序,所以使用動(dòng)態(tài)代理實(shí)現(xiàn)AOP攔截共能。
二、實(shí)現(xiàn)AOP攔截
1.定義Interceptor接口
- public interface Interceptor {
- //在調(diào)用之前調(diào)用該方法
- public void before(InvokeJniInfo invInfo);
- //在調(diào)用之后調(diào)用該方法
- public void after(InvokeJniInfo invInfo);
- //出現(xiàn)異常之后調(diào)用該方法
- public void exceptionThrow(InvokeJniInfo invInfo);
- }
2. 定義 InvokeJniInfo 類(lèi)
在Interceptor接口中的InvokeJniInfo類(lèi),該類(lèi)的定義如下:
- public class InvokeJniInfo {
- //被代理的對(duì)象
- Object proxy;
- //被調(diào)用的方法
- Method method;
- //被調(diào)用方法的參數(shù)列表
- Object[] args;
- //調(diào)用之后的結(jié)果
- Object result;
- //拋出的異常
- Throwable exception;
- public InvokeJniInfo(Object proxy,
- Method method,
- Object[] args,
- Object result,
- Throwable exception){
- this.proxy = proxy;
- this.method = method;
- this.args = args;
- this.result = result;
- this.exception = exception;
- }
- …………………………………………………………
- …………………………………………………………
所有成員的get/set方法
…………………………………………………………
…………………………………………………………
}
從該類(lèi)的成員變量可以知道,這個(gè)類(lèi)使用來(lái)將調(diào)用函數(shù)的基本信息如代理的對(duì)象,調(diào)用的方法,調(diào)用方法的參數(shù)等信息傳遞給Interceptor,使得在Interceptor 之中可以通過(guò)使用該對(duì)象作出相應(yīng)的攔截。
3.實(shí)現(xiàn)一個(gè)抽象的攔截器AbstractInterceptor
該攔截器實(shí)現(xiàn)了Interceptor接口,它里面的方法全都是空的,其目的是當(dāng)某些攔截器只是需要實(shí)現(xiàn)三個(gè)方法中的一個(gè)方法或者兩個(gè)方法的時(shí)候,就可以繼承該抽象類(lèi),覆蓋需要的實(shí)現(xiàn)的方法就可以了。
4.實(shí)現(xiàn)日志記錄攔截器LogInterceptor
該攔截器主要是實(shí)現(xiàn)在調(diào)用之前記錄日志,調(diào)用之后記錄日志和出現(xiàn)異常的時(shí)候記錄日志。其代碼如下:
public class LogInterceptor implements Interceptor {
private Log log = LogFactory.getLog(“初始化Log” );
public void before(InvokeJniInfo invInfo) {
//調(diào)用InvokeJniInfo對(duì)象的Method的getName方法獲取方法名
log.debug("Enter the" + invInfo.getMethod().getName());
}
public void after(InvokeJniInfo invInfo) {
//調(diào)用InvokeJniInfo對(duì)象的Method的getName方法獲取方法名
log.debug("Exit the" + invInfo.getMethod().getName());
}
public void exceptionThrow(InvokeJniInfo invInfo) {
//調(diào)用InvokeJniInfo對(duì)象的Method的getName方法獲取方法名
log.error("Call the" + invInfo.getMethod().getName() + " has error!");
//調(diào)用InvokeJniInfo對(duì)象的Exception的getStackTrace方法獲取具體異常并記錄
- log.error(invInfo.getException().getStackTrace());
- }
- }
5.實(shí)現(xiàn)初始化JavaBean攔截器InitParamsInterceptor
該類(lèi)繼承AbstractInterceptor,只需覆蓋before方法即可。其代碼如下:
- public class InitParamsInterceptor extends AbstractInterceptor {
- public void before(InvokeJniInfo invInfo) {
- if(invInfo.getArgs().length>0){
//初始化***個(gè)參數(shù)
- InitContainsObjectNullUtil.initContainsOutParameter(invInfo.getArgs()[0]);
- }
- }
- }
6.實(shí)現(xiàn)動(dòng)態(tài)代理處理器InterceptorHandler
該類(lèi)實(shí)現(xiàn)了java.lang.reflect.InvocationHandler接口。
public class InterceptorHandler implements InvocationHandler {
- private static Log log = LogFactory.getLog(InterceptorHandler.class);
//攔截器列表
private List interceptors = null;
//存放原始對(duì)象
private Object orginalObject;
//使用Proxy返回一個(gè)對(duì)象。注意這里傳進(jìn)去的對(duì)象的對(duì)象必須實(shí)現(xiàn)一個(gè)接口
- public Object bind(Object obj) {
- this.orginalObject = obj;
- return Proxy.newProxyInstance(obj.getClass().getClassLoader(), obj
- .getClass().getInterfaces(), this);
- }
- public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
- Object result = null;
- Throwable ex = null;
- InvokeJniInfo invInfo = new InvokeJniInfo(proxy,method,args,result,ex);
- log.debug("Invoking Before Intercepors!");
//實(shí)現(xiàn)方法調(diào)用之前進(jìn)行攔截的方法
- invokeInterceptorBefor(invInfo);
- try{
- log.debug("Invoking Proxy Method!");
- //調(diào)用方法
- result = method.invoke(orginalObject,args);
- invInfo.setResult(result);
- log.debug("Invoking After method!");
//實(shí)現(xiàn)方法調(diào)用之后進(jìn)行攔截的方法
- invokeInterceptorAfter(invInfo);
- }catch(Throwable tr){
- invInfo.setException(tr);
- log.error("Invoking exceptionThrow method!");
//實(shí)現(xiàn)出現(xiàn)異常進(jìn)行攔截的方法
- invokeInterceptorExceptionThrow(invInfo);
- }
- return result;
- }
//獲取攔截器列表
- private synchronized List getIntercetors(){
- if(null == interceptors){
- interceptors = new ArrayList();
//添加日志記錄攔截器
- interceptors.add(new LogInterceptor());
//添加初始化JavaBean攔截器
- interceptors.add(new InitParamsInterceptor());
//如果需要添加其他功能,可以很方便的添加其他的攔截器實(shí)現(xiàn)功能
- }
- return interceptors;
- }
- private void invokeInterceptorBefor(InvokeJniInfo invInfo){
- List interceptors = getIntercetors();
- int len = interceptors.size();
//遍歷所有攔截器,并調(diào)用攔截器的before方法
- for(int i = 0;i((Interceptor)interceptors.get(i)).before(invInfo);
- }
- }
- private void invokeInterceptorAfter(InvokeJniInfo invInfo){
- List interceptors = getIntercetors();
- int len = interceptors.size();
//遍歷所有攔截器,并調(diào)用攔截器的after方法
- for(int i = len - 1;i >= 0;i--){
- ((Interceptor)interceptors.get(i)).after(invInfo);
- }
- }
- private void invokeInterceptorExceptionThrow(InvokeJniInfo invInfo){
- List interceptors = getIntercetors();
- int len = interceptors.size();
//遍歷所有攔截器,并調(diào)用攔截器的exceptionThrow方法
- for(int i = len - 1;i >= 0;i--){
- ((Interceptor)interceptors.get(i)).exceptionThrow(invInfo);
- }
- }
- }
7.獲取動(dòng)態(tài)代理對(duì)象工廠InterceptorFactory
- public class InterceptorFactory {
- private static Log log = LogFactory.getLog(InterceptorFactory.class);
- public static Object getClassInstance(String clzName) {
- Class cls;
- Object obj = null;
- try {
- cls = Class.forName(clzName);
- obj = (Object) cls.newInstance();
- } catch (Exception e) {
- log.error(e.getStackTrace());
- }
- return obj;
- }
- public static Object getInterceptorProxyedObject(String clzName) {
- InterceptorHandler aopHandler = new InterceptorHandler();
- Object obj = getClassInstance(clzName);
- return aopHandler.bind(obj);
- }
- }
- 8.修改以前的代碼,使用動(dòng)態(tài)代理實(shí)現(xiàn)
- public class SOAP{
- private JniInterface jni = null;
- private Log log = 。。。;
- public SOAP(){
- jni=(JniInterface)InterceptorFactory.getInterceptorProxyedObject("JniClass");
- }
- /**方法A**/
- public JavaBeanA aMethod(JavaBeanA javaBeanA){
- return jni.aMethod(javaBeanA);
- }
……………………………………
……………………………………
等等,很多這樣的方法
……………………………………
……………………………………
}
從紅色代碼對(duì)比可以看出,省了很多代碼。
三、總結(jié)
1.必須徹底貫徹針對(duì)接口編成這一編程思想。
2.明白了這個(gè),是不是也明白了Spring的AOP的實(shí)現(xiàn)了?以及為什么要使用Spring的AOP的時(shí)候必須使用他的BeanFactory呢?
【編輯推薦】