【设计模式】代理模式

一、代理概念

1.1 代理是个什么东西

  • 代理,顾名思义,就是有两个对象A和B,通过A来访问B,就说A代理了B 或者 B被A代理
  • 体现在java中,就是有两个类:类Proxy和类Class,Proxy代理类Class,就是通过Proxy的对象来访问Class的对象
    • 那么怎么体现ProxyClass的关系呢,就是他们都实现同一个接口Interface
    • 虽然如此,但是Class实现Interface的方式是自己干活,而Proxy实现Interface的方式是喊Class干活
  • 比如有一个明星叫美丽,她的经纪人叫老王,他们都实现一个接口:明星,明星的定义是要会唱歌跳舞
    • 你是一个活动的组织人(Main),想找个明星来参加活动,想到了明星的实现:美丽
    • 但是你不能直接找美丽,你要联系她的经纪人,即代理:老王
    • 你告诉老王,活动上要唱歌跳舞,那是老王来唱歌跳舞吗?不是,老王实际去喊美丽来唱歌跳舞
// 首先我们定义一个interface明星
public interface Star {
	// 明星要会唱歌
	public void Sing();
	// 也要会跳舞
	public void Dance();
}

// 这是实现了明星接口的真的人
public class RealStar implements Star {
	private String name = "";
	
	public RealStar(String name) {
		this.name = name;
	}
	
	@Override
	public void Sing() {
		System.out.println(this.name + "在唱歌");
	}

	@Override
	public void Dance() {
		System.out.println(this.name + "在跳舞");
	}
}

// 这是经纪人老王
public class Laowang implements Star {
	// 老王代理的明星
	private Star star = null;
	
	public Laowang(Star star) {
		this.star = star;
	}

	@Override
	public void Sing() {
		star.Sing();  // 老王让代理的明星唱歌
	}

	@Override
	public void Dance() {
		star.Dance();  // 也让代理的明星跳舞
	}
}

// 你要办一个活动啦,要请明星
public class Party {
	public static void main(String[] args) {
		// 你想叫美丽来表演
		Star Meili = new RealStar("美丽");
		// 告诉老王你要美丽来唱歌跳舞
		Laowang laowang = new Laowang(Meili);
		// 老王说好的
		laowang.Sing();
		laowang.Dance();
	}
}
  • 运行之后的实际结果就是:
美丽在唱歌
美丽在跳舞
  • 经过例子,应该对代理有了一个基本的了解,可以明确代理的相关理论了

1.2 概念

  • 代理的定义
    • Provide a surrogate or placeholder for another object to control access to it
    • 为其他对象提供一种代理以控制对这个对象的访问
  • 代理的三要素
    • Subject 抽象主体角色
      • 可以是抽象类 或 接口,是一个最普通的业务类型定义,无特殊要求
    • RealSubject 具体主体角色
      • 也叫做被委托角色、被代理角色,是业务逻辑的具体执行者
    • Proxy 代理主体角色
      • 也叫做委托类、代理类,它负责对真实角色的应用,把所有抽象主体类定义的方法限制委托给真实主体角色实现,并且对其增强。

1.3 增强

  • 关于增强这个要点上例并未体现,而这一点很重要,通过代理类的自定义方法实现
    • 因为美丽是个明星,她只要专心提升唱歌跳舞的技能就行了,其他琐事交给经纪人做
    • 比如收钱,老王作为经纪人要替手下的明星收钱吧,所以我们修改一下老王的类
// 这是经纪人老王
public class Laowang implements Star {
	// 老王代理的明星
	private Star star = null;
	
	public Laowang(Star star) {
		this.star = star;
	}

	// 老王的自定义方法:收钱
	public void Money() {
		System.out.println("老王收钱");
	}

	@Override
	public void Sing() {
		Money();
		star.Sing();  // 老王让代理的明星唱歌
	}

	@Override
	public void Dance() {
		Money();
		star.Dance();  // 也让代理的明星跳舞
	}
}
  • 再运行一下main(),得到的结果就是:
老王收钱
美丽在唱歌
老王收钱
美丽在跳舞

二、代理的分类

  • 代理总共分为两大类
    • 静态代理:需要自己写代理类,又分为两小类
      • 普通代理
      • 强制代理
    • 动态代理:不需要自己写代理类

2.1 静态代理

  • 像第一节中那样既知道实现类又知道代理类的情况,其实是不多见的
  • 普通代理:只知代理类,不应知实现类;通过代理类访问实现类
  • 强制代理:只知实现类,不知代理类;但还是通过代理类访问实现类

2.1.1 普通代理

  • 对外部隐藏实现类,只通过代理类来达成需求,不能有直接访问实现类的可能
    • 比如你知道美丽,于是跳过老王私下联系美丽来参加活动,那老王肯定不高兴了啊,必须对外隐藏
  • 隐藏的方式就是修改构造方法
// 修改RealStar的构造方法
public class RealStar implements Star {
	private String name = "";
	
	public RealStar(Star star, String name) throws Exception {
		if (star == null) {
			throw new Exception(name + "不接私活的昂");
		} else {
			this.name = name;
		}
	}
	// 省略下方代码
}

// 修改老王的构造方法
public class Laowang implements Star {
	// 老王代理的明星
	private Star star = null;
	
	public Laowang(String name) {
		try {
			star = new RealStar(this, name);
		} catch(Exception e) {
			e.printStackTrace();
		}
	}
}

// 接着你办活动啦
public class Party {
	public static void main(String[] args) {
		// 直接告诉老王你要美丽来唱歌跳舞
		Laowang laowang = new Laowang("美丽");
		// 老王说好的
		laowang.Sing();
		laowang.Dance();
	}
}
  • 运行一下,得到结果:
老王收钱
美丽在唱歌
老王收钱
美丽在跳舞

2.1.2 强制代理

  • 强制代理与普通代理的前提恰恰相反:只知实现类,不知代理类
  • 但目的是一样的:通过代理访问实现类
  • 你要搞活动啦,想找美丽来表演,于是你直接给美丽打电话,美丽说不行啊你得找我经纪人,然后把老王的联系方式给你了
    • 这就是强制代理
  • 实现方式:实现类返回代理
// 在接口中新增找到代理方法
public interface Star {
	// 明星要会唱歌
	public void Sing();
	// 也要会跳舞
	public void Dance();
	// 找到自己的代理
	public Star getProxy();
}

// 修改实现类
public class RealStar implements Star {
	private String name = "";
	private Star proxy = null;  // 代理
	
	public RealStar(String name) {
		this.name = name;
	}
	
	// 返回代理
	@Override
	public Star getProxy() {
		proxy = new Laowang(this);
		return proxy;
	}
	
	@Override
	public void Sing() {
		if(this.isProxy()) {
			System.out.println(this.name + "在唱歌");
		} else {
			System.out.println("请找老王");
		}
	}

	@Override
	public void Dance() {
		if(this.isProxy()) {
			System.out.println(this.name + "在跳舞");
		} else {
			System.out.println("请找老王");
		}
	}
	
	// 判断是否有代理
	private boolean isProxy() {
		if (this.proxy == null) {
			return false;
		}
		return true;
	}
}

// 修改代理类
public class Laowang implements Star {
	// 老王代理的明星
	private Star star = null;
	
	public Laowang(Star star) {
		this.star = star;
	}

	@Override
	public Star getProxy() {
		return this;
	}

	// 老王的自定义方法:收钱
	public void Money() {
		System.out.println("老王收钱");
	}

	@Override
	public void Sing() {
		Money();
		star.Sing();  // 老王让代理的明星唱歌
	}

	@Override
	public void Dance() {
		Money();
		star.Dance();  // 也让代理的明星跳舞
	}
}

// 办活动啦
public class Party {
	public static void main(String[] args) {
		// 先找到美丽
		Star meili = new RealStar("美丽");
		// 通过美丽找到老王
		Laowang laowang = (Laowang)meili.getProxy();
		// 老王说好的
		laowang.Sing();
		laowang.Dance();
	}
}

2.2 动态代理

  • 很多教程都会说动态代理和静态代理的区别是:静态代理要自己手写代理类,而动态代理不用
  • 实际上,是的。但动态代理要手写一个实现了InvocationHandler的类,以完成代理功能
    • 所以个人认为,动态代理不用手写代理类这个区别,感觉像是一个文字游戏一样
    • 严格来讲,应该是动态代理不用手写实现三要素中的Subject的代理类而已
  • anyway,不论是手写Subject的代理类,还是手写InvocationHandler的实现类,总是代理功能的逻辑都是由编写代码的我们来决定和完成的
  • 还是明星、美丽、老王,我们使用动态代理的方式来完成
// 首先我们定义一个interface明星
public interface Star {
	// 明星要会唱歌
	public void Sing();
	// 也要会跳舞
	public void Dance();
}

// 这是明星接口的实现类
public class RealStar implements Star {
	private String name = "";

	public RealStar(String name) {
		this.name = name;
	}

	@Override
	public void Sing() {
		System.out.println(this.name + "在唱歌");
	}

	@Override
	public void Dance() {
		System.out.println(this.name + "在跳舞");
	}
}

// 重点就在这个老王的类了
public class Laowang implements InvocationHandler {
	// 被代理的实例
	Object obj = null;
	
	// 老王要代理谁
	public Laowang(Object obj) {
		this.obj = obj;
	}
	
	// 调用被代理的方法
	@Override
	public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
		method.invoke(this.obj, args);
		return null;
	}
}

// 我们继续办party
public class Party {
	public static void main(String[] args) {
		// 先确定要美丽来表演
		Star meili = new RealStar("美丽");
		// 定义一个handler
		InvocationHandler handler = new Laowang(meili);
		// 动态产生一个代理者
		Star proxy = (Star)Proxy.newProxyInstance(meili.getClass().getClassLoader(), 
											meili.getClass().getInterfaces(), 
											handler);
		// 让这个代理者来表演(其实代理者也是去找美丽)
		proxy.Sing();
		proxy.Dance();
	}
}
  • 这个程序是比较难理解的,我们一个类一个类来分析
  • Star这个interface,是三要素中的Subject,定义最基本的功能,没什么好说的
  • RealStar这个实现类,是三要素中的RealSubject,是具体执行逻辑的那个角色
  • Laowang这个类,要详细说一下了
    • 从文字游戏的角度来看,他确实是没有实现Star类,只是实现了InvocationHandler,所以可以说动态代理并没有手写代理类
    • 那么他是怎么实现代理功能的呢
      • 1、他实现了InvocationHandler,而InvocationHandler是JDK提供的动态代理接口,所以Laowang等于声明自己也是一个handler(我称呼他为增强处理器),所以后面可以作为参数传入Proxy的方法中去创建一个动态代理对象
      • 2、老王的构造方法,此处我们将其参数定义为Object,意味着创建老王的时候,传入什么对象,老王就代理什么对象。明白了吗,这个时候老王已经不仅仅是代理美丽或者说代理明星了,他可以代理任何东西,狗、猫、树都可以。
        • 如果把Object改成Star类型,那么老王就只能代理明星
      • 3、老王里的invoke方法,这是动态代理的核心。类似于拦截器一样的功能,不论你在main里面调用代理的任何方法,最终都是去执行这个invoke方法。invoke的三个参数后面会详细解释。
        • 例子中的invoke很简单,你要调用什么功能,invoke就去调用同样的功能,不做任何增强。
      • 4、总结下来就是,通过构造方法决定老王代理什么东西,通过invoke方法决定如何增强
  • Party这个类,总共分为4步
    • 第一步,创建被代理对象,就是指定我们要美丽这个明星来表演
    • 第二步,创建一个handler,把美丽传进去表示老王你要代理美丽
    • 第三步,动态创建一个代理对象proxy,通过Proxy类的静态方法newProxyInstance()实现,这里三个参数后面也会详细解释,老王带着美丽到场了!
    • 第四步,通过代理对象,完成功能,唱歌啊跳舞啊
  • 所以,个人认为,动态代理只是把静态代理里的代理类,换成了一个增强处理器而已。
    • 不论怎样,作为开发者,都要写满三要素才能实现代理功能

2.2.1 invoke()方法

public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
		// TODO
		return null;
}
  • 增强处理器的invoke方法,可以理解为一个强大的拦截器
    • 无论外部如何调用接口内的方法,最终都是进入invoke方法里执行逻辑
  • 三个参数:
    • Object proxy,对我们来说没什么实际意义,可以先忽略
    • Method method,就是外部调用的具体的方法了,调Sing()这里就是Sing(),调Dance()这里就是Dance()
    • Object[] args,很好理解,外部调用方法时传入的参数(参数很可能不止一个,所以是个数组)
  • 例子中的invoke(),没有增强只是原封不动的转调实现类方法,可以修改一下
    • 老王又要出来收钱啦,唱歌收10块,跳舞收20
public class Laowang implements InvocationHandler {
	// 以上代码省略
	
	// 自定义方法
	public void Money(int money) {
		System.out.println("老王收钱:" + money + "元");
	}
	
	// 调用被代理的方法
	@Override
	public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
		if(method.getName().equals("Sing")) {
			Money(10);
			method.invoke(this.obj, args);
		}
		if(method.getName().equals("Dance")) {
			Money(20);
			method.invoke(this.obj, args);
		}
		return null;
	}
}
  • 运行main后得到:
老王收钱:10元
美丽在唱歌
老王收钱:20元
美丽在跳舞

2.2.2 Proxy.newProxyInstance()方法

public static Object newProxyInstance(Classloader loader,
									@NotNull Class<?>[] interfaces,
									@NotNull reflect.InvocationHandler h)
								throws IllegalArgumentException
  • 参数1:生成代理对象的类装载器(一般使用被代理对象的类的类装载器)
  • 参数2:被代理对象们,通过接口数组指定(即被代理对象的接口们,例中是Star)
  • 参数3:增强处理器(传入InvocationHandler的实现类的对象,例中是Laowang)
  • 最后明确几个概念:
    • 通过Proxy.newProxyInstance()生成的动态处理器对象,会默认你全部实现了参数2中传入所有接口中的所有方法(它单方面对外宣布,这些我全都在增强处理器的invoke()中重写啦!)
    • 所以不论该对象在main中调用参数2的接口们中的任何方法,动态处理器都会回到参数3增强处理器里去执行invoke()方法
    • 实现动态代理必须有接口,没有接口无法实现动态代理
      • 所以有cglib代理(子类代理),不需要接口实现代理,就是另一个回事了

2.2.3 匿名内部类

  • 一个加餐,增强处理器也可以通过匿名内部类来实现
// 这里是接口Star
public interface Star {
	// 明星要会唱歌
	public void Sing();
	// 也要会跳舞
	public void Dance();
}

// 这里是实现类RealStar
public class RealStar implements Star {
	private String name = "";

	public RealStar(String name) {
		this.name = name;
	}

	@Override
	public void Sing() {
		System.out.println(this.name + "在唱歌");
	}

	@Override
	public void Dance() {
		System.out.println(this.name + "在跳舞");
	}
}

// 这里通过匿名内部类直接开party
public class Party {
	public static void main(String[] args) {
		// 先确定要美丽来表演
		Star meili = new RealStar("美丽");
		
		Star laowang = (Star)Proxy.newProxyInstance(meili.getClass().getClassLoader(), 
											meili.getClass().getInterfaces(), 
											(proxy, method, arg1) -> {
			System.out.println("老王收钱");
			method.invoke(meili, arg1);
			return null;
		});
		
		laowang.Sing();
		laowang.Dance();
	}
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值