代理模式-静态代理,jdk动态代理,CGLIB动态代理

视频资料:动态代理视频(黑马程序员)

源码在我的资源里

代理就是代替处理,就是另一个对象代理原来的对象处理某些逻辑

代理可以给我们带来什么?

  1. 在不改变原对象代码的基础上,对原对象的功能进行修改或者增强
  2. 解耦

java中的代理模式

java动态代理生成方式分三种:静态代理,基于 jdk (接口) 动态代理、CGLIB(父类)的动态代理
一个静态,两个动态
目标类:原对象,(演员本人) 代理类:代理模式产生的对象,在原有的基础上修改逻辑,并不改变目标类本身(替身)
替身比演员本人厉害,可以做出演员做不出的动作

静态代理

静态代理:我们自己手写的代理类,工程中有代理类的源码
继承某种程度上来说就是静态代理(子类的方法中调用了父类的方法)

继承:
父:

public class Customer {
    public String order(String foodName){
        return "已经下单点了"+foodName;
    }
}

子:

public class DeliveryClerk extends Customer{
    @Override
    public String order(String foodName) {
        String result = super.order(foodName);
        System.out.println("已经接受订单,正前往取餐途中...");
        System.out.println("已经取餐,正在配送...");
        return result+",已经搅拌均匀";
    }
}

test:

    public static void main(String[] args) {

        Customer customer = new DeliveryClerk();
        String result = customer.order("666");
        System.out.println(result);
    }
已经接受订单,正前往取餐途中...
已经取餐,正在配送...
已经下单点了666,已经搅拌均匀


接口实现
接口:

public interface OrderInterface {
    String order(String foodName);
}

”父类(目标类)“实现接口

public class Customer implements OrderInterface{
    @Override
    public String order(String foodName){
        return "已经下单点了"+foodName;
    }
}

”子类(代理类)“实现接口

public class DeliveryClerk2 implements OrderInterface{

    //把原来的对象传入,并保存带成员变量,也就是目标类对象
    private OrderInterface source;

    public DeliveryClerk2(OrderInterface orderInterface) {
        this.source = orderInterface;
    }

    @Override
    public String order(String foodName) {
        String result = source.order(foodName);
        System.out.println("已经接受订单,正前往取餐途中...");
        System.out.println("已经取餐,正在配送...");
        return result+",已经搅拌均匀";
    }
}

测试:

    public static void main(String[] args) {
        Customer customer = new Customer();
        //创建代理对象,也就是外卖小哥2
        DeliveryClerk2 deliveryClerk2 = new DeliveryClerk2(customer);
        String result = deliveryClerk2.order("红烧肉");
        System.out.println(result);
    }
已经接受订单,正前往取餐途中...
已经取餐,正在配送...
已经下单点了红烧肉,已经搅拌均匀

动态代理

动态代理,是在内存中生成代理对象的一种技术,也就是整个代理过程在内存中进行,我们不需要手写代理类的代码,也不会存在代理雷编译的过程,而是直接在运行期,在jvm中”凭空“造出一个代理类对象供我们使用。一般使用动态代理技术有以下两种:

基于jdk(接口)的动态代理

传入目标类,重写InvocationHandler接口中的invoke()方法,通过Proxy . newProxyInstance(ClassLoader loader , Class<?>[] interfaces , InvocationHandler h)获得我们需要的代理类

jdk自带的动态代理技术,需要使用一个静态方法来创建代理对象。它要求被代理对象,也就是目标类,必须实现接口。生成的代理对象和原对象都实现相同的接口,是兄弟关系

    public static void main(String[] args) {
        //准备一个目标类对象,也就是顾客对象
        Customer customer = new Customer();
        //使用JDK的API,动态的生成一个代理对象
        OrderInterface deliveryClerk = (OrderInterface) Proxy.newProxyInstance(
                customer.getClass().getClassLoader(),
                customer.getClass().getInterfaces(),
                new InvocationHandler() {
                    @Override
                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                        System.out.println(method.getName()+" 我执行了..."+args[0]);
                        return null;
                    }
                }
        );
        //调用代理对象,执行对应方法
        String result = deliveryClerk.order("麻辣香锅");
        System.out.println(result);
    }

输出:

order 我执行了...麻辣香锅
null

Proxy . newProxyInstance(ClassLoader loader , Class<?>[] interfaces , InvocationHandler h)

  1. loader :固定写法,指定目标类对象的类加载器即可,用于加载目标类及其接口的字节码文件
  2. interfaces :固定写法,指定目标类的实现的所有接口的字节码对象的数组,通常,使用目标类的字节码对象调用getInterfaces()方法即可得到
  3. InvocationHandler 是一个接口,有唯一一个方法invoke()方法,它会在代理类对象调用方法时执行,
    • Object invoke(Object proxy, Method method, Object[] args),我们在这个方法中完成对增强或者扩展代码逻辑的编写
    • proxy:代理类对象的一个引用,也就是Proxy . newProxyInstance的返回(我指我自己:😅),此引用几乎不会用到
    • method:对应的是出发invoke执行的方法的Method对象。假如我们调用xxx方法,xxx方法就会触发invoke的执行,那么method就是xxx方法对应的反射对象(Method对象)
    • args:代理对象调用方法时,传递的实际参数

现在我们只需要修改invoke中的代码就可以改变输出

 @Override
 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        //反射
        Object result = method.invoke(customer, args);
        System.out.println("已经接受订单,正前往取餐途中...");
        System.out.println("已经取餐,正在配送...");
        return result+",已经搅拌均匀";
    }

输出:

已经接受订单,正前往取餐途中...
已经取餐,正在配送...
已经下单点了麻辣香锅,已经搅拌均匀

我们来模拟一下jdk中的动态代理
我们把DeliveryClerk 方法中加入handler,在目标类中实现代理类


/**
 * Proxy。newProxyInstance(
         * ClassLoader loader ,
         * Class<?>[] interfaces ,
         * InvocationHandler h)
 * 方法中做的事情,从底层看一看到底jdk如何完成的动态代理
 */
public class DeliveryClerk implements OrderInterface {

    //接收外部传进来的handler
    private InvocationHandler handler;

    //InvocationHandler以有参构造的方式传进来
    public DeliveryClerk(InvocationHandler handler) {
        this.handler=handler;
    }

    @Override
    public String order(String foodName) {
        //每个方法的实现,实际上并没有做其他的事情,
        // 而是直接调用传进来的handler的invoke()方法
        //该invoke()方法就是我们在外面重写的invoke()方法
        try {
            //OrderInterface是传进来的参数
            Method method=OrderInterface.class.getMethod("order", String.class);

            String result =(String) handler.invoke(this, method, new Object[]{foodName});
            return result;
        } catch (Throwable e) {
            e.printStackTrace();
        }
        return null;
    }

    @Override
    public void test() {

    }
}

main:

public class DynamicTest {
    public static void main(String[] args) {
        //准备一个目标类对象,也就是顾客对象
        Customer customer = new Customer();

        InvocationHandler handler = new InvocationHandler() {
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                //methon反射调用
                Object result = method.invoke(customer, args);
                System.out.println("已经接受订单,正前往取餐途中...");
                System.out.println("已经取餐,正在配送...");
                return result+",已经搅拌均匀";
            }
        };

        OrderInterface deliveryClerk = new DeliveryClerk(handler);

        //调用代理对象,执行对应方法
        String result = deliveryClerk.order("麻辣香锅");
        System.out.println(result);
    }

代理类和目标类是兄弟关系:并列关系,不能互相转换

基于CGLIB(父类)的动态代理

引入jar包
asm-7.1.jar
cglib-3.3.0.jar

第三方CGLIB的动态代理技术,也是可以使用一个静态方法来创建代理对象。
它不要求目标类实现接口,但要求目标类不能是最终类,也就是不能被final修饰()final修饰的类不能被其他类继承

Enhancer . create(Class type,Callback callback)
参数作用:

  • type:指定我们要代理的目标类的字节码对象,也就是指定目标类的类型
  • callback:此单词的意思是回调,意思就是我们提供一个方法,它会在合适的时候帮我们调用它

callback就是一个接口,并不包含方法的声明。所以我们使用时通常使用它的一个子接口MethodInterceptor,此单词的意思叫做方法拦截器。
MethodInterceptor里面有一个方法intercept(Object proxy, Method method, Object[] objects, MethodProxy methodProxy)

和InvocationHandler类的invoke方法差不多
参数:
- proxy:代理类对象的一个引用,也就是Proxy . newProxyInstance的返回(我指我自己:😅),此引用几乎不会用到
- method:对应的是出发invoke执行的方法的Method对象。假如我们调用xxx方法,xxx方法就会触发invoke的执行,那么method就是xxx方法对应的反射对象(Method对象)
- objects:代理对象调用方法时,传递的实际参数
- methodProxy:方法的代理对象,一般也不作处理,这样暂时忽略

父子关系:代理对象可以用父类的引用接受的

public class DynamicTest {
    public static void main(String[] args) {
        //创建一个目标类
        Customer customer = new Customer();
        //使用CGLIB创建代理对象
        Customer deliverClerk = (Customer) Enhancer.create(customer.getClass(), new MethodInterceptor() {
            @Override
            public Object intercept(Object o, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
                if ("order".equals(method.getName())) {
                    Object result = method.invoke(customer, args);
                    System.out.println("已经接受订单,正前往取餐途中...");
                    System.out.println("已经取餐,正在配送...");
                    return result + ",额外添加佐料";
                } else {
                    return method.invoke(customer, args);
                }
            }
        });
        String result = deliverClerk.order("鱼香肉丝");
        System.out.printf(result);
    }
}

我们来模拟一下CGLIB中的动态代理

/**
 * 模拟在内存中Enhancer.create(Class type, Callback callback)方法
 */
public class DeliveryClerk extends Customer {

    private MethodInterceptor interceptor;

    public DeliveryClerk(MethodInterceptor interceptor) {
        this.interceptor = interceptor;
    }

    @Override
    public String order(String foodName) {
        try {
            Method method=Customer.class.getMethod("order", String.class);
            String result = (String)interceptor.intercept(this, method, new Object[]{foodName}, null);
            return  result;
        } catch (Throwable e) {
            e.printStackTrace();
        }
        return super.order(foodName);
    }

    @Override
    public void test() {
        super.test();
    }
}

注意Customer没有实现接口(因为CGLIB不要求目标类实现接口)

public class Customer {
    public String order(String foodName){
        return "已经下单点了"+foodName;
    }

    public void test() {

    }
}


public class DynamicTest {
    public static void main(String[] args) {
        //创建一个目标类
        Customer customer = new Customer();
        MethodInterceptor interceptor = new MethodInterceptor() {
            @Override
            public Object intercept(Object o, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
                if ("order".equals(method.getName())) {
                    Object result = method.invoke(customer, args);
                    System.out.println("已经接受订单,正前往取餐途中...");
                    System.out.println("已经取餐,正在配送...");
                    return result + ",额外添加佐料";
                } else {
                    return method.invoke(customer, args);
                }
            }
        };

        Customer deliveryClerk = new DeliveryClerk(interceptor);
        String result = deliveryClerk.order("111");
        System.out.printf(result);
    }
}

输出:

已经接受订单,正前往取餐途中...
已经取餐,正在配送...
已经下单点了111,额外添加佐料

在这里插入图片描述

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值