1. 程式人生 > >AOP初步嘗試——cglib代理和jdk代理整合

AOP初步嘗試——cglib代理和jdk代理整合

一、代理機制

首先簡單描述一下代理機制。核心程式碼完成後,我們想給程式碼增加一些附加功能,比如日誌,攔截等,這時不應該去修改核心程式碼,我們可以通過獲取代理,在核心程式碼以外增加程式碼,實現相關功能。同樣在我們沒有原始碼或無法觸碰原始碼時,也是如此。核心功能與附加功能分開,互不干擾,稱之為解耦,使開發過程更加方便。

代理分為靜態代理和動態代理,動態代理又有cglib代理和jdk代理之分。

1.靜態代理(針對介面):

首先,先定義一個介面,然後實現它,這就是我們的核心程式碼。
在這裡插入圖片描述
獲取其靜態代理,並增加附加功能,如:

public class StaticProxy implements IInterface {
	private IInterface realObj = new InterfaceAdpter();
	
	public StaticProxy() {
	}
	
	@Override
	public void show(String str) {
	//代理前後可以增加功能
		System.out.println("代理前~~~~~~");
		realObj.show(str);
		System.out.println("代理後~~~~~~");
	}
}

結果:
在這裡插入圖片描述
靜態代理很簡單,不再描述,只是用它來理解代理機制,其他代理和它類似。

2.生成cglib代理

//生成一個cglib代理
	@SuppressWarnings("unchecked")
	public <T> T getProxy(T obj) {
		Class<?> klass = obj.getClass();
		Enhancer enhancer = new Enhancer();
		enhancer.setSuperclass(klass);
		enhancer.setCallback(new MethodInterceptor() {
			
			@Override
			public Object intercept(Object object, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
				Object result = null;
				
				System.out.println("置前攔截...");
				try {
					result = method.invoke(obj, args);
					System.out.println("置後攔截...");
				} catch (Exception e) {
					System.out.println("異常時攔截...");
				}
				System.out.println("執行結果:" + result);
				
				return result;
			}
		});

		return (T) enhancer.create();
	}

我們用來做實驗的類:

public class NormalClass {
	public NormalClass() {
	}

	public final void fun() {
		System.out.println("這是一個final方法!");
	}
	
	public void normalAction() {
		System.out.println("執行了normalAction方法");
	}
}

執行結果:
在這裡插入圖片描述
由執行結果可得:發現無法攔截final方法。

3.生成jdk代理(針對介面)

一定要注意jdk是針對介面的,不要弄錯了!!!

@SuppressWarnings("unchecked")
	public <T> T jdkProxy(Object object) {
		Class<?> klass = object.getClass();
		ClassLoader classLoader = klass.getClassLoader();
		Class<?>[] interfaces = klass.getInterfaces();
		
		InvocationHandler invocationHandler = new InvocationHandler() {
			
			@Override
			public Object invoke(Object obj, Method method, Object[] args) throws Throwable {
				System.out.println("置前攔截...");
				Object result = method.invoke(object, args);
				System.out.println("置後攔截...");
				return result;
			}
		};
		
		return (T) Proxy.newProxyInstance(
					classLoader, interfaces, invocationHandler);
	}

執行結果:
在這裡插入圖片描述

二、AOP

然後我們開始整合,首先要做一個類,同時能生成cglib代理和jdk代理,使用者可以給出Class<?>類或Object物件,獲取代理(我們無法判斷使用者給的是哪一個,所以兩種方法應該實現)。然後使用者應該可以直接使用代理物件執行方法得到結果,無需再管背後的過程。
接著我們可以增加附加功能(此處定為攔截),使用者可以提前新增攔截器,在執行時攔截,可以在各個時候攔截,可以多重攔截,使用者也可能不想寫攔截,那我們就應該寫一個簡單的預設攔截器,讓其使用。

1.攔截

攔截可以分為前置攔截,後置攔截,以及異常時攔截。需要確定的資訊有:要攔截哪個類中的哪個方法(Class<?>和method),這是無論哪種攔截都需要的。
前置攔截:在方法執行前就進行攔截,如驗證身份資訊等,用於判斷該方法到底應不應該執行。我們需要一些引數(args,不一定是幾個),當身份驗證時,要根據傳來的身份資訊引數來確定。然後我們需要返回攔截是否成功的資訊,攔截成功,方法繼續執行,攔截失敗,則不再執行。
後置攔截:在方法執行完後進行攔截,可以修改執行結果。所以需要一個形參,即執行的結果,而返回的是修改後的結果。
異常攔截:出現異常時,對異常進行處理,無需返回值。

/**此為intercepter 攔截器 
 * 其中包括攔截的klass類  method方法兩個成員
 * 還有三個抽象方法:
 * 前置攔截器  後置攔截器 處理異常攔截器
 * 負責各個時候要做的攔截工作
 * */
public abstract class Intercepter {
	private Class<?> klass;
	private Method method;
	
	public Intercepter() {
	}
	
	public Intercepter(Class<?> klass, Method method) {
		this.klass = klass;
		this.method = method;
	}
	
	public Class<?> getKlass() {
		return klass;
	}
	
	public void setKalss(Class<?> klass) {
		this.klass = klass;
	}
	
	public Method getMethod() {
		return method;
	}
	
	public void setMethod(Method method) {
		this.method = method;
	}
	
	/**前置攔截器 執行之前就進行攔截
	 * @param <args> 引數
	 * @return 攔截是否成功
	 * */
	public abstract boolean beforeIntercept(Object[] args);
	
	/**後置攔截器 執行後對結果進行攔截
	 * @param <result> 執行好的結果  將結果給它進行對結果的攔截
	 * @return 將攔截處理後的結果返回
	 * */
	public abstract Object afterIntercept(Object result);
	
	/**處理異常攔截器 對出現的異常進行處理
	 * @param <Throwable> 異常  將異常給它 進行對異常的處理 攔截
	 * @return <void> 無返回值
	 * */
	public abstract void exceptionIntercept(Throwable e);

這是一個抽象方法,我們需要再做一個Adapter介面卡,以方便以後使用此時要注意:前置攔截器的返回值應置為true,保證程式的正常執行。這部分攔截器作為預設攔截器(這個攔截應當簡單,適於各種情況,且不影響程式執行),使用者以後若自己寫攔截器則可以選擇性覆蓋預設攔截器,若不寫也可以直接使用預設攔截器。

/**intercepter介面卡, 繼承了intercepter類
 * 方便以後對intercepter的處理
 * */
public class InterceptAdapter extends Intercepter {

	public InterceptAdapter() {
	}
	
	public InterceptAdapter(Class<?> klass, Method method) {
		super(klass, method);
	}

	@Override
	public boolean beforeIntercept(Object[] args) {
		return true;
	}

	@Override
	public Object afterIntercept(Object result) {
		return result;
	}

	@Override
	public void exceptionIntercept(Throwable e) {
	}
}

2.proxyClass類

每一個代理都有自己的攔截器,攔截器不止一個,所以可以做一個攔截器鏈,如此可形成一個類,該類只是一個model,只有get,set方法即可。

/**此類中 只有Object代理 以及 對此代理的攔截器列表
 * 以後處理的proxy均為T型別  這樣最後不用強轉 (object也可以  最後需要強轉)
 * */
public class ProxyClass {
	//代理和攔截鏈
	private Object proxy;
	private List<Intercepter> interceptList;
	
	public ProxyClass() {
		interceptList = new ArrayList<>();
	}
	
	@SuppressWarnings("unchecked")
	public <T> T getProxy() {
		return (T)proxy;
	}
	
	public <T> ProxyClass setProxy(T proxy) {
		this.proxy = proxy;
		
		return this;
	}
	
	public List<Intercepter> getInterceptList() {
		return interceptList;
	}
	
	/**向列表中新增攔截器
	 * */
	 * /這的IntercepterException是自定義的
	public void addIntercepter(Intercepter intercepter)  
											throws IntercepterException{
		if(interceptList.contains(intercepter)) {
			throw new IntercepterException("攔截器" 
					+ intercepter.getClass().getName()
					+ "已存在");
		}
		interceptList.add(intercepter);
	}
	
	/**從列表中移除攔截器
	 * */
	public void removeIntercepter(Intercepter intercepter) 
			throws IntercepterException{
		if(!interceptList.contains(intercepter)) {
			throw new IntercepterException("攔截器" 
					+ intercepter.getClass().getName()
					+ "不存在");
		}
		interceptList.remove(intercepter);
	}

3.代理整合

我們最重要的步驟就是做一個類,產生代理,把前面生成代理的方法放在一起,進行修飾,處理即可。不同之處在於,我們現在增加了攔截器鏈,所以在執行前後都要遍歷攔截器鏈,使用攔截器進行攔截。

/**這是一個工廠,得到各種的proxy,並將其存入ProxyClass類待用
 * 只有一個成員  ProxyClass類
 * @see ProxyClass
 * */
public class ProxyFactory {
	private ProxyClass proxyClass;
	
	public ProxyFactory() {
	}

	public ProxyClass getProxy() {
		return proxyClass;
	}
	
	/**通過呼叫內部方法 得到cglib代理 並將代理存入ProxyClass的proxy中
	 * @param <klass>  需要的類
	 * @param <object> 物件
	 * @return 得到的cglib代理
	 * */
	public <T> T getCglibProxy(Class<?> klass, Object object){
		proxyClass = new ProxyClass();
		proxyClass.setProxy(cglibProxy(klass, object));
		return(proxyClass.getProxy());
	}
	
	/**內部方法 使用enhancer得到cglib代理
	 * @param <klass>  需要的類
	 * @param <object> 物件
	 * @return 得到的cglib代理
	 * */
	@SuppressWarnings("unchecked")
	private <T> T cglibProxy(Class<?> klass, Object object) {
		Enhancer enhancer = new Enhancer();
		enhancer.setSuperclass(klass);
		enhancer.setCallback(new MethodInterceptor() {
			
			@Override
			public Object intercept(Object obj, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
				return doInvoke(object, method, args);
			}
		});
		
		 return (T)enhancer.create();
	}
	
	/**通過呼叫內部方法 得到jdk代理 並將代理存入ProxyClass的proxy中
	 * @param <klass>  需要的類
	 * @param <object> 物件
	 * @return 得到的jdk代理
	 * */
	public <T> T getJdkProxy(Class<?> klass, Object object){
		proxyClass = new ProxyClass();
		proxyClass.setProxy(jdkProxy(klass, object));
		return(proxyClass.getProxy());
	}
	
	/**內部方法 使用classLoader  InvocationHandler 得到jdk代理
	 * @param <klass>  需要的類
	 * @param <object> 物件
	 * @return 得到的jdk代理
	 * */
	@SuppressWarnings("unchecked")
	private <T> T jdkProxy(Class<?> klass, Object object) {
		ClassLoader classLoader = klass.getClassLoader();
		Class<?>[] interfaces = klass.getInterfaces();
		
		InvocationHandler invocationHandler = new InvocationHandler() {
			
			@Override
			public Object invoke(Object obj, Method method, Object[] args) throws Throwable {
				return doInvoke(object, method, args);
			}
		};
		
		return (T) Proxy.newProxyInstance(
					classLoader, interfaces, invocationHandler);
	}
	
	/**
	 * doInvoke之前要做的:
	 * 將方法和引數給它  遍歷攔截器列表,如果某個攔截器作用的方法等於提供的方法 則該方法需要進行攔截 否則繼續遍歷
	 * 並將攔截的結果返回
	 */
	private boolean doBeforeIntercept(Method method, Object[] args) {
		for(Intercepter i : proxyClass.getInterceptList()) {
			if(!i.getMethod().equals(method)) {
				continue;
			}
			if(i.beforeIntercept(args)== false) {
				return false;
			}
			//有多個攔截器,所以如果為true 不能返回,應該繼續迴圈
		}
		return true;
	}
	
	/**
	 * doInvoke之後要做的:
	 *  遍歷攔截器列表,如果某個攔截器作用的方法等於提供的方法 則進行攔截 否則繼續遍歷
	 * @param <method> 要攔截的方法
	 * @param <result> 執行結果
	 * @return 將攔截的結果返回
	 */
	private Object doAfterIntercept(Method method, Object result) {
		for(Intercepter i : proxyClass.getInterceptList()) {
			if(i.getMethod().equals(method)) {
				result = i.afterIntercept(result);
			}
		}
		return result;
	}
	
	/**
	 * 出現異常要做的:
	 *  遍歷攔截器列表,如果某個攔截器作用的方法等於提供的方法 則進行攔截 否則繼續遍歷
	 * @param <method> 要攔截的方法
	 * @param <e> 出現的異常
	 */
	private void doExceptionIntercept(Method method, Throwable e) {
		for(Intercepter i : proxyClass.getInterceptList()) {
			if(i.getMethod().equals(method)) {
				i.exceptionIntercept(e);
			}
		}
	}
	
	/**
	 * 內部方法 做反射 在反射前後加攔截
	 * @param  物件
	 * @param  方法
	 * @param  引數
	 */
	private Object doInvoke(Object object, Method method, Object[] args) {
		Object result = null;
		
		if(doBeforeIntercept(method, args) == false) {//前置攔截
			return null;
		}
		try {
			result = method.invoke(object, args);
			doAfterIntercept(method, result);//後置攔截
		} catch (Exception e) {
			doExceptionIntercept(method, e);//異常攔截
		}
		
		return result;
	}

4.BeanFactory類

將處理好的代理按存在map中key = className,value = ProxyClass,這樣使用者使用時,直接可以根據類來得到相應的代理以及攔截器,比較方便。這一個類是面向使用者的,之前的都是底層的,為這個類做鋪墊。因為面向使用者,所以不確定使用者給的是什麼,那就要全面考慮。
以cglib代理為例,使用者無論給class還是object,都要能得到代理,使用者還需要新增移除攔截器。

/**裡面有代理的map<類名稱 string, 代理類 proxyClass>
 * */
public class BeanFactory {
	private final static Map<String, ProxyClass> proxyMap;//這個map只能有一套
	
	static {
		proxyMap = new HashMap<>();
	}
	
	public BeanFactory() {
	}
	
	/**得到cglib代理 
	 * */
	public <T> T getCglibProxy(Class<?> klass) throws Exception  {
		return getCglibProxy(klass, klass.newInstance());
	}
	
	/**得到cglib代理 
	 * */
	public <T> T getCglibProxy(Object object) throws Exception  {
		return getCglibProxy(object.getClass(), object);
	}
	
	/**private 得到cglib代理 
	 * 判斷 map中是否有該類的proxy 若有 則取出 若沒有 則使用proxyFactory的getCglibProxy方法
	 * 取得代理 存入map 並返回
	 * */
	@SuppressWarnings("unchecked")
	private <T> T getCglibProxy(Class<?> klass, Object object) throws Exception {
		ProxyClass proxyClass = proxyMap.get(klass.getName());
		if(proxyClass != null) {
			return  proxyClass.getProxy();
		}
		
		ProxyFactory factory= new ProxyFactory();
		Object proxy = factory.getCglibProxy(klass, object);
		proxyMap.put(klass.getName(), factory.getProxy());
		return (T) proxy;
	}
	
	/**向某個類中新增某一攔截器
	 * 如果攔截器作用的klass與提供的klass不符  則直接返回 
	 * 通過klass得到map中的proxyClass 並使用其addIntercepter新增攔截器
	 * @param <klass> 類
	 * @param <intercepter> 需要新增的攔截器
	 * */
	public void addIntercepter(Class<?> klass, Intercepter intercepter) throws IntercepterException {
		if(!intercepter.getKlass().equals(klass)) {
			return;
		}
		proxyMap.get(klass.getName()).addIntercepter(intercepter);
	}
	
	/**移除某個類的某一攔截器
	 * 通過klass得到map中的proxyClass 並使用其中的removeIntercepter方法移除某個攔截器
	 * @param <klass> 類
	 * @param <intercepter> 需要移除的攔截器
	 * */
	public void removeIntercepter(Class<?> klass, Intercepter intercepter) throws IntercepterException {
		proxyMap.get(klass.getName()).removeIntercepter(intercepter);
	}
	
	/**得到jdk代理 
	 * */
	@SuppressWarnings("unchecked")
	public <T> T getJdkProxy(Class<?> klass) throws Exception  {
		return (T) getJdkProxy(klass, klass.newInstance());
	}
	
	/**得到jdk代理 
	 * */
	@SuppressWarnings("unchecked")
	public <T> T getJdkProxy(Object object) throws Exception  {
		return (T) getJdkProxy(object.getClass(), object);
	}
	
	/**private 得到jdk代理 
	 * 判斷 map中是否有該類的proxy 若有 則取出 若沒有 則使用proxyFactory的getJdkProxy方法
	 * 取得代理 存入map 並返回
	 * */
	@SuppressWarnings("unchecked")
	private <T> T getJdkProxy(Class<?> klass, Object object) throws Exception {
		ProxyClass proxyClass = proxyMap.get(klass.getName());
		
		if(proxyClass != null) {
			return  proxyClass.getProxy();
		}
		
		ProxyFactory factory= new ProxyFactory();
		Object proxy = factory.getJdkProxy(klass, object);
		
		proxyMap.put(klass.getName(), factory.getProxy());
		return (T) proxy;
	}

到此,整個過程就以及全部處理結束,下面來做嘗試。
實驗的類是:

public class NormalClass {
	private int num;
	
	public NormalClass() {
	}

	public void setNum(int num) {
		this.num = num;
	}
	
	public int getNum() {
		return num;
	}
	
	public final void fun() {
		System.out.println("這是一個final方法!");
	}
	
	public String normalAction(String str) {
		System.out.println(str);
		return str;
	}

攔截器為:

public class NormalClassIntercepter extends InterceptAdapter {

	public NormalClassIntercepter(Class<?> klass, Method method) {
		super(klass, method);
	}

	public NormalClassIntercepter() {
	}

	@Override
	public boolean beforeIntercept(Object[] args) {
		for (Object arg : args) {
			if(arg.equals("123")) {
				System.out.println("置前攔截:拒絕執行");
				return false;
			}
			System.out.println("置前攔截:" + args);
		}
		return true;
	}

	@Override
	public Object afterIntercept(Object result) {
		System.out.println("處理後的結果:" + result + "*****");
		return result;
	}
}

主程式:

public static void main(String[] args) throws Exception {
		Class<?> klass = NormalClass.class;
		Method method = klass.getDeclaredMethod("normalAction",
					new Class<?>[] {
						String.class} );
		NormalClassIntercepter intercepter1 
			= new NormalClassIntercepter(NormalClass.class, method);
		NormalClassIntercepter intercepter2 
			= new NormalClassIntercepter(NormalClass.class, method);
		
		BeanFactory proxyMap = new BeanFactory();
		try {
			NormalClass normal =  proxyMap.getCglibProxy(
					NormalClass.class);
			proxyMap.addIntercepter(klass, intercepter1);
			proxyMap.addIntercepter(klass, intercepter2);
			normal.normalAction("123");
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

執行結果:
在這裡插入圖片描述
將123改為其他字串,執行結果為:
在這裡插入圖片描述
關係圖:
在這裡插入圖片描述
注:此時攔截器的新增十分不便,以後再改進。
有一個異常沒有給出,大家自行新增即可。

指導:mec
如有錯誤,請指正,謝謝。