Java中代理有静态代理动态代理。静态代理的代理关系在编译时就确定了,而动态代理的代理关系是在运行期确定的。静态代理实现简单,适合于代理类较少且确定的情况,而动态代理则给我们提供了更大的灵活性。

Java中动态代理有JDK原生动态代理CGLIB动态代理两种。前者本质上是根据定好的接口动态生成静态代理类(该接口的实现类);后者则不需要事先定好接口而是可以直接根据类进行动态代理,其本质是根据指定的类动态生成静态代理类(指定的类的子类)。动态代理中,被代理的对象的所有方法都会被代理,除非在代理逻辑中进行方法筛选。

本质:

JDK原生动态代理:生成被代理对象所实现的接口的实现类;实现类中每个方法调用被代理对象的相应方法,只不过在调用前后加上了额外逻辑;要求被代理对象实现接口

CGLIB动态代理:生成被代理对象的子类;子类中的每个方法调用被代理对象(父类对象)中的相应方法,只不过在调用前后加上了额外处理;要求被代理对象不能被final修饰

动态代理是IOC、AOP等技术的基础。

静态代理和JDK动态代理

Dynamic proxies allow one single class with one single method to service multiple method calls to arbitrary classes with an arbitrary number of methods. A dynamic proxy can be thought of as a kind of Facade, but one that can pretend to be an implementation of any interface. Under the cover, it routes all method invocations to a single handler – the invoke() method.   https://www.baeldung.com/java-dynamic-proxies

示例:

 package com.marchon.proxytest;

 public interface IUserService {
public String getUserName();
public Integer getAge(String userName);
}

IUserService

 package com.marchon.proxytest;

 /**
* 被代理对象
*
* @author zsm
*
*/
public class UserServiceImpl implements IUserService { @Override
public String getUserName() {
String res = this.getClass() + ":hello";
System.out.println(res);
return res;
} @Override
public Integer getAge(String userName) {
Integer age = 20;
System.out.println(age);
return age;
} }

UserServiceImpl

 package com.marchon.proxytest;

 /**
* 代理对象(静态代理)<br>
* 缺点:<br>
* 1、代理类和被代理类实现相同的接口,代码重复、得为每个接口都实现相应的实现从而维护成本高 2、代理对象只服务于被代理对象,即每个被代理对象都得实现相应的代理对象
*
* @author zsm
*
*/
class UserServiceStaticProxy implements IUserService {
private IUserService proxiedObj; public UserServiceStaticProxy(IUserService proxiedObj) {
if (proxiedObj instanceof UserServiceStaticProxy) {
throw new RuntimeException("illegal proxiedObj proxied object");
} this.proxiedObj = proxiedObj;
} @Override
public String getUserName() {
System.out.println("before");
String res = proxiedObj.getUserName();
System.out.println("after");
return res; } @Override
public Integer getAge(String userName) {
System.out.println("before");
Integer age = proxiedObj.getAge(userName);
System.out.println("after");
return age;
} } public class Main_StaticProxy {
public static void main(String[] args) {
IUserService proxiedObj = new UserServiceImpl(); // UserServiceStaticProxy proxy = new UserServiceStaticProxy(proxiedObj);
IUserService proxyObject = new UserServiceStaticProxy(proxiedObj);
proxyObject.getUserName();
proxyObject.getAge("zhangsan");
}
}

StaticProxy

 package com.marchon.proxytest;

 import java.io.FileOutputStream;
import java.io.IOException;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy; import sun.misc.ProxyGenerator; /**
* 代理对象(动态代理)<br>
* 动态代理:在程序运行期间根据需要动态创建代理类及其实例来完成具体的功能。动态代理主要分为JDK动态代理和cglib动态代理两大类<br>
* 这里介绍jdk动态代理,其本质上是在运行时动态产生一个实现指定接口的静态代理类,指定接口的所有非final方法(包括继承的非final方法如toString)均会被代理。
*
* @author zsm
*
*/ class JdkDynamicProxyTemplate implements InvocationHandler {
private Object proxiedObj; public JdkDynamicProxyTemplate(Object proxiedObj) {
this.proxiedObj = proxiedObj;
} @Override
public Object invoke(Object proxyObj, Method method, Object[] args) throws Throwable {
// if (method.getName().equals("getUserName")) {//为避免所有非final方法都被代理,可作此判断
//
// } // System.out.println(proxyObj);//stack overflow, why?
// System.out.println(method);// public abstract java.lang.String com.marchon.proxytest.IUserService.getUserName() System.out.println("before");
Object res = method.invoke(proxiedObj, args);
System.out.println("after"); return res;
} } public class Main_JdkDynamicProxy {// 参阅:https://www.jianshu.com/p/269afd0a52e6
public static void main(String[] args) {
IUserService proxiedObj = new UserServiceImpl(); JdkDynamicProxyTemplate proxyTemplate = new JdkDynamicProxyTemplate(proxiedObj); // 第一个参数是指定代理类的类加载器(我们传入当前测试类的类加载器)
// 第二个参数是代理类需要实现的接口(我们传入被代理类实现的接口,这样生成的代理类和被代理类就实现了相同的接口)
// 第三个参数是invocation handler,用来处理方法的调用。这里传入我们自己实现的handler
IUserService proxyObject = (IUserService) Proxy.newProxyInstance(proxiedObj.getClass().getClassLoader(),
proxiedObj.getClass().getInterfaces(), proxyTemplate);// 创建包含被代理对象各方法的代理对象,显然可知:该代理对象实现了所传接口中(这里为IUserService)定义的各方法、代理对象的各方法实现为直接调用proxyTemplate相应的各方法实现。可以使用sum.misc下的ProxyGenerator生成动态代理类的字节码文件,再反编译出动态代理类源码 proxyObject.getUserName();
proxyObject.getAge("zhangsan");
proxyObject.toString();// 所有非final方法都会被代理,包括从Object继承的等 {
// 获取代理类字节码文件
String path = "$Proxy0.class";
byte[] classFile = ProxyGenerator.generateProxyClass("$Proxy0", proxiedObj.getClass().getInterfaces());
FileOutputStream out = null; try {
out = new FileOutputStream(path);
out.write(classFile);
out.flush();
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
out.close();
} catch (IOException e) {
e.printStackTrace();
}
}
} // jdk动态代理动态产生的代理类类似于如下静态代理
System.out.println();
System.out.println("equivalent static proxy test:");
new TmpEquivalentStaticProxy(proxyTemplate).getUserName(); }
} /**
*
* 上述生成的动态代理生成的代理类实际是类似于本类
*
* @author zsm
*
*/
class TmpEquivalentStaticProxy implements IUserService {// 真正由jdk动态代理生成的代理类还 extends Proxy
private InvocationHandler invokeHandler; public TmpEquivalentStaticProxy(InvocationHandler invokeHandler) {
this.invokeHandler = invokeHandler;
} @Override
public String getUserName() {
Object[] args = null;
Method getUserNameMethod = IUserService.class.getMethods()[0];// public abstract java.lang.String
// com.marchon.proxytest.IUserService.getUserName()
try {
return (String) invokeHandler.invoke(this, getUserNameMethod, args);
} catch (Throwable e) {
e.printStackTrace();
return null;
} } @Override
public Integer getAge(String userName) {
Object[] args = new Object[] { userName };
try {
Method getAgeMethod = IUserService.class.getMethods()[1];// public abstract java.lang.Integer
// com.marchon.proxytest.IUserService.getAge(java.lang.String)
return (Integer) invokeHandler.invoke(this, getAgeMethod, args);
} catch (Throwable e) {
e.printStackTrace();
return null;
} } }

JdkDynamic

springframework中的一个jdk动态代理示例:

由上可见,静态代理的各个方法具有共性:每个方法内的前后都做额外其他处理、中间调用被代理对象的相应方法。既然有共性,那就可以抽取共性以减少重复,也即将静态代理的方法抽象出一个”模板“,这样就不需要在代理对象中针对被代理对象的每个方法写额外逻辑,这其实就变成了动态代理。

JDK动态代理实际上是自动生成一个静态代理类并创建相应实例。代理类默认继承Porxy类,因为Java中只支持单继承,所以JDK动态代理只能去实现接口;代理类的方法都会去调用InvocationHandler的invoke()方法,故此需重写InvocationHandler的invoke()方法。

JDK动态代理为我们提供了非常灵活的代理机制,但也有不足:

被代理对象的所有非final方法(如从Object继承的toString、equals等)都会被代理(即都会在方法前后做与InvocationHandler的invoke()方法前后同样的处理,当然我们可以通过在invoke里对method name加以判断避免此情况),然而有时候我们并不希望这些方法被代理。

JDK动态代理是基于接口的(生成的动态代理类实际上extends Proxy implements IUserService)。如果要被代理的对象没有实现接口,该如何实现代理呢?可用下面要介绍的CGLIB动态代理。

从上面讨论可得知,静态代理和动态代理在运行时是一样的(都是静态代理了),其差异在运行时之前(即编译期)才存在,差异体现在开发者是否需要分别在每个与被代理方法对应的代理方法的前后写额外的处理逻辑:静态代理需要而动态代理不需要(后者不需要是因为该任务从由开发者负责转交由JVM来负责了)。因此,动态代理相比于静态代理的主要优势是为开发者提供便利,不用像静态代理那写重复的代理方法逻辑。

CGLIB动态代理(springframework)

与JDK动态代理不同,CGLIB(Code Generation Library)动态代理不需要事先定义接口,而是可以直接对类进行动态代理。CGLIB动态代理中被代理对象的所有非final方法默认也会被代理。

示例:

 package com.marchon.proxytest;

 import java.lang.reflect.Method;

 import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy; /**
* 代理对象(动态代理)<br>
* 动态代理:在程序运行期间根据需要动态创建代理类及其实例来完成具体的功能。动态代理主要分为JDK动态代理和cglib动态代理两大类<br>
* 这里介绍cglib动态代理,其本质上是在运行时动态产生一个实现继承指定类的的静态代理类,指定类的所有非final方法(包括继承的非final方法如toString)均会被代理。
*
* @author zsm
*
*/ class CglibDynamicProxyTemplate implements MethodInterceptor { @Override
public Object intercept(Object proxyObj, Method proxiedMethod, Object[] args, MethodProxy proxyMethod) throws Throwable {
// if (method.getName().equals("sayHello")) {//为避免所有非final方法都被代理,可作此判断
//
// } // System.out.println(proxyObj);//stack overflow, why?
// System.out.println(proxiedMethod);// public java.lang.String
// com.marchon.proxytest.HelloConcrete.sayHello(java.lang.String)
// System.out.println(proxyMethod);// org.springframework.cglib.proxy.MethodProxy@108c4c35 System.out.println("before");
Object res = proxyMethod.invokeSuper(proxyObj, args);
System.out.println("after");
return res;
} } class HelloConcrete {
public String sayHello(String username) {
return "hello " + username;
} public final String getAddress() {
return "beijing";
}
} public class Main_CglibDynamicProxy {
public static void main(String[] args) {
CglibDynamicProxyTemplate proxyTemplate = new CglibDynamicProxyTemplate(); Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(HelloConcrete.class);
enhancer.setCallback(proxyTemplate); HelloConcrete proxyObject = (HelloConcrete) enhancer.create();
System.out.println(proxyObject.sayHello("zhangsan"));// 会创建包含被代理对象所有非final方法的代理类,实际上代理类是被代理类的子类,故不会代理final方法;代理对象的各方法实现为直接调用proxyTemplate相应的各方法实现
proxyObject.hashCode();// 所有非final方法都会被代理,包括从Object继承的等
proxyObject.getAddress();// final方法不会被代理 // jdk动态代理动态产生的代理类类似于如下静态代理
System.out.println();
System.out.println("equivalent static proxy test:");
new TmpEquivalentStaticProxyOfCglib(proxyTemplate).sayHello("zhangsan");
}
} class TmpEquivalentStaticProxyOfCglib extends HelloConcrete {// 真正由cglib动态代理生成的代理类还 implements Factory
private MethodInterceptor methodInterceptor; public TmpEquivalentStaticProxyOfCglib(MethodInterceptor methodInterceptor) {
this.methodInterceptor = methodInterceptor;
} @Override
public String sayHello(String username) {
Object[] args = null;
Method proxiedMethod = super.getClass().getMethods()[0];
MethodProxy proxyMethod = null;// this.getClass().getMethods()[0];//不造如何获取MethodProxy对象,故此方法实际上跑不了 try {
return (String) methodInterceptor.intercept(this, proxiedMethod, args, proxyMethod);
} catch (Throwable e) {
e.printStackTrace();
return null;
} } // 无法override getAddress方法
}

CglibDynamicProxy

由上可见,此动态代理本质上是在运行时动态根据指定的类继承实现一个子类(故指定的类不能是final的),在子类中重写方法,方法的实现为调用MethodInterceptor中的intercept方法。

参考资料

https://www.jianshu.com/p/269afd0a52e6

https://www.cnblogs.com/liuyun1995/p/8144628.html

https://www.cnblogs.com/CarpenterLee/p/8241042.html JDK动态代理和CGLIB动态代理

https://blog.csdn.net/difffate/article/details/70552056 CGLIB动态代理

Java代理(静态代理、JDK动态代理、CGLIB动态代理)的更多相关文章

  1. jdk动态代理和cglib动态代理底层实现原理详细解析(cglib动态代理篇)

    代理模式是一种很常见的模式,本文主要分析cglib动态代理的过程 1. 举例 使用cglib代理需要引入两个包,maven的话包引入如下 <!-- https://mvnrepository.c ...

  2. 动态代理双剑客--JDK Proxy与CGLIB

    背景: 研究过设计模式的同胞们都知道代理模式可以有两种实现方案: 1.接口实现(或继承抽象类) 核心代码片段 ProxySubject-->>doOperation() //dosomet ...

  3. Java之代理(jdk静态代理,jdk动态代理,cglib动态代理,aop,aspectj)

    一.概念 代理是什么呢?举个例子,一个公司是卖摄像头的,但公司不直接跟用户打交道,而是通过代理商跟用户打交道.如果:公司接口中有一个卖产品的方法,那么公司需要实现这个方法,而代理商也必须实现这个方法. ...

  4. java的静态代理、jdk动态代理和cglib动态代理

    Java的代理就是客户端不再直接和委托类打交道,而是通过一个中间层来访问,这个中间层就是代理.使用代理有两个好处,一是可以隐藏委托类的实现:二是可以实现客户与委托类之间的解耦,在不修改委托类代码的情况 ...

  5. Java代理:静态代理、JDK动态代理和CGLIB动态代理

    代理模式(英语:Proxy Pattern)是程序设计中的一种设计模式.所谓的代理者是指一个类别可以作为其它东西的接口.代理者可以作任何东西的接口:网络连接.存储器中的大对象.文件或其它昂贵或无法复制 ...

  6. Spring 静态代理+JDK动态代理和CGLIB动态代理

    代理分为两种:静态代理 动态代理 静态代理:本质上会在硬盘上创建一个真正的物理类 动态代理:本质上是在内存中构建出一个类. 如果多个类需要进行方法增强,静态代理则需要创建多个物理类,占用磁盘空间.而动 ...

  7. Spring AOP JDK动态代理与CGLib动态代理区别

    静态代理与动态代理 静态代理 代理模式 (1)代理模式是常用设计模式的一种,我们在软件设计时常用的代理一般是指静态代理,也就是在代码中显式指定的代理. (2)静态代理由 业务实现类.业务代理类 两部分 ...

  8. Spring AOP详解 、 JDK动态代理、CGLib动态代理

    AOP是Aspect Oriented Programing的简称,面向切面编程.AOP适合于那些具有横切逻辑的应用:如性能监测,访问控制,事务管理以及日志记录.AOP将这些分散在各个业务逻辑中的代码 ...

  9. 【转载】Spring AOP详解 、 JDK动态代理、CGLib动态代理

    Spring AOP详解 . JDK动态代理.CGLib动态代理  原文地址:https://www.cnblogs.com/kukudelaomao/p/5897893.html AOP是Aspec ...

  10. Spring AOP中的JDK和CGLib动态代理哪个效率更高?

    一.背景 今天有小伙伴面试的时候被问到:Spring AOP中JDK 和 CGLib动态代理哪个效率更高? 二.基本概念 首先,我们知道Spring AOP的底层实现有两种方式:一种是JDK动态代理, ...

随机推荐

  1. CycleBarrier与CountDownLatch原理

    CountDownLatch 众所周知,它能解决一个任务必须在其他任务完成的情况下才能执行的问题,代码层面来说就是只有计数countDown到0的时候,await处的代码才能继续向下运行,例如: im ...

  2. 不依赖Spring使用AspectJ达到AOP面向切面编程

    网上大多数介绍AspectJ的文章都是和Spring容器混用的,但有时我们想自己写框架就需要抛开Spring造轮子,类似使用原生AspectJ达到面向切面编程.步骤很简单,只需要两步. 1.导入依赖 ...

  3. 补习系列(12)-springboot 与邮件发送【华为云技术分享】

    目录 一.邮件协议 关于数据传输 二.SpringBoot 与邮件 A. 添加依赖 B. 配置文件 C. 发送文本邮件 D.发送附件 E. 发送Html邮件 三.CID与图片 参考文档 一.邮件协议 ...

  4. Mysql系列(九)—— 性能分析explain执行计划

    explain是mysql中sql优化的一个重要手段.顾名思义,explain就是解释sql,用于表示sql是怎样执行的信息,即sql执行计划! 语法 explain statement statem ...

  5. 【HTML】前台input上传限制文件类型

    仅限制xls文件上传 <input id="uploadSkufile" type="file" value="批量导入" style ...

  6. thinkphp3.2.3使用formdata的多文件上传

    使用formdata的多文件上传  废话少说 直接上代码 1 JS部分 //选择文件后的处理 function handleFileSelect() { var exerciseid=$(" ...

  7. Sql与Oracle的差异

    /*整理背景201403订单中心数据库迁移(整理Oracle与SQL的差异)整理规则第一句为SQL Server 第二句为Oracle*/--数据类型int integervarchar varcha ...

  8. OO_BLOG4_UML系列学习

    目录 Unit4 作业分析 作业 4-1 UML类图解析器UmlInteraction 作业 4-2 扩展解析器(UML顺序图.UML状态图解析,基本规则验证) 架构设计及OO方法理解的演进 测试理解 ...

  9. Android 培训准备资料之project与module的区别(1)

    project和module的区别? 现在我们来看看在Android studio中怎样新建一个project (1)file->new->new project. Application ...

  10. Go 语言基础语法-Go

    Go 标记 Go 程序可以由多个标记组成,可以是关键字,标识符,常量,字符串,符号.如以下 GO 语句由 6 个标记组成: fmt.Println("Hello, World!") ...