代理模式

代理模式

为其他对象提供一种代理,以控制对这个对象的访问。

使用场景

  1. 保护目标对象
  2. 增强目标对象

静态代理

显式声明被代理对象

使用场景

代理类,只代理 一个被代理类

  • 定义一个person接口
public interface Person {
    void findLove();
}
  • 被代理类
public class Son implements Person {
    @Override
    public void findLove() {
        System.out.println("肤白 貌美 大长腿");
    }
}
  • 代理类
public class Father implements Person {
    private Person person;

    public Father(Son son) {
        this.person = son;
    }

    @Override
    public void findLove() {
        before();
        person.findLove();
        after();
    }

    private void before() {
        System.out.println("开始物色");
    }

    private void after() {
        System.out.println("寻找完毕");
    }
}
  • 测试类
public class StaticProxyTest {
    public static void main(String[] args) {
        Father father = new Father(new Son());
        father.findLove();
    }
}

动态代理

动态配置和替换被代理对象。动态代理和静态对比基本思路是一致的,只不过动态代理功能更加强大,可以代理很多类。

JDK代理

JDK动态代理,需要被代理类实现一个接口

  • 代理类
public class JDKMeipo implements InvocationHandler {
    private Object target;

    public Object getInstance(Object target) {
        this.target = target;

        Class clazz = target.getClass();
        return Proxy.newProxyInstance(clazz.getClassLoader(), clazz.getInterfaces(), this);
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

        before();
        Object object = method.invoke(this.target, args);
        after();

        return object;
    }

    private void before() {
        System.out.println("开始物色");
    }

    private void after() {
        System.out.println("寻找完毕");
    }
}
  • 被代理类
public class Girl implements Person {
    @Override
    public void findLove() {
        System.out.println("高富帅");
    }
}
  • 测试类
public class JDKProxyTest {
    public static void main(String[] args) {
        Object o = new JDKMeipo().getInstance(new Girl());
        Person instance = (Person) o;
        instance.findLove();

        // 输出代理对象,通过jad反编译 查看代理类内部情况
//        try {
//            byte[] bytes = ProxyGenerator.generateProxyClass("$Proxy0", new Class[]{Person.class});
//            FileOutputStream os = new FileOutputStream("F://$Proxy0.class");
//            os.write(bytes);
//            os.close();
//        } catch (Exception e) {
//            e.printStackTrace();
//        }

    }
}
  • 反编译出来的代理类

通过反编译可以看出来,生成的代理对象中实现了 被代理类所实现的接口,从而实现方法的增强

// Decompiled by Jad v1.5.8g. Copyright 2001 Pavel Kouznetsov.
// Jad home page: http://www.kpdus.com/jad.html
// Decompiler options: packimports(3) 

import com.hh.proxy.Person;
import java.lang.reflect.*;

public final class $Proxy0 extends Proxy
    implements Person
{

    public $Proxy0(InvocationHandler invocationhandler)
    {
        super(invocationhandler);
    }

    public final boolean equals(Object obj)
    {
        try
        {
            return ((Boolean)super.h.invoke(this, m1, new Object[] {
                obj
            })).booleanValue();
        }
        catch(Error _ex) { }
        catch(Throwable throwable)
        {
            throw new UndeclaredThrowableException(throwable);
        }
    }

    public final void findLove()
    {
        try
        {
            super.h.invoke(this, m3, null);
            return;
        }
        catch(Error _ex) { }
        catch(Throwable throwable)
        {
            throw new UndeclaredThrowableException(throwable);
        }
    }

    public final String toString()
    {
        try
        {
            return (String)super.h.invoke(this, m2, null);
        }
        catch(Error _ex) { }
        catch(Throwable throwable)
        {
            throw new UndeclaredThrowableException(throwable);
        }
    }

    public final int hashCode()
    {
        try
        {
            return ((Integer)super.h.invoke(this, m0, null)).intValue();
        }
        catch(Error _ex) { }
        catch(Throwable throwable)
        {
            throw new UndeclaredThrowableException(throwable);
        }
    }

    private static Method m1;
    private static Method m3;
    private static Method m2;
    private static Method m0;

    static 
    {
        try
        {
            m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[] {
                Class.forName("java.lang.Object")
            });
            m3 = Class.forName("com.hh.proxy.Person").getMethod("findLove", new Class[0]);
            m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);
            m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);
        }
        catch(NoSuchMethodException nosuchmethodexception)
        {
            throw new NoSuchMethodError(nosuchmethodexception.getMessage());
        }
        catch(ClassNotFoundException classnotfoundexception)
        {
            throw new NoClassDefFoundError(classnotfoundexception.getMessage());
        }
    }
}

CGLib代理

被代理类不用实现接口。

  • 被代理类
public class Boy {

    public void findLove() {
        System.out.println("白富美");
    }
}
  • 代理类
public class CglibMeipo implements MethodInterceptor {

    public Object getInstance(Class clazz) {
        //相当于Proxy,代理的工具类
        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 obj = methodProxy.invokeSuper(o, objects);
        after();

        return obj;
    }

    private void before() {
        System.out.println("开始物色");
    }

    private void after() {
        System.out.println("寻找完毕");
    }
}

  • 测试类
public class CglibProxyTest {
    public static void main(String[] args) {

        Boy boy = (Boy) new CglibMeipo().getInstance(Boy.class);
        boy.findLove();

    }
}

CGLib代理与JDK代理对比

  1. JDK动态代理是实现了被代理对象的接口,CGLib是继承了被代理对象。
  2. JDK 和 CGLib 都是在运行期生成字节码,JDK 是直接写 Class 字节码,CGLib 使用 ASM 框架写 Class 字节码,Cglib 代理实现更复杂,生成代理类比 JDK 效率低。
  3. JDK 调用代理方法,是通过反射机制调用,CGLib 是通过 FastClass 机制直接调用方法, CGLib 执行效率更高

静态代理和动态的本质区别

  1. 静态代理只能通过手动完成代理操作,如果被代理类增加新的方法,代理类需要同步 新增,违背开闭原则
  2. 动态代理采用在运行时动态生成代码的方式,取消了对被代理类的扩展限制,遵循开 闭原则。
  3. 若动态代理要对目标类的增强逻辑扩展,结合策略模式,只需要新增策略类便可完成, 无需修改代理类的代码

代理模式的优缺点

优点

  1. 代理模式能将代理对象与真实被调用的目标对象分离
  2. 一定程度上降低了系统的耦合度,扩展性好
  3. 可以起到保护目标对象的作用
  4. 可以对目标对象的功能增强

缺点

  1. 代理模式会造成系统设计中类的数量增加
  2. 在客户端和目标对象增加一个代理对象,会造成请求处理速度变慢
  3. 增加了系统的复杂度

你可能感兴趣的