`
kingsword
  • 浏览: 6946 次
社区版块
存档分类
最新评论

Java动态代理总结

    博客分类:
  • Java
 
阅读更多

a) 什么是Proxy Design Pattern?

在说Java动态代理之前,先复习下代理模式(Proxy Pattern)。如下类图所示:

一旦有了代理,它将代替被代理类去参与程序的业务逻辑。代理和被代理都实现了同样的接口,并且代理类会hold一个被代理类,这样当在代理上调用业务方法的时候,代理可以把真正的核心逻辑仍然让被代理类去完成。

 

b) Java的动态代理(Dynamic Proxy) 是怎么回事?

简单说动态代理是Java提供的一种机制,它是在运行的时候基于一个或多个接口(Interface)创造出实现该接口或多个接口的代理类实例。值得再加强一提的是,Dynamic Proxy是基于接口 的,它能为单个或者多个接口创建一个代理类实例。这个机制主要有两个类参与实现:

    - java.lang.reflect.Proxy

    - java.lang.reflect.InvocationHandler

 

1. java.lang.reflect.Proxy

Proxy是一个实现了Serializable的具体类,它里面的方法主要就是用来根据必要的条件创建出指定接口或者是多个接口的代理类。下面就是其提供的常用的public方法:

private final static Class[] constructorParams =
	{ InvocationHandler.class };
public static Object newProxyInstance(ClassLoader loader,
					  Class<?>[] interfaces,
					  InvocationHandler h)
	throws IllegalArgumentException
    {
	if (h == null) {
	    throw new NullPointerException();
	}

	/*
	 * Look up or generate the designated proxy class.
	 */
	Class cl = getProxyClass(loader, interfaces);

	/*
	 * Invoke its constructor with the designated invocation handler.
	 */
	try {
	    Constructor cons = cl.getConstructor(constructorParams);
	    return (Object) cons.newInstance(new Object[] { h });
	} catch (NoSuchMethodException e) {
	    throw new InternalError(e.toString());
	} catch (IllegalAccessException e) {
	    throw new InternalError(e.toString());
	} catch (InstantiationException e) {
	    throw new InternalError(e.toString());
	} catch (InvocationTargetException e) {
	    throw new InternalError(e.toString());
	}
    }

 

从这个方法的参数可以知道,要成功创建一个Proxy Instance,要具备以下条件:

1) ClassLoader ,一般使用当前对象的就可以了
2) Class<?>[] ,接口数组,这就是生成的代理类最后实现的接口
3) InvocationHandler ,配备一个实现了这个接口的类实例是一定 需要的,当在生成好的代理类实例上调用接口中的方法时,这些方法调用会被派发到这个InvocationHandler中去,下面将分作一节详细说它。

 

2. java.lang.reflect.InvocationHandler

这是一个接口,里面只有一个方法如下:

public Object invoke(Object proxy, Method method, Object[] args)
	throws Throwable;

  上面说到了,当在动态产生好的代理类实例上调用接口方法时,这个方法调用会被转到这个invoke方法里面来,就连Object中的hashCode, equal还有toString方法也躲不过,对它们的调用会被通通转到invoke里面来。这样在实际使用中,我们让InvocationHandler的实现类hold住一个被代理的类,然后就可以在invoke的里面对被代理的类方法进行所谓的拦截了,即使在被代理类的业务方法调用前或后另作一些其他的工作了。

 

这里结合最开始的类图给出代码举例。

首先是Client

public class Client {
    public static void main(String[] args) {
        Subject concreteSubject = new ConcreteSubject();
        Class[] interfaceNeedsImp = new Class[] {Subject.class};
        InvocationHandler handler = new SubjectProxy(concreteSubject);
        ClassLoader cl = Subject.class.getClassLoader();

        //Here the type casting you must use the interface
        //if using the concrete class, shxt will happen cause
        //the returned instance is the instance of the generated proxy class
        Subject proxySubject = (Subject) java.lang.reflect.Proxy.newProxyInstance(cl,
                interfaceNeedsImp,
                handler);

        proxySubject.greet();
        System.out.println();
        proxySubject.request();
    }
}

 值得一提的是注意转型那里,要用接口来转,用具体类转要出错。

 

Subject接口

//弄了两个方法表明是业务方法
public interface Subject {
    Subject request();
    void greet();
}
 

ConcreteSubject的代码

public class ConcreteSubject implements Subject {
    public void greet() {
        System.out.println("    greet() of ConcreteSubject invoked");
    }
    public Subject request() {
        System.out.println("    request() of ConcreteSubject invoked");
        return this;
    }
}

 

SubjectProxy的代码

 

public class SubjectProxy implements InvocationHandler {
    private Subject aConcreteSubject;
    public SubjectProxy(Subject target) {
        aConcreteSubject = target;
    }
    public Object invoke(Object proxy, Method method, Object[] args)
            throws Throwable {
        System.out.println("Proxy prints this before " + method.getName()
                                      + ".() of ConcreteSubject");
        Object o = method.invoke(aConcreteSubject, args);
        if (o instanceof Subject) {
            System.out.println("Proxy prints this after concrete " + method.getName()
                                      + ".() of ConcreteSubject");
            return proxy;
        }
        System.out.println("Proxy prints this after concrete" + method.getName()
                                      + ".() of ConcreteSubject");
        return o;
    }
}

 这里这个类和类图中传统的Proxy Pattern有出入了,这个SubjectProxy没有去实现Subject接口,而是实现了InvocationHandler。在Client类中,Proxy会把SubjectProxy和它需要实现的Subject做绑定的,也就是下面几句Client的代码,小小的重复贴一下之前在Client中的几行关键代码如下:

Subject proxySubject = (Subject) java.lang.reflect.Proxy.newProxyInstance(cl,
                interfaceNeedsImp, //一群等待被实现的"接口"同志们
                handler);

 这差不多就是Java的动态代理机制了。下面是程序的输出,一目了然,ConcreteSubject的方法都被拦截了:

 

 

Proxy prints this before greet.() of ConcreteSubject
    greet() of ConcreteSubject invoked
Proxy prints this after concretegreet.() of ConcreteSubject

Proxy prints this before request.() of ConcreteSubject
    request() of ConcreteSubject invoked
Proxy prints this after concrete request.() of ConcreteSubject

 

额外需要注意的:

 

 

1. 网上有好多人在讨论InvocationHandler中invoke方法的第一个参数proxy怎么用?

答案是有时候接口方法会把当前接口引用作为返回值返回,参考上面例子中的request()业务方法。它返回了Subject引用,如果在代理中拦截到该方法不做特别处理的话,返回出去的就是ConcerteSubject的引用,若想继续使用SubjectProxy到底,就可以判断一下返回参数proxy。所以在上面例子里的SubjectProxy中有下面的代码段:

if (o instanceof Subject) {
            System.out.println("Proxy prints this after concrete " + method.getName()
                                        + ".() of ConcreteSubject");
            return proxy;
        }

 

2. java.lang.reflect.Proxy 的源代码写得挺精彩。它使用到了loaderToCache的一个WeakHashMap,使程序在做缓存的同时不会泄漏内存,值得学习。

 

3. 当代理需要实现多个接口,并且多个接口中有些方法duplicate了,那顺序会起决定作用,在Class[]中最前面的那个接口会被认为是方法的提供者!

  • 大小: 19.5 KB
分享到:
评论

相关推荐

    java动态代理总结(狗星例子)

    狗星例子所以java文件 博文链接:https://uule.iteye.com/blog/859862

    Java代理模式Java动态代理

    自己总结的代理模式和Java中的动态代理模式,有源码

    Java实现动态代理

    这几个文件是我当时自己总结的,这几天突然翻出来了,上传上来给需要的同学们看看,说不定有人需要呢

    Java中的动态代理

    本资源是本人学习过程中不断查找和总结的结果,希望对 有需要的 人员 有所帮助

    Java 基础核心总结 +经典算法大全.rar

    《Java 基础核心总结》 Java 概述 什么是 Java2 Java 的特点Java 开发环境 JDK JRE Java 开发环境配置 Java 基本语法 数据类型基础语法运算符 Java 执行控制流程条件语句 if 条件语句 if...else 条件语句if...else ...

    java基础学习总结笔记

    知识主要包括:Java基础常识、如何安装Java工具、Java语言的基础组成、Java面向对象、Java多线程、Java常用类、集合(重点)、IO流、GUI图形界面、网络编程、正则表达式、反射、注解、类加载器、动态代理等等,另外...

    Java基础知识点总结.docx

    Java学习更是如此,知识点总结目录如下: 目录 一、 Java概述 3 二、 Java语法基础 5 数据类型 5 运算符号 14 语句 15 函数 15 方法重载(Overloadjing)与重写(Overriding) 16 数组 17 总结 18 三、 常见关键字 ...

    java代理模式总结

    对java代理模式有个详细而深入的讲解,从jdk api的角度来讲解的。。。

    java基础核心总结归纳---参考手册--心得手册-学习资料-总结经验

    代理 17 Static 17 Final 17 接⼝和抽象类 18 接⼝ 18 抽象类 18 异常 18 认识 Exception 18 什么是 Throwable 18 常⻅的 Exception 19 与 Exception 有关的 Java 关键字 19 什么是 Error 20 内部类 20 集合 20 ...

    JAVA的反射机制与动态代理

    JAVA的反射机制与动态代理,在实际开发中的总结,该文档以pdf的格式显示!

    Java的动态代理、反射机制和数据库连接池技术

    里面有很多我做项目时候总结的ppt文档和示例。 包括很多一些Java机制用法和一下常用技术 希望大家喜欢

    Java 基础核心总结 java全方面基础知识 java开发人员必备

    Java 基础核心总结 java全方面基础知识 java开发人员必备 通过带着读者手写简化版 Spring 框架,了解 Spring 核心原理。在手写Spring 源码的过程中会摘取整体框架中的核心逻辑,简化代码实现过程,保留核心功能,...

    java 面试题 总结

    EJB容器是EJB组件的代理,EJB组件由容器所创建和管理。客户通过容器来访问真正的EJB组件。 21、Static Nested Class 和 Inner Class的不同。 Static Nested Class是被声明为静态(static)的内部类,它可以不依赖...

    java深度历险

    JAVA反射与动态代理 40 基本用法 40 处理泛型 42 动态代理 42 使用案例 43 参考资料 44 JAVA I/O 45 流 45 缓冲区 47 字符与编码 48 通道 49 参考资料 52 JAVA安全 53 认证 53 权限控制 55 加密、解密与签名 57 安全...

    「Java学习+面试指南」一份涵盖大部分 Java 程序员所需要掌握的核心知识 准备 Java 面试,首选.zip

    Java 代理模式详解 BigDecimal 详解 Java 魔法类 Unsafe 详解 Java SPI 机制详解 Java 语法糖详解 集合 知识点/面试题总结 : Java 集合常见知识点&面试题总结(上) (必看 ) Java 集合常见知识点&面试题总结(下) (必...

    「Java学习+面试指南」一份涵盖大部分 Java 程序员所需要掌握的核心知识

    Java 代理模式详解 BigDecimal 详解 Java 魔法类 Unsafe 详解 Java SPI 机制详解 Java 语法糖详解 集合 知识点/面试题总结: Java 集合常见知识点&面试题总结(上) (必看 ) Java 集合常见知识点&面试题总结(下) (必...

    (java)AOP代理方面的简单介绍

    在学习java面向过程中,关于AOP代理这块,当时思路不是很清晰,借鉴了很多资料进行了解,后面把自己的一些见解整理成浅显的笔记进行加深印象,现在把它分享给学习AOP代理思路的朋友们,希望我的一点浅显的总结能够...

    Java网络高级编程

    本书第1-3章介绍了网络进程通信的主要技术。...第11章简明地介绍移动代理编程技术。 本书可作为高校计算机应用、网络信息、电子商务技术等专业级学生及研究生的教材,也可以作为Java网络编程爱好者的自学参考书。

    java23种设计模式总结

    java23种设计模式总结 . 设计模式 1.1 创建型模式 1.1.1 工厂方法 1.1.2 抽象工厂 1.1.3 建造者模式 .. 1.1.4 单态模式 .. 1.1.5 原型模式 . 1.2 结构型模式 ... 1.2.1 适配器模式 1.2.2 桥接模式 ........

Global site tag (gtag.js) - Google Analytics