代理模式

1. 代理模式

代理模式给某一个对象提供一个代理对象,并由代理对象控制对原对象的引用。通俗的来讲代理模式就是我们生活中常见的中介。

在这里插入图片描述

2. 为什么要用代理模式

  • 中介隔离作用
    在某些情况下,一个客户类不想或者不能直接引用一个委托对象,而代理类对象可以在客户类和委托对象之间起到中介的作用,其特征是代理类和委托类实现相同的接口。
  • 开闭原则,增加功能
    代理类除了是客户类和委托类的中介之外,我们还可以通过给代理类增加额外的功能来扩展委托类的功能,这样做我们只需要修改代理类而不需要再修改委托类,符合代码设计的开闭原则。代理类主要负责为委托类预处理消息、过滤消息、把消息转发给委托类,以及事后对返回结果的处理等。代理类本身并不真正实现服务,而是同过调用委托类的相关方法,来提供特定的服务。真正的业务功能还是由委托类来实现,但是可以在业务功能执行的前后加入一些公共的服务。例如我们想给项目加入缓存、日志这些功能,我们就可以使用代理类来完成,而没必要打开已经封装好的委托类。

3. 代理模式实现

按照代理创建的时期分类,两种:静态代理、动态代理。
静态代理是由程序员创建或特定工具自动生成源代码,在对其编译。在程序员运行之前,代理类.class文件就已经被创建了。
动态代理是在程序运行时通过反射机制动态创建的。

3.1 静态代理

  1. 创建服务类接口
	/**
	 * 接口 -- 买房交易
	 * @author Administrator
	 */
	interface JiaoYi1 {
		void maifang(String houseName, double money);
	}
  1. 实现服务接口
	/**
	 * 购房者
	 * @author Administrator
	 */
	class FangNu1 implements JiaoYi1 {	
		@Override
		public void maifang(String houseName, double money) {
			System.out.println("购房者买房,成为房奴.");
		}
	}
  1. 创建代理类
   /**     
    * 中介 -- 代理     * 
    * @author Administrator 
    */
    class ZhongJieProxy1 implements JiaoYi1 {
    	   FangNu1 fangNu = new FangNu1();
    	   public ZhongJieProxy1(FangNu1 fangNu) {
    	     this.fangNu = fangNu;
    	   }
    	   @Override
    	   public void maifang(String houseName, double money) {
    	     System.out.println("静态代理前置内容 >> 中介挑选房子 ... ");
    	     fangNu.maifang(houseName, money);
    	     System.out.println("静态代理后置内容 >> 中介收取中介费,办手续交房 ... ");
    	  	}
     }
  1. 编写测试类
	public class Proxy01 {
		public static void main(String[] args) {
			// 我们要代理的真实对象
			FangNu1 fangNu = new FangNu1();
			// 代理对象
			ZhongJieProxy1 proxy = new ZhongJieProxy1(fangNu);
			proxy.maifang("上善小区", 1500000);
		}
	}
  • 静态代理总结:

    • 优点:可以做到在符合开闭原则的情况下对目标对象进行功能扩展。

    • 缺点:我们得为每一个服务都得创建代理类,工作量太大,不易管理。同时接口一旦发生改变,代理类也得相应修改。

3.2 动态代理

在动态代理中我们不再需要再手动的创建代理类,我们只需要编写一个动态处理器就可以了。真正的代理对象由JDK再运行时为我们动态的来创建。

  1. 创建服务类接口
	/**
	 * 接口 -- 买房交易
	 * @author Administrator
	 */
	interface JiaoYi2 {
		void maifang(String houseName, double money);
	}
  1. 实现服务接口
	/**
	 * 购房者
	 * @author Administrator
	 */
	class FangNu2 implements JiaoYi2 {
		@Override
		public void maifang(String houseName, double money) {
			System.out.println("购房者买房,成为房奴.");
		}
	}
  1. 创建代理类
	/**
	 * 中介 -- 代理 -- 1
	 * @author Administrator
	 */
	class ZhongJieProxy2 implements InvocationHandler {
		JiaoYi2 fangNu;
	
		public ZhongJieProxy2(JiaoYi2 fangNu) {
			this.fangNu = fangNu;
		}
	
	//	每一个动态代理类都必须要实现InvocationHandler这个接口,并且每个代理类的实例都关联到了一个handler,
	//	当我们通过代理对象调用一个方法的时候,这个方法的调用就会被转发为由InvocationHandler这个接口的 invoke 方法来进行调用。
	//	InvocationHandler这个接口的唯一一个方法 invoke 方法:
	//	该方法接收3个参数:
	//	proxy:  指代我们所代理的那个真实对象
	//	method: 指代的是我们所要调用真实对象的某个方法的Method对象
	//	args:  指代的是调用真实对象某个方法时接受的参数
		@Override
		public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
			// 在代理真实对象前我们可以添加一些自己的操作
			System.out.println("静态代理前置内容 >> 中介挑选房子 ... ");
			System.out.println("Method:" + method);
	
			// 当代理对象调用真实对象的方法时,其会自动的跳转到代理对象关联的handler对象的invoke方法来进行调用
			Object obj = method.invoke(fangNu, args); 
			System.out.println("方法参数:"+ Arrays.toString(args));
	
			// 在代理真实对象后我们也可以添加一些自己的操作
			System.out.println("静态代理后置内容 >> 中介收取中介费,办手续交房 ... ");
			return obj;
		}
	}
	/**
	 * 中介 -- 代理 -- 2
	 * 
	 * @author Administrator
	 *
	 */
	class ZhongJieProxy2_2 implements InvocationHandler {
		JiaoYi2 fangNu;
	
		public ZhongJieProxy2_2(JiaoYi2 fangNu) {
			this.fangNu = fangNu;
		}
	
	//	每一个动态代理类都必须要实现InvocationHandler这个接口,并且每个代理类的实例都关联到了一个handler,
	//	当我们通过代理对象调用一个方法的时候,这个方法的调用就会被转发为由InvocationHandler这个接口的 invoke 方法来进行调用。
	//	InvocationHandler这个接口的唯一一个方法 invoke 方法:
	//	该方法接收3个参数:
	//	proxy:  指代我们所代理的那个真实对象
	//	method: 指代的是我们所要调用真实对象的某个方法的Method对象
	//	args:  指代的是调用真实对象某个方法时接受的参数
		@Override
		public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
			// 在代理真实对象前我们可以添加一些自己的操作
			System.out.println("静态代理前置内容 >> 中介先收取中介费,再 挑选房子 ... ");
			System.out.println("Method:" + method);
	
			// 当代理对象调用真实对象的方法时,其会自动的跳转到代理对象关联的handler对象的invoke方法来进行调用
			Object obj = method.invoke(fangNu, args); 
			System.out.println("方法参数:"+Arrays.toString(args));
			System.out.println("方法返回值:"+obj);// 返回值
	
			// 在代理真实对象后我们也可以添加一些自己的操作
			System.out.println("静态代理后置内容 >> 中介,办手续交房 ... ");
			return obj;
		}
	
	}
  1. 编写测试类
	/**
	 * 动态代理 -- JDK
	 * @author Administrator
	 */
	public class Proxy02 {
		public static void main(String[] args) {
			// 我们要代理的真实对象
			JiaoYi2 fangNu = new FangNu2();
			// 我们要代理哪个真实对象,就将该对象传进去,最后是通过该真实对象来调用其方法的
			InvocationHandler proxy = new ZhongJieProxy2(fangNu);
			InvocationHandler proxy2_2 = new ZhongJieProxy2_2(fangNu);
			
			 /*
	         * 通过Proxy的newProxyInstance方法来创建我们的代理对象,三个参数:
	         * 第一个参数 :加载器用于定义代理类的类加载器 ,handler.getClass().getClassLoader() ,我们这里使用handler这个类的ClassLoader对象来加载我们的代理对象
	         * 第二个参数: 被代理的对象的类的接口列表实现 ,realSubject.getClass().getInterfaces(),我们这里为代理对象提供的接口是真实对象所实行的接口,表示我要代理的是该真实对象,这样我就能调用这组接口中的方法了
	         * 第三个参数: 设置调用处理程序,将方法调用分派给的代理对象, handler, 我们这里将这个代理对象关联到了上方的 InvocationHandler 这个对象上
	         */
			JiaoYi2 jiaoYiYuan = (JiaoYi2) Proxy.newProxyInstance(proxy.getClass().getClassLoader(),
					fangNu.getClass().getInterfaces(), proxy);
			
			JiaoYi2 jiaoYiYuan2_2 = (JiaoYi2) Proxy.newProxyInstance(proxy2_2.getClass().getClassLoader(),
					fangNu.getClass().getInterfaces(), proxy2_2);
			
			jiaoYiYuan.maifang("上善小区", 1500000);
			System.out.println("-----------------------------");
			jiaoYiYuan2_2.maifang("阳光花园", 6000000);
		}
	}

3.3 CGLIB代理

JDK实现动态代理需要实现类通过接口定义业务方法,对于没有接口的类,如何实现动态代理呢,这就需要CGLib了。CGLib采用了非常底层的字节码技术,其原理是通过字节码技术为一个类创建子类,并在子类中采用方法拦截的技术拦截所有父类方法的调用,顺势织入横切逻辑。但因为采用的是继承,所以不能对final修饰的类进行代理。JDK动态代理与CGLib动态代理均是实现Spring AOP的基础。

  1. 编写执行对象类
	/**
	 * 购房者
	 * 
	 * @author Administrator
	 */
	class FangNu3 {
	
		public void maifang(String houseName, double money) {
			System.out.println("购房者买房,成为房奴.");
		}
	
	}
  1. 编写代理类
	/**
	 * 中介 -- 代理
	 * cglib是针对类来实现代理的,原理是对指定的业务类生成一个子类,并覆盖其中业务方法实现代理。因为采用的是继承,所以不能对final修饰的类进行代理。
	 * @author Administrator
	 */
	class ZhongJieProxy3 implements MethodInterceptor {
		Object target;// 业务类对象,供代理方法中进行真正的业务方法调用
	
		// 相当于JDK动态代理中的绑定
		public Object getInstance(Object target) {
			this.target = target; // 给业务对象赋值
			Enhancer enhancer = new Enhancer(); // 创建加强器,用来创建动态代理类
			enhancer.setSuperclass(this.target.getClass()); // 为加强器指定要代理的业务类(即:为下面生成的代理类指定父类)
			// 设置回调:对于代理类上所有方法的调用,都会调用CallBack,而Callback则需要实现intercept()方法进行拦
			enhancer.setCallback(this);
			// 创建动态代理类对象并返回
			return enhancer.create();
		}
	
		//实现回调方法 
		@Override
		public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
			System.out.println("静态代理前置内容 >> 中介挑选房子 ... ");
			System.out.println("Method:" + method);
	
			Object obj = methodProxy.invokeSuper(proxy, args);//调用业务类(父类中)的方法
	
			// 在代理真实对象后我们也可以添加一些自己的操作
			System.out.println("静态代理后置内容 >> 中介收取中介费,办手续交房 ... ");
			return obj;
		}		
	}

CGLIB代理总结: CGLIB创建的动态代理对象比JDK创建的动态代理对象的性能更高,但是CGLIB创建代理对象时所花费的时间却比JDK多得多。所以对于单例的对象,因为无需频繁创建对象,用CGLIB合适,反之使用JDK方式要更为合适一些。同时由于CGLib由于是采用动态创建子类的方法,对于final修饰的方法无法进行代理。

4 比较总结

  • 静态代理是通过在代码中显式定义一个业务实现类一个代理,在代理类中对同名的业务方法进行包装,用户通过代理类调用被包装过的业务方法;
  • JDK动态代理是通过接口中的方法名,在动态生成的代理类中调用业务实现类的同名方法;
  • CGlib(Code Generation Library)动态代理(Spring)是通过继承业务类,生成的动态代理类是业务类的子类,通过重写业务方法进行代理;
  • 17
    点赞
  • 75
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值