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代理模式相关推荐

  1. JVM(一)一文读懂Java编译全过程

    一文读懂Java编译全过程 java代码首先要通过前端编译器编译成.class字节码文件,然后再按一定的规则加载到JVM(java 虚拟机)内运行,有三种运行方式,解释模式(javac).编译模式(C ...

  2. java中date类型如何赋值_一文读懂java中的Reference和引用类型

    简介 java中有值类型也有引用类型,引用类型一般是针对于java中对象来说的,今天介绍一下java中的引用类型.java为引用类型专门定义了一个类叫做Reference.Reference是跟jav ...

  3. 一文读懂Java中File类、字节流、字符流、转换流

    一文读懂Java中File类.字节流.字符流.转换流 第一章 递归:File类: 1.1:概述 java.io.File 类是文件和目录路径名的抽象表示,主要用于文件和目录的创建.查找和删除等操作. ...

  4. 一文读懂 Java 工程师学习路线!

    作者 | 三太子敖丙 来源 | 三太子敖丙(ID:NiceOffer) 在写这个文章之前,我花了点时间,自己臆想了一个电商系统,基本上算是麻雀虽小五脏俱全,我今天就用它开刀,一步步剖析,我会讲一下我们 ...

  5. 一文读懂Java泛型中的通配符 ?

    之前不太明白泛型中通配符"?"的含义,直到我在网上发现了Jakob Jenkov的一篇文章,觉得很不错,所以翻译过来,大家也可以点击文末左下角的阅读原文看英文版的原文. 下面是我的 ...

  6. 一文读懂Java内存模型(JMM)及volatile关键字

    点赞再看,养成习惯,公众号搜一搜[一角钱技术]关注更多原创技术文章. 本文 GitHub org_hejianhui/JavaStudy 已收录,有我的系列文章. 前言 并发编程从操作系统底层工作的整 ...

  7. 一文读懂Java 11的ZGC为何如此高效

    导读:GC是大部分现代语言内置的特性,Java 11 新加入的ZGC号称可以达到10ms 以下的 GC 停顿,本文作者对这一新功能进行了深入解析.同时还对还对这一新功能带来的其他可能性做了展望.ZGC ...

  8. java序列化_夯实Java基础系列22:一文读懂Java序列化和反序列化

    本系列文章将整理到我在GitHub上的<Java面试指南>仓库,更多精彩内容请到我的仓库里查看 https://github.com/h2pl/Java-Tutorial 喜欢的话麻烦点下 ...

  9. 一文读懂NoSQL的模式 | 时光机

    戳蓝字"CSDN云计算"关注我们哦! 时光机:搭载这部时光机,带您回顾<程序员>大量优秀文章,重温经典技术干货,我们发现硬核技术永不过时,对于get要点.solve难题 ...

  10. java中this_夯实Java基础系列7:一文读懂Java 代码块和执行顺序

    目录 #java中的构造方法 #构造方法简介 #构造方法实例 #例-1 #例-2 #java中的几种构造方法详解 #普通构造方法 #默认构造方法 #重载构造方法 #java子类构造方法调用父类构造方法 ...

最新文章

  1. 使用Bioconda管理生信软件(以bwa为例)
  2. Android BaseAdapter 例子
  3. python调用spark和调用hive_Spark(Hive) SQL数据类型使用详解(Python)
  4. DataScience:风控场景之金融评分卡模型的构建(逻辑回归)开发(转评分卡)、使用过程(线上实现)之详细攻略
  5. Hive权限与HDFS权限分离导致的一些问题
  6. 产品铭牌要求_AMPULM:电力变压器铭牌有哪些主要技术参数,你都知道吗?
  7. Android2.2快速入门
  8. 236 Lowest Common Ancestor of a Binary Tree
  9. MapInfo启动时,提示the Microsoft jet engine is not available
  10. access insert语句怎么写_ySQL中特别实用的几种SQL语句送给大家
  11. 微软发布.NET 6,NET Framework的最新版本现已推出
  12. 计算机科学导论定理八,计算机科学导论:思想与方法
  13. 数字证书及 CA 的扫盲介绍
  14. java 系统找不到路径_java IOException:系统找不到指定的路径
  15. Origin画法——简单的分布图画法
  16. SpaceSyntax【空间句法】之DepthMapX学习:第二篇 输出了什么东西 与 核心概念
  17. 机器人硬件需求描述标准化
  18. Bit、Byte、KB
  19. 程序员离职的3个大忌!
  20. 中兴c300业务板_中兴OLT C300板卡添加

热门文章

  1. 屁孩君儿子讲解 1090:含k个3的数
  2. JAVA体系书籍大全
  3. 计算机虚拟建造创新实践英语,室内设计专业虚拟实践教学环节的设计
  4. tiptop 编译运行_TIPTOP GP ERP二次开发FQA问题集
  5. 3D商城是什么样的?
  6. 中职计算机办公自动化教学,计算机办公自动化教学的几点探析
  7. 企业内部钉钉H5微应用(免登录)Spring Boot项目实战
  8. EF CORE 一对多、多对多添加、修改
  9. 报名考试系统的设计(Python后端服务器Flask框架)
  10. 加油吧,数字化转型@广汽菲克“尝鲜”Nutanix企业云操作系统