动态代理

什么是动态代理

什么是代理模式(Proxy)
定义:给目标对象提供一个代理对象,并由代理对象控制对目标对象的引用
在代理模式中,是需要代理对象和目标对象实现同一个接口(如果是不同的接口,那就是适配器模式了)
代理模式是Java中常见的一种模式,英文名字叫走Proxy或者Surrogate,代理的本意是一个人代表另一个人,或者一个机构代表另一个机构,采取行动,因而,代理和现实生活中的中介有很大的类似,你买房子、卖房子,可以自己去操作,但是需要了解和买卖房产无关的细节,如契税等,而找一个中介,则不用关心这些与买卖房产无直接关系的中间细节,只关心业务本身。

为什么要用代理
最最最主要的原因就是,在不改变目标对象方法的情况下对方法进行增强,比如,我们希望对方法的调用增加日志记录,或者对方法的调用进行拦截,等等…

动态代理
在java的动态代理机制中,有两个重要的类或接口,一个是 InvocationHandler(Interface)、另一个则是 Proxy(Class),这一个类和接口是实现我们动态代理所必须用到的。
每一个动态代理类都必须要实现InvocationHandler这个接口,并且每个代理类的实例都关联到了一个handler,当我们通过代理对象调用一个方法的时候,这个方法的调用就会被转发为由InvocationHandler这个接口的 invoke 方法来进行调用。

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

(1)Proxy类的代码被固定下来,不会因为业务的逐渐庞大而庞大;
(2)可以实现AOP编程,这是静态代理无法实现的;
(3)解耦,如果用在web业务下,可以实现数据层和业务层的分离。
(4)动态代理的优势就是实现无侵入式的代码扩展。
静态代理这个模式本身有个大问题,如果类方法数量越来越多的时候,代理类的代码量是十分庞大的。所以引入动态代理来解决此类问题

动态代理的两种方式以及区别

JDK动态代理:利用反射机制生成一个实现代理接口的匿名类,在调用具体方法前调用InvokeHandler来处理。

CGlib动态代理:利用ASM(开源的Java字节码编辑库,操作字节码)开源包,将代理对象类的class文件加载进来,通过修改其字节码生成子类来处理。

区别:JDK代理只能对实现接口的类生成代理;CGlib是针对类实现代理,对指定的类生成一个子类,并覆盖其中的方法,这种通过继承类的实现方式,不能代理final修饰的类。

总结:1.JDK代理使用的是反射机制实现aop的动态代理,CGLIB代理使用字节码处理框架asm,通过修改字节码生成子类。所以jdk动态代理的方式创建代理对象效率较高,执行效率较低,cglib创建效率较低,执行效率高;2.JDK动态代理机制是委托机制,具体说动态实现接口类,在动态生成的实现类里面委托hanlder去调用原始实现类方法,CGLIB则使用的继承机制,具体说被代理类和代理类是继承关系,所以代理类是可以赋值给被代理类的,如果被代理类有接口,那么代理类也可以赋值给接口。

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

        /**
         * 动态代理
         * 特点:字节码随用随创建,随用随加载,不用上来就创建类
         * 作用:不修改源码的基础上对方法增强
         * 分类:
         *      基于接口的动态代理:涉及的类 Proxy JDK官方提供
         *      基于子类的动态代理
         *
         * 基于接口的动态代理:
         * 如何创建代理对象: 使用Proxy类中的newProxyInstance方法
         * 创建代理对象的要求: 被代理对象最少实现一个接口,如果没有则不能使用
         * newProxyInstance方法的参数:
         *      ClassLoader: 类加载器,它用于加载代理对象字节码,和被代理对象使用相同的加载器(固定写法)
         *      Class[]: 字节码数组,用于让代理对象和被代理对象有相同的方法(固定写法)
         *      InvocationHandle: 用于提供增强的代码
         *          它是让我们写如何代理,我们一般都是写一个该接口的实现类,通常情况下都是匿名内部类,但不是必须的
         *          此接口的实现类都是谁用谁写
         */

在这里插入图片描述
接口 IProducer.java

package com.proxy;
/**
 * 对生产厂家要求的接口
 */
public interface IProducer {
    public void saleProduct(float money);
    public void afterService(float money);
}

实现类 Producer.java

package com.proxy;
/**
 * 生产者
 */
public class Producer implements IProducer{
    public void saleProduct(float money) {
        System.out.println("销售产品,并拿到钱"+money);
    }
    public void afterService(float money) {
        System.out.println("提供售后服务,并拿到钱"+money);
    }
}

代理增强 Client.java

package com.proxy;
import org.junit.Test;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

public class Client {
    @Test
    public void test1() {
        final Producer producer = new Producer();
        //producer.saleProduct(10000f);
       
        IProducer proxyProducer = (IProducer) Proxy.newProxyInstance(producer.getClass().getClassLoader(), producer.getClass().getInterfaces(), new InvocationHandler() {
            /**
             * 作用: 执行被代理对象的任何接口方法都会经过该方法
             * @param proxy: 代理对象的引用
             * @param method: 当前执行的方法
             * @param args: 当前执行方法所需的参数
             * @return    和被代理对象方法有相同的返回值
             * @throws Throwable
             */
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                //提供增强的代码
                Object returnValue = null;
                //1、获取方法执行的参数
                Float money = (Float) args[0];
                //2、判断当前方法是不是销售
                if ("saleProduct".equals(method.getName())) {
                    returnValue = method.invoke(producer,money*0.8f);
                }
                return returnValue; //上面的producer需要用final修饰,在匿名内部类中使用必须用final修饰
            }
        });
        proxyProducer.saleProduct(10000f); //这里的10000不加f也能运行成功
    }
}

运行结果
在这里插入图片描述

基于子类的动态代理(CGlib动态代理)

首先导入jar包依赖

        <!-- https://mvnrepository.com/artifact/cglib/cglib -->
        <!--用于动态代理中的基于子类的动态代理-->
        <dependency>
            <groupId>cglib</groupId>
            <artifactId>cglib</artifactId>
            <version>2.2.2</version>
        </dependency>

在这里插入图片描述

        /**
         * 动态代理
         * 特点:字节码随用随创建,随用随加载,不用上来就创建类
         * 作用:不修改源码的基础上对方法增强
         * 分类:
         *      基于接口的动态代理:涉及的类 Proxy JDK官方提供
         *      基于子类的动态代理:涉及的类 Enhancer 第三方cglib库提供
         *
         * 基于子类的动态代理:
         * 如何创建代理对象: 使用Enhancer类中的create 方法
         * 创建代理对象的要求: 被代理类不能是最终类,即不能被final修饰
         * create方法的参数:
         *      Class: 字节码,它用于指定被代理对象的字节码
         *      Callback: 用于提供增强的代码
         *          它是让我们写如何代理,我们一般都是写一个该接口的实现类,通常情况下都是匿名内部类,但不是必须的
         *          此接口的实现类都是谁用谁写
         *          我们一般写的都是该接口的子接口实现类:MethodInterceptor
         */

在这里插入图片描述
实现类 Producer.java

package com.cglib;
/**
 * 生产者
 */
public class Producer {
    public void saleProduct(float money) {
        System.out.println("销售产品,并拿到钱"+money);
    }
    public void afterService(float money) {
        System.out.println("提供售后服务,并拿到钱"+money);
    }
}

代理增强 Client.java

package com.cglib;

import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import org.junit.Test;
import java.lang.reflect.Method;

public class Client {
    @Test
    public void test1() {
        final Producer producer = new Producer();
  
        Producer cglibProducer = (Producer) Enhancer.create(producer.getClass(), new MethodInterceptor() {
            /**
             * 执行被代理对象的任何接口方法都会经过该方法
             * @param proxy
             * @param method
             * @param args
             *      以上三个参数和基于接口的动态代理中的invoke方法的参数是一样的
             * @param methodProxy:当前执行方法的代理对象
             * @return  一样的
             * @throws Throwable
             */
            public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
                //提供增强的代码
                Object returnValue = null;
                //1、获取方法执行的参数
                Float money = (Float) args[0];
                //2、判断当前方法是不是销售
                if ("saleProduct".equals(method.getName())) {
                    returnValue = method.invoke(producer,money*0.8f);
                }
                return returnValue; //上面的producer需要用final修饰,在匿名内部类中使用必须用final修饰
            }
        });
        cglibProducer.saleProduct(12000f);
    }
}

运行结果
在这里插入图片描述

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值