动态代理




动态代理简单入门

动态代理技术在实际开发中用到的非常多,所以有必要详解一下这门技术。这门技术非常难,因此学起来还是比较困难的,但我们不怕困难,定要啃下这块硬骨头。
要想理解动态代理这门技术,必须明确两个概念:

  • 代理对象存在的价值主要用于拦截对真实业务对象的访问
  • 代理对象有什么方法?
    一般来说,真实业务对象具有什么方法,那么代理对象就会具备相应的方法。

现在要生成某一个对象的代理对象,这个代理对象通常也要编写一个类来生成,所以首先要编写用于生成代理对象的类。那么如何编写生成代理对象的类呢?
要想编写生成代理对象的类,必须考虑到两个要素:

  • 代理谁?
    须设计一个类变量,以及一个构造函数,记住代理类代理哪个对象。
  • 如何生成代理对象?
    设计一个方法生成代理对象(在方法内编写代码生成代理对象是此处编程的难点)

Java提供了一个Proxy类,调用它的newInstance方法可以生成某个对象的代理对象,使用该方法生成代理对象时,需要三个参数:

  1. 生成代理对象使用哪个类装载器
  2. 生成哪个对象的代理对象,通过接口指定
  3. 生成的代理对象的方法里干什么事,由开发人员编写handler接口的实现来指定

初学者必须理解,或不理解必须记住的2件事情:

  • Proxy类负责创建代理对象时,如果指定了handler(处理器),那么不管用户调用代理对象的什么方法,该方法都是调用处理器的invoke方法。
  • 由于invoke方法被调用需要三个参数:代理对象方法方法的参数,因此不管代理对象哪个方法调用处理器的invoke方法,都必须把自己所在的对象、自己(调用invoke方法的方法)、方法的参数传递进来。

相关概念看完之后,大家可能还是处在云里雾里中,但没关系,我们接下来通过一个例子来领大家入门。
李宇春,网上称春哥(在此处我无意诋毁她),她在还没成名前,一些大老板可能会点她唱歌或跳舞,那么她就会自己去给这些金主唱歌或跳舞,可能是有些许迫不得已吧!但是她成名之后,就会有自己的代理人,即经纪人,一些金主点她唱歌或跳舞时,其代理人就会跳出来,拦截对李宇春的访问,这个时候由代理人出面,若要我代理的对象——李宇春唱歌,必须给1万美刀;若要我代理的对象——李宇春跳舞,必须给2万美刀。这个场景用代码来描述就应该是这样。
首先设计真实的业务对象,即李宇春。

public class Liyuchun {
    public void sing() {
        System.out.println("春哥唱歌了!!");
    }
    public void dance() {
        System.out.println("春哥跳舞了!!");
    }
}
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

接下来我们就要编写生成代理对象的类,首先明确其要代理的对象是李宇春,接着就要设计一个方法生成代理对象(即代理人)。

public class LiyuchunProxy {
    private Person chunchun = new Liyuchun();

    public Xxx(返回值) createProxy() {
        return ...
    }
}
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

这儿,我们就要思考生成代理对象的方法——createProxy()的返回值的类型应该是什么了?不可能是李宇春,即Liyuchun,那到底应该是什么类型呢?在Java里面有这样一个约定:要想创建某一个对象的代理对象,那么该对象必须实现一个接口
由于李宇春是一个人,所以我们抽出一个的接口。

public interface Person {
    void sing();
    void dance();
}
 
 
  • 1
  • 2
  • 3
  • 4

真实的业务对象(即李宇春)的代码就应修改为:

public class Liyuchun implements Person {

    @Override
    public void sing() {
        System.out.println("春哥唱歌了!!");
    }

    @Override
    public void dance() {
        System.out.println("春哥跳舞了!!");
    }

}
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

那这样我们就明确了生成代理对象的方法——createProxy()的返回值的类型了,即为Person,通过该方法产生李宇春的代理,李宇春的代理同样也是一个人,对不对?接下来,我们使用Java提供了的Proxy类,调用它的newInstance方法生成李宇春的代理对象(即代理人),代码大概是这样:

public class LiyuchunProxy {
    private Person chunchun = new Liyuchun();

    public Person createProxy() {
        // 产生某个对象的代理对象
        return (Person) Proxy.newProxyInstance(LiyuchunProxy.class.getClassLoader(), chunchun.getClass().getInterfaces(), new InvocationHandler() {
            /*
             * proxy:把代理对象自身传递进来
             * method:代表当前调用的方法
             * args:当前调用方法的参数
             */
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                在这里面编写代码来指定产生的代理对象干什么事情...
                return null;
            }
        });
    }
}
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19

假设这样的生成代理对象的类编写好了,有些金主来点春哥唱歌或跳舞,那么其代理人拦截下来之后,就调用代理人自己的唱歌或跳舞方法。这样代码就是这样的:

public class Demo {
    public static void main(String[] args) {
        LiyuchunProxy proxy = new LiyuchunProxy();
        Person person = proxy.createProxy();
        person.sing();
        person.dance();
    }
}
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

只要调用代理对象的唱歌、跳舞方法,实际上都是调用invoke方法里面的代码,即invoke方法由唱歌、跳舞方法来调用,当唱歌、跳舞方法来调用invoke方法做事情的时候,会给invoke方法传递一些参数进来。这说明代理对象到底干什么事情是由invoke方法里面的代码来决定的
这样我们再回过头来彻底编写完生成代理对象的类。

public class LiyuchunProxy {
    private Person chunchun = new Liyuchun();

    public Person createProxy() {
        // 产生某个对象的代理对象
        return (Person) Proxy.newProxyInstance(LiyuchunProxy.class.getClassLoader(), chunchun.getClass().getInterfaces(), new InvocationHandler() {
            /*
             * proxy:把代理对象自身传递进来
             * method:代表当前调用的方法
             * args:当前调用方法的参数
             */
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                // 在这里面编写代码来指定产生的代理对象干什么事情
                String methodName = method.getName();
                if (methodName.equals("sing")) {
                    System.out.println("拿1万刀来!!!");
                    method.invoke(chunchun, args); // 春春唱歌了
                } else if (methodName.equals("dance")) {
                    System.out.println("拿2万刀来!!!");
                    method.invoke(chunchun, args); // 春春跳舞了
                } else {
                    System.out.println("春哥不支持这个功能!!!"); // 比如金主要春哥陪酒,但春哥怎能是那样的人呢!
                }
                return null;
            }
        });
    }
}
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29

动态代理进阶

动态代理技术怎可能像上面那样简单呢?动态代理入门之后,我们再来看看还算是比较复杂的动态代理。
金主除了点春哥唱歌之外,还要指定她专门唱哪首歌,春哥唱完歌之后还要对金主说一声谢谢;他们除了点春哥跳舞之外,还要指定她专门跳什么舞,春哥跳完舞之后还得给金主们一个飞吻!在这样的场景下,Person接口的代码应修改为:

public interface Person {
    String sing(String name);
    String dance(String name);
}
 
 
  • 1
  • 2
  • 3
  • 4

真实业务对象(即李宇春)的代码应修改为:

public class Liyuchun implements Person {

    @Override
    public String sing(String name) {
        System.out.println("春哥唱"+name+"歌了!!");
        return "谢谢哟!!!";
    }

    @Override
    public String dance(String name) {
        System.out.println("春哥跳"+name+"舞了!!");
        return "飞吻!!!";
    }

}
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

生成代理对象的类的代码应修改为:

public class LiyuchunProxy {
    private Person chunchun = new Liyuchun();

    public Person createProxy() {
        // 产生某个对象的代理对象
        return (Person) Proxy.newProxyInstance(LiyuchunProxy.class.getClassLoader(), chunchun.getClass().getInterfaces(), new InvocationHandler() {
            /*
             * proxy:把代理对象自身传递进来
             * method:代表当前调用的方法
             * args:当前调用方法的参数
             */
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                // 在这里面编写代码来指定产生的代理对象干什么事情
                String methodName = method.getName();
                if (methodName.equals("sing")) {
                    System.out.println("拿1万刀来!!!");
                    /*
                     * 春春要唱歌了,唱完歌还要对金主们说声:谢谢哟!!!
                     * 由于是代理对象拦截了对真实业务对象的访问,所以若是不将"谢谢哟!!!"返回,
                     * 结果就是其返回给代理对象了,金主们肯定要不爽了。
                     */
                    return method.invoke(chunchun, args);
                } else if (methodName.equals("dance")) {
                    System.out.println("拿2万刀来!!!");
                    /*
                     * 春春要跳舞了,跳完舞还要给金主们一个飞吻!!!
                     * 同样,若是不将"飞吻!!!"返回,
                     * 结果就是其返回给代理对象了,金主们肯定要日娘了。
                     */
                    return method.invoke(chunchun, args);
                } else {
                    System.out.println("春哥不支持这个功能!!!"); // 比如金主要春哥陪酒,但春哥怎能是那样的人呢!
                }
                return null;
            }
        });
    }
}
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39

这样,金主要点春哥唱《江南皮革厂》,并且还要我们的春哥跳钢管舞,那么代码就该是这样的:

public class Demo {
    public static void main(String[] args) {
        LiyuchunProxy proxy = new LiyuchunProxy();
        Person person = proxy.createProxy();

        String result = person.sing("江南皮革厂");
        System.out.println(result);
        result = person.dance("钢管舞");
        System.out.println(result);

    }
}
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

运行结果:在Eclipse的控制台打印
这里写图片描述




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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值