• java设计模式之代理模式


    一:代理模式

    1.什么是代理模式?
    代理模式是一种结构型设计模式, 让你能够提供对象的替代品或其占位符。 代理控制着对于原对象的访问, 并允许在将请求提交给对象前后进行一些处理。
    代理模式的基本介绍
    1.代理模式:为一个对象提供一个替身,以控制对这个对象的访问。即通过代理对象访问目标对象.这样做的好处是:可以在目标对象实现的基础上,增强额外的功能操作,即扩展目标对象的功能。
    2.被代理的对象可以是远程对象、创建开销大的对象或需要安全控制的对象
    3.代理模式有不同的形式, 主要有三种 静态代理、动态代理 (JDK代理、接口代理)和 Cglib代理 (可以在内存动态的创建对象,而不需要实现接口, 他是属于动态代理的范畴) 。

    2.代理模式适合应用场景
    1.延迟初始化 (虚拟代理)。 如果你有一个偶尔使用的重量级服务对象, 一直保持该对象运行会消耗系统资源时, 可使用代理模式。
    2.访问控制 (保护代理)。 如果你只希望特定客户端使用服务对象, 这里的对象可以是操作系统中非常重要的部分, 而客户端则是各种已启动的程序 (包括恶意程序), 此时可使用代理模式。
    3. 本地执行远程服务 (远程代理)。 适用于服务对象位于远程服务器上的情形。
    4. 记录日志请求 (日志记录代理)。 适用于当你需要保存对于服务对象的请求历史记录时。
    5. 缓存请求结果 (缓存代理)。 适用于需要缓存客户请求结果并对缓存生命周期进行管理时, 特别是当返回结果的体积非常大时。
    6. 智能引用。 可在没有客户端使用某个重量级对象时立即销毁该对象。

    3.代理模式优缺点
    优点:
    1.你可以在客户端毫无察觉的情况下控制服务对象。
    2.如果客户端对服务对象的生命周期没有特殊要求, 你可以对生命周期进行管理。
    3.即使服务对象还未准备好或不存在, 代理也可以正常工作。
    4.开闭原则。 你可以在不对服务或客户端做出修改的情况下创建新代理。
    缺点:
    1.代码可能会变得复杂, 因为需要新建许多类。
    2.服务响应可能会延迟。

    4.代理(Proxy)模式分为三种角色:
    抽象主题(Subject)类: 通过接口或抽象类声明真实主题和代理对象实现的业务方法。
    真实主题(Real Subject)类: 实现了抽象主题中的具体业务,是代理对象所代表的真实对
    象,是最终要引用的对象。
    代理(Proxy)类 : 提供了与真实主题相同的接口,其内部含有对真实主题的引用,它可以访
    问、控制或扩展真实主题的功能。

    二:代理模式代码实现

    2.1静态代理

    静态代码模式的基本介绍
    静态代理在使用时,需要定义接口或者父类,被代理对象(即目标对象)与代理对象一起实现相同的接口或者是继承相同父类
    应用实例具体要求:
    1.定义一个接口:ITeacherDao
    2.目标对象TeacherDAO实现接口ITeacherDAO
    3.使用静态代理方式,就需要在代理对象TeacherDAOProxy中也实现ITeacherDAO
    4.调用的时候通过调用代理对象的方法来调用目标对象.
    5.特别提醒:代理对象与目标对象要实现相同的接口,然后通过调用相同的方法来调用目标对象的方法。

    //接口(抽象主题类)
    public interface ITeacherDao {
    	
    	void teach(); // 授课的方法
    }
    //真实主题(目标对象)
    public class TeacherDao implements ITeacherDao {
    
    	@Override
    	public void teach() {
    		System.out.println(" 老师授课中  。。。。。");
    	}
    
    }
    
    
    //代理对象,静态代理(和真实主题一样都是需要实现抽象主题类的接口)
    public class TeacherDaoProxy implements ITeacherDao{
    	
    	private ITeacherDao target; // 目标对象,通过接口来聚合
    	
    	
    	//构造器
    	public TeacherDaoProxy(ITeacherDao target) {
    		this.target = target;
    	}
    	@Override
    	public void teach() {
    		System.out.println("开始代理  完成某些操作。。。。。 ");//方法
    		target.teach();
    		System.out.println("提交。。。。。");//方法
    	}
    }
    
    		public static void main(String[] args) {
    		//创建目标对象(被代理对象)
    		TeacherDao teacherDao = new TeacherDao();
    		
    		//创建代理对象, 同时将被代理对象传递给代理对象
    		TeacherDaoProxy teacherDaoProxy = new TeacherDaoProxy(teacherDao);
    		
    		//通过代理对象,调用到被代理对象的方法
    		//即:执行的是代理对象的方法,代理对象再去调用目标对象的方法 
    		teacherDaoProxy.teach();
    	}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45

    静态代理优缺点
    1.优点:在不修改目标对象的功能前提下, 能通过代理对象对目标功能扩展
    2.缺点:因为代理对象需要与目标对象实现一样的接口,所以会有很多代理类一旦接口增加方法,目标对象与代理对象都要维护

    2.2.动态代理

    动态代理模式的基本介绍
    1.代理对象,不需要实现接口,但是目标对象要实现接口,否则不能用动态代理
    2.代理对象的生成,是利用JDK的API,动态的在内存中构建代理对象
    3.动态代理也叫做:JDK代理、接口代理

    //接口(抽象主题类)
    public interface ITeacherDao {
    
    	void teach(); // 授课方法
    	void sayHello(String name);
    }
    
    
    //真实主题(目标对象)
    public class TeacherDao implements ITeacherDao {
    
    	@Override
    	public void teach() {
    		System.out.println(" 老师授课中.... ");
    	}
    
    	@Override
    	public void sayHello(String name) {
    		System.out.println("hello " + name);
    	}
    	
    }
    
    //代理对象,动态代理(和静态代理不同的是不需要都实现抽象主题的接口)
    public class ProxyFactory {
    
    	//维护一个目标对象 , Object
    	private Object target;
    
    	//构造器 , 对target 进行初始化
    	public ProxyFactory(Object target) {
    		
    		this.target = target;
    	} 
    	
    	//给目标对象 生成一个代理对象
    	public Object getProxyInstance() {
    		
    		//说明
    		/*
    		 *  public static Object newProxyInstance(ClassLoader loader,
                                              Class[] interfaces,
                                              InvocationHandler h)
                                              
                //1. ClassLoader loader : 指定当前目标对象使用的类加载器, 获取加载器的方法固定
                //2. Class[] interfaces: 目标对象实现的接口类型,使用泛型方法确认类型
                //3. InvocationHandler h : 事情处理,执行目标对象的方法时,会触发事情处理器方法, 会把当前执行的目标对象方法作为参数传入
    		 */
    		return Proxy.newProxyInstance(target.getClass().getClassLoader(), 
    				target.getClass().getInterfaces(), 
    				new InvocationHandler() {
    					
    					@Override
    					public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    						// TODO Auto-generated method stub
    						System.out.println("JDK代理开始~~");
    						//反射机制调用目标对象的方法
    						Object returnVal = method.invoke(target, args);
    						System.out.println("JDK代理提交");
    						return returnVal;
    					}
    				}); 
    	}
    	
    	
    }
    
    		public static void main(String[] args) {
    	
    		//创建目标对象
    		ITeacherDao target = new TeacherDao();
    		
    		//给目标对象,创建代理对象, 可以转成 ITeacherDao
    		ITeacherDao proxyInstance = (ITeacherDao)new ProxyFactory(target).getProxyInstance();
    	
    		// proxyInstance=class com.sun.proxy.$Proxy0 内存中动态生成了代理对象
    		System.out.println("proxyInstance=" + proxyInstance.getClass());
    		
    		//通过代理对象,调用目标对象的方法
    		//proxyInstance.teach();
    		
    		proxyInstance.sayHello(" tom ");
    	}
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84

    2.3.Cglib代理

    Cglib代理模式的基本介绍
    1.静态代理和JDK代理模式都要求目标对象是实现一个接口,但是有时候目标对象只是一个单独的对象,并没有实现任何的接口,这个时候可使用目标对象子类来实现代理-这就是Cglib代理
    2.Cglib代理也叫作子类代理,它是在内存中构建一个子类对象从而实现对目标对象功能扩展, 有些书也将Cglib代理归属到动态代理。
    3.Cglib是一个强大的高性能的代码生成包,它可以在运行期扩展java类与实现java接口.它广泛的被许多AOP的框架使用,例如Spring AOP,实现方法拦截
    4.在AOP编程中如何选择代理模式:

    1. 目标对象需要实现接口,用JDK代理
    2. 目标对象不需要实现接口,用Cglib代理
      5.Cglib包的底层是通过使用字节码处理框架ASM来转换字节码并生成新的类
      cglib代理jar包:
      asm-tree.jar
      asm-commons.jar
      asm.jar
      cglib-2.2.jar
    //真实主题(目标对象)
    public class TeacherDao {
    
    	public String teach() {
    		System.out.println(" 老师授课中  , 我是cglib代理,不需要实现接口 ");
    		return "hello";
    	}
    }
    
    //代理对象,Cglib代理(和jdk动态代理不同的是目标对象不需要实现接口)
    public class ProxyFactory implements MethodInterceptor {
    
    	//维护一个目标对象
    	private Object target;
    	
    	//构造器,传入一个被代理的对象
    	public ProxyFactory(Object target) {
    		this.target = target;
    	}
    
    	//返回一个代理对象:  是 target 对象的代理对象
    	public Object getProxyInstance() {
    		//1. 创建一个工具类
    		Enhancer enhancer = new Enhancer();
    		//2. 设置父类
    		enhancer.setSuperclass(target.getClass());
    		//3. 设置回调函数
    		enhancer.setCallback(this);
    		//4. 创建子类对象,即代理对象
    		return enhancer.create();
    		
    	}
    	
    
    	//重写  intercept 方法,会调用目标对象的方法
    	@Override
    	public Object intercept(Object arg0, Method method, Object[] args, MethodProxy arg3) throws Throwable {
    		// TODO Auto-generated method stub
    		System.out.println("Cglib代理模式 ~~ 开始");
    		Object returnVal = method.invoke(target, args);
    		System.out.println("Cglib代理模式 ~~ 提交");
    		return returnVal;
    	}
    }
    	public static void main(String[] args) {
    		//创建目标对象
    		TeacherDao target = new TeacherDao();
    		//获取到代理对象,并且将目标对象传递给代理对象
    		TeacherDao proxyInstance = (TeacherDao)new ProxyFactory(target).getProxyInstance();
    
    		//执行代理对象的方法,触发intecept 方法,从而实现 对目标对象的调用
    		String res = proxyInstance.teach();
    		System.out.println("res=" + res);
    	}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
  • 相关阅读:
    基于H5+Android的高校自动排课APP系统
    重新执行先前已撤消的操作-视频监控程序
    石子合并( 动态规划 + 区间dp )
    Guava精讲(三)-Caches,同步DB数据到缓存
    Spring IoC源码:createBean(中)
    用Python采集球员信息,成功预测到了球赛胜负?
    【linux】Could not update ICEauthority file /home/xxx/.ICEauthority问题解决
    1117 Eddington Number
    vue 实现左侧导航栏,右侧锚点定位滚动到指定位置(超简单方法)
    『算法导论』什么是算法?什么是程序?
  • 原文地址:https://blog.csdn.net/m0_47944994/article/details/127899512