设计模式之代理模式

代理模式

目标

  • 掌握代理模式的应用场景和实现原理
  • 了解静态代理和动态代理的区别
  • 了解CGLib和JDK Proxy的根本区别
  • 手写实现定义的动态原理

代理模式的定义

  • 代理模式是指为其他对象提供一种代理,以控制对这个对象的访问
  • 代理对象在客服端和目标对象之间起到中介作用
  • 属于结构型设计模式

代理模式的适用场景

  • 保护目标对象
  • 增强目标对象

静态代理

//demo  帮儿子找对象
//新建一个抽象角色
public interface IPersion {
    void findLove();
}
//建立一个真实的需求方 
public class Xiaozhang implements IPersion {
    @Override
    public void findLove() {
        System.out.println("小张要求:肤白貌美大长腿");
    }
}

//代理 
public class LaoZhang implements IPersion {

    private Xiaozhang xiaozhang;

    public LaoZhang(Xiaozhang xiaozhang) {
        this.xiaozhang = xiaozhang;
    }

    @Override
    public void findLove() {
        System.out.println("老张开始物色");
        xiaozhang.findLove();
        System.out.println("开始交往");
    }
}
//测试
public class Test {
    public static void main(String[] args) {
        LaoZhang laoZhang = new LaoZhang(new Xiaozhang());
        laoZhang.findLove();
    }
}
打印:
老张开始物色
小张要求:肤白貌美大长腿
开始交往

JDK动态代理
java动态代理机制中有两个重要的类和接口InvocationHandler(接口)和Proxy(类),这一个类Proxy和接口InvocationHandler是我们实现动态代理的核心;
1.InvocationHandler接口是proxy代理实例在调用处理程序时实现的一个接口,每一个proxy代理实例都有一个关联的调用处理程序;在代理实例调用方法时,方法调用被编码分派到调用处理程序的invoke方法。

InvocationHandler
每一个动态代理类的调用处理程序都必须实现InvocationHandler接口,并且每个代理类的实例都关联到了实现该接口的动态代理类调用处理程序中,当我们通过动态代理对象调用一个方法时候,这个方法的调用就会被转发到实现InvocationHandler接口类的invoke方法来调用

   /**
    * proxy:代理类代理的真实代理对象com.sun.proxy.$Proxy0
    * method:我们所要调用某个对象真实的方法的Method对象
    * args:指代代理对象方法传递的参数
    */
    public Object invoke(Object proxy, Method method, Object[] args)
        throws Throwable;

2.Proxy类就是用来创建一个代理对象的类,它提供了很多方法,但是我们最常用的是newProxyInstance方法。

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

这个方法的作用就是创建一个代理类对象,它接收三个参数,我们来看下几个参数的含义:

  • loader:一个classloader对象,定义了由哪个classloader对象对生成的代理类进行加载
  • interfaces:一个interface对象数组,表示我们将要给我们的代理对象提供一组什么样的接口,如果我们提供了这样一个接口对象数组,那么也就是声明了代理类实现了这些接口,代理类就可以调用接口中声明的所有方法。
  • h:一个InvocationHandler对象,表示的是当动态代理对象调用方法的时候会关联到哪一个InvocationHandler对象上,并最终由其调用。

案例

//
public interface IPersion {
    void findLove();
}
//小李
public class XiaoLi implements IPersion {
    @Override
    public void findLove() {
        System.out.println("小李要求:有车有房");
    }
}
//小张
public class Xiaozhang implements IPersion {
    @Override
    public void findLove() {
        System.out.println("小张要求:肤白貌美大长腿");
    }
}
//媒婆
public class JDKMeipo implements InvocationHandler {
	//真实的代理对象
    private IPersion target;
	//拿到目标对象引用的方法(生成代理对象)
    public IPersion getInstence(IPersion target){
        this.target = target;
        Class<?> clazz = target.getClass();

		/**
         * 通过Proxy类的newProxyInstance方法创建代理对象,我们来看下方法中的参数
         * 第一个参数:clazz.getClassLoader(),使用handler对象的classloader对象来加载我们的代理对象
         * 第二个参数:clazz.getInterfaces(),这里为代理类提供的接口是真实对象实现的接口,这样代理对象就能像真实对象一样调用接口中的所有方法
         * 第三个参数:this 即 handler,我们将代理对象关联到上面的InvocationHandler对象上
         */
        return (IPersion) Proxy.newProxyInstance(clazz.getClassLoader(),clazz.getInterfaces(),this);
    }

	
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        before();
        //method是外部动态获取的
        Object result = method.invoke(this.target, args);
        after();
        return result;
    }

    private void before() {
        System.out.println("我是媒婆,已经收集到你的需求,开始物色");
    }
    private void after() {
        System.out.println("双方同意,开始交往");
    }
}
//测试
public class Test {
    public static void main(String[] args) {
    	//要代理的真实对象
        JDKMeipo meipo = new JDKMeipo();
        //代理对象的调用处理程序,我们将要被代理的真实对象传入代理对象的调用处理的构造函数中,最终代理对象的调用处理程序会调用真实对象的方法
        IPersion xiaozhang = meipo.getInstence(new Xiaozhang());
        xiaozhang.findLove();

        IPersion xiaoli = meipo.getInstence(new XiaoLi());
        xiaoli.findLove();
    }
}
打印:
我是媒婆,已经收集到你的需求,开始物色
小张要求:肤白貌美大长腿
双方同意,开始交往
我是媒婆,已经收集到你的需求,开始物色
小李要求:有车有房
双方同意,开始交往

CGLib动态代理

案例

//不需要实现接口
public class Xiaozhang{
    public void findLove() {
        System.out.println("小张要求:肤白貌美大长腿");
    }
}

public class CglibMeipo implements MethodInterceptor {

    //拿到目标对象引用的方法
    public Object getInstence(Class<?> clazz){
        Enhancer enhancer = new Enhancer();
        //setSuperclass表示设置要代理的类
        enhancer.setSuperclass(clazz);
        //setCallback表示设置回调即MethodInterceptor的实现类
        enhancer.setCallback(this);
        //使用create()方法生成一个代理对象,注意要强转一下,因为返回的是Object。
        return enhancer.create();
    }

    private void before() {
        System.out.println("我是媒婆,已经收集到你的需求,开始物色");
    }
    private void after() {
        System.out.println("双方同意,开始交往");
    }

    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        before();
        Object result = methodProxy.invokeSuper(o, objects);
        after();
        return result;
    }
}
//测试
public class Test {
    public static void main(String[] args) {
        Xiaozhang xiaozhang = (Xiaozhang) new CglibMeipo().getInstence(Xiaozhang.class);
        xiaozhang.findLove();
    }
}
打印:
我是媒婆,已经收集到你的需求,开始物色
小张要求:肤白貌美大长腿
双方同意,开始交往

JDK的动态代理、CGLib的动态代理原理基本相似
都是通过底层逻辑动态生成一个类(字节码重组)保存在内存中,这个代理类必须实现目标类的接口或者继承目标类(即必须在同一个继承体系),通过反射调用代理类方法的时候就可以找到被代理类相同方法名的方法
总结:
JDK动态代理和Cglib的动态代理不同点
查看他们的生成的代理类的.class文件可以看出
Cglib是继承的方式,覆盖父类方法
JDK采用的实现方式,必须要求目标对象一定要实现一个接口
思想:通过生成字节码,重新组成一个新的类
JDKProxy 对于用户而言,依赖更强,调用也更加复杂 (实现InvocationHandler接口)
Cglib对目标类没有任何要求(实现MethodInterceptor接口)
都需要一个getInstance()方法生成代理,都有一个invoke()方法来回调

Cglib效率更高,性能也更好,底层没有用到反射
JDKProxy 生成逻辑较为简单,执行效率要低,每次都要用到反射

注意:Cglib,目标代理类不能有final修饰的方法,会忽略final修饰的方法

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值