为什么要使用代理
设计模式中定义为:为其他对象提供一种代理以控制对这个对象的访问。
在某些情况下,一个客户不想或者不能直接引用另一个对象,此时代理对象可以在客户端和目标对象之间起到中介的作用。
代理分类
代理分为静态代理与动态代理
静态代理:由程序员创建或由特定工具自动生成源代码,再对其编译。在程序运行前,代理类的.class文件就已经存在了。
动态代理:在程序运行时,运用反射机制动态创建而成。
在Java中有两种实现方式,1、实现接口InvocationHandler;2、使用cglib。
代理示例
首先定义一个接口
package com.proxy;
public interface IPay {
void assemData(String msg);
void sendData(String data);
}
接口实现类
package com.proxy;
public class PayImpl implements IPay {
@Override
public void assemData(String msg) {
System.out.println("我是被代理的类,我被调用了。");
}
@Override
public void sendData(String data) {
System.out.println("我是被代理的类,我被调用了。");
}
}
静态代理
再定义一个接口的实现类,该类中会多出一个类型为IPay接口的实例变量
package com.proxy;
public class PayImpl1 implements IPay{
private IPay pay;
public PayImpl1(IPay pay) {
super();
this.pay = pay;
}
@Override
public void assemData(String msg) {
System.out.println("===before invoke===");
pay.assemData("发送银行的数据");
System.out.println("===after invoke===\r");
}
@Override
public void sendData(String data) {
System.out.println("===before invoke===");
pay.assemData("发送银行的数据");
System.out.println("===after invoke===\r");
}
}
测试代码
IPay pay = new PayImpl();
IPay payProxy = new PayImpl1(pay);
payProxy.assemData("发送到银行的数据");
运行效果
===before invoke===
我是被代理的类,我被调用了。
===after invoke===
动态代理
方式一
定义一个代理类,该代理类需要实现InvocationHandler接口,代码如下:
package com.proxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
/**
* 动态代理类只能代理接口,代理类都需要实现接口InvocationHandler。
* 被重写的invoke方法就是调用被代理接口的所有方法时需要调用的。
* @author liuxd
*
*/
public class DynamicProxy implements InvocationHandler {
private Object obj;
public DynamicProxy(Object obj) {
super();
this.obj = obj;
}
public static Object newInstance(Object obj) {
return java.lang.reflect.Proxy.newProxyInstance(obj.getClass()
.getClassLoader(), obj.getClass().getInterfaces(),
new DynamicProxy(obj));
}
@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
Object result;
System.out.println("===before invoke===");
result = method.invoke(obj, args);
System.out.println("===after invoke===\r");
return result;
}
}
测试代码
IPay pay = new PayImpl();
IPay payProxy = (IPay) DynamicProxy.newInstance(pay);
payProxy.assemData("发送到银行的数据");
运行效果同上
方式二
package com.proxy;
import java.lang.reflect.Method;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
/**
* 使用cglib实现动态代理
*
* @author liuxd
*
*/
public class CglibProxy implements MethodInterceptor {
private Object target = null;
public CglibProxy(Object target) {
super();
this.target = target;
}
/**
* 创建代理对象
*
* @param target
* @return
*/
public Object getProxy() {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(this.target.getClass());
// 回调方法
enhancer.setCallback(this);
// 创建代理对象
return enhancer.create();
}
@Override
public Object intercept(Object obj, Method method, Object[] args,
MethodProxy proxy) throws Throwable {
Object result = null;
System.out.println("===before invoke===");
result = proxy.invokeSuper(obj, args);
System.out.println("===after invoke===\r");
return result;
}
}
测试代码
PayImpl impl = new PayImpl();
CglibProxy cglib = new CglibProxy(impl);
PayImpl payProxy = (PayImpl) cglib.getProxy();
payProxy.assemData("发送到银行的数据");
运行效果同上
不知道大家注意到两种动态代理实现方式的区别没有,这里简单说下自己的研究结果:
实现InvocationHandler接口这种方式,其实在Proxy中它是使用$Proxy+顺序号 implements 你传入的接口这种方式动态的在jvm中生成了一个类实例,所以,你的业务类必须是接口、实现类的形式。
而使用cglib实现动态代理,它是动态生成了一个类,这个类继承了你传入的类,所以,你传入的类不用必须是接口、实现类这种形式,可以直接实现类。
各类代理特点
1、静态代理只能代理一个接口或实现类。
2、静态代理如果接口类发生了变化,那么,实现类、代理类都要进行调整。
3、动态代理可以代理多个接口类或实现类(cglib方式特有)。