JAVA动态代理

本文详细介绍了动态代理的设计模式,包括JDK动态代理和Cglib动态代理的工作原理,以及如何通过接口实现无侵入式地为已有方法添加额外功能。以杨超越为例,展示了如何创建代理对象并调用其方法,实现代码扩展和职责转移。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

动态代理是一种设计模式,它允许在不修改原对象的基础上增强该对象的方法。动态代理分为两种类型:JDK动态代理和Cglib动态代理。

  • JDK动态代理:它针对接口进行代理,需要定义被代理的接口和其实现类。JDK动态代理通过实现InvocationHandler接口并重写invoke方法来定义方法的增强方式。在运行过程中,会生成一个代理类,如XXProxy.class,其中代理对象中实现的接口调用的是invoke方法,该方法中植入了代理对象的逻辑。客户端通过代理类来调用真实对象的方法。
  • Cglib动态代理:它可以在运行期间扩展Java类和Java接口。Cglib动态代理通过引入jar包cglib,使用增强器Enhancer去创建一个业务代理类,实质上是为需要代理的真实类生成了一个子类,然后通过回调时调用intercept方法植入逻辑。

动态代理的核心方法是通过newInstance创建真实对象,并通过反射机制获取构造函数来构造真实对象,从而在运行过程中生成了代理类。这样,客户端调用时,实际上是通过代理类来间接访问真实对象。

比如说在Student类中有一个eat方法,它有个动作,“盛饭”,现在我想要在eat方法中添加两个动作“拿筷子”,“吃饭”。若直接添加在eat方法中,这属于侵入式修改,在实际开发中一般不会这样做。
image.png
我们可以把新增的代码交给一个“代理人”让他帮你处理
当需要使用该eat方法时,可以先找代理运行新增的方法,然后让代理再去找Student
如图:
image.png
这样就实现了无侵入式的给代码增加额外的功能,使得原本的方法体简洁明了,


具体实例:
杨超越是一个大明星,把她看作一个,她有唱歌跳舞的方法。
假如说有人要请她去唱歌、跳舞
但是开始唱歌需要一系列准备活动:“准备话筒,收钱”
跳舞也是一样需要准备:“准备场地,收钱”

这些行为肯定不能交给杨超越去做,她只需要专注于唱歌跳舞即可
所以我们把准备活动的行为交给代理去做。

即对象如果嫌身上干的事太多的话,可以通过代理来转移部分职责。

假如说这时有人需要杨超越唱歌跳舞,客户不可能直接先找到杨超越本人,肯定是找到她的代理。代理先完成准备工作,再去找杨超越唱歌跳舞

引用:

所以说代理人也需要有,唱歌、跳舞的方法

即对象有什么方法想被代理,代理就一定要有对应的方法

image.png

  • 代理如何知道自己需要代理什么方法?

可以定义一个接口:接口内就是需要被代理的方法,
杨超越本身也要实现该接口!


现在就为杨超越创建一个代理!
代码实现:

思路逻辑如下:

1.BigStar类,用于创建杨超越的实例对象

//注意这里还未实现了任何接口
public class BigStar{
    //属性:姓名
    private String name;

    //无有参构造
    public BigStar() {
    }

    public BigStar(String name) {
        this.name = name;
    }

    /**
     * 唱歌
     * @param
     * @return
     */
    public String sing(String song){
        System.out.println(this.name+"正在唱"+song);
        return "谢谢大家";
    }

    /**
     * 跳舞
     *
     */
    public void dance(){
        System.out.println(this.name+"正在跳舞");
    }

    //在该例子中ser、get、toString方法用不上
}

2.Show接口:里面放明星需要被代理的方法

public interface Show {

    //我们可以把所有想要被代理的方法定义在接口当中
    //唱歌
    public abstract String sing(String song);
    //
    //    //跳舞
    public abstract void dance();
}

3.这时BigStar就要实现Show接口,如图:
image.png

4.ProxyUtil类:代理设计类

  1. 首先在该类中创建一个方法public static Show createProxy(BigStar y)
  2. 再调用java.lang.reflect.Proxy类的newProxyInstance(…)方法,并用接口show接收,Show s=(Show) Proxy.newProxyInstance();表示 为实现了Show接口的类(即大明星类)创建一个代理对象

接口Show定义了两个抽象方法:sing(String name)和dance()。
String sing(String song):这是一个抽象方法,表示代理对象可以“唱歌”,并接受一个表示歌曲名字的参数。
void dance():另一个抽象方法,表示代理对象可以“跳舞”。
通过Proxy.newProxyInstance()创建的代理对象将能够调用这些方法,并且在实际执行Show接口中定义的方法前或后插入自定义逻辑(如权限检查、日志记录等)。

/**
 * 当前类:
 * 设计代理人
 */
public class ProxyUtil {

    /*
     *
     * ***方法的作用:
     *       传一个明星的对象,创建一个代理
     *
     *  形参:
     *       被代理的明星实例对象
     *
     *  返回值:
     *       创建好的代理对象
     *
     *
     *
     * 需求:
     *   外面的人想要大明星唱一首歌
     *   1. 获取代理的对象
     *      代理对象 = ProxyUtil.createProxy(大明星的对象);
     *   2. 再调用代理的唱歌方法
     *      代理对象.唱歌的方法("只因你太美");
     * */

    //静态方法:方便调用

    public static Show createProxy(BigStar y) {
/* java.lang.reflect.Proxy类:提供了为对象产生代理对象的方法:
public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)
参数一:用于指定用哪个类加载器,去加载生成的代理类(一般用本类)
参数二:指定接口,这些接口用于指定生成的代理长什么,也就是有哪些方法(是接口数组)
参数三:用来指定生成的代理对象要干什么事情(匿名内部类)
*/
        //目的:为实现了Show接口的类(即大明星类)创建一个代理对象 / s:代理对象
        Show s = (Show) Proxy.newProxyInstance(
                ProxyUtil.class.getClassLoader(),//参数一:用于指定用哪个类加载器,去加载生成的代理类(一般用本类)
                new Class[]{Show.class},//参数二:指定接口,这些接口用于指定生成的代理长什么,也就是有哪些方法(是接口数组)
                new InvocationHandler() {//参数三:用来指定生成的代理对象要干什么事情(匿名内部类)
                    @Override
                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                        /*
                         * 参数一:代理的对象
                         * 参数二:要运行的方法 如:sing、dance
                         * 参数三:调用sing方法时,传递的实参
                         * */
                        if (method.getName().equals("sing")) {
                            System.out.println("准备话筒,收钱");
                        } else if (method.getName().equals("dance")) {
                            System.out.println("准备场地,收钱");
                        }

                        //***执行到这,说明这时代理的事做完了,现在去找杨超越唱歌或跳舞
                        //参数一:杨超越的实现对象,参数二:方法实参
                        return method.invoke(y, args);//谢谢
                    }
                }
        );
        //方法结束,返回创建好的代理人
        return s;
    }


}

5.测试类:

public class Test {
    public static void main(String[] args) {

        /**
         * 需求:
         *   外面的人想要大明星唱一首歌
         *   1. 获取代理的对象
         *      代理对象 = ProxyUtil.createProxy(大明星的对象);
         *   2. 再调用代理的唱歌方法
         *      代理对象.唱歌的方法("只因你太美");
         */
        //调用ProxyUtil中的静态方法创建一个代理,发现要传递一个大明星的实例对象

        Show proxy = ProxyUtil.createProxy(new BigStar("杨超越"));
        //再用代理对象去调用唱歌
        String results = proxy.sing("北京欢迎你");//因为sing方法有返回值,所以接收一下
        System.out.println(results);
        //或跳舞方法
        proxy.dance();


    }
}

image.png
总览图:

Snipaste_2024-02-18_22-02-00.png

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

成果、

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值