一篇文章让你搞懂Java中的静态代理和动态代理
什么是代理模式
代理模式是常用的java设计模式,在Java中我们通常会通过new一个对象再调用其对应的方法来访问我们需要的服务。代理模式则是通过创建代理类(proxy)的方式间接地来访问我们需要的服务。
举一个生活中的例子:像我们在网上通过中介租到其背后房东的房子,因为房东也嫌麻烦想省事,此时中介就相当于代理而房东则是被代理,两者是分开的,这样我们就不会直接访问到房东,大部分情况下在中介手中租到的房子都会比原价要贵一些,这好比代理的作用,即不需要通过房东中介也可以在原有房价基础上进行增减或者添加其他的推广方式等操作来进行出租。
在Java中也是如此,我们需要遵循类的单一性原则,只有功能单一这个类被改动的可能性才会最小或者说在尽量不修改源码的前提下进行方法扩展,这样也可以避免在不清楚别人代码的情况下进行修改所导致的各种问题等等。
代理的优缺点
优点:
可以提高程序灵活性和可扩展性。
在不修改原代码的基础上,扩展和增强实现;
代码解耦,在代理中通过参数可以判断真实类,做出不同的响应或调用;
隐藏部分实现过程和细节,可以起到保护目标对象的作用
缺点:
由于在客户端和真实对象之间增加了代理对象,因此有些类型的代理模式可能会造成请求的处理速度变慢;
实现代理模式需要额外的工作,有些代理模式的实现非常复杂。
静态代理在委托类变多的情况时会显的非常臃肿,不方便阅读与使用
静态代理
由程序员创建或工具生成代理类的源码,再编译代理类,即代理类和委托类的关系再程序运行前就已经存在
实现步骤:
定义一个接口和接口的实现类
创建一个代理类同样的实现上述接口
将目标对象注入代理类中然后在代理类方法中调用目标类中的对应方法,这样我们就可以通过代理类在不修改原有方法的基础上进行扩展
代码实现:
/**
* 目标接口
*/
interface Hobby {
//唱
void sing();
//跳
void dance();
}
/**
* 目标类
*/
class MyHobby implements Hobby {
@Override
public void sing() {
System.out.println("sing...");
}
@Override
public void dance() {
System.out.println("dance...");
}
}
/**
* 代理类
*/
class Hobbystaticproxy implements Hobby {
Hobby hobby;
public Hobbystaticproxy(Hobby hobby) {
this.hobby = hobby;
}
@Override
public void sing() {
rap();
hobby.sing();
rap();
}
@Override
public void dance() {
rap();
hobby.dance();
rap();
}
public void rap() {
System.out.println("static-rap...");
}
}
/**
* main方法
*/
class Test {
public static void main(String[] args) {
Hobby hobby = new MyHobby();
Hobbystaticproxy hobbystaticproxy = new Hobbystaticproxy(hobby);
hobbystaticproxy.sing();
hobbystaticproxy.dance();
}
}
输出结果:

可以看到我们在没有修改目标类的情况下通过代理类来实现按目标接口并且分别在目标类的 唱、跳方法前后都扩展了一个rap方法,这就是静态代理
缺点:接口类变化会影响实现类和代理类;比如方法修改返回值、参数类型、增加方法,实现类和代理类都需要修改,不灵活而且很麻烦。
动态代理
使用反射和字节码的技术,在运行期创建指定接口或类的子类,以及其实例对象,控制被代理对象的访问,使用的工具有 jdkproxy、cglibproxy 等。
上述静态代理在程序运行前就需要把对应的类和方法写好,这样就被写死了,这只是一个简单的接口里面也只有两个方法,那如果接口中有几十个方法都需要扩展呢,总不能一个个地手动去添加吧,所以有了我们的动态代理
实现步骤:
定义一个接口和接口的实现类
创建一个代理类实现
InvocationHandler接口(指定运行时生成代理类需要完成的具体任务重写
InvocationHandler接口中的invoke方法创建被代理类的对象,调用处理程序最后通过代理类对象来调用对应方法
代码实现:
/**
* 目标接口
*/
interface Hobby {
void sing();
void dance();
}
/**
* 目标类
*/
class MyHobby implements Hobby {
@Override
public void sing() {
System.out.println("sing...");
}
@Override
public void dance() {
System.out.println("dance...");
}
}
/**
* 代理类
*/
class HobbyDynamicProxy implements InvocationHandler{
private final Object obj;
public HobbyDynamicProxy(Object obj){
this.obj = obj;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
rap();
Object result = method.invoke(obj, args);
rap();
return result;
}
public void rap() {
System.out.println("Dynamic-rap...");
}
}
/**
* main方法
*/
class Test {
public static void main(String[] args) {
InvocationHandler renterHandler = new HobbyDynamicProxy(new MyHobby());
Hobby hobbyProxy = (Hobby)Proxy.newProxyInstance(Hobby.class.getClassLoader(), new Class[]{Hobby.class},renterHandler);
hobbyProxy.sing();
hobbyProxy.dance();
}
}
输出结果:

InvocationHandler 接口 和 invoke 方法介绍
InvokationHandler是Java 反射包里面的一个接口通过用户类来实现,来激发一个动态代理类的方法。
它只有一个方法:
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable;
Object:实现方法的代理对象
Method:代理实例激发的方法,Porxy参数中的接口方法
Object[]:传递给方法的一系列参数
静态代理和动态代理的区别
静态代理:
在jvm运行之前就已经获取到代理类的class信息。代理类需要开发者自己写好,即开发者需要自己实现代理类的.java文件,也就是说在项目编译之前就需要存在代理类的.java文件,然后在编译阶段就可以将代理类的.java文件编译成.class文件,从而得到代理类的class信息;
动态代理:
不需要开发人员自己实现代理类的,也就是项目代码中是不存在代理类的.java文件的,既然代理类未由开发者实现,那么程序经过编译之后肯定也不会有代理类的.class文件。也就是说经过编译之后程序未启动运行之前,关于代理类的信息我们一无所知,它是在程序运行过程中需要用到的时候才会由jvm动态生成的,而且生成之后也只存在于内存中,不会写到磁盘保存成.class文件,更加不会保存为.java文件;
总之一句话就是静态代理是需要开发人员自己实现代理类的逻辑的,且代理类的class信息是在程序运行之前就已经可以获取到的了,而动态代理是不需要开发人员自己实现代理类的,
一篇文章让你搞懂Java中的静态代理和动态代理的更多相关文章
- 轻松搞懂Java中的自旋锁
前言 在之前的文章<一文彻底搞懂面试中常问的各种“锁”>中介绍了Java中的各种“锁”,可能对于不是很了解这些概念的同学来说会觉得有点绕,所以我决定拆分出来,逐步详细的介绍一下这些锁的来龙 ...
- 一文彻底搞懂Java中的环境变量
一文搞懂Java环境变量 记得刚接触Java,第一件事就是配环境变量,作为一个初学者,只知道环境变量怎样配,在加上各种IDE使我们能方便的开发,而忽略了其本质的东西,只知其然不知其所以然,随着不断的深 ...
- Java中的反射机制和动态代理
一.反射概述 反射机制指的是Java在运行时候有一种自观的能力,能够了解自身的情况为下一步做准备,其想表达的意思就是:在运行状态中,对于任意一个类,都能够获取到这个类的所有属性和方法:对于任意一个对象 ...
- 五分钟学Java:一篇文章带你搞懂spring全家桶套餐
原创声明 本文首发于微信公众号[程序员黄小斜] 本文作者:黄小斜 转载请务必在文章开头注明出处和作者. 本文思维导图 什么是Spring,为什么你要学习spring? 你第一次接触spring框架是在 ...
- 一文搞懂--Java中重写equals方法为什么要重写hashcode方法?
Java中重写equals方法为什么要重写hashcode方法? 直接看下面的例子: 首先我们只重写equals()方法 public class Test { public static void ...
- 一文搞懂 Java 中的枚举,写得非常好!
知识点 概念 enum的全称为 enumeration, 是 JDK 1.5 中引入的新特性. 在Java中,被 enum关键字修饰的类型就是枚举类型.形式如下: enum Color { RED, ...
- 来吧,一文彻底搞懂Java中最特殊的存在——null
没事的时候,我并不喜欢逛 P 站,而喜欢逛 programcreek 这些技术型网站,于是那天晚上,在夜深人静的时候,我就发现了一个专注基础但不容忽视的主题.比如说:Java 中的 null 到底是什 ...
- 这一篇文章帮你搞定Java(含Java全套资源)
当下想学习Java开发的人越来越多,对于很多零基础的人来说,没有相关的视频教程及相关的学习线路,学起来是一件很费劲的事情,还有很多人从网上及其它渠道购买视频,这些视频资料的价格对于刚毕业的大学生来说也 ...
- java中的静态分派和动态分派
多态是java的基本特征之一,多态即一个对象具有多种形态(多种表达形式,猴子是动物的一种的表现形式),例如:子类是父类的一种形态. 当方法重载时,就会涉及到多态. 1:在重载时是通过参数的静态类型,而 ...
随机推荐
- linux服务器监控脚本
#!/bin/bash #获取cpu使用率 cpuUsage=`top -n 1 | awk -F '[ %]+' 'NR==3 {print $2}'` #获取磁盘使用率 data_name=&qu ...
- Apache DolphinScheduler新一代分布式工作流任务调度平台实战-中
@ 目录 架构设计 总体架构 启动流程图 架构设计思想简述 负载均衡 缓存 实战使用 参数 参数优先级 内置参数 基础内置参数 衍生内置参数 本地参数和全局参数 工作流传参 数据源管理 支持数据源 创 ...
- SmartIDE v1.0.23 一个非常不敏捷的发布
SmartIDE v1.0版本(CLI Build v1.0.23.4650,Server Build v1.0.23.4646)已经发布,在超过4000 个 Builds 之后,我们终于发布了v1. ...
- Geometrics类定义
首先我的结构图是这样的,当然你自己喜欢怎么改都行.这个不影响,只要包含到正确的头文件就行. geometrics是几何类的基础,所有几何的碰撞都是从这个类继承下来的.书中也说的很清楚了,大家可以阅读对 ...
- mui 登录之后tab切换页面会失灵
我的app做完刚进去的时候底部导航栏的tab切换是正常的,但是退出之后重新登录,我在首页用reload进行了刷新,之后就引发了一些问题,tab切换有时候会失灵,登录转态的改变不成功.原来是reload ...
- Apache DolphinScheduler 2.0.1 来了,备受期待的一键升级、插件化终于实现
✎ 编 者 按:好消息!Apache DolphinScheduler 2.0.1 版本正式发布! 本版本中,DolphinScheduler 经历了一场微内核+插件化的架构改进,70% 的代码被重构 ...
- Dolphin Scheduler 1.2.0 部署参数分析
本文章经授权转载 1 组件介绍 Apache Dolphin Scheduler是一个分布式易扩展的可视化DAG工作流任务调度系统.致力于解决数据处理流程中错综复杂的依赖关系,使调度系统在数据处理流程 ...
- Apache DolphinScheduler 是如何诞生的
作者 | 代立冬,李岗 排版 | 开源之道小助理 Apache DolphinScheduler,简称"DS", 中文名 "小海豚调度"(海豚聪明.人性化,又左 ...
- Taurus.MVC WebAPI 入门开发教程5:控制器安全校验属性【HttpGet、HttpPost】【Ack】【Token】【MicroService】。
系列目录 1.Taurus.MVC WebAPI 入门开发教程1:框架下载环境配置与运行. 2.Taurus.MVC WebAPI 入门开发教程2:添加控制器输出Hello World. 3.Tau ...
- tomcat线程池
tomcat线程池和普通的线程池设计上有所区别,下面主要来看看它是如何设计的 tomcat中线程池的创建 org.apache.tomcat.util.net.AbstractEndpoint#cre ...