java动态代理

原文地址:java动态代理

代理

最近在学习 Spring 框架,AOP涉及到动态代理的知识,故整理一下,了解动态代理之前,我们首先应该了解下什么是代理。

代理是基本的设计模式之一,它是你为了提供额外的或不同的操作,而插入的用来代替的"实际"对象的
对象,这些操作通常涉及与"实际"对象的通信,因此代理通常充当着中间人的角色。

当我们希望跟踪对方法的调用,或者希望度量这些调用的开销时,我们肯定不希望将这些代码合并到业务代码中,此时我们就可以使用代理来实现。下面我们通过一个示例了解下代理

代码演示1

定义一个接口

public interface Behavior {
    void eat();
    void say(String str);
}

接口的实现类

public class Student implements Behavior {

    @Override
    public void eat() {
        System.out.println("我是学生,我要吃学生餐");
    }

    @Override
    public void say(String str) {
        System.out.println(str);
    }
}

定义一个代理类

public class SimpleProxy implements Behavior {

    private Behavior behavior;

    public SimpleProxy(Student behavior) {
        this.behavior = behavior;
    }

    @Override
    public void eat() {
        System.out.println("---- 方法执行前逻辑 ----");
        behavior.eat();
        System.out.println("---- 方法执行后逻辑 ----");
    }

    @Override
    public void say(String str) {
        System.out.println("---- 方法执行前逻辑 ----");
        behavior.say(str);
        System.out.println("---- 方法执行后逻辑 ----");
    }
}

测试

    @Test
    public void test(){
        SimpleProxy simpleProxy = new SimpleProxy(new Student());
        simpleProxy.eat();
        System.out.println("\n");
        simpleProxy.say("hello,teacher");
    }
    

打印

动态代理

Java的动态代理比代理的思想更进一步。因为它可以动态的创建代理并动态的处理对所代理方法的调用在动
态代理上所做的所有调用都会被重定到单一的调用处理器上他的工作是揭示调用的类型并确定相应的对策

实现Java动态代理的过程中涉及到一个接口 InvocationHandler 以及一个类 Proxy。

接口:InvocationHandler

    InvocationHandler is the interface implemented by the invocation handler 
    of a proxy instance.
    译:
       InvocationHandler是代理实例化对象的调用处理程序(invocation handler)实现的接口。
    
    Each proxy instance has an associated invocation handler.When a method is 
    invoked on a proxy instance, the method invocation is encoded and dispatched 
    to the {@code invoke}method of its invocation handler.
    译:
        每个代理实例化对象都具有一个关联的调用处理程序(invocation handler)。当代理实例化对象
        调用方法时,将对方法调用进行编码并将其指派到它的调用处理程序的 invoke 方法

InvocationHandler 仅有一个方法invoke()

      Object invoke(Object proxy,Method method,Object[] args)throws Throwable
           * @param proxy 调用方法的代理实例化对象
           * @param method 指代的是我们所要调用真实对象的某个方法的Method对象
           * @param args 指代的是调用真实对象某个方法时接受的参数

类:Proxy

    Proxy provides static methods for creating dynamic proxy classes and instances,
    and it is also the superclass of all dynamic proxy classes created by those 
    methods.
    译:
        Proxy 提供了创建动态代理类和实例化对象的方法,同时也是这些方法创建的所有动态代理类的超类

方法:newProxyInstance

    public static Object newProxyInstance(ClassLoader loader,
                                  Class<?>[] interfaces,
                                  InvocationHandler h)
                           throws IllegalArgumentException
     该方法返回一个指定接口的动态代理实例化对象,该接口可以将方法调用指派到指定的调用处理程
     序(invocation handler)
       
     参数:
        loader - 定义代理类的类加载器
        interfaces - 代理类要实现的接口列表
        h - 指派方法调用的调用处理程序

代码演示2

定义一个接口

public interface Behavior {
    void eat();
    void say(String str);
}

接口的实现类

public class Student implements Behavior {

    @Override
    public void eat() {
        System.out.println("我是学生,我要吃学生餐");
    }

    @Override
    public void say(String str) {
        System.out.println(str);
    }
}

创建一个动态代理处理器

/**
 * Created by lizhen on 2018/9/3 下午8:51
 * 定义一个动态代理处理器,该类必须实现 InvocationHandler 接口
 */
public class DynamicProxyHandler implements InvocationHandler {

    // 目标对象
    private Object target;

    public DynamicProxyHandler(Object target){
        this.target = target;
    }

    /**
     * @param proxy 调用方法的代理实例
     * @param method 指代的是我们所要调用真实对象的某个方法的Method对象
     * @param args 指代的是调用真实对象某个方法时接受的参数
     */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

        System.out.println("------- 方法调用前操作 ------");
        System.out.println("proxy:" + proxy.getClass());
        System.out.println("Method:" + method);
        method.invoke(target, args);// 当代理对象调用真实对象的方法时,其会自动的跳转到代理对象关联的handler对象的invoke方法来进行调用
        System.out.println("------- 方法调用后操作 ------");

        return method.invoke(target, args);
    }
}

测试

   @Test
    public void test() {

        // 我们要代理的真实对象
        Student student = new Student();

        // 我们要代理哪个真实对象,就将该对象传进去,最后是通过该真实对象来调用其方法的
        InvocationHandler handler = new DynamicProxyHandler(student);

        /*
         *  实例化代理对象
         * loader:定义代理类的类加载器,一个ClassLoader对象,定义了由哪个ClassLoader对象来对生成的代理对象进行加载
         * interfaces: student.getClass().getInterfaces(),我们这里为代理对象提供的接口是真实对象所实行的接口,表示我要代理的是该真实对象,这样我就能调用这组接口中的方法了
         * h: 一个InvocationHandler对象,表示的是当我这个动态代理对象在调用方法的时候,会关联到哪一个InvocationHandler对象上
         */
        Behavior proxies = (Behavior) Proxy.newProxyInstance(student.getClass().getClassLoader(), student.getClass().getInterfaces(), handler);
        System.out.println(proxies.getClass().getName());

        proxies.eat();

        System.out.println("\n");
        System.out.println("=========================== 看 我 华 不 华 丽 ================================");
        System.out.println("\n");

        proxies.say("hello,teacher");
    }
    

打印

代码演示3

   通过内部类定义具体的InvocationHandler

定义接口、实现类同上

创建 ProxyFactory

public class ProxyFactory {

    // 目标对象
    private Object target;

    public ProxyFactory(Object target){
        this.target = target;
    }

    // 给目标对象生成代理对象(内部类)
    public Object getProxyInstance(){
        return Proxy.newProxyInstance(
                target.getClass().getClassLoader(),
                target.getClass().getInterfaces(),
                new InvocationHandler(){
                    @Override
                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                        System.out.println("------- 方法调用前操作 ------");
                        System.out.println("proxy:" + proxy.getClass());
                        if (args != null){
                            for (Object obj: args) {
                                System.out.println("args:" + obj);
                            }
                        }
                        System.out.println("Method:" + method);
                        method.invoke(target, args);// 当代理对象调用真实对象的方法时,其会自动的跳转到代理对象关联的handler对象的invoke方法来进行调用
                        System.out.println("------- 方法调用后操作 ------");

                        return null;
                    }
                }
        );
    }
}

测试

@Test
public void test(){
    Student student = new Student();
    ProxyFactory proxyFactory = new ProxyFactory(student);
    Behavior behavior = (Behavior)proxyFactory.getProxyInstance();
    System.out.println("\n");
    System.out.println(behavior.getClass().getName());
    System.out.println("\n");

    behavior.eat();

    System.out.println("\n");
    System.out.println("=========================== 看 我 华 不 华 丽 ================================");
    System.out.println("\n");

    behavior.say("hello,teacher");
}

打印

Reference

1.java的动态代理机制详解 以及文章的评论使我受益匪浅

2. Java Platform SE 8

3.《Thinking In Java》

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 218,546评论 6 507
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,224评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 164,911评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,737评论 1 294
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,753评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,598评论 1 305
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,338评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,249评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,696评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,888评论 3 336
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,013评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,731评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,348评论 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,929评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,048评论 1 270
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,203评论 3 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,960评论 2 355

推荐阅读更多精彩内容

  • 转自https://blog.csdn.net/briblue/article/details/73928350前...
    扎Zn了老Fe阅读 311评论 1 0
  • 原文 代理模式 代理模式是常用的 Java 设计模式,它的特征是代理类与委托类有同样的接口,代理类主要负责为委托类...
    Coder_Y阅读 1,182评论 0 1
  • 在我们计统八七.二班同学的记忆史上一定不会忘记2017年9月23和24这两个幸福的日子,天南地北、四面八方...
    伊人_a05b阅读 706评论 0 1
  • 这是詹姆斯连续进东部决赛的第八年。 这八年的夏天里,不管是詹黑还是詹密,都会摇头惊叹,这个男人的极限到底在哪里。 ...
    强壮左手阅读 1,659评论 6 12
  • 窗前最后一点雪花从半空中摇摇晃晃地飘落 放下手中的笔,远远望去,窗外一片银装素裹的世界 地理课...
    夜亮阅读 227评论 0 0