Java代理梳理

参考&详细资料:
java 代理 理解原理及实现
JDK的Proxy技术实现AOP,InvocationHandler和Proxy详解——Spring AOP(三)
JAVA JDK的动态代理反射实现
Spring AOP中的JDK和CGLib动态代理哪个效率更高?

一、静态代理

由程序员创建或特定工具自动生成源代码,也就是在编译时就已经将接口,被代理类,代理类等确定下来。在程序运行之前,代理类的.class文件就已经生成。
代理模式最主要的就是有一个公共接口,一个具体的类,一个代理类,代理类持有具体类的实例,代为执行具体类实例方法。上面说到,代理模式就是在访问实际对象时引入一定程度的间接性,因为这种间接性,可以附加多种用途。这里的间接性就是指不直接调用实际对象的方法,那么我们在代理过程中就可以加上一些其他用途。

二、动态代理

代理类在程序运行时创建的代理方式被称为动态代理。

1、JDK代理

要实现动态代理首先要求代理对象需要实现接口,即需要有一个接口类一个实现类。其次,需要一个类实现InvocationHandler接口用来进行代理事务的处理。该类需要通过构造方法获得目标类对象的引用,同时实现invoke方法。之后利用Proxy的工厂方法newProxyInstance()创建一个代理对象,这个对象同样可以实现对具体类的代理功能。

实现原理:

JDK 动态代理主要涉及到 java.lang.reflect 包中的两个类:ProxyInvocationHandler。InvocationHandler 是一个接口,通过实现该接口定义横切逻辑,并通过反射机制调用目标类的代码,动态将横切逻辑和业务逻辑编制在一起。 Proxy 利用 InvocationHandler 动态创建一个符合某一接口的实例,生成目标类的代理对象。

实现过程:
  1. 通过实现InvocationHandler接口创建自己的调用处理器
  2. 通过为Proxy类指定ClassLoader对象和一组interface来创建动态代理
  3. 通过反射机制获取动态代理类的构造函数,其唯一参数类型就是调用处理器接口类型;
  4. 通过构造函数创建动态代理类实例,构造时调用处理器对象作为参数传入;
程序示例:
//接口类 计算接口
interface ICal {
	public int Cal(int a, int b);
}
//加法实现
class ClassAdd implements ICal {
	@Override 
	public int Cal(int a, int b) {
	System.out.println("ClassAdd!");
	return a+b;
	}
}
//减法实现
class ClassSub implements ICal {
	@Override
	public int Cal(int a, int b) {
		System.out.println("ClassSub!");
		return a-b;
	}
}


//动态代理类,实现InvocationHandler接口
class MyInvocationHandler implements InvocationHandler {
	private Object target;
	public DynamicProxy(Object ac) {
		this.target = ac;
	}
	//动态生成代理对象
	public Object getDynamicProxyObject() {
		return Proxy.newProxyInstance(this.target.getClass().getClassLoader(),this.target.getClass().getInterfaces(),this);
	}
	@Override
	public Object invoke(Object proxy,Method method,Object[] arg) throws Throwable {
		//调用之前可以做一些处理
		System.out.println("Method before!");
		Object result = method.invoke(target,arg);
		//调用之后也可以做一些处理
		System.out.println("Method after!");
		return result;
	}
}

参数说明:
newProxyInstance()方法的三个参数
第一个参数loader:一个ClassLoader对象,定义了由哪个ClassLoader对象来对生成的代理对象进行加载
第二个参数interfaces:一个Interface对象的数组,表示的是我将要给我需要代理的对象提供一组什么接口,如果我提供了一组接口给它,那么这个代理对象就宣称实现了该接口(多态),这样我就能调用这组接口中的方法了;
第三个参数:这个动态代理在调用方法时所关联的InvocationHandler对象。

invoke()方法参数
proxy:所代理的真实对象;
method:所要调用真实对象的某个方法的Method对象;
args:调用真实对象某个方法时接收的参数。
可以看出在动态生成代理对象时用到了反射机制。

测试代码:

class DynamicProxyTest {
	//创建加法类的代理对象
	ICal calProxyObject = (ICal) new MyInvocationHandler(new ClassAdd()).getDynamicProxyObject();
	//调用加法类的计算方法
	int add = calProxyObject.Cal(5,3);
	System.out.println(add);
}
补充:

实际上,代理对象的创建需要类加载器,目标接口类以及InvocationHandler对象,整个过程可以用一行代码完成

ICal calProxyObject = (ICal)Proxy.newInstance(ClassAdd.class.getClassLoader(),Class<?>[] {ICal.class},MyInvocationHandler(new ClassAdd));

Proxy.newInstance()方法之所以需要以上三个参数是和代理对象的生成流程有关。
newInstance()方法会调用Proxy的静态方法getProxyClass(),这个方法需要类加载器目标接口类,用来生成代理对象的class文件,并将其加载到虚拟机,这个过程中还给这个代理类起了个类名和包名。实际上完成这个过程后已经完成了代理类的生成,每个代理类都继承自Proxy,Proxy中包含有InvocationHandler接口的引用,接下来只需要通过代理类的构造方法将InvocationHandler对象传进去即可完成代理对象的生成

注意!!:
要利用Proxy产生某个对象的代理对象,这个对象必须实现一个接口,Proxy动态代理技术只能基于接口进行代理。

2、CGLIB代理

CGLIB相比于JDK原生动态代理,不要求代理对象必须实现某个接口。CGLIB采用继承的方式完成代理,即可理解为代理类是原类的子类
CGLIB代理主要分为两步,首先定义一个类实现MethodInterceptor接口,并实现intercept()方法。然后再使用待代理对象时通过CGLIB获取代理对象。包括如下代码:

Enhancer enhancer = new Enhancer(); 
enhancer.setSuperclass(HelloConcrete.class);  //设置enhancer对象的父类
enhancer.setCallback(new MyMethdInterceptor()); //设置enhancer的回调对象
HelloConcrete proxy = (HelloConcrete) enhancer.create(); //创建代理对象
proxy.sayHello();  //通过代理对象调用目标方法

代理对象的所有非final方法的调用都会转发给MethodInterceptor.intercept()方法

public Object intercept (Object obj, java.lang.reflect.Method method,Object[] args,MethodProxy proxy) throws Throwable;

Intercept方法的4个参数
1)obj表示增强的对象,即实现这个接口类的一个对象;(即生成的代理对象)
2)method表示要被拦截的方法
3)args表示要被拦截方法的参数
4)proxy表示要触发父类的方法对象(即代理方法)。

使用:

利用CGLIB中的Enhancer字节码生成器生成代理对象,它是被代理类的子类。代理对象的所有非final方法的调用都会回调给intercept()方法进行拦截,该方法由自定义MethodInterceptor接口实现类实现,并加入自己的代理逻辑。

原理:

cglib封装了ASM,可以在运行期动态生成一个要代理类的子类,子类重写要代理的类的所有不是final的方法。在子类中采用方法拦截的技术拦截所有父类方法的调用,顺势织入横切逻辑。它比使用java反射的JDK动态代理要快。

底层:

使用字节码处理框架ASM来转换字节码并生成新的类。

缺点:

对于final方法,无法进行代理。

详细可参考:
CGLIB动态代理实现原理

三、总结与对比

1、三种方式对比:

在这里插入图片描述

2、JDK与CGLIB性能对比

Spring AOP中的JDK和CGLib动态代理哪个效率更高?

3、3. Spring AOP中的动态代理:

Spring里面有一个AOP编程。即面向切面编程,其实就是动态代理。当我们交给Spring一个对象,它就会返回代理给我们,它在返回代理对象的时候,首先会检查我们这个对象有没有实现一个接口,如果我们这个类有接口,它使用Java的动态代理技术来帮我们构建出代理对象;如果我们这个类没有实现接口,它会使用CGLIB这套API,采用创建子类的方式来创建代理对象。
所以我们所实现AOP动态代理的两种方式就是JDK的Proxy和CGLIB。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值