zoukankan      html  css  js  c++  java
  • 设计模式

    QA

    动态代理是什么?有哪些应用?

    动态代理是运行时动态生成代理类。

    动态代理的应用有 spring aop、hibernate 数据查询、测试框架的后端 mock、rpc,Java注解对象获取等。

    怎么实现动态代理?

    JDK 原生动态代理和 cglib 动态代理。

    • JDK 原生动态代理是基于接口实现的 
    • !!!重要的两个类!!!
      Proxy类
         Proxy.newProxyInstance 用来生成代理实例
      InvocationHandler接口
         要@Override invoke()方法,拦截所有原始类的各种方法,可在其前后增加逻辑
    • 而 cglib 是基于继承当前类的子类实现的
    • !!!重要的两个类!!!
      Enhancer类
         Enhancer.create(clazz, this) 获取被代理后的目标类
      MethodInterceptor接口
         要@Override intercept()方法,拦截所有原始对象的各种方法,可在其前后增加逻辑

    1-为什么需要代理?

    当想给一个类的每个method进行扩充(统计、打印耗时)、拦截(判断是否登录)时:

    常规思路

    • 修改了原来的代码逻辑
    • 每个涉及的method都需要改动,改动量很大

    代理思路

    可以使用代理(静态代理/动态代理),统一处理这类额外功能(非核心业务功能),让原来的代码仍然只关心最核心的业务逻辑。

    2-静态代理

    关键点

    代理类ServiceImplProxy和被代理类ServiceImpl实现同样的Service接口

    代理类ServiceImplProxy持有被代理类ServiceImpl的引用

    代理类ServiceImplProxy在接口方法中,填充扩展/拦截功能,核心逻辑依然由被代理类ServiceImpl的引用来完成

      

    代码实例

    接口 service

    public interface Service {
     
      void play(String xxx);
      
      String run(String xxx);
      
    }
    

    被代理类 ServiceImpl

    public class ServiceImpl implements Service{
    
    	@Override
    	public void play(String xxx){
      	//....
    	}
    
    	@Override
    	public String run(String xxx){
      	//...
    	}
    
    }
    

    代理类 ServiceImplProxy

    public class ServiceImplProxy implements Service{
    
    	private Service service;
    	
    	//***关键*** 持有ServiceImpl的引用
    	public ServiceImplProxy() {
    		service = new ServiceImpl(); 
    	}
    
    	/**
    	* 扩展功能
    	*/
    	@Override
    	public void play(String xxx){
    		long start = System.currentTimeMillis();//计时开始
    
    		//***关键*** 执行真正的逻辑
    		service.play(xxx);
    
      		long end = System.currentTimeMillis();//计时结束
    		System.out.println("耗时:" + (end - start) + "毫秒");//打印耗时
    
    	}
    	
    	/**
    	* 拦截功能
    	*/
    	@Override
    	public String run(String xxx){
      		//添加拦截逻辑
    		if("xxx".equals("...")){
      			//***关键*** 执行真正的逻辑
    			service.run(xxx);
    		}
    		//...
    	}
    
    }
    

    优缺点

    优点:

    • 可以不修改原目标类(指ServiceImpl)的代码
    • 可以在代理类中对功能进行拦截和扩充

    缺点:

    • 代理类需要实现与目标类一样的接口,会导致代理类数量较多,不易维护
    • 一旦接口增加方法,目标类和代理类都需要维护

    鉴于这两个缺点,JDK 提供了动态代理

    3-动态代理,JDK(基于接口)

    为何要引入JDK动态代理

    为了解决静态代理的带来的以下问题 -> 引入了JDK动态代理:

    • 代理类需要实现与目标类一样的接口,会导致代理类数量较多,不易维护
    • 一旦接口增加方法,目标类和代理类都需要维护

    代码示例

    JDK动态代理的实现和静态代理一样,不同的是代理类的创建方式不同:

    • 静态代理是直接新增一个代理类。
    • JDK动态代理需要
      1. 持有目标类对象 - 代理类的通过构造方法传入目标类对象(会),代理类持有目标类对象 (第1步掌握“目标类的实例”,与第2步自动生成代理类无关(因为Proxy.newProxyInstance只需传入clazz,不需要实例),与第3步反射调用方法有关(因为哪怕增加别的逻辑,但核心逻辑依然要调用“目标类的实例”)
      2. 创建代理对象 - 通过JDK的Proxy类, Proxy.newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h) 方法获取代理对象, 和一个"调用处理器InvocationHandler"来实现的,通过Proxy来生成代理类实例,而这个代理实例通过调用处理器InvocationHandler的invoke()接收不同的参数, 反射调用真实对象的方法。
      3. 代理类实现 InvocationHandler 接口,重写invoke(Object proxy, Method method, Object[] args)方法。在此需要反射调用第1步传入的“目标类的实例”method.invoke(“目标类的实例”, args),并在此周围添加更多的逻辑,如过滤器等。

    1-实现JDK动态代理的util代码(拷贝到任何项目中,都能直接使用) 

    !!!重要的两个类!!!
    Proxy类
       Proxy.newProxyInstance 用来生成代理实例
    InvocationHandler接口
       要@Override invoke()方法,拦截所有原始类的各种方法,可在其前后增加逻辑

    2-实际例子

       

    优缺点

    优点:见“为何要引入JDK动态代理”

    缺点:只能对该类,所实现接口中定义的方法,进行代理。 

    详细解析

    4-动态代理,CGlib(基于类)

    为何要引入CGlib动态代理

    为了解决 JDK 的动态代理无法代理不实现接口的类的问题 -> 引入使用 CGLib 的实现动态代理。

    CGLib(Code Generator Library)是一个强大的、高性能的代码生成库。底层使用了ASM(一个短小精悍的字节码操作框架)来操作字节码生成新的类。动态生成一个目标类的子类,子类重写要代理的类的所有不是final的方法。在子类中采用方法拦截的技术拦截所有父类方法的调用,顺势织入横切逻辑。它比使用java反射的JDK动态代理要快。

    关键点

    • CGlib动态代理需要
      • maven中添加cglib依赖
      • 不再持有目标类对象 - 构造函数就是默认的空构造函数
      • 创建代理对象 - 使用cglib自带的Enhancer.create(clazz), 参数是目标类的clazz对象。Enhancer类是CGLib中的一个字节码增强器,它可以方便的对你想要处理的类进行扩展,以后会经常看到它。
      • 定义拦截器/处理器 - 在调用目标方法时,CGLib会回调MethodInterceptor(Object obj, Method method, Object[] params, MethodProxy proxy)接口方法拦截,来实现你自己的代理逻辑,类似于JDK中的InvocationHandler接口
        • 参数:Object为由CGLib动态生成的代理类实例,Method为上文中实体类所调用的被代理的方法引用,Object[]为参数值列表,MethodProxy为生成的代理类对方法的代理引用。
        • 返回:从代理实例的方法调用返回的值。
        • 其中,proxy.invokeSuper(obj,arg) 调用代理类实例上的proxy方法的父类方法(即实体类TargetObject中对应的方法)

    优缺点

    优点:完全不受代理类必须实现接口的限制

    缺点:对于final方法,无法进行代理

    详细解析

    CGLib 动态代理 https://blog.csdn.net/meism5/article/details/90781518

    CGLIB(Code Generation Library) 介绍与原理 https://www.runoob.com/w3cnote/cglibcode-generation-library-intro.html

  • 相关阅读:
    微信坚硬的后脚跟
    [项目整理]Win32,MFC的可执行文件只能运行一次
    美司法部索要维基解密志愿者谷歌账户内容
    QML性能
    OSGi 的核心配置、动态化及问题
    OSGi 的由来和本质特性
    机器视觉与计算机视觉
    人工智能与深度学习
    活着就能改变世界
    选择与执行
  • 原文地址:https://www.cnblogs.com/frankcui/p/13875709.html
Copyright © 2011-2022 走看看