jdk和CGLIB的动态代理

首先来看jdk实现的动态代理:

它只能对实现接口的类生成代理,而不能针对类。换句话说,如果目标对象实现了接口,默认情况下都会采用jdk的动态代理。

现在我们用最简单的代码小例子来看看怎么实现动态代理这个过程的:

package flex.cc.financialManagement.accountReceivable.web;

public interface TestService {
	public void say();//定义一个测试方法
}

他的实现类也就是要被代理的类:

package flex.cc.financialManagement.accountReceivable.web;

public class TestServiceImpl implements TestService{

	@Override
	public void say() {
		System.out.println("我是jdk动态代理的目标对象!");
	}

}

这之前都是我们平时常用的东西,没有什么新奇的,接下来就要使用jdk的动态代理增强TestServiceImpl这个类了,首先我们要知道,想要实现jdk的动态代理,我们就必须实现InvocationHandler这个接口,这个是必须的,然后重写他的invoke方法。

package flex.cc.financialManagement.accountReceivable.web;

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

public class TestJDKImpl implements InvocationHandler{

	private Object obj;
	//生成代理对象
	public Object bind(Object obj){
		this.obj=obj;
		return Proxy.newProxyInstance(obj.getClass().getClassLoader(),obj.getClass().getInterfaces(),this);
	}
	@Override
	public Object invoke(Object proxy, Method method, Object[] arg2)
			throws Throwable {
		// TODO Auto-generated method stub
		System.out.println("------方法之前的操作!------");
		Object invoke = method.invoke(obj, arg2);//这里要特别注意
		System.out.println("++++++方法之后的操作!++++++");
		return invoke;
	}
}
这里的bind方法就是生成代理对象的方法,这个过程基本就是写死的这种逻辑,没有什么技巧。其中说明一下newProxyInstance中参数的含义:第一个参数,含义是目标对象的加载器;第二个参数是:目标对象的接口类型;第三个参数是:这个参数很重要就是在生成代理对象后我们调用方法的时候哪一个对象上,在这里标示TestJDKImpl的对象。

在说明一下invoke第一个参数传递的生成的代理对象,而在method.invoke(obj, arg2);中传递的obj则是被代理的对象,不要弄混了。

最后我们来测试一下:

package flex.cc.financialManagement.accountReceivable.web;

public abstract class Test1 {

	public static void main(String[] args) {
		TestService b=new TestServiceImpl();
		TestJDKImpl a=new TestJDKImpl();
		TestService bind = (TestService) a.bind(b);//代理对象
		bind.say();
	}
}

输出结果如下:

------方法之前的操作!------
我是jdk动态代理的目标对象!
++++++方法之后的操作!++++++

在原有的方法前后都增强了;这个过程就是jdk动态代理的简单实现。这里还要着重说明一点就是为什么在测试中调用代理对象的say()方法就会执行TestJDKImpl中的invoke方法,这里一开始我也不明白为什么但有大神给出了解释:

http://rejoy.iteye.com/blog/1627405
有兴趣的可以自己查看一下;


接下来来实现一下CGLIB动态代理:

与JDk动态代理不同之处在于,CGLIB动态代理是针对类实现的代理;换句话说没有实现接口的类的动态代理必须使用CGLIB才能实现动态代理;创建一个目标类:

package flex.cc.financialManagement.accountReceivable.web;

public class TestCglibimpl {

	public void say(){
		System.out.println("我是CGLIB动态代理!");
	}
}

要实现CGLIB动态代理需要实现MethodInterceptor接口,动态生成被代理的子类,所以不能给final修饰的类实现代理,

package flex.cc.financialManagement.accountReceivable.web;

import java.lang.reflect.Method;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;

public class CreatCglibModel implements MethodInterceptor{

	private Object o;
	public Object getObject(Object obj){
		o=obj;
		Enhancer enhancer=new Enhancer();
		enhancer.setSuperclass(obj.getClass());
		enhancer.setCallback(this);
		return enhancer.create();
	}
	
	@Override
	public Object intercept(Object arg0, Method method, Object[] arg2,
			MethodProxy arg3) throws Throwable {
		System.out.println("我是修改前的数据操作------");
		Object invoke = arg3.invokeSuper(arg0, arg2);//和下面两个方法都可以
		//Object invoke = arg3.invoke(o, arg2);
		//Object invoke = method.invoke(o, arg2);
		System.out.println("我是调用后的数据操作======");
		return invoke;
	}
}
这里的Enhancer是CGLIB的一个字节码增强器,它可以方便的对你想要处理的类进行扩展;

这里intercept方法下面的注释掉的两个调用都是可以实现的 但是参数是由区别的,这跟JDK的不同;不过他们三个之间的调用区别大家可以去自己查看一下。

package flex.cc.financialManagement.accountReceivable.web;

public class TestCglib {

	public static void main(String[] args) {
		TestCglibimpl a=new TestCglibimpl();
		CreatCglibModel b=new CreatCglibModel();
		a = (TestCglibimpl) b.getObject(a);
		a.say();
	}
}

测试结果:

我是修改前的数据操作------
我是CGLIB动态代理!
我是调用后的数据操作======


猜你喜欢

转载自blog.csdn.net/hbl6016/article/details/80075159