文章目录

  • 1. 为什么要用静态代理
  • 2. 静态代理的实现
  • 3. 静态代理的缺点
  • 4. 动态代理
    • 4.1 JDK动态代理
      • 4.1.1 InvocationHandler
      • 4.1.2 Class
      • 4.1.3 ClassLoader
    • 4.2 JDK动态代理编码
    • 4.3 JDK动态代理编码注意事项
    • 4.4 Cglib动态代理
      • 4.4.1 MethodInterceptor
    • 4.5 Cglib动态代理编码
  • 5. 对于两种方式的总结
  • 6. JDK动态代理与Cglib动态代理的区别

1. 为什么要用静态代理

个人博客地址:
http://xiaohe-blog.top

在分层开发中,哪个层次对我们来说更加重要呢 ?Dao?Service?Controller?

肯定是Service层了,不管做什么样的项目,其主要任务就是通过各个Service的分工合作来满足用户的需求。Dao专注于与数据库打交道,Controller负责处理前端传来的数据。Service负责连接二者完成业务。

那么Service中包含了哪些代码 ?

我们要在Service中写两种代码 :附加功能与核心功能。

附加功能例如开启事务、记录一下这个操作进行的日期、机器的运行状态…

核心功能才是我们完成业务的代码。

附加功能的特点是什么呢?通用,例如事务无非就是开启、提交、回滚。假如附加业务有20行,一个addUser需要这么多附加业务,那还有dalateUser、getUser、UpdateUser…甚至其他的StudentService也需要这些代码,一个项目加起来怕是有上千行这样重复的代码。

那我们能不能把它们抽取出来制作为一个类,让这个类代替Service的附加功能,需要的时候直接调用呢?

这就是代理,将附加业务抽取出来,让其他的类完成,需要的时候直接调用即可。

这里有两个名词 :代理类、原始类。

代理类 :被抽取出来的附加功能。

原始类 :包含核心业务的类。


如果你还没有搞懂,那我们来看看代理模式典型案例 :租房 。在早期租房过程中,有两个角色 :租户、房东。

租户要去看租房信息、联系房东、看房、付钱。

房东需要去发布房子广告、带领客户看房、收钱。

按照一切皆对象的原则,我们可以将二者抽象为两个类

设想一下你是房东,你的主要目的是什么 ?收钱啊,打广告、带人看房多累的事啊我tm一年啥也不干了就在外面打广告呢,老子只想拿钱,拿钱就是我的核心业务,其他我都不想干。

这时候另一个行业兴起了 :中介。你不想打广告?我帮你打!你不想带客户看房?我帮你带!虽然我没房,但是我愿意花这个时间挣这个钱啊。于是房东将租房的事交给中介…

中介去打广告、带租户看房,等到满意了直接让房东收钱就好。

注意这里并不是我们想的房东调用中介的附加功能,而是全权交给中介,中介调用房东的核心功能。

中介就是代理类,房东是原始类。

于是我们就能总结静态代理的概念与好处 :

1. 概念: 通过代理类为原始类增加额外功能

2. 好处: 利于原始类功能的扩展与维护

2. 静态代理的实现

并且中介的方法名也要叫“出租房屋”,不叫出租房屋叫啥?出租电动车?肯定要与房东想要的一样啊。各位可能想的是将房东设计为接口,让中介实现它。但是房东的核心功能要实现,那么房东这个接口中全部都是默认方法了,很不美观。我们让房东和中介实现同样的接口不就行了?

在这里,中介是代理对象;房东依然是原始对象。

所以我们先定义一个接口:

public interface UserService {// 出租房屋public void rentHome();
}

让房东和中介都实现这个接口

// 房东
public class UserServiceImpl implements UserService{@Overridepublic void rentHome() {System.out.println("收钱...");}
}

中介完成自己的核心功能,它的核心功能就是给房东添加附加功能。

// 中介
public class UserServiceProxy implements UserService{private UserServiceImpl fangDong = new UserServiceImpl();@Overridepublic void rentHome() {System.out.println("打广告...");System.out.println("带客户看房...");System.out.println("签合同...");// 调用核心业务fangDong.rentHome();System.out.println("给房客提供后续业务..");}
}

以后,我们的房东可以专注于核心业务:“收钱” 的完成,无需关心其他。

这样,我们就完成了静态代理模式的开发。

3. 静态代理的缺点

虽然刚学静态代理,但是可以看出静态代理的缺点 :

一个房东租房需要编写一个代理类 ,那如果还有什么租车、租游戏号都需要中介…那么我们是不是要写很多代理类?

马上一个项目全是代理类得了。那我们能不能让别人替我们创建代理类呢?可以使用动态代理来解决这个问题。

动态代理很重要!!!

4. 动态代理

动态代理解决了静态代理的“代理类繁杂”问题,但也付出了代价 :编写难度高。希望大家可以坚持。

动态代理分为两种:

  1. JDK动态代理

  2. Cglib动态代理

他们的区别会在下文介绍。

4.1 JDK动态代理

JDK为我们提供了一个接口完成动态代理的开发 :Proxy,它有一个静态方法 newProxyInstance()

这个方法的参数有三个,初学者很容易被劝退,这将是我们学习的重点,也是难点。笔者将会将他们分为三个小模块分别讲解。(CGLIB代理也是这几个,现在学了,等会就不讲了)

Proxy.newInstance(ClassLoader loader, // 类加载器Class<?>[] interfaces, // 接口Class数组InvocationHandler h // 某个神秘的接口
)

4.1.1 InvocationHandler

我们实现动态代理的主要目的是 “实现附加业务”,所以先来看看在哪里编写附加业务。

InvocationHandler是一个接口,我们需要实现它并将实现类传进去。它只有一个方法 :invoke

public interface InvocationHandler {public Object invoke(Object proxy, Method method, Object[] args) throws Throwable;
}

参数 :

Object proxy

核心业务对象。你为谁添加代附加功能?房东?那proxy就是房东对象。值得一提的是,这个proxy是代理后的对象而不是原始对象。

Method method

核心业务。你为哪一个方法添加额外功能 ?租房?那method就是租房方法.

可以通过method.invoke(原始对象的实例, args) 调用它。

Object[] args

核心业务的参数。

返回值:

Object :核心业务的返回值。附加业务要与核心业务的返回值相同,今天你为房东代理,返回值为钱,明天你为宠物代理,返回的是动物…那么我们怎么知道核心业务要返回什么呢?刚才看到参数中的 Method代表核心业务,那我们是否可以执行它,获取它的返回值!本来核心业务就需要执行,我不仅执行了,还获取返回值返回,一举两得!

于是InvocationHandler可以这样写 :

public MyInvocationHandler implements InvocationHandler {private UserService = new UserServiceImpl();public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {System.out.println("打广告...");System.out.println("带客户看房...");System.out.println("签合同...");// 调用核心业务,返回值保留,以后返回。Object ret = method.invoke(userService, args);System.out.println("给房客提供后续业务..");return ret;}
}

(当然用匿名内部类也可以,下面的完整编码为了减少代码量就是用了匿名内部类)

Proxy.newProxyInstance(x, x, new MyInvocationHandler());

4.1.2 Class

在静态代理的学习中我们说过 :代理类和原始类要实现相同的接口。并且 Class 这个参数的参数名也是 interfaces,所以这个参数就可以是 userService.getClass().getInterfaces()。

userService.getClass().getInterfaces()
Proxy.newProxyInstance(x, userService.getClass().getInterfaces();new MyInvocationHandler());

4.1.3 ClassLoader

学过反射应该知道这个东西,它叫类加载器,为什么需要类加载器?不管是核心业务类的读取还是动态代理类的创建,都需要用到类加载器,相比于上面的那个类,这个就很简单了。

我们可能不知道类加载器分为几种,也不知道类加载器如何单独创建,但是我们可以借助别人的。

// 用谁的都一样,只要不是JDK内置类的类加载器就行。
// 可以是
userService.getClass().getClassLoader();
Proxy.newProxyInstance(userService.getClass(), userService.getClass().getInterfaces(),new MyInvocationHandler());

4.2 JDK动态代理编码

学习Proxy.newProxyInstance()的三个参数后,我们就可以完成JDK动态代理的代码编写了。

动态代理编程分为三步 :

  1. 创建原始对象
  2. 完成 InvocationHandler 代理
  3. 调用 Proxy.newProxyInstance
@Test
public void test() {//1. 创建原始对象UserService userService = new UserServiceImpl();//2. 匿名内部类创建 InvocationHandlerInvocationHandler handler = new InvocationHandler() {@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {System.out.println("打广告...");System.out.println("带客户看房...");System.out.println("签合同...");// 调用核心业务,返回值保留,以后返回。Object ret = method.invoke(userService, args);System.out.println("给房客提供后续业务..");return ret;}};//3. 调用 Proxy.newProxyInstanceUserService proxyInstance = (UserService) Proxy.newProxyInstance(userService.getClass().getClassLoader(),userService.getClass().getInterfaces(),new MyInvocationHandler())proxyInstance.rentHome();
}

4.3 JDK动态代理编码注意事项

刚才我们在完成InvocationHandler 类时这样写 :

在完成另外两个参数时也是用了userService.getClass() 而没用使用 UserService.class来获取类实例,为什么呢?

我们可以先试试能不能使用 :

果不其然,报错!那么为什么呢?

对于为什么不能使用proxy直接传参,刚才在介绍时已经说了,invoke需要的是原始方法,proxy是代理后对象。

那么我们将proxy换成 userService行不行呢?

可以看到依旧不行,那么问题一定在 UserService.classuserService.getClass() 的区别。

在学习反射我们学了获取Class实例的方法有 .class、。getClass(),也学了他俩的区别 :

.class是编译时Class实例,它不受多态影响。

.getClass是运行时Class实例,说白了就是多态实例。

大白话:

对于:
UserService userService = new UserServiceImpl();

UserService.class获取的是 UserService 的Class实例。

userService.getClass() 获取的是 UserServiceImpl 的Class实例

我们可以进行验证 :

UserService userService1 = new UserServiceImpl();
UserServiceImpl userService2 = new UserServiceImpl();
System.out.println(userService1.getClass() == UserService.class);
System.out.println(userService2.getClass() == UserService.class);
System.out.println(userService1.getClass() == userService2.getClass());
// 无奖竞答:这个答案是什么?

UserService.class 获取的是 UserService 的Class

userService1 获取的是 UserServiceImpl 的Class

userService2 获取的是 UserServiceImpl 的Class

所以答案是 false、false、true。

所以在完成动态代理编码时一定要注意使用 原始对象的Class实例,而不是类的Class实例。

4.4 Cglib动态代理

学习JDK动态代理后,Cglib就容易一点了。

JDK动态代理和Cglib动态代理的区别,先别说底层原理,就我们“肉眼可见”可见的最大区别,就是 :

JDK动态代理基于接口;Cglib动态代理基于继承

Cglib动态代理需要代理类继承原始类

Cglib提供了一个类 :Enhancer,这个类有一个create()方法生成动态代理类。

Enhancer不是接口,所以直接new Enhancer之后将它的成员变量赋值,例如要指定父类是谁、类加载器用啥样的…

所以在调用create()方法之前,我们需要指定几个参数 :

1、类加载器 setClassLoader();

2、父类Class实例 setSuperclass();

3、MethodInterceptor 实现类 setCallback();

前两个类已经不用讲了,现在就是一个新的类 :MethodInterceptor 接口。

(它与Springaop中的MethodInterceptor可不是同一个包下的啊,不要搞混)

接下来学习一下它。

4.4.1 MethodInterceptor

public interface MethodInterceptor extends Callback {Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable;
}

参数 :

(它跟InvocationHandler有三个一样的参数,就不再赘述,最后一个参数用不到,提一下。)

Object o

代理后的对象。

Method method

核心业务。

Object[] args

核心业务的参数。

MethodProxy methodProxy

生成的代理类对方法的代理引用

返回值:

Object

这个不用说了吧,跟上面JDK动态代理InvocationHandler.invoke()的返回值一样,都代表核心业务的返回值。

于是 MethodInterceptor可以这样写 :

public MyMethodInterceptor implements MethodInterceptor {private UserService = new UserServiceImpl();Object intercept(Object o, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {System.out.println("打广告...");System.out.println("带客户看房...");System.out.println("签合同...");// 调用核心业务,返回值保留,以后返回。Object ret = method.invoke(userService, args);System.out.println("给房客提供后续业务..");return ret;}
}

(当然用匿名内部类也可以,下面的完整编码为了减少代码量就是用了匿名内部类)

4.5 Cglib动态代理编码

原始对象为父,代理类为子。

// 原始对象
public class UserServiceImpl {public void rendHome() {System.out.println("收钱...");}
}

完整代码如下 :

@Test
public void testCglib() {UserServiceImpl userService = new UserServiceImpl();MethodInterceptor methodInterceptor = new MethodInterceptor() {@Overridepublic Object intercept(Object o, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {System.out.println("打广告...");System.out.println("带客户看房...");System.out.println("签合同...");// 调用核心业务,返回值保留,以后返回。Object ret = method.invoke(userService, args);System.out.println("给房客提供后续业务..");return ret;}};Enhancer enhancer = new Enhancer();// 设置三个属性: 类加载器、父类Class实例、MethodInterceptor实例enhancer.setClassLoader(userService.getClass().getClassLoader());enhancer.setSuperclass(userService.getClass());enhancer.setCallback(methodInterceptor);// 获取代理类UserServiceImpl userServiceProxy = (UserServiceImpl) enhancer.create();userServiceProxy.rentHome();
}

5. 对于两种方式的总结

我画了一张流程图,希望对大家有帮助 :

6. JDK动态代理与Cglib动态代理的区别

1、使用技术不同

  • JDK动态代理原理是拦截器+反射机制,将原始类拦下进行包装。

  • CGLIB动态代理原理是动态字节码,通过修改字节码生成子类。

2、受用对象不同

  • JDK动态代理是针对接口的代理技术
  • CGLIB动态代理针对继承的代理技术

当有接口时使用JDK动态代理,没有接口只能继承时,使用CGLIB动态代理。

不写了,累了,种地去了

静态代理、动态代理概念及使用相关推荐

  1. 【Android 插件化】Hook 插件化框架 ( Hook 技术 | 代理模式 | 静态代理 | 动态代理 )

    Android 插件化系列文章目录 [Android 插件化]插件化简介 ( 组件化与插件化 ) [Android 插件化]插件化原理 ( JVM 内存数据 | 类加载流程 ) [Android 插件 ...

  2. 设计模式之代理模式(静态代理动态代理)

    目录 1.什么是代理模式 2.代理模式的结构 3.代理模式的实现 3.1 静态代理和动态代理概念 3.2 静态代理 3.3 动态搭理 3.3.1 代码实现 3.3.2 Proxy类讲解 4.动态代理V ...

  3. 面试被问到Java 静态代理/动态代理?不用怕,这样子就可以!!

    理解Java动态代理需要对Java的反射机制有一定了解 什么是代理模式# 在有些情况下,一个客户不能或者不想直接访问另一个对象,这时需要找一个中介帮忙完成某项任务,这个中介就是代理对象. 例如,购买火 ...

  4. ❤️Spring的静态、动态代理模式

    ❤️Spring的静态/动态代理模式 为什么要学习代理模式,因为AOP的底层机制就是动态代理! 代理模式: 静态代理 动态代理 1.静态代理 静态代理角色分析 抽象角色 : 一般使用接口或者抽象类来实 ...

  5. 静态代理,动态代理,Cglib代理详解

    一.静态代理 新建一个接口 定义一个玩家方法: package com."".proxy.staticc;public interface Iplayer { public voi ...

  6. java中的静态、动态代理模式以及Spring中的CgLib动态代理解读(面试必问)

    java中的静态.动态代理模式以及Spring中的CgLib动态代理解读(面试必问) 静态代理 动态代理 CgLib动态代理     基础知: 反射知识 代理(Proxy)是一种设计模式,提供了对目标 ...

  7. Android开发如何理解Java静态代理 动态代理及动态生成代理对象原理 看这篇就够了

    动态代理与静态代理 前言 代理模式 静态代理 动态代理 JDK代理 动态生成代理对象原理 生成class数据源码 动态代理类真身 总结 前言 近期在研究Hook技术,需要用到动态代理,说到动态代理就会 ...

  8. 【Java】代理模式(静态代理动态代理)

    CONTENT 代理模式 静态代理 动态代理 JDK 动态代理(基于接口) CGLIB 动态代理(基于类继承) JDK 动态代理 v.s. CGLIB 动态代理 JDK 动态代理为什么必须基于接口 R ...

  9. Java代理模式/静态代理/动态代理

    代理模式:即Proxy Pattern,常用的设计模式之一.代理模式的主要作用是为其他对象提供一种代理以控制对这个对象的访问. 代理概念 :为某个对象提供一个代理,以控制对这个对象的访问. 代理类和委 ...

  10. Java代理模式——静态代理动态代理

    proxy mode 1. 什么是代理 1.1 例子解释 1.2 作用 2. 静态代理 2.1 优缺点分析 2.2 以厂家卖u盘用代码说明 3. 动态代理 3.1 什么是动态代理 3.2 jdk实现原 ...

最新文章

  1. 3.MOC文件解读(下)——MOC文件中的函数
  2. [TYVJ] P1016 装箱问题
  3. UTF-8 BOM
  4. mysql json invalid json text_MySQL 5.7新增加的json数据类型
  5. android淡入淡出动画_在Android中淡入动画示例
  6. 自定义你的VSCode:主题、文件图标、快捷键、设置、schema、插件
  7. 查看Linux版本系统信息方法汇总
  8. 上汽集团减发员工绩效工资75% 时效长度3到6个月
  9. 加深认识与理解ADO.NET
  10. JSK-389 同因查找【入门】
  11. 书籍推荐:《Java数据结构与算法》
  12. Pandas系列(八)字符串处理
  13. iOS Xcode热重载工具: InjectionIII 使用简介(无需重启,快速刷新界面,动态调试)
  14. 大表join大表_阿里开发规范:超过三张表,禁止join骚操作
  15. Web(一)基础学习
  16. 软件中的易用性设计及测试(二)
  17. dnfdpl服务器维护了,魔兽世界TBC:地狱火半岛隐藏的“大恐怖”,魔能机甲成双出没!...
  18. java jlabel 字体大小_java-如何在更改字体大小时更改JLabel的大小...
  19. 新手做独立站需要掌握哪些技能
  20. 数据库的运算----选择,投影,连接

热门文章

  1. 营销软文写作_软文营销写作_软文写作服务_智能写作服务商|Giiso智搜
  2. 停牌期间前复权价格波动检查
  3. 整数划分问题(分治算法经典)
  4. 连载:14,如何成为一个好的CTO
  5. uni-app - 最详细 H5 网页接入微信登录功能,提供公众号配置与详细注释示例代码(移动端网页实现点击登录按钮后 调用微信公众号授权登录功能 详细讲解接入流程与详细示例代码)官方最新超级详细教程
  6. 学校计算机室应该配备哪种灭火器,学校教学楼应配备的灭火器型号是什么呢
  7. Linux TCP SYN包不返回问题解析
  8. 微信群分享:Python网络爬虫初探
  9. 前端原生微信小程序实战经验总结
  10. How to activate office 2010