您的位置:首页 > 编程语言 > Java开发

JAVA代理-静态代理和动态代理详解

2017-03-13 15:58 477 查看

一 前言

代理在我们Java和Android学习中经常用到,例如Android插件化用到了大量的代理。代理包含静态代理和动态代理,我先来了解一下静态代理。

二 静态代理

2.1 什么是静态代理

代理和被代理对象在代理之前就是确定的,他们都实现相同的接口或者继承相同的抽象类。

2.2 实现静态代理方式

(1)继承法:代理类直接继承被代理类,实现其原有方法,并添加一些额外功能。

(2)聚合方法:代理类实现相同的功能接口(实现相同接口,不同代理也可以进行相互代理),并在内声明一个被代理类的对象(类似封装),通过内部对象实现其原有方法,并添加额外功能。简单说

聚合:一个类中使用另一个类的对象。

我们举个例子是汽车行驶 ,记录汽车行驶的时间,分别用继承法和聚合方法实现。

1.继承法实现代码

Car被代理类,InheritProxyCar是代理类,InheritProxyCar类继承Car类

//汽车行驶接口
public interface Movable {
void move();
}
//Car被代理类 Car类实现 Movable接口
public class Car implements Movable {
@Override
public void move() {
try {
//随机睡眠充当行驶
Thread.sleep(new Random().nextInt(1000));
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
/**
* 继承代理
* InheritProxyCar是代理类
* InheritProxyCar类继承Car类
*/
public class InheritProxyCar extends Car {
private final static String TAG = "ProxyCar";
public void move(){
long startTime = System.currentTimeMillis();
Log.e(TAG, "move: 开始行使");
super.move();//调用父类的move方法
long endTime = System.currentTimeMillis();
Log.e(TAG, "move: 汽车行驶结束的时间 "+(endTime-startTime)+"毫秒" );
}
}

//继承方法实现代理 InheritProxyCar是代理类 Car被代理类
InheritProxyCar inheritProxyCar =new InheritProxyCar();
//代理类执行 move方法
inheritProxyCar.move();


控制台日志

03-16 16:46:51.871 4110-4110/zhangqilu.com.proxy E/ProxyCar: move: 开始行使
03-16 16:46:52.869 4110-4110/zhangqilu.com.proxy E/ProxyCar: move: 汽车行驶总时间 998毫秒


2.聚合方法实现代码

//PolymerProxyCar是代理类实现Movable接口
public class PolymerProxyCar implements Movable {
private final static String TAG = "PolymerProxyCar";
private Car car;//被代理类对象

public PolymerProxyCar(Car car){//被代理类对象传入
this.car = car;
}

@Override
public void move() {
long startTime = System.currentTimeMillis();
Log.e(TAG, "move: 开始行使");
car.move();//执行被代理类对象方法
long endTime = System.currentTimeMillis();
Log.e(TAG, "move: 汽车行驶总时间 "+(endTime-startTime)+"毫秒");
}
}

//创建代理对象实例
PolymerProxyCar polymerProxyCar = new PolymerProxyCar(new Car());
//代理类执行 move方法
polymerProxyCar.move();


控制台日志

03-16 17:03:05.246 18786-18786/zhangqilu.com.proxy E/PolymerProxyCar: move: 开始行使
03-16 17:03:05.903 18786-18786/zhangqilu.com.proxy E/PolymerProxyCar: move: 汽车行驶总时间 657毫秒


3静态代理的两种实现方式对比(继承方式和聚合方式)

继承的方式:如果使用继承的方式来实现我们代理功能的叠加,

我们的代理类会无限的膨胀下去。

聚合的方式:

由于代理类和被代理类都实现了相同的接口,那么代理类的构造参数就可以传入该相同的接口,这样在后面功能叠加的时候就可以传入其他功能的代理类,因为他们都实现了相同的父接口。从而达到功能叠加的作用。

聚合代理优于继承代理。因为实现功能叠加的情况下,聚合代理通过相互代理可以实现功能重用,而继承代理必须写多个类来实现多功能叠加。

聚合的方式比继承的方式灵活很多,通过聚合的方式,代理之间也是可以相互传递的,相互组合。

但静态代理只能代理一种类型的被代理类,换个类型的就不行了,这需要动态代理。

三 动态代理

动态代理我们主要说JDK动态代理和CGLIB动态代理。

JDK动态代理 :只能代理实现接口的类,对没有实现接口的类不能实现JDK动态代理。

CGLIB动态代理:针对类来实现代理的,对指定目标类产生一个子类,通过方法拦截技术,拦截所有父类方法调用。

3.1 JDK动态代理

JDK动态代理 只能代理实现接口的类,对没有实现接口的类不能实现JDK动态代理

JDK动态代理实现步骤:

1. 创建被代理的类及接口(示例代码 Car 类就是被代理类,Movable接口)。

2. 创建一个实现接口InvocationHandler的类,必须实现invoke方法(示例代码 CarInvocationHandler类)。

3. 调用Proxy的静态方法( newProxyInstance(ClassLoader loader,Class[] interfaces,InvocationHandler h)),创建一个代理类。

4. 通过代理调用方法。

我们来看看Proxy这个类

Proxy这个类的作用就是用来动态创建一个代理对象的类,它提供了许多的方法,但是我们用的最多的就是 newProxyInstance 这个方法:

public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) throws IllegalArgumentException

loader:  一个ClassLoader对象,定义了由哪个ClassLoader对象来对生成的代理对象进行加载

interfaces:  一个Interface对象的数组,表示的是我将要给我需要代理的对象提供一组什么接口,如果我提供了一组接口给它,那么这个代理对象就宣称实现了该接口(多态),这样我就能调用这组接口中的方法了

h:  一个InvocationHandler对象,表示的是当我这个动态代理对象在调用方法的时候,会关联到哪一个InvocationHandler对象上


代码实现 Car类 和Movable接口代码在见静态代理:

public class CarInvocationHandler implements InvocationHandler {
private final static String TAG = "CarInvocationHandler";
private Object targetObject;
public CarInvocationHandler(Object object){
this.targetObject = object;
}

/**
* @param proxy   被代理对象
* @param method  被代理对象的方法
* @param args    方法的参数
* @return 方法的返回值
* @throws Throwable
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
long startTime = System.currentTimeMillis();
Log.e(TAG, "move: 开始行使");
Object object = method.invoke(targetObject);
long endTime = System.currentTimeMillis();
Log.e(TAG, "move: 汽车行驶总时间 "+(endTime-startTime)+"毫秒");
return object;
}
}
//创建被代理类Car实例
Car car = new Car();
//创建CarInvocationHandler 实例
CarInvocationHandler carInvocationHandler = new CarInvocationHandler(car);
//获取Car的类类型
Class aClass = car.getClass();
Movable movable = (Movable) Proxy.newProxyInstance(aClass.getClassLoader(),aClass.getInterfaces(),carInvocationHandler);
movable.move();


控制台日志

03-16 19:25:58.845 29772-29772/zhangqilu.com.proxy E/CarInvocationHandler: move: 开始行使
03-16 19:25:59.270 29772-29772/zhangqilu.com.proxy E/CarInvocationHandler: move: 汽车行驶总时间 425毫秒


JDK动态代理局限性:jdk中的动态代理通过反射类Proxy和InvocationHandler回调接口实现,要求委托类必须实现一个接口,只能对该类接口中定义的方法实现代理,这在实际编程中有一定的局限性。这就引入了CJLIB动态代理。

3.2 CJLIB动态代理

CJLIB是一个开源项目,地址如下:https://github.com/cglib/cglib

针对类来实现代理的,对指定目标类产生一个子类,通过方法拦截技术,拦截所有父类方法调用。

使用时我们要添加cglib依赖或者jar。

下面我们来看使用代码

// AirPlane 被代理类
public class AirPlane {
public void move() {
try {
Thread.sleep(new Random().nextInt(1000));
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}

public class CGLIBProxy implements MethodInterceptor{
private final static String TAG = "CGLIBProxy";
private Enhancer enhancer ;

public Object getProxy(Class c1){
enhancer = new Enhancer();
enhancer.setSuperclass(c1);//设置创建子类的类
enhancer.setCallback(this);//设置回调
return  enhancer.create();
}
/**
*拦截所有目标类方法的调用
* @param o 目标类的实例
* @param method 目标方法的反射对象
* @param objects 目标方法的参数
* @param methodProxy 代理类的实例
* @return
* @throws Throwable
*/
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
long startTime = System.currentTimeMillis();
Log.e(TAG, "move: 开始行使");
//代理类调用父类的方法
methodProxy.invokeSuper(o,objects);
long endTime = System.currentTimeMillis();
Log.e(TAG, "move: 汽车行驶总时间 "+(endTime-startTime)+"毫秒" );
return null;
}
}

主函数执行
CGLIBProxy cglibProxy = new CGLIBProxy();
AirPlane airPlane = (AirPlane) cglibProxy.getProxy(AirPlane.class);
airPlane.move();


注意:CGLIB不能对“final”修饰的类进行代理。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息