1. 概述
1.1 定义
- 代理模式(Proxy),顾名思义,有代表打理的意思。在某种情况下,当客户端不能或不适合直接访问目标业务对象时,目标业务对象可以通过代理把自己的业务托管起来,使客户端间接地通过代理进行业务访问。意思就是,代理方以目标业务对象的名义代理了它的业务。
- 就比如生活中常见的例子:明星经纪人对明星推广业务的代理,家用路由器代理互联网服务上网业务等等。
1.2 组成部分
- 抽象角色:通过接口或抽象类声明真实角色实现的业务方法。
- 真实角色:实现抽象角色,定义真实角色所要实现的业务逻辑,供代理角色调用。
- 代理角色:实现抽象角色,是真实角色的代理,通过真实角色的业务逻辑方法来实现抽象方法,并可以附加自己的操作。
2. 静态代理
2.1 引出代理模式的一个简单业务场景
- 看下面简单的例子,现在要对实现类里的 playBall() 方法增强功能,比方说在狗狗玩球之前先要有球(即买球,买多大的球等),需要在下面业务功能的基础上怎么扩展?
package com.liu.susu.design.pattern.proxy.statics.example1;
/**
* @Description 使用静态代理前思考业务场景:
* 业务需求:现在在狗狗拿到球之前需要添加新的功能,
* 比方说根据狗狗的年龄等判断买大小合适的球,
* 这种情况我们怎么扩展现在的逻辑,即怎么实现对 playBall() 方法增强功能?
* @Author susu
* @date 2022-02-11
**/
public interface PetPlayInterface {
void playBall();//玩球
}
class DogPlay implements PetPlayInterface {
@Override
public void playBall() {
System.out.println("狗狗喜欢玩球,狗狗又去玩球去了……");
}
}
- 根据上面的代码,如果要进行扩展,可以直接在原有的 playBall() 方法里添加业务逻辑,但是一般我们扩展功能尽量保持在原有方法不动的基础上进行扩展,要怎么实现,就是我们接下来的代理模式,请先看下面的用静态代理实现。
2.2 静态代理例子1:
- 直接看代码:
① 实现业务的接口 和 实现业务的被代理类
② 代理类以及增强被代理的方法
③ 测试类
我们看到,代理类再调方法时,虽然没用new 被代理类,但是被代理类方法执行了(在代理类里),这就是静态代理模式,我们再举个例子,你就会更明白些
2.3 静态代理例子2:
- 上面例子是代理买球,接下来这个例子稍微复杂一点点,根据狗狗年龄来判断买大球还是小球(比如年龄大于1岁,买大号球,否则小号球)
- 直接看代码来感受
① 在上述的基础上,公共接口和被代理类保持不变
② 增加一个实体类 Dogs
③ 代理类
④ 测试类
3. 动态代理
3.1 JDK代理(接口代理)
- 就是代理类在实例化是是动态生成的,也就是说我们不需要专门针对某个接口去编写代码获取一个代理类,而是在接口运行时动态生成。对,理论很好说简单一句话,还是看看代码怎么实现的吧!
3.1.1 JDK动态代理例子1
- 为了便于对比,接下来的例子都是两个
- 我们来根据代码说明:
① PetPlayInterface、DogPlay、PetPlayProxy、Dogs同我们静态代理2的代码,此处不再截图了;
② 新添加一个例子,需要注意的是:和狗狗的例子不一样的是,方法虽然没有返回值,但是有参数,很简单,如图:
③ 创建InvocationHandler
接口的实现类,重写invoke
方法,通过这种方式,客户端在通过代理类的对象调用方法 playBall() 时,就会自动调用下面的 invoke 方法,invoke
方法里具体的参数我们在例子2优化代码时再讲解,先看简单的实现,如图:
④ 测试类
⑤ 看测试效果:
报这个错,是因为我们方法针对有参无参不通用,无参可执行,有参报错,修改一下方法,让有参可执行即可
我们下面还有优化,所以此处就做了简单的调整,简单看个效果即可:
ok,有参可执行,但是我们也看到了,此处可优化,下面我就针对参数以及返回值简单说说,请继续看下面优化后的例子……
3.1.2 JDK动态代理例子2
- 直接看代码吧
① 狗狗里新增2个方法,一个如下三个方法
void playBall();//玩球
void eatFood(String food);//吃食物
List<String> likeFruit(String ...fruits);
逻辑没啥可说的,注意参数和返回值,直接看图:
② 再看小孩的:
③ 主要看 PublicPlayProxy2.java
注意下面解释 invoke 方法的参数以及返回值,以及根据业务类里方法的参数优化重写 invoke 优化。
④ 测试1
⑤ 测试2,都是一样的,这个简单看看即可
分析需优化的代码
- 不知道读者有没用注意到,我们上面的例子在客户端测试的时候还是有代理类的出现,那么好像并没有很好地体现动态代理,我看《秒懂设计模式》书上,作者是这么个设计思路,但我没太明白,好像此处并没用动态生成代理对象呀,为啥就动态代理了?,如下图:
- 可能是看知识比较浅,对作者书本知识的理解的还不够透彻,但是不管怎么样,我还是想将上面的再给优化化一下,请继续往下看吧:
3.1.3 JDK动态代理例子3
- 改动的地方:
① 新增 PublicPlayProxy3 (将PublicPlayProxy2
改为PublicPlayProxy3
),但是内容几乎保持不变
② 新增MakeProxyInstance.java
,注意这是核心代码,体现动态性的,如图:
③ 再看测试类ClientJdkProxyTest3.java
,:
④ 测试结果:
分析体现的动态性
- 对比上面的写法,这种优化后的实现方式可谓相当简单,整个过程代理类的代理对象没用出现,是程序执行的过程中动态创建的,所以这才体现了我们所说的动态性嘛!
- ok,到这里算是完美了,相信看到这里的你应该已经非常明白了!
3.2 Cglib 代理
- 今天先到这里,这个后续有空了再补上吧!
4. 附代码
4.1 静态代理代码
4.1 例子1代码
package com.liu.susu.design.pattern.proxy.statics.example2;
/**
* @Description 测试静态代理1
* @Author susu
* @date 2022-02-11
**/
public interface PetPlayInterface {
void playBall();//玩球
}
/**
* 被代理类:目标业务
*/
class DogPlay implements PetPlayInterface {
@Override
public void playBall() {
System.out.println("狗狗喜欢玩球,狗狗又去玩球去了……");
}
}
/**
* 代理类
*/
class PetPlayProxy implements PetPlayInterface {
private DogPlay dogPlay;//被代理对象
//①法: 通过有参构造传入被代理的实例化对象
public PetPlayProxy(DogPlay dogPlay) {
this.dogPlay = dogPlay;
}
//②法: 在无参构造器里直接实例化被代理对象
public PetPlayProxy() {
this.dogPlay = new DogPlay();
}
@Override
public void playBall() {
bugBall();//代理做的事 相当于在不改变被代理 playBall() 方法的前提下,扩展了功能
dogPlay.playBall();//目标对象要做的事(即:代理类将控制权移交给被代理类)
}
public void bugBall() {
System.out.println("代理帮忙买球,卖玩球之后狗狗就可以玩了");
}
}
/**
* 测试类
*/
class ClientStaticProxyTest {
public static void main(String[] args) {
System.out.println("①法: 通过有参构造传入被代理的实例化对象");
DogPlay dogPlay = new DogPlay();
PetPlayProxy petPlayProxy = new PetPlayProxy(dogPlay);
petPlayProxy.playBall();//即:使用的时候不需要被代理调用方法,直接代理对象执行(包括扩展的功能)
System.out.println("\n②法: 在无参构造器里直接实例化被代理对象");
PetPlayInterface petPlayInterface = new PetPlayProxy();//② 直接实例化代理对象
petPlayInterface.playBall();
}
}
4.2 例子2代码
package com.liu.susu.design.pattern.proxy.statics.example3;
/**
* @Description 测试静态代理2 根据狗狗年龄判断买大小合适的球
* @Author susu
* @date 2022-02-11
**/
public interface PetPlayInterface {
void playBall();//玩球
}
/**
* 被代理类:目标业务
*/
class DogPlay implements PetPlayInterface {
@Override
public void playBall() {
System.out.println("狗狗喜欢玩球,狗狗又去玩球去了……");
}
}
/**
* 代理类
*/
class PetPlayProxy implements PetPlayInterface {
private DogPlay dogPlay;//被代理对象
private Dogs dog;
public PetPlayProxy() {
}
public PetPlayProxy(Dogs dog) {
this.dogPlay = new DogPlay();//① 构造器里直接实例化被代理对象
this.dog = dog;
}
@Override
public void playBall() {
bugBall(dog);
dogPlay.playBall();//目标对象要做的事(即:代理类将控制权移交给被代理类)
}
/**
* 新增一个方法:根据狗的年龄判断买什么样的球
*/
public void bugBall(Dogs dog) {
if (dog.getAge()>1){
System.out.println("狗狗-->"+dog.getName()+"的年龄是:"+dog.getAge()+",是成犬了,买大号球");
}else {
System.out.println("狗狗-->"+dog.getName()+"的年龄是:"+dog.getAge()+",还没成犬,买小号球吧");
}
}
}
class Dogs{
private String name;
private int age;
public Dogs(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
/**
* 测试类
*/
class ClientStaticProxyTest {
public static void main(String[] args) {
PetPlayInterface petPlayInterface = new PetPlayProxy(new Dogs("麦兜",2));
petPlayInterface.playBall();
}
}
4.2 动态代理代码
4.1 JDK 动态代理
4.1.1 JDK 动态代理例子1
package com.liu.susu.design.pattern.proxy.dynamic.jdk.example1;
/**
* @Description
* @Author susu
* @date 2022-02-11
**/
public interface PetPlayInterface {
void playBall();//玩球
}
/**
* 被代理类1:DogPlay
*/
class DogPlay implements PetPlayInterface {
@Override
public void playBall() {
System.out.println("狗狗喜欢玩球,狗狗又去玩球去了……");
}
}
/**
* 狗狗代理类
*/
class PetPlayProxy implements PetPlayInterface {
private DogPlay dogPlay;//被代理对象
private Dogs dog;
public PetPlayProxy() {
}
public PetPlayProxy(Dogs dog) {
this.dogPlay = new DogPlay();//① 构造器里直接实例化被代理对象
this.dog = dog;
}
@Override
public void playBall() {
bugBall(dog);
dogPlay.playBall();//目标对象要做的事(即:代理类将控制权移交给被代理类)
}
/**
* 新增一个方法:根据狗的年龄判断买什么样的球
*/
public void bugBall(Dogs dog) {
if (dog.getAge()>1){
System.out.println("狗狗-->"+dog.getName()+"的年龄是:"+dog.getAge()+",是成犬了,买大号球");
}else {
System.out.println("狗狗-->"+dog.getName()+"的年龄是:"+dog.getAge()+",还没成犬,买小号球吧");
}
}
}
class Dogs{
private String name;
private int age;
public Dogs(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
package com.liu.susu.design.pattern.proxy.dynamic.jdk.example1;
/**
* @Description
* @Author susu
* @date 2022-02-11
**/
public interface ChildPlayInterface {
void playToyCar(String type);
}
/**
* 被代理类2:ChildPlay
*/
class ChildPlay implements ChildPlayInterface{
@Override
public void playToyCar(String type) {
System.out.println("小孩子喜欢玩玩具车,去玩球玩玩具车去了,好像是-->"+type);
}
}
//代理类
class ChildPlayProxy implements ChildPlayInterface{
private ChildPlay childPlay;
public ChildPlayProxy(){
childPlay = new ChildPlay();
}
@Override
public void playToyCar(String type) {
childPlay.playToyCar(type);
}
}
4.1.2 JDK 动态代理例子2
- 小孩相关的
package com.liu.susu.design.pattern.proxy.dynamic.jdk.example2;
/**
* @Description
* @Author susu
* @date 2022-02-11
**/
public interface ChildPlayInterface {
void playToyCar();
void eatFood(String food);//吃食物
boolean likeFruit(String ...fruits);//喜欢的水果
}
/**
* 被代理类2:ChildPlay
*/
class ChildPlay implements ChildPlayInterface{
@Override
public void playToyCar() {
System.out.println("小孩子喜欢玩玩具车,去玩球玩具车去了……");
}
@Override
public void eatFood(String food) {
System.out.println("这个食物是:"+food+",小孩子很喜欢吃的……");
}
@Override
public boolean likeFruit(String ...fruits) {
boolean isLike = false;//是否有孩子喜欢的食物
for (String fruit : fruits) {
if ("apple".equals(fruit)){
System.out.println("是苹果,是小孩子喜欢的水果……");
isLike = true;
}else if ("banana".equals(fruit)){
System.out.println("是香蕉,是小孩子喜欢的水果……");
isLike = true;
}else if ("grape".equals(fruit)){
System.out.println("是葡萄,太酸了,是有些小孩子不喜欢的水果……");
}else{
System.out.println("其他水果,不知道小孩子是否喜欢……");
}
}
return isLike;
}
}
class ChildPlayProxy implements ChildPlayInterface{
private ChildPlay childPlay;
public ChildPlayProxy(ChildPlay childPlay) {
this.childPlay = childPlay;
}
@Override
public void playToyCar() {
childPlay.playToyCar();
}
@Override
public void eatFood(String food) {
childPlay.eatFood(food);
}
@Override
public boolean likeFruit(String... fruits) {
return childPlay.likeFruit(fruits);
}
}
- 狗狗相关的
package com.liu.susu.design.pattern.proxy.dynamic.jdk.example2;
import java.util.ArrayList;
import java.util.List;
/**
* @Description
* @Author susu
* @date 2022-02-11
**/
public interface PetPlayInterface {
void playBall();//玩球
void eatFood(String food);//吃食物
List<String> likeFruit(String ...fruits);//喜欢的水果
}
/**
* 被代理类1:DogPlay
*/
class DogPlay implements PetPlayInterface {
@Override
public void playBall() {
System.out.println("狗狗喜欢玩球,狗狗又去玩球去了……");
}
@Override
public void eatFood(String food) {
System.out.println("这个食物是:"+food+",狗狗很喜欢吃的……");
}
@Override
public List<String> likeFruit(String ...fruits) {
List<String> likeFruitList = new ArrayList<String>();
for (String fruit : fruits) {
if ("apple".equals(fruit)){
System.out.println("是苹果,是狗狗喜欢的水果……");
likeFruitList.add(fruit);//把喜欢吃的水果返回
}else if ("banana".equals(fruit)){
System.out.println("是香蕉,狗狗不喜欢吃香蕉……");
}else if ("pear".equals(fruit)){
System.out.println("是梨,是狗狗喜欢的水果……");
likeFruitList.add(fruit);//把喜欢吃的水果返回
}else if ("banana".equals(fruit)){
System.out.println("是香蕉,狗狗不喜欢吃香蕉……");
}else if ("grape".equals(fruit)){
System.out.println("是葡萄,狗狗是不能吃葡萄的,会中毒……");
}else{
System.out.println("未知水果,不知道狗狗能不能吃的水果,确认后再给狗狗吃……");
}
}
return likeFruitList;
}
}
/**
* 狗狗代理类
*/
class PetPlayProxy implements PetPlayInterface {
private DogPlay dogPlay;//被代理对象
public PetPlayProxy() {
this.dogPlay = new DogPlay();//① 构造器里直接实例化被代理对象
}
@Override
public void playBall() {
dogPlay.playBall();//目标对象要做的事(即:代理类将控制权移交给被代理类)
}
@Override
public void eatFood(String food) {
dogPlay.eatFood(food);
}
@Override
public List<String> likeFruit(String... fruits) {
return dogPlay.likeFruit(fruits);
}
}
- PublicPlayProxy2
package com.liu.susu.design.pattern.proxy.dynamic.jdk.example2;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
/**
* @FileName PublicPlayProxy2
* @Description 通用的动态代理类2——处理方法有参或无参的情况
* @Author susu
* @date 2022-02-11
**/
public class PublicPlayProxy2 implements InvocationHandler {
private Object origin;//被代理的真实对象
public PublicPlayProxy2() {
}
public PublicPlayProxy2(Object origin) {
this.origin = origin;//方法① 通过构造器,注入被代理对象
}
public void bindOrigin(Object origin){
this.origin = origin;//方法② 通过调用方法,进行绑定从而给被代理对象赋值
}
/**
* 通过代理类的对象调用方法时,动态调用被代理类的同名方法
* 客户端通过代理类的对象调用方法playBall()时,就会自动调用 invoke 方法
* 即:将被代理类执行的方法playBall()的功能就声明在了invoke 方法中
* @param proxy 动态获取的:被代理的代理类
* @param method 动态获取的:动态要调用的方法
* @param args 客户端调用时传来的参数值
* @return java.lang.Object 动态调用方法 method 时的返回值作为该 invoke 方法的返回值
* @Author susu
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("jdk动态代理,begin 被代理的实际业务……");
Object result;
if (args == null){
result = method.invoke(origin);//无参数的方法
}else {
result = method.invoke(origin, args);//有参数的方法
}
System.out.println("方法的返回值 result 是--->"+result);
System.out.println("jdk动态代理,end");
return result;
}
}
- 测试类:
package com.liu.susu.design.pattern.proxy.dynamic.jdk.example2;
import org.junit.Test;
import java.lang.reflect.Proxy;
/**
* @FileName ClientJdkProxyTest2 JDK动态代理测试类2
* @Description
* @Author susu
* @date 2022-02-11
**/
public class ClientJdkProxyTest2 {
public static void main(String[] args) {
/**************************测试狗狗的******************************************/
// new PublicPlayProxy2(new PetPlayProxy()) 通过构造器注入被代理的真实对象
PetPlayInterface petPlayInterface = (PetPlayInterface) Proxy.newProxyInstance(
PetPlayProxy.class.getClassLoader(),
PetPlayProxy.class.getInterfaces(),
new PublicPlayProxy2(new PetPlayProxy())
);
System.out.println("===========① 测试狗狗 playBall(),代理============");
petPlayInterface.playBall();
System.out.println("\n===========② 测试狗狗 eatFood(String food),代理============");
petPlayInterface.eatFood("火龙果");
petPlayInterface.eatFood(null);
System.out.println("\n===========③ 测试狗狗 likeFruit(String ...fruits),代理============");
petPlayInterface.likeFruit("grape","apple","banana","pear");
}
@Test
public void testChildProxy(){
/**************************测试小孩的******************************************/
ChildPlay childPlay = new ChildPlay();
PublicPlayProxy2 publicPlayProxy2 = new PublicPlayProxy2();
publicPlayProxy2.bindOrigin(childPlay);//通过 bindOrigin 方法注入被代理的真实对象
ChildPlayInterface childPlayInterface = (ChildPlayInterface) Proxy.newProxyInstance(
ChildPlayProxy.class.getClassLoader(),
ChildPlayProxy.class.getInterfaces(),
publicPlayProxy2
);
System.out.println("===========① 测试小孩 playBall(),代理============");
childPlayInterface.playToyCar();
System.out.println("\n===========② 测试小孩 eatFood(String food),代理============");
childPlayInterface.eatFood("火龙果");
childPlayInterface.eatFood(null);
System.out.println("\n===========③ 测试小孩 likeFruit(String ...fruits),代理============");
childPlayInterface.likeFruit("grape","apple","banana","pear");
}
}
4.1.3 JDK 动态代理例子3
- 在上面做的优化,所以只贴新增不一样的代码了
package com.liu.susu.design.pattern.proxy.dynamic.jdk.example2;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
/**
* @FileName PublicPlayProxy3
* @Description 通用的动态代理类3
* @Author susu
* @date 2022-04-15
**/
public class PublicPlayProxy3 implements InvocationHandler {
private Object origin;//被代理的真实对象
public PublicPlayProxy3(Object origin) {
this.origin = origin;//方法① 通过构造器,注入被代理对象
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//调用被代理对象方法
System.out.println("jdk动态代理,开始被代理的实际业务……");
Object result ;
if (args == null){
result = method.invoke(origin);//无参数的方法
}else {
result = method.invoke(origin,args);//有参数的方法
}
System.out.println("jdk动态代理,结束");
System.out.println("方法的返回值 result 是--->"+result);
return result;
}
}
/**
* 用JDK动态代理时,获取动态代理实例
*/
class MakeProxyInstance{
/**
* @Description: 动态获取一个代理类对象:根据被代理类获取代理类对象
* @param object 被代理类
* @return java.lang.Object
* @Author susu
*/
public static Object getProxyInstance(Object object){
PublicPlayProxy3 publicPlayProxy3 = new PublicPlayProxy3(object);
Object proxyInstance = Proxy.newProxyInstance(
object.getClass().getClassLoader(),
object.getClass().getInterfaces(),
publicPlayProxy3
);
return proxyInstance;
}
}
package com.liu.susu.design.pattern.proxy.dynamic.jdk.example2;
/**
* @FileName ClientJdkProxyTest3
* @Description JDK动态代理测试类3——测试通过动态获取代理类
* @Author susu
* @date 2022-02-11
**/
public class ClientJdkProxyTest3 {
public static void main(String[] args) {
DogPlay dogPlay = new DogPlay();
/**
* 通过代理类,获取代理类的对象
* 动态获取代理类的动态性体现在:整个执行过程没用出现代理类
*/
PetPlayInterface proxyInstance
= (PetPlayInterface) MakeProxyInstance.getProxyInstance(dogPlay);
proxyInstance.likeFruit("apple","grape");
System.out.println("\n======================\n");
ChildPlay childPlay = new ChildPlay();
ChildPlayInterface childPlayInterface
= (ChildPlayInterface) MakeProxyInstance.getProxyInstance(childPlay);
childPlayInterface.likeFruit("apple","grape");
}
}
4.2 Cglib 代理
5. 打包源码
6. 参考书籍
- 书籍:刘韬 版的《秒懂设计模式》;