原文同步发表至个人博客【夜月归途】

原文链接:http://www.guitu18.com/se/java/2018-06-29/17.html

作者:夜月归途
出处:http://www.guitu18.com/
本博客中未标明转载的文章归作者夜月归途和博客园所有。
欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。

本博客关于Java动态代理相关内容直达链接:

  1. JDK动态代理浅析
  2. Cglib动态代理浅析
  3. JDK动态代理深入理解分析并手写简易JDK动态代理(上)
  4. JDK动态代理深入理解分析并手写简易JDK动态代理(下)

Java中的动态代理设计模式是非常经典且非常重要的设计模式之一,在感叹设计者的天才设计至于,我们想去探究一下这个设计模式是如何来实现的;

著名的spring框架的AOP的原理就是Java的动态代理机制;

在Spring中动态代理是实现有两种:JDK动态代理和Cglib动态代理,本篇分析的是JDK动态代理的实现;

查看下篇 [ Cglib动态代理浅析 ]

在探究动态代理之前,先看一下静态代理,这可以帮助我们更好的理解动态代理;

以公司老板和员工为例,现在Boss有一些项目要完成(定义接口)

public interface Boss {
public void doSomethinig();
public void finishTasks(String name);
}

这些工作肯定是需要雇员工去做的(去实现老板定义的接口)

public class Employee implements Boss {
@Override
public void doSomethinig() {
System.out.println("员工工作中 ...");
}
@Override
public void finishTasks(String name) {
System.out.println("员工正在完成项目 " + name + " ...");
}
}

我们先使用静态代理,这时候我们雇一个项目经理负责代理员工完成工作,可能有点不恰当哈,先这么理解吧;(同样需要实现老板的接口,代理员工执行任务)

public class Manager implements Boss {
private Employee employee = new Employee();
@Override
public void doSomethinig() {
// 员工完成工作之前
System.out.println(">>>>>员工完成工作之前");
// 员工正在完成工作
employee.doSomethinig();
// 员工完成工作之后
System.out.println("员工完成工作之后>>>>>");
}
@Override
public void finishTasks(String name) {
// 员工完成工作之前
System.out.println(">>>>>员工完成工作之前");
// 员工正在完成工作
employee.finishTasks(name);
// 员工完成工作之后
System.out.println("员工完成工作之后>>>>>");
}
}

写个测试类

public class TestProxy {
public static void main(String[] args) {
Manager manager = new Manager();
manager.doSomethinig();
manager.finishTasks("Java静态代理");
}
}

刚开始,我也一直很疑惑,员工都已经实现Boss的所有方法了,而且在测试类中也直接new出来员工对象了,直接员工.方法执行不就完了,为什么还要多此一举使用一个代理类来完成执行呢?先看测试执行结果:

>>>>>员工完成工作之前
员工工作中 ...
员工完成工作之后>>>>> >>>>>员工完成工作之前
员工正在完成项目 Java静态代理 ...
员工完成工作之后>>>>>

可以看出在项目经理代理类中,我们其实是可以在员工方法执行之前和执行之后做一些操作的,具体可以做什么和能做什么,你们发挥自己想象力;

代理模式应用在实际项目中,是能够帮我们完成很多事情的,比如个大框架中最常见的日志记录,数据库事务控制,权限验证等等,就不一一列举了(当然它们使用的肯定不是静态代理,而是接下来要说的动态代理了);

但是静态代理又一个非常大的弊端,就是每个接口都需要一个代理类去实现和执行,随着业务的数量越来越复杂的时候,代理类的代码量也是十分惊人的,这对于项目来说是很难去管理维护的;而动态代理的出现,正是用来解决这个问题的;

在Java的动态代理机制中,有两个至关重要的对象:

InvocationHandler 接口
Proxy 类

我们看看JDK API是怎么描述InvocationHandler 的(摘自JDK API 1.6.0中文版)

public interface InvocationHandler
InvocationHandler 是代理实例的调用处理程序实现的接口。
每个代理实例都具有一个关联的调用处理程序。
对代理实例调用方法时,将对方法调用进行编码并将其指派到它的调用处理程序的 invoke 方法。
invoke方法描述(摘自JDK API 1.6.0中文版) invoke Object invoke(Object proxy,Method method,Object[] args)throws Throwable
在代理实例上处理方法调用并返回结果。
在与方法关联的代理实例上调用方法时,将在调用处理程序上调用此方法。 参数:
proxy
- 在其上调用方法的代理实例,指代我们所代理的那个真实对象
method
- 对应于在代理实例上调用的接口方法的 Method 实例。
- Method 对象的声明类将是在其中声明方法的接口,该接口可以是代理类赖以继承方法的代理接口的超接口。
- 指代的是我们所要调用真实对象的某个方法的Method对象
args
- 包含传入代理实例上方法调用的参数值的对象数组,如果接口方法不使用参数,则为 null。
- 基本类型的参数被包装在适当基本包装器类(如 java.lang.Integer 或 java.lang.Boolean)的实例中。
- 指代的是调用真实对象某个方法时接受的参数
返回:
从代理实例的方法调用返回的值。
如果接口方法的声明返回类型是基本类型,则此方法返回的值一定是相应基本包装对象类的实例;
否则,它一定是可分配到声明返回类型的类型。
如果此方法返回的值为 null 并且接口方法的返回类型是基本类型,则代理实例上的方法调用将抛出 NullPointerException。
否则,如果此方法返回的值与上述接口方法的声明返回类型不兼容,则代理实例上的方法调用将抛出 ClassCastException。

我们再来看看Proxy类(摘自JDK API 1.6.0中文版)

public class Proxy extends Object implements Serializable
Proxy 提供用于创建动态代理类和实例的静态方法,它还是由这些方法创建的所有动态代理类的超类。
Proxy.newProxyInstance方法(摘自JDK API 1.6.0中文版) public static Object newProxyInstance(
ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h)
throws IllegalArgumentException
返回一个指定接口的代理类实例,该接口可以将方法调用指派到指定的调用处理程序。
- 此方法相当于:
Proxy.getProxyClass(loader, interfaces).
getConstructor(new Class[] { InvocationHandler.class }).
newInstance(new Object[] { handler });
- Proxy.newProxyInstance 抛出 IllegalArgumentException,原因与 Proxy.getProxyClass 相同。 参数:
loader
- 定义代理类的类加载器
- ClassLoader对象,定义了由哪个ClassLoader对象来对生成的代理对象进行加载
interfaces
- 代理类要实现的接口列表
- Interface对象的数组,表示的是我将要给我需要代理的对象提供一组什么接口
- 如果我提供了一组接口给它,那么这个代理对象就宣称实现了该接口(多态),这样我就能调用这组接口中的方法了
h
- 指派方法调用的调用处理程序
- InvocationHandler对象,表示的是当我这个动态代理对象在调用方法的时候,会关联到哪一个InvocationHandler对象上
返回:
一个带有代理类的指定调用处理程序的代理实例,它由指定的类加载器定义,并实现指定的接口

现在,我们使用动态代理来决绝刚才的问题,Boss和员工还是原来的

public interface Boss {
public void doSomethinig();
public void finishTasks(String name);
}

员工

public class Employee implements Boss {
@Override
public void doSomethinig() {
System.out.println("员工工作中 ...");
}
@Override
public void finishTasks(String name) {
System.out.println("员工正在完成项目 " + name + " ...");
}
}

这时候我们使用动态代理,雇一个领导,来代理所有的事情;

public class Leader implements InvocationHandler {
private Object target;
// 返回一个代理对象,并绑定被代理对象
public Object getProxyInstance(Object object) {
this.target = object;
// Proxy的newProxyInstance方法需要三个参数
return Proxy.newProxyInstance(
// 1.一个类加载器,通常可以从已经被加载的对象中获取类加载器
object.getClass().getClassLoader(),
// 2.希望代理实现的接口列表
object.getClass().getInterfaces(),
// 3.一个InvocationHandler接口的实现
this);
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 这里我们简单输出一下invoke的第二第三个参数
System.out.println("method : " + method);
System.out.println("args : " + args);
if (args != null) {
System.out.print("arg : ");
for (Object arg : args) {
System.out.print(arg + " ");
}
System.out.println();
}
// 员工完成工作之前
System.out.println(">>>>>员工完成工作之前");
// 员工正在完成工作
Object invoke = method.invoke(target, args);
// 员工完成工作之后
System.out.println("员工完成工作之后>>>>>");
return invoke;
}
}

测试类:

public class LeaderTest {
public static void main(String[] args) {
Employee employee = new Employee();
Leader leader = new Leader();
Boss boss = (Boss) leader.getProxyInstance(employee);
boss.doSomethinig();
boss.finishTasks("Java动态代理");
}
}

控制台输出:

Method : public abstract void com.guitu18.Boss.doSomethinig()
args : null
>>>>>员工完成工作之前
员工工作中 ...
员工完成工作之后>>>>> Method : public abstract void com.guitu18.Boss.finishTasks(java.lang.String)
args : [Ljava.lang.Object;@1c7c054
arg : Java动态代理
>>>>>员工完成工作之前
员工正在完成项目 Java动态代理 ...
员工完成工作之后>>>>>

我们在方法中打印invoke方法的第二个参数Method method,从控制台看出,我们在调用方法的时候,实际上使用的是代理对象来调用真实对象的方法的:

Method : public abstract void com.guitu18.Boss.doSomethinig()
Method : public abstract void com.guitu18.Boss.finishTasks(java.lang.String)

而打印invoke方法的第三个参数Object[] args时我们发现,该数组其实就是真实方法接收的参数,如果没有则为null;

args : null

args : [Ljava.lang.Object;@1c7c054
arg : Java动态代理

现在,我们可以在method.invoke方法的前后都加上自己的一些操作;

在各大框架中,使用动态代理的比比皆是,比如如著名的Spring框架的AOP原理,比如Shiro的权限验证,拦截器等等;

从以上代码中,我们也可以看出,使用动态代理之后,代理类的代码量已经被固定下来了,不会随着业务的复杂和庞大而变得越来越多;

其实在这里,我们已经可以把代码写成代理工厂模式了,简化一些操作:

public class ProxyFactory {
public static Object getProxyInstance(final Object target) {
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("开始事务...");
// 执行方法
Object invoke = method.invoke(target, args);
// 方法执行后
System.out.println("提交/回滚事务...");
return invoke;
}
});
}
}

在ProxyFactory里,提供了一个静态方法,我么仅需要传递进来一个代理类的具体实现,就能够获得一个代理对象,在这个方法中,Proxy.newProxyInstance需要的第三个参数,是以匿名内部类的方式去提供和定义的,更加简洁和灵活;

编写测试类:

public class ProxyFactoryTest {
public static void main(String[] args) {
Boss boss = (Boss) ProxyFactory.getProxyInstance(new Employee());
boss.doSomethinig();
boss.finishTasks("Java动态代理");
}
}

控制台输出:

开始事务...
员工工作中 ...
提交/回滚事务... 开始事务...
员工正在完成项目 Java动态代理 ...
提交/回滚事务...

在动态代理技术里,由于不管用户调用代理对象的什么方法,都是调用开发人员编写的处理器的invoke方法(这相当于invoke方法拦截到了代理对象的方法调用)。

并且,开发人员通过invoke方法的参数,还可以在拦截的同时,知道用户调用的是什么方法,因此利用这两个特性,就可以实现一些特殊需求;

例如:

数据库事务的操作,在方法执行前开启事务,执行完毕提交事务,如果方法执行出错,回滚事务; 拦截用户的访问请求,以检查用户是否有访问权限,有权限继续执行,没权限不执行做其他操作; 增强,动态为某个对象添加额外的功能;

尾巴:

  1. 动态代理是十分强大的设计模式,代理类的代码量被固定下来,不会因为业务的逐渐庞大而庞大;
  2. 可以实现AOP编程,实际上静态代理也可以实现,总的来说,AOP可以算作是代理模式的一个典型应用;
  3. 解耦,通过参数就可以判断真实类,不需要事先实例化,更加灵活多变;

JDK动态代理浅析的更多相关文章

  1. JDK动态代理深入理解分析并手写简易JDK动态代理(下)

    原文同步发表至个人博客[夜月归途] 原文链接:http://www.guitu18.com/se/java/2019-01-05/27.html 作者:夜月归途 出处:http://www.guitu ...

  2. JDK动态代理深入理解分析并手写简易JDK动态代理(上)

    原文同步发表至个人博客[夜月归途] 原文链接:http://www.guitu18.com/se/java/2019-01-03/27.html 作者:夜月归途 出处:http://www.guitu ...

  3. Cglib动态代理浅析

    原文同步发表至个人博客[夜月归途] 原文链接:http://www.guitu18.com/se/java/2018-06-29/18.html 作者:夜月归途 出处:http://www.guitu ...

  4. 何为代理?jdk动态代理与cglib代理、spring Aop代理原理浅析

    原创声明:本博客来源为本人原创作品,绝非他处摘取,转摘请联系博主 代理(proxy)的定义:为某对象提供代理服务,拥有操作代理对象的功能,在某些情况下,当客户不想或者不能直接引用另一个对象,而代理对象 ...

  5. java jdk动态代理模式举例浅析

    代理模式概述 代理模式是为了提供额外或不同的操作,而插入的用来替代”实际”对象的对象,这些操作涉及到与”实际”对象的通信,因此代理通常充当中间人角色. java中常用的动态代理模式为jdk动态代理和c ...

  6. jdk动态代理与cglib代理、spring Aop代理原理-代理使用浅析

    原创声明:本博客来源为本人原创作品,绝非他处摘取,转摘请联系博主 代理(proxy)的定义:为某对象提供代理服务,拥有操作代理对象的功能,在某些情况下,当客户不想或者不能直接引用另一个对象,而代理对象 ...

  7. JDK动态代理

    一.基本概念 1.什么是代理? 在阐述JDK动态代理之前,我们很有必要先来弄明白代理的概念.代理这个词本身并不是计算机专用术语,它是生活中一个常用的概念.这里引用维基百科上的一句话对代理进行定义: A ...

  8. 静态代理和利用反射形成的动态代理(JDK动态代理)

    代理模式 代理模式的定义:为其他对象提供一种代理以控制对这个对象的访问.在某些情况下,一个对象不适合或者不能直接引用另一个对象,而代理对象可以在客户端和目标对象之间起到中介的作用. 静态代理 1.新建 ...

  9. Spring中的JDK动态代理

    Spring中的JDK动态代理 在JDK1.3以后提供了动态代理的技术,允许开发者在运行期创建接口的代理实例.在Sun刚推出动态代理时,还很难想象它有多大的实际用途,现在动态代理是实现AOP的绝好底层 ...

随机推荐

  1. vue基于webpack说明

    1.文件build里的check-versions.js:检查node和npm版本, 此文件里的 (1)require('chalk')引入一个模块,定义输入终端样式 (2) require('sem ...

  2. echarts (geo/map) 渐变效果

    这两天帮人搞了下中国范围内仓库量统计的需求,查了下echarts 里的文档找到类似的demo(链接:https://ecomfe.github.io/echarts-examples/public/e ...

  3. CF F. Shovels Shop(前缀和预处理+贪心+dp)

    F. Shovels Shop time limit per test 2 seconds memory limit per test 256 megabytes input standard inp ...

  4. ubuntu Anaconda install

    在文件目录下执行: bash Anaconda3-4.2.0-Linux-x86_64.sh 根据提示输入回车 这里需要查看注册信息,回车浏览完信息即可 阅读完注册信息后,这里输入“yes” 回车即可 ...

  5. 《Android插件化开发指南》勘误

    一些常识: 1)全书70个代码例子中,涉及到插件的例子,请先assemble插件的项目,这会在HostApp项目中生成assets目录,并在该目录下plugin1.apk.这样,HostApp才能正常 ...

  6. SUSE12Sp3-.NET Core 2.2.1 runtime安装

    1.安装libicu依赖 1.在线安装 sudo mkdir /usr/local/dotnet #创建目录 cd /usr/local/dotnet sudo wget https://downlo ...

  7. [Swift]LeetCode396. 旋转函数 | Rotate Function

    Given an array of integers A and let n to be its length. Assume Bk to be an array obtained by rotati ...

  8. Redis 设计与实现 (二)--数据库

    typedef struct redisDb { dict *dict; /* The keyspace for this DB */ dict *expires; /* Timeout of key ...

  9. Ubuntu12.04下安ns-3.29及Ubuntu换源方法

    目录 1.下载ns-3.29 2.安装gcc-4.9.2 3.编译.测试ns-3.29 第一种:更新,文章开头说的 第二种,更新源 主机型号:Ubuntu12.04 仿真环境版本:NS-3.29 安装 ...

  10. Python面向对象中的类和对象

    类和对象 目标 类和对象的概念 类和对象的关系 类的设计 01. 类和对象的概念 类 和 对象 是 面向对象编程的 两个 核心概念 1.1 类 类 是对一群具有 相同 特征 或者 行为 的事物的一个统 ...