一、定义
代理模式给某一个对象提供一个代理对象,并由代理对象控制对原对象的引用。
二、为什么要用代理模式?
在某些情况下,一个客户类不想或者不能直接引用一个委托对象,而代理类对象可以在客户类和委托对象之间起到中介的作用,其特征是代理类和委托类实现相同的接口。
代理类除了是客户类和委托类的中介之外,我们还可以通过给代理类增加额外的功能来扩展委托类的功能,这样做我们只需要修改代理类而不需要再修改委托类,符合代码设计的开闭原则。代理类主要负责为委托类预处理消息、过滤消息、把消息转发给委托类,以及事后对返回结果的处理等。代理类本身并不真正实现服务,而是同过调用委托类的相关方法,来提供特定的服务。真正的业务功能还是由委托类来实现,但是可以在业务功能执行的前后加入一些公共的服务。例如我们想给项目加入缓存、日志这些功能,我们就可以使用代理类来完成,而没必要打开已经封装好的委托类。
三、有哪几种代理模式?
我们有多种不同的方式来实现代理。如果按照代理创建的时期来进行分类的话,可以分为两种:静态代理、动态代理。
静态代理是由程序员创建或特定工具自动生成源代码,在对其编译。在程序员运行之前,代理类.class文件就已经被创建了。
动态代理是在程序运行时通过反射机制动态创建的。
四、静态代理
1、实现
接口
public interface Person {
void speak();
}
实现类
class Actor implements Person {
private String content;
public Actor(String content) {
this.content = content;
}
@Override
public void speak() {
System.out.println(this.content);
}
}
代理类
class Agent implements Person {
private Actor actor;
private String before;
private String after;
public Agent(Actor actor, String before, String after) {
this.actor = actor;
this.before = before;
this.after = after;
}
@Override
public void speak() {
// before speak
System.out.println("Before actor speak, Agent say: " + before);
// real speak
this.actor.speak();
// after speak
System.out.println("After actor speak, Agent say: " + after);
}
}
测试
public static void main(String[] args) {
// TODO Auto-generated method stub
Agent a = new Agent(new Actor("我是演员"), "我之前是一个演员", "我以后也是一个演员");
a.speak();
}
输出
Before actor speak, Agent say: 我之前是一个演员
我是演员
After actor speak, Agent say: 我以后也是一个演员
2、缺点
我们得为每一个服务都得创建代理类,工作量太大,不易管理。同时接口一旦发生改变,代理类也得相应修改。
五、JDK动态代理
1、 实现
public class DynamicProxyHandler implements InvocationHandler {
private Object object;
public DynamicProxyHandler(Object object) {
super();
this.object = object;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// TODO Auto-generated method stub
System.out.println("之前我是一个演员");
Object result = method.invoke(object, args);
System.out.println("之后我还是一个演员");
return result;
}
}
public static void main(String[] args) {
Person p = new Actor("我是演员");
Person agentP = (Person) Proxy.newProxyInstance(Person.class.getClassLoader(), new Class[]{Person.class}, new DynamicProxyHandler(p));
agentP.speak();
}
2、原理
关于InvocationHandler
每一个动态代理类都必须要实现InvocationHandler这个接口,并且每个代理类的实例都关联了一个handler,当我们通过代理对象调用一个方法的时候,这个方法的调用就会被转发为由InvocationHandler这个接口的 invoke 方法来进行调用。我们来看看InvocationHandler这个接口的唯一一个方法 invoke 方法:
Object invoke(Object proxy, Method method, Object[] args) throws Throwable
我们看到这个方法一共接受三个参数,那么这三个参数分别代表如下:
- proxy:指代JDK动态生成的最终代理对象
- method:指代的是我们所要调用真实对象的某个方法的Method对象
- args:指代的是调用真实对象某个方法时接受的参数
关于Proxy
Proxy这个类的作用就是用来动态创建一个代理对象的类,它提供了许多的方法,但是我们用的最多的就是 newProxyInstance 这个方法:
public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces, InvocationHandler h) throws IllegalArgumentException
这个方法的作用就是得到一个动态的代理对象,其接收三个参数,我们来看看这三个参数所代表的含义:
- loader:一个ClassLoader对象,定义了由哪个ClassLoader来对生成的代理对象进行加载
- interfaces:一个Interface对象的数组,表示的是我将要给我需要代理的对象提供一组什么接口,如果我提供了一组接口给它,那么这个代理对象就宣称实现了该接口(多态),这样我就能调用这组接口中的方法了
- InvocationHandler:表示的是当我这个动态代理对象在调用方法的时候,会关联到哪一个InvocationHandler对象上
源码部分
@CallerSensitive
public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h)
throws IllegalArgumentException
{
if (h == null) {
throw new NullPointerException();
}
// 获得所有实现的接口
final Class<?>[] intfs = interfaces.clone();
final SecurityManager sm = System.getSecurityManager();
if (sm != null) {
checkProxyAccess(Reflection.getCallerClass(), loader, intfs);
}
/*
* Look up or generate the designated proxy class.
*/
// 生成代理对象的class文件(其内部实现是先生成字节码文件,然后通过类加载加载生成class实例)
Class<?> cl = getProxyClass0(loader, intfs);
/*
* Invoke its constructor with the designated invocation handler.
*/
try {
// 使用构造器以及传入的InvocationHandler来实例化代理对象
final Constructor<?> cons = cl.getConstructor(constructorParams);
final InvocationHandler ih = h;
if (sm != null && ProxyAccessHelper.needsNewInstanceCheck(cl)) {
// create proxy instance with doPrivilege as the proxy class may
// implement non-public interfaces that requires a special permission
return AccessController.doPrivileged(new PrivilegedAction<Object>() {
public Object run() {
return newInstance(cons, ih);
}
});
} else {
return newInstance(cons, ih);
}
} catch (NoSuchMethodException e) {
throw new InternalError(e.toString());
}
}
关于生成的代理类:
- 通过jdk动态代理生成的代理对象会继承Proxy,并实现被代理的接口,由于java不能多继承,所以JDK的动态代理不支持对实现类的代理,只支持接口的代理。
- 生成静态代码块来初始化接口中方法的Method对象,以及Object类的equals、hashCode、toString方法。(使用了反射)
- 提供了一个使用InvocationHandler作为参数的构造方法,代理类内部的方法的实现其实都是调用了InvocationHandler的invoke方法。
详细参考:https://blog.csdn.net/zhangdefeng2008/article/details/79399898
六、cglib动态代理
1、实现
ublic class CGlibAgent implements MethodInterceptor {
private Object proxy;
public Object getInstance(Object proxy) {
this.proxy = proxy;
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(this.proxy.getClass());
// 回调方法
enhancer.setCallback(this);
// 创建代理对象
return enhancer.create();
}
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
// TODO Auto-generated method stub
System.out.println("之前我是一个码农");
Object result = methodProxy.invokeSuper(o, objects);
System.out.println("之后我就是一个rapper了");
return result;
}
}
public static void main(String[] args) {
// TODO Auto-generated method stub
CGlibAgent cGlibAgent = new CGlibAgent();
Rapper rapper = (Rapper) cGlibAgent.getInstance(new Rapper());
rapper.speak();
}
2、原理
CGLIB是一种字节码增强库,利用其提供的字节码技术可以实现动态代理。其底层依赖ASM字节码技术。
通过字节码技术为一个类创建子类,并在子类中采用方法拦截的技术拦截所有父类方法的调用。(即MethodInterceptor的intercept方法)
七、JDK动态代理和Gglib动态代理的区别
- JDK动态代理是实现了被代理对象的接口,因此被代理的对象必须实现了某个接口,而Cglib是继承了被代理对象,因此不能代理final修饰的类。
- JDK和Cglib都是在运行期生成字节码,JDK是直接写Class字节码,Cglib使用ASM框架写Class字节码,Cglib代理实现更复杂,生成代理类比JDK效率低。
- JDK调用代理方法,是通过反射机制调用,Cglib是通过FastClass机制直接调用方法,Cglib执行效率更高。