【一文读懂】Java代理模式
1 定义
定义:为其他对象提供一种代理以控制对这个对象的访问。在某些情况下,一个对象不适合或者不能直接引用另一个对象,而代理对象可以在客户端和目标对象之间起到中介的作用。
代理对象和目标对象通常会继承相同的接口,来保持行为的一致性。
2 目的
- 职责清晰:真实的角色只关心核心的业务逻辑。
- 保护对象:代理对象可以在客户端和目标对象之间起到中介的作用,这样起到了中介的作用和保护了目标对象的作用。
- 高扩展性:是实现AOP等的前提。
3 分类
区分动态代理和静态代理主要是从代理类的创建时序和类的确定性。
3.1 静态代理
代理类在编译之前就已创建并明确,目标对象和代理对象会继承相同的接口以保持行为的一致性,而且代理对象对目标的增强是确定的。
下面一个简单例子来实现静态代理:
首先,定义一个用户操作的接口
public interface UserDao {public void create();public void delete();public String get();
}
然后,实现一个目标对象类来继承改接口
public class UserDaoImpl implements UserDao {@Overridepublic void create() {System.out.println("UserDaoImpl: create");}@Overridepublic void delete() {System.out.println("UserDaoImpl: delete");}@Overridepublic String get() {System.out.println("UserDaoImpl: get");return null;}
}
然后,写一个静态代理类,同样的继承用户接口,此处添加属性UserDaoImpl ,并利用构造函数完成实例的传入。
public class UserDaoImplProxy implements UserDao{private UserDaoImpl userDaoImpl;public UserDaoImplProxy(UserDaoImpl userDaoImpl){this.userDaoImpl = userDaoImpl;}@Overridepublic void create() {System.out.println("UserDaoImplProxy: create");userDaoImpl.create();}@Overridepublic void delete() {System.out.println("UserDaoImplProxy: delete");userDaoImpl.delete();}@Overridepublic String get() {System.out.println("UserDaoImplProxy: get");userDaoImpl.get();return null;}
}
最后在main函数中调用代理类
public class ScheduleApplication {public static void main(String[] args) {UserDaoImplProxy userDaoImplProxy = new UserDaoImplProxy(new UserDaoImpl());userDaoImplProxy.create();}
}
可以看到打印如下所示
UserDaoImplProxy: create
UserDaoImpl: create
这样实现的好处是整个代理类的功能是确定的,且一目了然,对于帮助读懂代理类功能是有帮助的。
但是,如果有太多的目标类需要被代理,那么这项工作就显得过于繁琐,可能维护起来的困难不次于直接改动目标类的实现。这时,就需要动态代理的功能了。
3.2 动态代理
根据应用场景和实现方式的不同,动态代理又可以分为JDK动态代理和cglib动态代理,动态代理实现
3.2.1 JDK动态代理
JAVA反射机制 :是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意方法和属性;这种动态获取信息以及动态调用对象方法的功能称为java语言的反射机制。
JDK动态代理 :是基于Java反射机制,在程序运行过程中,通过被代理的接口来动态生成代理类的字节码二进制流,并加载运行(java反射-对象方法的执行)的过程。因此,JDK的动态代理只适用于实现了接口的对象。
首先介绍下JDK动态代理的使用方法:
先创建目标接口和实现类,为了说明动态代理的特性,特意创建两个接口
//目标接口1
public interface TeamDao {public void create();
}
//目标接口2
public interface UserDao {public void create();
}
//目标实现类1
public class TeamDaoImpl implements TeamDao {@Overridepublic void create() {System.out.println("TeamDaoImpl: create");}
}
//目标实现类2
public class UserDaoImpl implements UserDao {@Overridepublic void create() {System.out.println("UserDaoImpl: create");}
}
定义动态代理增强类
public class DynamicProxy implements InvocationHandler {private Object target;public DynamicProxy(Object target){this.target = target;}@Overridepublic Object invoke(Object o, Method method, Object[] objects) throws Throwable {System.out.println("Dynamics run Begin");Object result = method.invoke(target, objects);System.out.println("Dynamics run End");return result;}
}
最后,编写客户端代码进行类的调用
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;public class ScheduleApplication {public static void main(String[] args) {//目标类的InvocationHandler设置InvocationHandler userHandler = new DynamicProxy(new UserDaoImpl());//创建实现了UserDao接口的代理类$Proxy0的实例UserDao userDao = (UserDao) Proxy.newProxyInstance(Client.class.getClassLoader(),new Class[]{UserDao.class},userHandler);//代理类(即继承Proxy类)方法的调用,实际上执行的是其InvocationHandler的invoke方法userDao.create();//InvocationHandler teamHandler = new DynamicProxy(new TeamDaoImpl());//创建实现了TeamDao接口的代理类$Proxy1的实例TeamDao teamDao = (TeamDao) Proxy.newProxyInstance(Client.class.getClassLoader(),new Class[]{TeamDao.class}, teamHandler);teamDao.create();}
}
执行结果为:
Dynamics run Begin
UserDaoImpl: create
Dynamics run End
Dynamics run Begin
TeamDaoImpl: create
Dynamics run End
可以发现,代理类方法的调用执行的是InvocationHandler的invoke方法,那么第一个问题是:
为什么代理类的方法调用执行的是InvocationHandler的invoke方法?
我们首先看一下生成的代理类,此处需要将生成的class文件保存到本地,然后反编译得到其源码,其中,将JDK动态生成的class文件保存到本地的方法是在代码中添加
System.getProperties().put("jdk.proxy.ProxyGenerator.saveGeneratedFiles", "true");
利用Idea的反编译插件Pug,得到代理类的源码是
package com.sun.proxy;import com.example.zyq.schedule.ProxyDemo.UserDao;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;public final class $Proxy0 extends Proxy implements UserDao {private static Method m1;private static Method m2;private static Method m3;private static Method m0;public $Proxy0(InvocationHandler var1) throws {super(var1);}public final boolean equals(Object var1) throws {try {return (Boolean)super.h.invoke(this, m1, new Object[]{var1});} catch (RuntimeException | Error var3) {throw var3;} catch (Throwable var4) {throw new UndeclaredThrowableException(var4);}}public final String toString() throws {try {return (String)super.h.invoke(this, m2, (Object[])null);} catch (RuntimeException | Error var2) {throw var2;} catch (Throwable var3) {throw new UndeclaredThrowableException(var3);}}public final void create() throws {try {super.h.invoke(this, m3, (Object[])null);} catch (RuntimeException | Error var2) {throw var2;} catch (Throwable var3) {throw new UndeclaredThrowableException(var3);}}public final int hashCode() throws {try {return (Integer)super.h.invoke(this, m0, (Object[])null);} catch (RuntimeException | Error var2) {throw var2;} catch (Throwable var3) {throw new UndeclaredThrowableException(var3);}}static {try {m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));m2 = Class.forName("java.lang.Object").getMethod("toString");m3 = Class.forName("com.example.zyq.schedule.ProxyDemo.UserDao").getMethod("create");m0 = Class.forName("java.lang.Object").getMethod("hashCode");} catch (NoSuchMethodException var2) {throw new NoSuchMethodError(var2.getMessage());} catch (ClassNotFoundException var3) {throw new NoClassDefFoundError(var3.getMessage());}}
}
我们暂且不去考虑$Proxy0.class是如何生成的,可以看到对该类create方法的调用,实际执行的就是调用的InvocationHandler(也就是此处DynamicProxy)实现类的invoke方法。
现在我们可以得到JDK动态代理的实现主要通过是java.lang.reflect.Proxy类和java.lang.reflect.InvocationHandler接口,其中:
- Proxy类:Proxy提供静态方法用来创建代理对象类和实例。(Proxy provides static methods for creating objects that act like instances of interfaces but allow for customized method invocation.)
- InvocationHandler接口:用来约束调用者实现
3.2.2 cglib动态代理
CGLIB动态代理的原理:就是用Enhancer生成一个原有类的子类,并且设置好callback到proxy, 则原有类的每个方法调用都会转为调用实现了MethodInterceptor接口的proxy的intercept() 函数
- net.sf.cglib.proxy.Enhancer 主要的增强类。
- net.sf.cglib.proxy.MethodInterceptor 主要的方法拦截类,它是Callback接口的子接口,需要用户实现。
- net.sf.cglib.proxy.MethodProxy JDK的java.lang.reflect.Method类的代理类,可以方便的实现对源对象方法的调用。
定义CGLib的拦截类,继承MethodInterceptor接口并实现拦截(intercept)方法。
import java.lang.reflect.Method;public class CGLibDynamicProxy implements MethodInterceptor {@Overridepublic Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {System.out.println("Before Cglib Dynamic");Object result = methodProxy.invokeSuper(o, objects);System.out.println("After Cglib Dynamic");return result;}
}
客户端调用函数:
public class CglibApplication {public static void main(String[] args){Enhancer enhancer = new Enhancer();CGLibDynamicProxy cgLibDynamicProxy = new CGLibDynamicProxy();enhancer.setSuperclass(UserDaoImpl.class);enhancer.setCallback(cgLibDynamicProxy);UserDaoImpl userDao = (UserDaoImpl)enhancer.create();userDao.create();}
}
执行结果为:
Before Cglib Dynamic
UserDaoImpl: create
After Cglib Dynamic
完成代理的调用。
【一文读懂】Java代理模式相关推荐
- JVM(一)一文读懂Java编译全过程
一文读懂Java编译全过程 java代码首先要通过前端编译器编译成.class字节码文件,然后再按一定的规则加载到JVM(java 虚拟机)内运行,有三种运行方式,解释模式(javac).编译模式(C ...
- java中date类型如何赋值_一文读懂java中的Reference和引用类型
简介 java中有值类型也有引用类型,引用类型一般是针对于java中对象来说的,今天介绍一下java中的引用类型.java为引用类型专门定义了一个类叫做Reference.Reference是跟jav ...
- 一文读懂Java中File类、字节流、字符流、转换流
一文读懂Java中File类.字节流.字符流.转换流 第一章 递归:File类: 1.1:概述 java.io.File 类是文件和目录路径名的抽象表示,主要用于文件和目录的创建.查找和删除等操作. ...
- 一文读懂 Java 工程师学习路线!
作者 | 三太子敖丙 来源 | 三太子敖丙(ID:NiceOffer) 在写这个文章之前,我花了点时间,自己臆想了一个电商系统,基本上算是麻雀虽小五脏俱全,我今天就用它开刀,一步步剖析,我会讲一下我们 ...
- 一文读懂Java泛型中的通配符 ?
之前不太明白泛型中通配符"?"的含义,直到我在网上发现了Jakob Jenkov的一篇文章,觉得很不错,所以翻译过来,大家也可以点击文末左下角的阅读原文看英文版的原文. 下面是我的 ...
- 一文读懂Java内存模型(JMM)及volatile关键字
点赞再看,养成习惯,公众号搜一搜[一角钱技术]关注更多原创技术文章. 本文 GitHub org_hejianhui/JavaStudy 已收录,有我的系列文章. 前言 并发编程从操作系统底层工作的整 ...
- 一文读懂Java 11的ZGC为何如此高效
导读:GC是大部分现代语言内置的特性,Java 11 新加入的ZGC号称可以达到10ms 以下的 GC 停顿,本文作者对这一新功能进行了深入解析.同时还对还对这一新功能带来的其他可能性做了展望.ZGC ...
- java序列化_夯实Java基础系列22:一文读懂Java序列化和反序列化
本系列文章将整理到我在GitHub上的<Java面试指南>仓库,更多精彩内容请到我的仓库里查看 https://github.com/h2pl/Java-Tutorial 喜欢的话麻烦点下 ...
- 一文读懂NoSQL的模式 | 时光机
戳蓝字"CSDN云计算"关注我们哦! 时光机:搭载这部时光机,带您回顾<程序员>大量优秀文章,重温经典技术干货,我们发现硬核技术永不过时,对于get要点.solve难题 ...
- java中this_夯实Java基础系列7:一文读懂Java 代码块和执行顺序
目录 #java中的构造方法 #构造方法简介 #构造方法实例 #例-1 #例-2 #java中的几种构造方法详解 #普通构造方法 #默认构造方法 #重载构造方法 #java子类构造方法调用父类构造方法 ...
最新文章
- 使用Bioconda管理生信软件(以bwa为例)
- Android BaseAdapter 例子
- python调用spark和调用hive_Spark(Hive) SQL数据类型使用详解(Python)
- DataScience:风控场景之金融评分卡模型的构建(逻辑回归)开发(转评分卡)、使用过程(线上实现)之详细攻略
- Hive权限与HDFS权限分离导致的一些问题
- 产品铭牌要求_AMPULM:电力变压器铭牌有哪些主要技术参数,你都知道吗?
- Android2.2快速入门
- 236 Lowest Common Ancestor of a Binary Tree
- MapInfo启动时,提示the Microsoft jet engine is not available
- access insert语句怎么写_ySQL中特别实用的几种SQL语句送给大家
- 微软发布.NET 6,NET Framework的最新版本现已推出
- 计算机科学导论定理八,计算机科学导论:思想与方法
- 数字证书及 CA 的扫盲介绍
- java 系统找不到路径_java IOException:系统找不到指定的路径
- Origin画法——简单的分布图画法
- SpaceSyntax【空间句法】之DepthMapX学习:第二篇 输出了什么东西 与 核心概念
- 机器人硬件需求描述标准化
- Bit、Byte、KB
- 程序员离职的3个大忌!
- 中兴c300业务板_中兴OLT C300板卡添加
热门文章
- 屁孩君儿子讲解 1090:含k个3的数
- JAVA体系书籍大全
- 计算机虚拟建造创新实践英语,室内设计专业虚拟实践教学环节的设计
- tiptop 编译运行_TIPTOP GP ERP二次开发FQA问题集
- 3D商城是什么样的?
- 中职计算机办公自动化教学,计算机办公自动化教学的几点探析
- 企业内部钉钉H5微应用(免登录)Spring Boot项目实战
- EF CORE 一对多、多对多添加、修改
- 报名考试系统的设计(Python后端服务器Flask框架)
- 加油吧,数字化转型@广汽菲克“尝鲜”Nutanix企业云操作系统