由反射引出的Java动态代理与静态代理
写在开头
在《深入剖析Java中的反射,由浅入深,层层剥离!》这篇文章中我们讲反射时,曾提到过Java的动态代理中使用了反射技术,那么好,今天我们要就着反射的索引,来学习一下Java中的代理!
代理模式
在Java中有多达23种的设计模式(后面Java基础更新完后,会找个时间详细的去写写这些设计模式),恰当的设计模式的使用能够提升代码的效率,简化代码的复杂性。
而今天我们要说的代理模式就是其中之一,所谓代理是为某对象提供一种代理以控制对该对象的访问。即客户端通过代理间接地访问该对象,从而限制、增强或修改该对象的一些特性。
大白话:买房的(客户方),房产销售(代理方),卖房的(委托方)

在Java中有静态代理和动态代理两种实现方式,继续放下看!!!
静态代理
所谓静态代理,一般是针对编译期就已经完成了接口,实现类,代理类的定义,我们对目标对象的增强需要手工去完成,一个目标对象就要有个代理类,非常不灵活。
静态代理的实现步骤
1,因为代理类与被目标对象有相似的行为(共同),所以我们先创建一个接口。
public interface SaleHouse {
String saleHouse();
}
2,提供接口的实现类,当做目标对象
public class SaleHouseImpl implements SaleHouse{
@Override
public String saleHouse() {
return "我要卖房子啦!!!";
}
}
3,代理类同样也要实现接口,并在目标方法前后做一些控制操作
public class SaleHouseProxy implements SaleHouse{
private SaleHouse saleHouse;
//提供一个包含目标对象的有参构造
public SaleHouseProxy(SaleHouse saleHouse) {
this.saleHouse = saleHouse;
}
@Override
public String saleHouse() {
//调用方法前,我们可以加一些自己的控制
System.out.println("我要收代理费!!!");
System.out.println("--------------------");
String s = saleHouse.saleHouse();
System.out.println(s);
System.out.println("--------------------");
//调用方法后,我们依旧可以操作
System.out.println("我要拿提成!!!");
return "这就是静态代理";
}
}
4,客户端调用代理类,并传入目标对象
public class Test {
public static void main(String[] args) throws FileNotFoundException {
//客户端调用静态代理
SaleHouse saleHouse = new SaleHouseImpl();
SaleHouseProxy saleHouseProxy = new SaleHouseProxy(saleHouse);
saleHouseProxy.saleHouse();
}
}
5,控制台查看打印结果
我要收代理费!!!
--------------------
我要卖房子啦!!!
--------------------
我要拿提成!!!
动态代理
其实无论是静态代理还是静态代理,在我们的日常开发中,使用的都是很多,但对于SpringAop、RPC等框架来说,动态代理发挥着相当大的作用,动态代理具有:运行时控制,灵活性更好的特点。
那怎么实现动态代理呢?
如下三种方式:
JDK 动态代理
CGLib 动态代理
使用 Spring aop 模块完成动态代理功能 //今天先不说这个
JDK动态代理
实现步骤:
1,定义一个接口及其实现类;
代码同静态代理中步骤1,步骤2;
2,自定义 InvocationHandler (调用处理器)并重写invoke方法,在 invoke 方法中我们会调用原生方法(被代理类的方法)并自定义一些处理逻辑;
public class JdkDynamicInvocationHandler implements InvocationHandler {
//代理类中的真实对象
private final Object target;
public JdkDynamicInvocationHandler(Object target) {
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//调用方法前,我们可以加一些自己的控制
System.out.println("我要收代理费!!!");
Object invoke = method.invoke(target, args);
//调用方法后,我们依旧可以操作
System.out.println("我要拿提成!!!");
return invoke;
}
}
3,通过 Proxy.newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h) 方法创建代理对象;其实,这一步也可以写在第2步的代码里,不过为了代码的可读性,我们进行解耦实现!
3.1,定义一个工厂类,在工厂类中通过Proxy.newProxyInstance()方法获取某个类的代理对象
public class JdkDynamicProxyFactory {
public static Object getProxy(Object target) {
return Proxy.newProxyInstance(
target.getClass().getClassLoader(), // 目标类的类加载器
target.getClass().getInterfaces(), // 代理需要实现的接口,可指定多个
new JdkDynamicInvocationHandler(target) // 代理对象对应的自定义 InvocationHandler
);
}
3.2,客户端传入目标对象,实现代理扩展
//客户端调用静态代理
SaleHouse proxySaleHouse = (SaleHouse) JdkDynamicProxyFactory.getProxy(new SaleHouseImpl());
proxySaleHouse.saleHouse();
4,控制台输出
我要收代理费!!!
我要卖房子啦!!!
我要拿提成!!!
【扩展】
关于Proxy类的静态工厂方法newProxyInstance()如何创建代理实例的过程,感兴趣的可以去读源码,或者参考下面这篇文章《代理模式在开源代码中的应用》
CGLIB 动态代理
其实在JDK动态代理中有一个弊端,那就是只能代理接口或接口的实现类,那么未实现任何接口的类就不能代理了吗?答案是否定的,因为咱们有CGLIB!
CGLIB (Code Generation Library) 是一个基于ASM的字节码生成库,它允许我们在运行时对字节码进行修改和动态生成,CGLIB 通过继承方式实现代理。
实现步骤:
1,引入cglib依赖
因为是第三方实现的动态代理,所以在使用前先引入依赖包
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>3.3.0</version>
</dependency>
2,定义一个类;
public class Person {
public void eat(){
System.out.println("我在吃饭!!!");
}
}
3,自定义 MethodInterceptor 并重写 intercept 方法,intercept 用于拦截增强被代理类的方法,和 JDK 动态代理中的 invoke 方法类似;
public class CglibMethodInterceptor implements MethodInterceptor {
/**
* @param o 被代理的对象(需要增强的对象)
* @param method 被拦截的方法(需要增强的方法)
* @param args 方法入参
* @param methodProxy 用于调用原始方法
*/
@Override
public Object intercept(Object o, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
//调用方法前,我们可以加一些自己的控制
System.out.println("饭前先洗手");
Object object = methodProxy.invokeSuper(o, args);
//调用方法前,我们可以加一些自己的控制
System.out.println("饭后要擦嘴");
return object;
}
}
4,创建一个工厂类,用来构建代理对象,通过 Enhancer 类的 create()方法实现;
public class CglibProxyFactory {
public static Object getProxy(Class<?> clazz) {
// 创建动态代理增强类
Enhancer enhancer = new Enhancer();
// 设置类加载器
enhancer.setClassLoader(clazz.getClassLoader());
// 设置被代理类
enhancer.setSuperclass(clazz);
// 设置方法拦截器
enhancer.setCallback(new CglibMethodInterceptor());
// 创建代理类
return enhancer.create();
}
}
5、客户端调用,通过反射传入Person类信息
public static void main(String[] args) throws FileNotFoundException {
//客户端调用静态代理
Person person = (Person) CglibProxyFactory.getProxy(Person.class);
person.eat();
}
6、输出
饭前先洗手
我在吃饭!!!
饭后要擦嘴
OK,终于码完了动态代理,自己还去看了很久的源码,头昏脑涨!
结尾彩蛋
如果本篇博客对您有一定的帮助,大家记得留言+点赞+收藏呀。原创不易,转载请联系Build哥!

由反射引出的Java动态代理与静态代理的更多相关文章
- Java代理模式/静态代理/动态代理
代理模式:即Proxy Pattern,常用的设计模式之一.代理模式的主要作用是为其他对象提供一种代理以控制对这个对象的访问. 代理概念 :为某个对象提供一个代理,以控制对这个对象的访问. 代理类和委 ...
- 【Java】代处理?代理模式 - 静态代理,动态代理
>不用代理 有时候,我希望在一些方法前后都打印一些日志,于是有了如下代码. 这是一个处理float类型加法的方法,我想在调用它前打印一下参数,调用后打印下计算结果.(至于为什么不直接用+号运算, ...
- java中代理,静态代理,动态代理以及spring aop代理方式,实现原理统一汇总
若代理类在程序运行前就已经存在,那么这种代理方式被成为 静态代理 ,这种情况下的代理类通常都是我们在Java代码中定义的. 通常情况下, 静态代理中的代理类和委托类会实现同一接口或是派生自相同的父类. ...
- java 基础 --- 动态代理和静态代理
问题 : 代理的应用场景是什么 动态代理的底层原理是什么,为什么只能继承接口 概述 代理模式是设计模式的一种,简单地说就是调用代理类的方法实际就是调用真实类的方法.这种模式在AOP (切面编程)中非 ...
- Java设计模式学习06——静态代理与动态代理(转)
原地址:http://blog.csdn.net/xu__cg/article/details/52970885 一.代理模式 为某个对象提供一个代理,从而控制这个代理的访问.代理类和委托类具有共同的 ...
- Spring中AOP的两种代理方式(Java动态代理和CGLIB代理)
第一种代理即Java的动态代理方式上一篇已经分析,在这里不再介绍,现在我们先来了解下GCLIB代理是什么?它又是怎样实现的?和Java动态代理有什么区别? cglib(Code Generation ...
- java运行原理、静态代理和动态代理区分
1.java的编译和运行原理: ■ 编译:将源文件 .java 文件,通过编译器(javac 命令) 编译成 字节码文件 .class 文件. ■ 运行,通过类加载器(以二进制流形式)把字节码加载进J ...
- Java中的代理模式--静态代理和动态代理本质理解
代理模式定义:为其他对象提供了一种代理以控制对这个对象的访问. 代理模式的三种角色: Subject抽象主题角色:抽象主题类可以是抽象类也可以是接口,是一个最普通的业务类型定义,无特殊要求. Real ...
- Java代理(静态代理、JDK动态代理、CGLIB动态代理)
Java中代理有静态代理和动态代理.静态代理的代理关系在编译时就确定了,而动态代理的代理关系是在运行期确定的.静态代理实现简单,适合于代理类较少且确定的情况,而动态代理则给我们提供了更大的灵活性. J ...
- Java动态代理与静态代理以及它能为我们做什么
相信我们在网上和平时学习和工作中或多或少都接触过Java的代理模式,经常听到什么静态代理.动态代理的一些名词.但我们是否真的很清楚这些呢?至少我在面试时,发现很多人并不很清楚. 首先代理比较好理解,就 ...
随机推荐
- 硬核调试实操 | 手把手带你实现 Serverless 断点调试
导读:在应用开发过程中,或者开发完成后,若出现执行结果不符合我们的预期时,通常需要进行一定的调试工作.但是在 Serverless 架构下,调试工作往往会受到一些环境因素限制,如所开发的应用在本地是比 ...
- vue学习笔记 五、创建子组件实例
系列导航 vue学习笔记 一.环境搭建 vue学习笔记 二.环境搭建+项目创建 vue学习笔记 三.文件和目录结构 vue学习笔记 四.定义组件(组件基本结构) vue学习笔记 五.创建子组件实例 v ...
- Visual Studio2019 使用WCF服务
什么是WCF Windows Communication Foundation (WCF) 是一个框架,用于生成面向服务的应用程序.它取代了较旧的进程间通信技术,例如 ASMX Web 服务..NET ...
- DFT Architecture
Design For Test 在实际生产过程中产生的physical defect是导致芯片功能出错的根本原因 如何根据结构产生测试向量呢?主要考虑physical defect physical ...
- 问题--如何快速复制Typora(Markdown)中的代码块/区块等
使用快捷键ctrl+E,选中相同格式的文字,然后快速复制即可
- JavaScript - input 上传图片 并展示 (食用简单)
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF ...
- [转帖]PostgreSQL数据库的版本历史及关键变化
https://cloud.tencent.com/developer/article/2311843 举报 PostgreSQL是一个强大的开源关系型数据库,它的发展历程充满了创新和卓越的设计.让我 ...
- [转帖]如何用 30s 给面试官讲清楚什么是 Session-Cookie 认证
https://www.jianshu.com/p/e1121d4d7084 引言 由于 HTTP 协议是无状态的,完成操作关闭浏览器后,客户端和服务端的连接就断开了,所以我们必须要有一种机制来保证客 ...
- [转帖]tikv性能参数调优
https://www.cnblogs.com/FengGeBlog/p/10278368.html#:~:text=max-%20bytes%20-for-level-%20base%20%3D%2 ...
- 【转帖】在ECS上配置skywalking-nginx-lua
https://help.aliyun.com/document_detail/197660.html 此处以在CentOS 7.0上的操作为例. 配置Lua运行环境. 安装工具库. yum in ...