设计模式之代理模式

代理模式属于结构型模式,是23种设计模式中较为重要的一种,代理模式分为静态代理和动态代理,动态代理又分为基于接口实现的动态代理和基于子类实现的动态代理;在jdk源码中,很多底层的实现也是基于代理模式的,例如创建线程的方式之一实现Runnable接口就使用了静态代理模式,而Spring框架最为重要的AOP的实现是基于动态代理模式,可见,学习代理模式是我们能看懂底层源码实现原理的一个基础。

静态代理

关于静态代理,我们以小明结婚为例,看以下代码:

//代理对象和真实对象共同实现的接口,里面有结婚方法
interface A{
	void marry();
}
//真实对象
class Person implements A{
	@Override
	public void marry() {
		System.out.println("我要结婚啦~~~");
	}
}
//代理对象
class Proxy implements A{
    //代理类聚合了真实类
	private A a;
	public Proxy(A a){
		this.a = a;
	}
	@Override
	public void marry() {
		before();
		//真实对象去结婚
		a.marry();
		after();
	}
	//增强方法:对真实对象进行一个增强
	public void before(){
		System.out.println("结婚前,布置现场");
	}
	public void after(){
		System.out.println("结婚后,收拾现场");
	}
}
/**
 * 使用生活中的结婚例子来理解静态代理:小明要结婚,会去找婚庆公司帮忙准备结婚的一些事宜
 * 小明是真实对象,婚庆公司是代理对象,代理了真实对象,帮助小明去结婚,真正结婚的是小明
 * 
 * 静态代理模式通过代理对象可以做很多真实对象做不了的事情,实现对真实对象的一个增强
 * 同时真实对象也可以专注的去做一件事情
 * 
 * 就好比婚庆公司可以在小明结婚之前帮他布置现场,结婚之后收拾现场,而小明只需要专心结婚即可
 * 
 * 静态代理模式要求代理对象和真实对象实现同一个接口
 * @author cj_chen
 *
 */
public class Client {
	public static void main(String[] args) {
		//创建一个真实对象
		Person xiaoming = new Person();
		//创建一个代理对象,代理了小明这个真实对象
		Proxy proxy = new Proxy(xiaoming);
		//通过代理对象去执行真实对象的方法,实现一个增强
		proxy.marry();
	}
}

关于静态代理模式:

  1. 要求代理对象和真实对象实现同一个接口
  2. 一个真实角色就会产生一个代理角色,导致代码量翻倍,开发效率降低
  3. 静态代理模式通过代理对象可以做很多真实对象做不了的事情,实现对真实对象的一个增强,同时真实对象也可以专注的去做一件事情

静态代理模式在jdk源码中的应用:

实现Runnable接口创建线程的方式用到了静态代理模式;回顾一下步骤:先创建一个Runnable接口的实现类对象,然后创建一个Thread类,并将Runnable接口的实现类作为参数传递进去,然后调用Thread类对象的start方法开启多线程;

Runnable接口的实现类对象是真实对象,Thread类对象是代理对象,静态代理模式要求代理对象和真实对象继承同一个接口,Runnable接口的实现类对象和Thread类对象都继承了Runnable接口,所以完全符合静态代理模式】

动态代理

关于动态代理,我们以生产者生产手机和销售手机为例,看以下代码:

//被代理角色/真实角色实现的接口
public interface IProducer {
	//生产手机
	void produce();
	//销售手机
	void sale();
}
//被代理角色
public class Producer implements IProducer{
	@Override
	public void produce() {
		System.out.println("生产者生产手机啦~~~");
	}
	@Override
	public void sale() {
		System.out.println("生产者销售手机啦~~~");
	}
}
public class Client {
	public static void main(String[] args) {
		IProducer producer = new Producer();
		producer.produce();
	}
}

一般来说,客户端可以直接调用真实对象的方法;现在我们有这么一个需求:想要在生产者(真实对象)执行生产方法和销售方法之前打印一句日志,并且是要在不修改原有代码的前提下完成,这也是为了满足设计模式的开闭原则,这时候就可以使用动态代理模式

public class Client {
	public static void main(String[] args) {
		//先创建一个真实角色
		IProducer producer = new Producer();
		//基于接口方式创建动态代理角色
		IProducer proxy = (IProducer)Proxy.newProxyInstance(producer.getClass().getClassLoader(),
				producer.getClass().getInterfaces(),
				new InvocationHandler() {
					/**
		             * 作用:执行被代理对象的任何接口方法都会经过该方法
		             * 方法参数的含义
		             * @param proxy   代理对象的引用
		             * @param method  当前执行的方法
		             * @param args    当前执行方法所需的参数
		             * @return        和被代理对象方法有相同的返回值
		             * @throws Throwable
		             */
					@Override
					public Object invoke(Object proxy, Method method, Object[] args)
							throws Throwable {
						//增强真实角色的代码
						System.out.println("打印日志lalala~~~");
						return method.invoke(producer, args);
					}
				});
		proxy.produce();	
	}
}

关于动态代理:

  1. 分类
  • 基于接口的动态代理:使用JDK官方给我们提供好的一个Proxy类(java.lang.reflect)来动态的创建代理对象,创建方法是使用Proxy类中的newProxyInstance方法,该方法需要传递三个参数,一是ClassLoader类加载器,用于加载代理对象字节码的,和被代理对象使用相同的类加载器;二是Class[]字节码数组,用于让代理对象和被代理对象有相同方法;三是InvocationHandler,用于提供增强的代码,也就是让我们写如何代理;基于接口的动态代理弊端就是要求被代理类最少实现一个接口,如果没有则不能使用
  • 基于子类的动态代理:需要依赖第三方cglib库,如果是maven项目则导入cglib相关的依赖即可,基于子类的动态代理要求被代理类不能是最终类
  1. 好处:
  • 不修改原有代码的基础上对方法进行增强
  • 解决了静态代理一个真实对象必须有一个代理对象,导致类数量大大增加的缺点,一个动态代理类可以代理多个类,只要实现的是同一个接口即可
  1. PS:动态代理模式底层使用的就是java的反射,要想真正理解动态代理模式,建议先看一下反射相关的知识!
    如果对于动态代理模式“一个动态代理类可以代理多个类,只要实现的是同一个接口即可”这个优点不够理解的话,可以看下面这个代码的例子(摘抄自狂神的笔记:https://www.bilibili.com/video/BV1mc411h719?p=11
    在这里插入图片描述
    代码分析:以上代码其实就是自己写了一个类,这个类包含一个生成代理角色的方法,并且也已经写好了InvocationHandler类的invoke方法,即写好了代码增强的逻辑;这个类还聚合了一个Object类型的接口,根据我们传入的不同真实对象即可动态生成不同代理类

动态代理的应用:
Spring的AOP(面向切面编程)就运用到了动态代理模式,换言之,AOP的实现方式就是使用动态代理技术,AOP就是在不改变原有代码的基础上,可以将要增强的代码横切进去,例如要给原来的业务逻辑代码增加打印日志或者事务管理等;简单的说它就是把我们程序重复的代码抽取出来,在需要执行的时候,使用动态代理的技术,在不修改源码的 基础上,对我们的已有方法进行增强

关于Spring AOP的相关知识,推荐一篇文章:https://www.cnblogs.com/ysocean/p/7476379.html

©️2020 CSDN 皮肤主题: 1024 设计师:上身试试 返回首页