Spring中AOP的代理模式

首先,自己實現AOP。

本Demo的目的是要實現調用每個目標方法時,啓用方法的性能監控,在目標類方法調用完成時記錄方法花費的時間。

1. 先定義接口文件

package com.hik.aopImpl.proxy;

public interface ForumService {
  
  public void removeTopic(int topicId);
  public void removeForum(int forum);
}

2. 實現類代碼:

package com.hik.aopImpl.proxy;

public class ForumServiceImpl implements ForumService{

    public void removeTopic(int topicId) {
        //1 開始對該方法進行性能監控
        PerformaceMontior.begin("com.hik.aopImpl.proxy.ForumServiceImpl.removeTopic");
        System.out.println("模擬刪除Topic記錄:" + topicId);
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        //2 結束對該方法進行性能監控
        PerformaceMontior.end();
    }

    public void removeForum(int forumId) {
        //1 開始對該方法進行性能監控
        PerformaceMontior.begin("com.hik.aopImpl.proxy.ForumServiceImpl.removeForum");
        System.out.println("模擬刪除Forum記錄:" + forumId);
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        //2 結束對該方法進行性能監控
        PerformaceMontior.end();
    }
}


3. 性能監視實現類文件:

package com.hik.aopImpl.proxy;

public class PerformaceMontior {
private static ThreadLocal<MethodPerformace> PerformaceMontior = new ThreadLocal<MethodPerformace>();//通過創建一個ThreadLocal保存調用線程相關的性能監視信息
    public static void begin(String method) {
        System.out.println("Begin Montior ....");
        MethodPerformace mp = new MethodPerformace(method);
        PerformaceMontior.set(mp);
    }

    public static void end() {
        System.out.println("end Montior ....");
        MethodPerformace mp = PerformaceMontior.get();
        mp.printPerformace();
    }

}
package com.hik.aopImpl.proxy;

public class MethodPerformace {
    private long begin;
    private long end;
    private String serviceMethod;
    
    public MethodPerformace(String serviceMethod) {
        this.begin = System.currentTimeMillis();
        this.serviceMethod = serviceMethod;
    }


    public void printPerformace() {
        this.end = System.currentTimeMillis();
        System.out.println(serviceMethod + "一共花費了" + (end - begin) + "毫秒。");
    }

}

4. 測試:

package com.hik.aopImpl.proxy;

import java.lang.reflect.Proxy;

public class TestForumService {
    public static void main(String[] args) {
        ForumService f = new ForumServiceImpl();
        f.removeForum(10);
        f.removeTopic(1012);
    }
}

運行結果:


以上則自己實現了AOP日誌記錄。但是這些非業務邏輯的興建空間代碼破壞了ForumServiceImpl業務代碼的純粹性。我們希望通過代理的方式,將業務類方法中開啓和結束性能監控的這些橫切代碼從業務類中完全移除,並通過JDK動態代碼技術或者CGLib動態代理技術將橫切代碼動態植入到目標方法相應位置中。

JDK動態代理

JDK的動態代理主要涉及到java.lang.reflect包中的兩個類,Proxy和InvocationHandler。其中InvocationHandler是一個接口,可以通過實現該定義的橫切邏輯,並通過反射機制調用目標類的代碼,動態將橫切邏輯和業務邏輯編織在一起。

首先,我們從業務類ForumServiceImpl中刪除性能監控的橫切代碼。使ForumServiceImpl 只負責具體的業務邏輯(刪除ForumServiceImpl中標號爲1、2的代碼)。

添加JDK動態代碼代碼:

package com.hik.aopImpl.proxy;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;

public class PerformanceHandler implements InvocationHandler {
    private Object target;
    
    public PerformanceHandler(Object target) {
        super();
        this.target = target;
    }

    public Object invoke(Object proxy, Method method, Object[] args)
            throws Throwable {
        PerformaceMontior.begin(target.getClass().getName() + "." + method.getName());
        Object obj = method.invoke(target, args);
        PerformaceMontior.end();
        return obj;
    }

}
相應的,更新測試代碼:
package com.hik.aopImpl.proxy;

import java.lang.reflect.Proxy;

public class TestForumService {
    public static void main(String[] args) {
        ForumService f = new ForumServiceImpl();
        
        PerformanceHandler ph = new PerformanceHandler(f);
        ForumService proxy = (ForumService) Proxy.newProxyInstance(f.getClass().getClassLoader(), f.getClass().getInterfaces(), ph);
 
        proxy.removeForum(10);
        proxy.removeTopic(1012);
    }
}
運行結果:

NOTE: JDK 動態代理技術只能使用於有接口,對於沒有接口的方法,則只能使用、CGLib代理。

CGLib動態代理

CGLib採用的是非常底層的字節碼技術,可以爲一個類創建子類。
package com.hik.aopImpl.proxy;

import java.lang.reflect.Method;

import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;

public class CglibProxy implements MethodInterceptor {
    
    private Enhancer enhancer = new Enhancer();
    
    public Object getProxy(Class clazz){
        enhancer.setSuperclass(clazz);
        enhancer.setCallback(this);
        
        return enhancer.create();
    }

    public Object intercept(Object obj, Method method, Object[] arg,
            MethodProxy proxy) throws Throwable {
        PerformaceMontior.begin(obj.getClass().getName() + "." + method.getName());
        Object result = proxy.invokeSuper(obj, arg);
        PerformaceMontior.end();
        return result;
    }

}

更新測試類:
package com.hik.aopImpl.proxy;

import java.lang.reflect.Proxy;

public class TestForumService {
    public static void main(String[] args) {
        ForumService f = new ForumServiceImpl();
        
        CglibProxy cg = new CglibProxy();
        ForumServiceImpl proxy = (ForumServiceImpl) cg.getProxy(ForumServiceImpl.class);
        
        proxy.removeForum(10);
        proxy.removeTopic(1012);
    }
}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章