代理模式

定义

为其他对象提供一种代理,以控制对对象的访问,结构型模式。某些情况下,一个对象不适合或者不能直接引用对象,而代理对象可以在客户端和目标对象之间起到个中介的作用。

  • 作用:一是保护目标对象,二是增强目标对象;而我们在代码里一般都是为了增强目标对象,像spring中的aop。

静态代理

静态代理.png
  • 代理主题
/**
 * 主题
 */
public interface Subject {
    void doSomething();
}
  • 真实执行角色
/**
 * 真正的执行主题
 */
public class RealSubject implements Subject{

    @Override
    public void doSomething() {
        System.out.println("RealSubject doSomething");
    }
}
  • 静态代理类
/**
 * 静态代理类
 */
public class StaticProxy implements Subject{
    private RealSubject realSubject;

    private StaticProxy(RealSubject realSubject) {
        this.realSubject = realSubject;
    }

    @Override
    public void doSomething() {
        System.out.println("proxy doSomething1");
        realSubject.doSomething();
        System.out.println("proxy doSomething2");
    }
}

动态代理

JDK

JDK动态代理.png
  • JdkProxy
/**
 * jdk动态代理,通过代理对象的接口生成代理方法,即被代理者
 * 必须有实现的接口
 */
public class JdkProxy implements InvocationHandler {

    Object subject;

    public Object newProxyInstance(Object subject) {
        this.subject = subject;
        Class classSbject = subject.getClass();
        return Proxy.newProxyInstance(classSbject.getClassLoader(), classSbject.getInterfaces(), this);
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        before();
        Object result = method.invoke(subject, args);
        after();
        return result;
    }

    private void before() {
        System.out.println("before doSomething");
    }

    private void after() {
        System.out.println("after doSomething");
    }
}
  • 调用客户端
/**
 * 调用jdk动态代理的客户端
 */
public class Client {
    public static void main(String[] args) {
        JdkProxy jdkProxy = new JdkProxy();
        Subject subjectProxyInstance = (Subject) jdkProxy.newProxyInstance(new RealSubject());
        subjectProxyInstance.doSomething();
    }
}

JDK代理所真正执行的类

  • 让我们把断点打在ProxyGenerator.generateProxyClass方法里,并通过System.getProperties().put设置saveGeneratedFiles这个变量为true,我们就可以看到真实生成的代理类是怎么样的了


    jdk生成类位置.png
jdk生成类文件夹位置.png
  • jdk代理生成的代理类
public final class $Proxy0 extends Proxy implements Subject {
    private static Method m1;
    private static Method m3;
    private static Method m2;
    private static Method m0;

    public $Proxy0(InvocationHandler var1) throws  {
        super(var1);
    }

    public final boolean equals(Object var1) throws  {
        try {
            return (Boolean)super.h.invoke(this, m1, new Object[]{var1});
        } catch (RuntimeException | Error var3) {
            throw var3;
        } catch (Throwable var4) {
            throw new UndeclaredThrowableException(var4);
        }
    }

    public final void doSomething() throws  {
        try {
            super.h.invoke(this, m3, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    public final String toString() throws  {
        try {
            return (String)super.h.invoke(this, m2, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    public final int hashCode() throws  {
        try {
            return (Integer)super.h.invoke(this, m0, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    static {
        try {
            m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
            m3 = Class.forName("com.example.proxy.dynamicproxy.jdkproxy.Subject").getMethod("doSomething");
            m2 = Class.forName("java.lang.Object").getMethod("toString");
            m0 = Class.forName("java.lang.Object").getMethod("hashCode");
        } catch (NoSuchMethodException var2) {
            throw new NoSuchMethodError(var2.getMessage());
        } catch (ClassNotFoundException var3) {
            throw new NoClassDefFoundError(var3.getMessage());
        }
    }
}
  • 对比前面的JdkProxy,我们可以发现,真正执行的方法就是JdkProxy里面的invoke方法

CGLIB

  • cglib代理主题
/**
 * cglib代理主题
 */
public class CglibSubject {
    public void doSomething(String sport) {
        System.out.println("do " + sport);
    }
}
  • cglib动态代理
/**
 * cglib动态代理
 */
public class CglibProxy implements MethodInterceptor {
    public Object getProxyInstance(Class clazz) {
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(clazz);
        enhancer.setCallback(this);
        return enhancer.create();
    }

    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        before();
        Object result = methodProxy.invokeSuper(o, objects);
        after();
        return result;
    }

    private void before() {
        System.out.println("cglib before doSomething");
    }

    public void after() {
        System.out.println("cglib after doSomething");
    }
}
  • 客户端
public class Client {
    public static void main(String[] args) {
        System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "D://cglib_proxy_classes");
        CglibSubject cglibSubjectProxy = (CglibSubject) new CglibProxy().getProxyInstance(CglibSubject.class);
        cglibSubjectProxy.doSomething("basketball");
    }
}

通过设置DebuggingClassWriter.DEBUG_LOCATION_PROPERTY,我们可以看到cglib生成的代理类


cglib1.png

我们看这三个类中比较重要的方法

  • CglibSubject328a458c.class
public final void doSomething(String var1) {
        // 即继承了MethodInterceptor的CglibProxy
        MethodInterceptor var10000 = this.CGLIB$CALLBACK_0;
        if (var10000 == null) {
            CGLIB$BIND_CALLBACKS(this);
            var10000 = this.CGLIB$CALLBACK_0;
        }

        if (var10000 != null) {
            // 调用拦截器方法
            var10000.intercept(this, CGLIB$doSomething$0$Method, new Object[]{var1}, CGLIB$doSomething$0$Proxy);
        } else {
            super.doSomething(var1);
        }
    }
  • CglibSubjectf11f64a9
    public Object invoke(int var1, Object var2, Object[] var3) throws InvocationTargetException {
        //真实被代理的对象
        CglibSubject var10000 = (CglibSubject)var2;
        int var10001 = var1;

        try {
            switch(var10001) {
            case 0:
                // 根据索引选择真实执行的方法,而不是反射
                var10000.doSomething((String)var3[0]);
                return null;
                ......
  }
  • CglibSubject328a458c8d4d8eb1
    • 和CglibSubjectf11f64a9一样主要是通过下标执行方法

JDK代理与CGLIB代理比较

  • JDK生成代理是靠接口,CGLIB是覆盖方法
  • JDK的无法生成extend的方法,CGLIB无法覆盖final的方法
  • JDK是靠反射,CGLIB是靠下标找到执行方法,CGLIB这点比JDK高效

代理、装饰、适配比较

  • 这三个设计模式从使用上来说比较相像,都是持有一个对象,再在调用持有对象方法时做些额外的事。它们的区别主要在于意图,代理是增强,装饰是扩展,适配是转换。

优缺点

  • 优点
    • 解耦
    • 保护目标
    • 增强功能
  • 缺点
    • 增加类的数量
    • 增加复杂度

你可能感兴趣的:(代理模式)