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
 */
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
	 */
	@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
 */
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

你可能感兴趣的:(【Java】)