【版权声明】未经博主同意,谢绝转载!(请尊重原创,博主保留追究权)

https://www.cnblogs.com/cnb-yuchen/p/18002823

出自【进步*于辰的博客

参考笔记一,P83;笔记二,P75.4。

1、概述

什么是代理模式?“代理模式”指通过为目标对象(原代码)创建代理对象,将附加功能(附加代码)注入目标对象的方法,从而实现附加功能的设计模式,分为静态代理和动态代理。

什么是静态代理?“静态代理”指为目标类手动创建代理类的代理方式。

什么是动态代理?“动态代理”指在不变动原代码的情况下,通过反射动态创建代理对象的代理方式。(注:“反射”是动态代理的底层,不可见)

2、静态代理的两种形式

2.1 面向接口

特点:目标对象与代理对象隶属于同一接口。

看下述代码:

1、公共接口:目标类和代理类的公共接口。

interface IService {
int transfer(int money);
}

2、目标类。

class Target implements IService {
@Override
public int transfer(int money) {
System.out.println("转账金额:" + money);
return 1;
}
}

3、代理类。

class Proxy implements IService {
private Target target;
public Proxy(Target target) {
this.target = target;
} @Override
public int transfer(int score) {
System.out.println("打开事务");// 附加功能
int x = target.transfer(score);
System.out.println("关闭事务");
return x;
}
}

测试。

class Test {
public static void main(String[] args) {
Proxy proxy = new Proxy(new Target());// 创建代理对象
int x = proxy.transfer(10);
if (x > 0)
System.out.println("转账成功");
else
System.out.println("转账失败");
}
}

测试结果:

2.2 面向继承

特点:目标对象与代理对象是继承关系,代理对象继承于目标对象。

看下述代码:

1、目标类。

class Target {
public int transfer(int money) {
System.out.println("转账金额:" + money);
return 1;
}
}

3、代理类。

class Proxy extends Target {
@Override
public int transfer(int money) {
System.out.println("打开事务");// 附加功能
int x = super.transfer(money);
System.out.println("关闭事务");
return x;
}
}

测试。

class Test {
public static void main(String[] args) {
Proxy proxy = new Proxy();// 创建代理对象
int x = proxy.transfer(20);
if (x > 0)
System.out.println("转账成功");
else
System.out.println("转账失败");
}
}

测试结果:

3、动态代理的两种形式

PS:静态代理需要手动创建代理类,进而创建代理对象,很冗余。换个思路,反射可以根据 Class 信息创建实例,故可以通过反射为目标对象创建代理对象,则无需创建代理类,这就是“动态代理”。

3.1 JDK动态代理

特点:面向接口,隶属于Java API

看下述代码:

1、公共接口。

/**
* 目标对象与代理对象的公共接口
* 注:因为JDK动态代理面向接口,故目标对象和代理对象实现于同一接口
*/
interface IService {
int transfer(int money);
}

2、目标类。

class Target implements IService {
@Override
public int transfer(int money) {
System.out.println("转账金额:" + money);
return 1;
}
}

测试。

class Test {
public static void main(String[] args) {
Target target = new Target();
/**
* 通过 newProxyInstance() 创建代理对象
* 第一个参数是目标对象的类加载器,指定为哪个目标对象创建代理对象;
* 第二个参数是目标对象实现的接口,指定目标对象和代理对象的公共接口;
* 第三个参数是拦截器对象,指定用哪个拦截器来创建代理对象,需要实现 InvocationHandler 接口。
*/
// 由于代理对象 proxy 是通过反射创建于JVM,并无类存在,故要上转为公共接口 IService
IService proxyInstance = (IService) Proxy.newProxyInstance(target.getClass().getClassLoader(),
target.getClass().getInterfaces(),
new InvocationHandler() {
/**
* 代理(调用 transfer())时执行的方法
* @param proxy 代理对象,即 proxyInstance,暂不知如何使用
* @param method 目标对象的 Method 的 class 对象
* @param args 目标对象的 Method 的形参数组
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("打开事务");// 附加功能
// invoke() 是反射中 Method 对象执行时调用的方法,故动态代理是通过反射调用目标对象被代理的方法
Object result = method.invoke(target, args);
System.out.println("关闭事务");
return result;
}
});// 创建代理对象
int x = proxyInstance.transfer(50);
if (x > 0)
System.out.println("转账成功");
else
System.out.println("转账失败");
}
}

测试结果:



可以用Lambda表达式进行简化。

3.2 Cglib动态代理

特点:面向继承,隶属于Spring API

看下述代码:

1、目标类。

class Target {
public int transfer(int money) {
System.out.println("转账金额:" + money);
return 1;
}
}

2、代理类。

/**
* Cglib动态代理类,需实现接口 MethodInterceptor
*/
class DynamicProxy implements MethodInterceptor {
private Object target;
public DynamicProxy(Object target) {
this.target = target;
}
public Object createProxy() {
Enhancer proxy = new Enhancer();// Enhancer 类是一种类生成器
proxy.setCallback(this);// 设置拦截器,指定回对象为自身(暂不理解)
proxy.setSuperclass(target.getClass());// 设置父类,指定为哪个目标对象创建代理对象
return proxy.create();// 创建代理对象
} /**
* 代理(调用 transfer())时执行的方法
* @param proxy 代理对象,即 proxyInstance,暂不知如何使用
* @param method 目标对象的 Method 的 class对象
* @param args 目标对象的 Method 的参数数组
* @param methodProxy 代理方法,即 Target.transfer(),暂不知如何使用
*/
@Override
public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
System.out.println("打开事务");// 附加功能
// invoke() 是反射中 Method 对象执行时调用的方法,故动态代理是通过反射调用目标对象被代理的方法
Object result = method.invoke(target, args);
System.out.println("关闭事务");
return result;
}
}

测试。

class Test {
public static void main(String[] args) {
Target target = new Target();
Target proxyInstance = (Target) new DynamicProxy(target).createProxy();
int x = proxyInstance.transfer(100);
if (x > 0)
System.out.println("转账成功");
else
System.out.println("转账失败");
}
}

测试结果:

同样可以用Lambda表达式进行简化,不过代理对象的创建(proxy.create())需要对 Enhancer 类的属性进行一些设置,故进行了封装。

注意:JDK动态代理和Cglib动态代理皆可拦截所有方法,包括:toString()hashcode()。不能拦截由 final 修饰方法,如:getClass()

最后

本文中的例子是为了阐述静态代理和动态代理的实现思想、方便大家理解而简单举出的,不一定有实用性,大家自行扩展。

本文完结。

[Java]静态代理、动态代理(基于JDK1.8)的更多相关文章

  1. java静态和动态代理原理

    一.代理概念 为某个对象提供一个代理,以控制对这个对象的访问. 代理类和委托类有共同的父类或父接口,这样在任何使用委托类对象的地方都可以用代理对象替代.代理类负责请求的预处理.过滤.将请求分派给委托类 ...

  2. java 笔记(3) —— 动态代理,静态代理,cglib代理

    0.代理模式 代理模式是常用的java设计模式,他的特征是代理类与委托类有同样的接口. 代理类主要负责为委托类预处理消息.过滤消息.把消息转发给委托类,以及事后处理消息等. 代理类与委托类之间通常会存 ...

  3. 轻松理解 Java 静态代理/动态代理

    目录 什么是代理模式 定义 代理模式的主要角色 优点 缺点 静态代理 动态代理 JDK原生动态代理 例子 分析 小结 CGLIB动态代理 例子 分析 final类型 其他方案 尾声 理解Java动态代 ...

  4. 杨晓峰-Java核心技术-6 动态代理 反射 MD

    目录 第6讲 | 动态代理是基于什么原理? 典型回答 考点分析 知识扩展 反射机制及其演进 动态代理 精选留言 Markdown版本笔记 我的GitHub首页 我的博客 我的微信 我的邮箱 MyAnd ...

  5. 一文读懂Java中的动态代理

    从代理模式说起 回顾前文: 设计模式系列之代理模式(Proxy Pattern) 要读懂动态代理,应从代理模式说起.而实现代理模式,常见有下面两种实现: (1) 代理类关联目标对象,实现目标对象实现的 ...

  6. 8、Spring教程之静态代理/动态代理

    为什么要学习代理模式,因为AOP的底层机制就是动态代理! 代理模式: 静态代理 动态代理 学习aop之前 , 我们要先了解一下代理模式! 静态代理 静态代理角色分析 抽象角色 : 一般使用接口或者抽象 ...

  7. Java 反射 设计模式 动态代理机制详解 [ 转载 ]

    Java 反射 设计模式 动态代理机制详解 [ 转载 ] @author 亦山 原文链接:http://blog.csdn.net/luanlouis/article/details/24589193 ...

  8. Java学习笔记--动态代理

    动态代理 1.JDK动态代理 JDK1.3之后,Java提供了动态代理的技术,允许开发者在运行期创建接口的代理实例.JDK的动态代理主要涉及到java.lang.reflect包中的两个类:Proxy ...

  9. 【SSH系列】静态代理&&动态代理

    从设计模式说起 代理模式是二十三中设计模式中的一种,代理模式就是指由一个代理主题来操作真实的主题,真实的主题执行具体的业务操作,而代理主题负责其她相关业务,简而言之,代理模式可以由以下三个部分组成: ...

  10. 细说java系统之动态代理

    代理模式 在深入学习动态代理之前,需要先掌握代理模式.只有深刻理解了代理模式的应用,才能充分理解Java动态代理带来的便利. 在生活中存在许多使用"代理模式"的场景,比如:村里的张 ...

随机推荐

  1. 从零开始的react入门教程(七),react中的状态提升,我们为什么需要使用redux

    壹 ❀ 引 在前面的文章中,我们了解到react中的数据由props与State构成,数据就像瀑布中的水自上而下流动,属于单向数据流.而这两者的区别也很简单,对于一个组件而言,如果说props是外部传 ...

  2. STM32的串口通信UART/TTL

    常用的串口pin STM32的串口是基础通信方式, 每个型号都带多组串口, 一般都使用默认的组, 可以参考芯片的datasheet, 去看pinout and pin definitions, stm ...

  3. 案例分享:某品牌音响系列协议调试工具(搜寻主机,查询通道,基本控制API,云音乐API,语言节目API等,可增删改指令)

    需求   某音响品牌需要一套完整的协议调试工具,提供给研发人员,渠道商,客户,现场人员等使用:  1.使用sqlite3数据库存储协议,  2.搜寻主机,操作主机:  3.探测云端API,调试API: ...

  4. sqlserver数据库jar包下载

    链接:https://pan.baidu.com/s/1mCx5JpVpmU6uUaqMITxP_Q提取码:4piq 说明:若链接失效,联系会及时补上!

  5. 【LeetCode剑指offer 01】数组中重复的数字、两个栈实现队列

    数组中重复的数字 数组中重复的数字 找出数组中重复的数字. 在一个长度为 n 的数组 nums 里的所有数字都在 0-n-1 的范围内.数组中某些数字是重复的,但不知道有几个数字重复了,也不知道每个数 ...

  6. 02、NATS单节点部署

    接下来,我们一起看看如何部署一个单节点的 nats 服务器,这样后续学习 nats 的功能和特性的时候,会更加的清晰,那我们一起看看如何部署单节点的nats服务,后面在学习如何部署集群版的 nats. ...

  7. 在Avalonia项目中使用MediatR和MS.DI库实现事件驱动通信

    大家好,我是沙漠尽头的狼! AvaloniaUI是一个强大的跨平台.NET客户端开发框架,让开发者能够针对Windows.Linux.macOS.Android和iOS等多个平台构建应用程序.在构建复 ...

  8. C++ STL函数对象 仿函数

    1 //STL函数对象 仿函数 2 #include<iostream> 3 #include<string> 4 5 using namespace std; 6 7 8 / ...

  9. pycharm/Intellij idea双击打不开,没有反应,下列方法亲测有用!

    第一种方法: 看看你的微软C++运行库是不是误删了.....我就这么干过...以前有个软件捆绑这个 安装了 结果我后来给删了 ,导致我pycharm 和intellij idea全都打不开 !!!各位 ...

  10. 通过 TCPView KPKIService.exe 删掉 (原来是单点登录的中间件)

    叫 统一安全中间件,就是个第三方做的key的安全检查,谁知道是哪年装的 (原来是单点登录的中间件) 资料 https://baijiahao.baidu.com/s?id=17173842191483 ...