前奏

代理模式
  • 代理模式是常用的java设计模式,他的特征是代理类与委托类有同样的接口,代理类主要负责为委托类预处理消息、过滤消息、把消息转发给委托类,以及事后处理消息等。 代理类与委托类之间通常会存在关联关系,一个代理类的对象与一个委托类的对象关联,代理类的对象本身并不真正实现服务,而是通过调用委托类的对象的相关方法,来提供特定的服务。
按照代理的创建时期,代理类可以分为两种。
  • 静态代理:由程序员创建或特定工具自动生成源代码,再对其编译。在程序运行前,代理类的.class文件就已经存在了。
  • 动态代理:在程序运行时,运用反射机制动态创建而成。
使用Java动态代理机制的好处:
  • 减少编程的工作量:假如需要实现多种代理处理逻辑,只要写多个代理处理器就可以了,无需每种方式都写一个代理类。
  • 系统扩展性和维护性增强,程序修改起来也方便多了(一般只要改代理处理器类就行了)。
使用Java动态代理机制的限制:
  • 目前根据GOF的代理模式,代理类和委托类需要都实现同一个接口。也就是说只有实现了某个接口的类可以使用Java动态代理机制。但是,事实上使用中并不是遇到的所有类都会给你实现一个接口。因此,对于没有实现接口的类,目前无法使用该机制。
  • 有人说这不是废话吗,本来Proxy模式定义的就是委托类要实现接口的啊!但是没有实现接口的类,该如何实现动态代理呢?
  • 当然不是没有办法,那就是大名鼎鼎的CGLib(Code Generation Library)...
基本过程
  • 首先创建委托类对象,将其以构造函数传入代理处理器,代理处理器ProxyHandler中会以Java反射方式调用该委托类对应的方法。
  • 然后使用Java反射机制中的Proxy.newProxyInstance方式创建一个代理类实例,创建该实例需要指定该实例的类加载器,需要实现的接口(即目标接口),以及处理代理实例接口调用的处理器。
  • 最后,调用代理类目标接口方法时,会自动将其转发到代理处理器中的invoke方法内,invoke方法内部实现预处理,对委托类方法调用,事后处理等逻辑。

相关类介绍

在java的动态代理机制中,有两个重要的类或接口:InvocationHandler、Proxy,这一个类和接口是实现我们动态代理所必须用到的。首先我们先来看看java的API帮助文档是怎么样对这两个类进行描述的:

一、InvocationHandler
InvocationHandler is the interface implemented by the invocation handler of a proxy instance.
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 invoke method of its invocation handler.
每一个动态代理类都必须要实现InvocationHandler这个接口,并且每个代理类的实例都关联到了一个handler,当我们通过代理对象调用一个方法的时候,这个方法的调用就会被转发为由InvocationHandler这个接口的 invoke 方法来进行调用。

我们来看看InvocationHandler这个接口的唯一一个方法 invoke 方法:
  1. Object invoke(Object proxy, Method method, Object[] args) throws Throwable
  • proxy:指代我们所代理的那个真实对象
  • method:指代的是我们所要调用真实对象的某个方法的Method对象
  • 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 这个方法
Returns an instance of a proxy class for the specified interfaces that dispatches method invocations to the specified invocation handler.
这个方法的作用就是得到一个动态的代理对象
  1. public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) throws IllegalArgumentException
  • loader:一个ClassLoader对象,定义了由哪个ClassLoader对象来对生成的代理对象进行加载
  • interfaces:一个Interface对象的数组,表示的是我将要给我需要代理的对象提供一组什么接口,如果我提供了一组接口给它,那么这个代理对象就宣称实现了该接口(多态),这样我就能调用这组接口中的方法了
  • h:一个InvocationHandler对象,表示的是当我这个动态代理对象在调用方法的时候,会关联到哪一个InvocationHandler对象上

示例

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class Test {
    public static void main(String[] args) throws Exception {
        //我们要代理的真实对象
        Subject realSubject = new RealSubject();
        //我们要代理哪个真实对象,就将该对象传进去,最后是通过该真实对象来调用其方法的
        InvocationHandler handler = new DynamicProxy(realSubject);
        //通过Proxy的newProxyInstance方法来创建我们的代理对象
        Subject subject = (Subject) Proxy.newProxyInstance(handler.getClass().getClassLoader(),//定义了由哪个ClassLoader对象来对生成的代理对象进行加载
                realSubject.getClass().getInterfaces(), //表示的是我将要给我需要代理的对象提供一组什么接口,之后我这个代理对象就会实现了这组接口
                //因为我这个代理对象实现了这组接口,所以可以将这个代理对象强转为Subject类型
                handler);//表示的是当我这个动态代理对象在调用方法的时候,会关联到哪一个InvocationHandler对象上
        
          System.out.println("代理对象的类名:" + subject.getClass().getName());//【$Proxy0】,最后一个数字表示对象的标号
        subject.rent();//通过代理对象来调用实现的那种接口中的方法,这个时候程序就会跳转到由这个代理对象关联到的 handler 中的invoke方法去执行
        System.out.println();
        subject.hello("包青天");
    }
}
//1、首先我们定义了一个Subject类型的接口
interface Subject {
    public void rent();
    public void hello(String str);
}
//2、接着,定义了一个类来实现这个接口,这个类就是我们的真实对象
class RealSubject implements Subject {
    @Override
    public void rent() {
        System.out.println("出租房屋");
    }
    @Override
    public void hello(String str) {
        System.out.println("hello:" + str);
    }
}
//3、然后,定义一个动态代理类,每一个动态代理类都必须要实现 InvocationHandler 这个接口
class DynamicProxy implements InvocationHandler {
    private Object subject;//这个就是我们要代理的真实对象
    public DynamicProxy(Object subject) {
        this.subject = subject;
    }
    @Override
    public Object invoke(Object object, Method method, Object[] args) throws Throwable {
        //在代理真实对象前我们可以添加一些自己的操作
        System.out.println("调用的方法: " + method);
        //当代理对象调用真实对象的方法时,其会自动的跳转到代理对象关联的handler对象的invoke方法来进行调用
        method.invoke(subject, args);
        //在代理真实对象后我们也可以添加一些自己的操作
        System.out.println("调用完毕");
        return null;
    }
}
为什么我们可以将newProxyInstance方法返回的Object类型的对象转化为Subject类型的对象?
原因就是,在newProxyInstance这个方法的第二个参数上,我们给这个代理对象提供了一组什么接口,那么我这个代理对象就会实现了这组接口,这个时候我们当然可以将这个代理对象强制类型转化为这个接口的对象,因为这里的接口是Subject类型,所以就可以将其转化为Subject类型了。

同时我们一定要记住,通过 Proxy.newProxyInstance 创建的代理对象是在jvm运行时动态生成的一个对象,它并不是我们的InvocationHandler类型,也不是我们定义的那组接口的类型,而是在运行时动态生成的一个对象,并且命名方式都是这样的形式,以$开头,Proxy为中,最后一个数字表示对象的标号。
 
我们来看看这两句
subject.rent();
subject.hello("world");
这里是通过代理对象来调用实现的那种接口中的方法,这个时候程序就会跳转到由这个代理对象关联到的 handler 中的invoke方法去执行,而我们的这个 handler 对象又接受了一个 RealSubject类型的参数,表示我要代理的就是这个真实对象,所以此时就会调用 handler 中的invoke方法去执行。

更简洁的示例

public class Test {
    public static void main(String[] args) {
        ITest iTest = (ITest) Proxy.newProxyInstance(ITest.class.getClassLoader(), new Class<?>[] { ITest.class }, new InvocationHandler() {
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                Integer a = (Integer) args[0];
                Integer b = (Integer) args[1];
                System.out.println("方法名:" + method.getName());
                System.out.println("参数:" + a + " , " + b);
                return null;
            }
        });
        iTest.add(3, 5);
    }
}
interface ITest {
    public void add(int a, int b);
}

动态代理 Proxy InvocationHandler的更多相关文章

  1. JDK动态代理 Proxy InvocationHandler

  2. java 动态代理范例 InvocationHandler与Proxy

    java 动态代理范例 InvocationHandler与Proxy,拦截与代理 java.lang.reflect.Proxy,Proxy 提供用于创建动态代理类和实例的静态方法.newProxy ...

  3. 浅谈Java代理一:JDK动态代理-Proxy.newProxyInstance

    浅谈Java代理一:JDK动态代理-Proxy.newProxyInstance java.lang.reflect.Proxy:该类用于动态生成代理类,只需传入目标接口.目标接口的类加载器以及Inv ...

  4. Java动态代理之InvocationHandler最简单的入门教程

    网上关于Java的动态代理,Proxy和InvocationHandler这些概念有讲解得非常高深的文章.其实这些概念没有那么复杂.现在咱们通过一个最简单的例子认识什么是InvocationHandl ...

  5. JAVA设计模式-动态代理(Proxy)示例及说明

    在Mybatis源码解析,一步一步从浅入深(五):mapper节点的解析文章的最后部分,我们提到了动态代理的概念,下面我们就简单了解一下动态代理. 一,概念 代理设计模式的目的就是在不直接操作对象的前 ...

  6. JAVA设计模式-动态代理(Proxy)源码分析

    在文章:JAVA设计模式-动态代理(Proxy)示例及说明中,为动态代理设计模式举了一个小小的例子,那么这篇文章就来分析一下源码的实现. 一,Proxy.newProxyInstance方法 @Cal ...

  7. 动态代理proxy与CGLib的区别

    什么是代理? 静态代理与动态代理 静态代理实例 JDK动态代理实例 CGLib 简介 CGLib 与JDK动态代理的区别 代理模式是Java中常见的一种模式,英文名字叫走Proxy或者Surrogat ...

  8. java动态代理Proxy

    package com.gz_06; public interface StudentDao { public void login(); public void regist(); } packag ...

  9. JDK动态代理(Proxy)的两种实现方式

    JDK自带的Proxy动态代理两种实现方式 前提条件:JDK Proxy必须实现对象接口 so,创建一个接口文件,一个实现接口对象,一个动态代理文件 接口文件:TargetInterface.java ...

随机推荐

  1. Python自动化运维之30、Flask框架

    Flask 官网:http://flask.pocoo.org/ flask是一个基于Python开发并且依赖jinja2模板和Werkzeug WSGI服务的一个微型框架,对于Werkzeug本质是 ...

  2. WCF返回JSON的详细配置

    开发环境:VS2008,c# 1.新建个WCF服务网站 文件-新建-网站-WCF服务 2,运行一下,提示配置WEB.CONFIG,点击确认. 3,打开web.config增加如下节点: <ser ...

  3. Google研究员Ilya Sutskever:成功训练LDNN的13点建议

    Google研究员Ilya Sutskever:成功训练LDNN的13点建议 摘要:本文由Ilya Sutskever(Google研究员.深度学习泰斗Geoffrey Hinton的学生.DNNre ...

  4. unity3d中的http通信

    转载 http://blog.csdn.net/mfc11/article/details/8188785的博客,如果侵权,请留言我及时删除! 前言 Unity3d 是一个跨平台的引擎,在移动互联网浪 ...

  5. 马云专访二:点评阿里雅虎交易、BAT三家、互联网巨头与政府关系

    记者:我们不得不要说到你和雅虎之间的事情了.你知道,雅虎对整个互联网业的意义不只是一家公司,它有它象征的意义,重要的是,雅虎对阿里巴巴的意义更加非同寻常,当你最后决定用76亿美元从雅虎“赎身”的时候, ...

  6. Monthly Expense(二分)

    Time Limit: 2000MS   Memory Limit: 65536K Total Submissions: 11196   Accepted: 4587 Description Farm ...

  7. 不成功的TCA代码

    %--brain mask with the brain tissue mask_name = 'C:\Users\Administrator\Desktop\workspace\preprocess ...

  8. [Locked] Read N Characters Given Read4 & Read N Characters Given Read4 II - Call multiple times

    Read N Characters Given Read4 The API: int read4(char *buf) reads 4 characters at a time from a file ...

  9. [Locked] Binary Tree Vertical Order Traversal

    Binary Tree Vertical Order Traversal Given a binary tree, return the vertical order traversal of its ...

  10. ruby 安装 运行

    Ruby基础 一 简介 1.Ruby在windows平台下的安装 (1)下载地址:http://rubyinstaller.org/downloads/ (2)安装过程 这里我们选择安装路径为 D:\ ...