1. 程式人生 > >Cglib之Enhancer建立動態代理

Cglib之Enhancer建立動態代理

CGLIB是一個強大、高效能的位元組碼生成庫,它用於在執行時擴充套件Java類和實現介面;本質上它是通過動態的生成一個子類去覆蓋所要代理的類(非final修飾的類和方法)。Enhancer是一個非常重要的類,它允許為非介面型別建立一個JAVA代理,Enhancer動態的建立給定類的子類並且攔截代理類的所有的方法,和JDK動態代理不一樣的是不管是介面還是類它都能正常工作。

  • net.sf.cglib.proxy.Callback介面:在cglib包中是一個很關鍵的介面,所有被net.sf.cglib.proxy.Enhancer類呼叫的回撥(callback)介面都要繼承這個介面。
  • net.sf.cglib.proxy.MethodInterceptor介面:是通用的回撥(callback)型別,他經常被AOP用來實現攔截(intercept)方法的呼叫;

MethodInterceptor介面只定義了一個方法:

package net.sf.cglib.proxy;

import java.lang.reflect.Method;

public abstract interface MethodInterceptor
  extends Callback
{
  public abstract Object intercept(Object paramObject, Method paramMethod, Object[] paramArrayOfObject, MethodProxy paramMethodProxy)
    throws Throwable;
}

1.AOP模擬場景示例

建立一個HelloWorld類:

package com.test;

public class HelloWorld {

	public String say(boolean say) throws Exception {
		System.out.println("Hello Student");
		if(!say) {
			throw new Exception("回答錯誤!");
		}
		return "回答正確!";
	}
}

建立Enhancer增強代理類的回撥ProxyFactory類:

package controllers;

import java.lang.reflect.Method;

import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
/**
 * 
 * 所有方法的代理的回撥
 * 
 * @version 1.0
 * @since JDK1.7
 * @author yaomy
 * @company 上海朝陽永續資訊科技有限公司
 * @date 2018年9月18日 上午10:38:40
 */
public class ProxyFactory implements MethodInterceptor {

	//要代理的真實物件
	private Object obj;
	
	public Object createProxy(Object target) {
		this.obj = target;
		Enhancer enhancer = new Enhancer();
		//設定代理目標
		enhancer.setSuperclass(this.obj.getClass());
		//設定單一回調物件,在呼叫中攔截對目標方法的呼叫
		enhancer.setCallback(this);
		//設定類載入器
		enhancer.setClassLoader(this.obj.getClass().getClassLoader());
		
		return enhancer.create();
	}
	/**
	 * 
	 * 方法描述 當對基於代理的方法回撥時,在呼叫原方法之前會呼叫該方法
	 * 攔截對目標方法的呼叫
	 *
	 * @param obj 代理物件
	 * @param method 攔截的方法
	 * @param args 攔截的方法的引數
	 * @param proxy 代理
	 * @return
	 * @throws Throwable
	 * 
	 * @author yaomy
	 * @date 2018年9月18日 上午11:03:27
	 */
	@Override
	public Object intercept(Object obj, Method method, Object[] args,
			MethodProxy proxy) throws Throwable {
		Object result = null;
		try {
			//前置通知
			before();
			result = proxy.invokeSuper(obj, args);
			//後置通知
			after();
		} catch (Exception e) {
			//異常通知
			exception();
		} finally {
			//方法返回前通知
			beforeReturning();
		}
		
		return result;
	}

	private void before() {
		System.out.println("before method invoke...");
	}
	private void after() {
		System.out.println("after method invoke...");
	}
	private void exception() {
		System.out.println("exception method invoke...");
	}
	private void beforeReturning() {
		System.out.println("beforeReturning method invoke...");
	}
}

模擬場景類:

package controllers;

public class EnhancerTest {

	public static void main(String[] args) throws Exception {
		HelloWorld hello = new HelloWorld();
		ProxyFactory proxy = new ProxyFactory();
		HelloWorld world = (HelloWorld)proxy.createProxy(hello);
		String result = world.say(false);
		System.out.println(result);
	}
}

輸出結果是:

before method invoke...
Hello Student
after method invoke...
beforeReturning method invoke...
回答正確!

把true改為false的結果是:

before method invoke...
Hello Student
exception method invoke...
beforeReturning method invoke...
null
2.net.sf.cglib.proxy.MethodInterceptor能夠滿足任何的攔截需求,但是對於有些情況可能有點過度;為了簡化和提高效能,cglib包提供了一些專門的回撥(callback)型別。

net.sf.cglib.proxy.FixedValue:為提高效能,FixedValue回撥對強制某一特定方法返回固定值。

package controllers;

import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.FixedValue;

public class Test {

	public static void main(String[] args) {
		HelloWorld hello = new HelloWorld();
		Enhancer enhancer = new Enhancer();
		//設定代理目標
		enhancer.setSuperclass(hello.getClass());
		//設定單一回調物件,在呼叫中攔截對目標方法的呼叫
		enhancer.setCallback(new FixedValue() {
			
			@Override
			public Object loadObject() throws Exception {
				// TODO Auto-generated method stub
				return "FixedValue";
			}
		});
		//設定類載入器
		enhancer.setClassLoader(hello.getClass().getClassLoader());
		
		Object obj = enhancer.create();
		System.out.println(obj);
	}
}

net.sf.cglib.proxy.NoOp:NoOp回撥把對方法呼叫直接委派到這個方法在父類中的實現(也可以理解成真實物件直接呼叫方法);

	public static void main(String[] args) throws Exception {
		HelloWorld hello = new HelloWorld();
		Enhancer enhancer = new Enhancer();
		//設定代理目標
		enhancer.setSuperclass(hello.getClass());
		//設定單一回調物件,在呼叫中攔截對目標方法的呼叫
		enhancer.setCallback(NoOp.INSTANCE);
		//設定類載入器
		enhancer.setClassLoader(hello.getClass().getClassLoader());
		
		HelloWorld obj = (HelloWorld)enhancer.create();
		System.out.println(obj.say(true));
	}

net.sf.cglib.proxy.LazyLoader:當實際的物件需要延遲裝載時,可以使用LazyLoader回撥。一旦實際物件被裝載,它將被每一個呼叫代理物件的方法使用; net.sf.cglib.proxy.Dispatcher:Dispathcer回撥和LazyLoader回撥有相同的特點,不同的是,當代理方法被呼叫時,裝載物件的方法也總要被呼叫; net.sf.cglib.proxy.ProxyRefDispatcher:ProxyRefDispatcher回撥和Dispatcher一樣,不同的是,它可以把代理物件作為裝載物件方法的一個引數傳遞;

3.使用CallbackFilter設定回撥

net.sf.cglib.proxy.CallbackFilter允許我們在方法層設定回撥(callback),根據我們對方法處理的需求設定不同的回撥;如下有一個類HelloWorld,裡面有兩個方法save和update,save方法需要做前置和後置處理,但是update方法不需要:

package controllers;

public class HelloWorld {


	public String save() {
		System.out.println("save...");
		return "save";
	}
	public String update() {
		System.out.println("update...");
		return "update";
	}
}
package controllers;

import java.lang.reflect.Method;

import net.sf.cglib.proxy.CallbackFilter;
/**
 * 
 * 回撥過濾器
 * 
 * @version 1.0
 * @since JDK1.7
 * @date 2018年9月19日 上午11:02:12
 */
public class CallBackFilterTest implements CallbackFilter{
	/**
	 * 方法返回的值是和callback回撥介面陣列一一對應的陣列下標
	 */
	@Override
	public int accept(Method method) {
		String name = method.getName();
		if("save".equals(name)) {
			return 0;
		}
		return 1;
	}
	
}

accept方法中對代理方法和回撥進行了匹配,返回的值是某個方法在回撥陣列中的索引;下面是Test類的示例,在這個例子中save方法使用了ProxyFactory回撥,update方法使用了NoOp回撥;:

package controllers;

import net.sf.cglib.proxy.Callback;
import net.sf.cglib.proxy.CallbackFilter;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.FixedValue;
import net.sf.cglib.proxy.NoOp;

public class Test {

	public static void main(String[] args) throws Exception {
		HelloWorld hello = new HelloWorld();
		Enhancer enhancer = new Enhancer();
		//設定代理目標
		enhancer.setSuperclass(hello.getClass());
		//設定呼叫過濾器
		CallbackFilter filter = new CallBackFilterTest();
		enhancer.setCallbackFilter(filter);
		//建立各個目標代理方法的回撥,回撥的順序要與過濾器索引一致
		Callback callbacks[] = new Callback[] {new ProxyFactory(), NoOp.INSTANCE};
		//設定單一回調物件,在呼叫中攔截對目標方法的呼叫
//		enhancer.setCallback(NoOp.INSTANCE);
		enhancer.setCallbacks(callbacks);
		//設定類載入器
		enhancer.setClassLoader(hello.getClass().getClassLoader());
		
		HelloWorld obj = (HelloWorld)enhancer.create();
		
		System.out.println(obj.update());
		System.out.println("=============");
		System.out.println(obj.save());
	}
}

輸出結果如下:

update...
update
=============
before method invoke...
save...
after method invoke...
beforeReturning method invoke...
save