• java的静态代理和动态代理(jdk、cglib)


    一、代理模式

    代理的概念来自于设计模式中的代理模式,先了解一下代理模式

    1、结构图

    2、参与者

    Subject:接口,定义代理类和实际类的共用接口

    RealSubject:实际类,实现Subject这个接口

    Proxy:代理类,实现Subject这个接口,内部引用一个RealSubject实际类

    3、描述

    Proxy实现了Subject接口,内部引用一个RealSubject实际类,RealSubject能做的Proxy都会

    于是Proxy代替了RealSubject,实例化到Subject里交给客户端调用,而客户端不知道自己实际用的是谁

    客户端调用了Subject接口中的Request()方法,实际上访问的是Proxy

    Proxy类提供了一个客户端对RealSubject的间接性访问的过程,在这个过程中可以做很多的控制

    4、意图

    控制对象的访问

    5、应用场景

    虚代理:根据需要来创建开销很大的对象,该对象只有在需要的时候才会被真正创建

    远程代理:用来在不同的地址空间上代表同一个对象,这个不同的地址空间可以是在本机,也可以在其他机器上,在java里面最典型的就是RMI技术

    copy-on-write代理:在客户端操作的时候,只有对象确实改变了,才会真的拷贝(或克隆)一个目标对象,算是虚代理的一个分支

    保护代理:控制对原始对象的访问,如果有需要,可以给不同的用户提供不同的访问权限,以控制他们对原始对象的访问

    Cache代理:为那些昂贵操作的结果提供临时的存储空间,以便多个客户端可以共享这些结果

    防火墙代理:保护对象不被恶意用户访问和操作

    同步代理:使多个用户能够同时访问目标对象而没有冲突

    二、静态代理

    java中的静态代理就是代理模式最简单的实现,下面是示例代码:

    //接口
    public interface Subject {
    	void Request();
    }
    
    //实际类
    public class RealSubject implements Subject {
    	public void Request() {
    		System.out.println("执行具体的功能");
    	}
    }
    
    //代理类
    public class Proxy implements Subject {
    	private Subject realSubject = null;
    
    	public Proxy(Subject subject) {
    		realSubject = subject;
    	}
    
    	public void Request() {
    		System.out.println("静态代理开始");
    		realSubject.Request();
    		System.out.println("静态代理结束");
    	}
    }
    
    //客户端
    public class Client {
    	public static void main(String[] args) {
    		RealSubject realSubject = new RealSubject();
    		realSubject.Request();
    		
    		Subject subject = new Proxy(realSubject);
    		subject.Request();
    	}
    }
    

    执行的结果是:

    静态代理的局限:

    一个代理类Proxy只能服务Subject一种接口,如果有10个不同类型的对象,需要创建10个代理类,类的数量激增、也增加了代码的冗余

    如果只用一个代理类,就能完成全部的代理功能就好了,于是就有了动态代理

    三、动态代理

    静态代理的代码,在编译后就生成了代理类的.class字节码文件,所以在程序运行以前,代理类和实际类的关系就确定了

    而动态代理不同,代理类的字节码是在程序运行期间,由JVM根据反射等机制动态生成的,代理类和实际类的关系是在程序运行时确定的

    依托反射等机制,动态代理可以动态生成任意类型的代理类,一个代理的处理程序就可以处理多种对象,程序更加的灵活,也更容易扩展

    spring的AOP、RMI远程代理就是依靠动态代理来实现的

    java中的动态代理有两种,一种是java自带的jdk动态代理,一种是基于cglib的动态代理

    1、jdk动态代理

    jdk1.3开始,java语言提供了对动态代理的支持,主要会使用到java.lang.reflect包下的Proxy和InvocationHandler类

    (1)、Proxy

    Proxy类提供了用于创建动态代理类和实例对象的方法,它是所创建的动态代理类的父类

    其中newProxyInstance()方法用来获得动态生成的代理类实例,第一个参数是实际类的类加载器,第二个参数是实际类的接口列表,第三个参数是代理处理程序类

    (2)、InvocationHandler

    InvocationHandler接口是代理处理程序类的实现接口,每一个被生成的代理类实例都可以提供一个相关的代理处理程序类,代理类实例的方法被调用时会回调invoke()方法

    invoke()方法用来处理代理类实例的方法调用并返回结果,实现自己的代理逻辑,第一个参数是代理类实例,第二个参数是需要代理的方法,第三个参数是方法的参数数组

    //接口
    public interface Subject {
    	void Request();
    }
    
    //实际类
    public class RealSubject implements Subject {
    	public void Request() {
    		System.out.println("执行具体的功能");
    	}
    }
    
    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.Method;
    
    //代理处理程序类
    public class ProxyHandler implements InvocationHandler {
    	
    	private Object target;
    	
    	public ProxyHandler(Object target) {
    		this.target = target;
    	}
    	
    	@Override
    	public Object invoke(Object proxy, Method method, Object[] param) throws Throwable {
    		System.out.println("jdk动态代理开始");
    		Object result = method.invoke(target, param);
        	System.out.println("jdk动态代理结束");
        	
    		return result;
    	}
    }
    
    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.Proxy;
    
    //客户端
    public class Client {
    	public static void main(String[] args) {
    		RealSubject realSubject = new RealSubject();
    		
    		InvocationHandler handler = new ProxyHandler(realSubject);
    		Subject subject = (Subject)Proxy.newProxyInstance(realSubject.getClass().getClassLoader(), realSubject.getClass().getInterfaces(), handler);
    		
    		subject.Request();
    	}
    }
    

    执行的结果是:

    通过"realSubject.getClass().getInterfaces()"这句可以看到,jdk动态代理的实现是依赖接口的

    如果是抽象类或者一般的类需要进行代理,jdk动态代理就无法满足了,只有使用cglib动态代理

    2、cglib动态代理

    cglib是一个强大的高性能的代码生成包,广泛的被许多AOP的框架使用,它的开源地址在https://github.com/cglib/cglib

    通过它实现动态代理,主要用到import net.sf.cglib.proxy包下的MethodInterceptor、MethodProxy和Enhancer类

    (1)、MethodInterceptor

    MethodInterceptor类是方法拦截器,代理类实例的方法被调用时会回调MethodInterceptor的intercept()方法拦截,用来实现自己的代理逻辑,类似于jdk动态代理的InvocationHandler接口

    (2)、MethodProxy

    MethodProxy是intercept()方法中的第四个参数的类型,它是实际类方法的代理引用,使用methodProxy比使用jdk自身的method在效率上会有提升

    (3)、Enhancer

    Enhancer用来动态创建实际类子类的代理类实例,setSuperclass()方法设置实际类为父类,setCallback()方法建立方法回调,create()方法创建一个代理类实例

    //实际类
    public class RealSubject {
    	public void Request() {
    		System.out.println("执行具体的功能");
    	}
    	
    	public final void RequestFinal() {
    		System.out.println("执行具体的功能(final)");
    	}
    }
    
    import java.lang.reflect.Method;
    import net.sf.cglib.proxy.Enhancer;
    import net.sf.cglib.proxy.MethodInterceptor;
    import net.sf.cglib.proxy.MethodProxy;
    
    //方法拦截器
    public class ProxyInterceptor implements MethodInterceptor {
    	public Object intercept(Object proxy, Method method, Object[] params, MethodProxy methodProxy) throws Throwable {
    		System.out.println("cglib动态代理开始");
    		Object result = methodProxy.invokeSuper(proxy, params);
    		System.out.println("cglib动态代理开始");
    		
    		return result;
    	}
    	
    	//创建代理类实例
    	public Object newProxy(Object target) 
    	{
    		Enhancer enhancer = new Enhancer();
    		enhancer.setSuperclass(target.getClass());
    		enhancer.setCallback(this);
    		return enhancer.create();
    	}
    }
    
    //客户端
    public class Client {
    	public static void main(String[] args) {
    		RealSubject realSubject = new RealSubject();
    		
    		ProxyInterceptor proxyInterceptor = new ProxyInterceptor();
    		RealSubject subject = (RealSubject)proxyInterceptor.newProxy(realSubject);
    		
    		subject.Request();
    		subject.RequestFinal();
    	}
    }
    

    执行的结果是:

    通过输出的结果可以发现final的方法cglib是无法处理的,因为cglib是基于继承的代理,final的方法无法被重写

    3、jdk、cglib动态代理的区别

    jdk动态代理是由java内部的反射机制生成一个实现代理接口的匿名类,被代理的类必须要实现一个接口,如果某个类没有实现接口则不能生成代理对象,实际使用中会有一些局限性

    cglib动态代理底层是借助asm来实现的,加载实际类的.class文件,修改其字节码生成子类来处理,比java反射效率要高一些,不过生成子类的方式也带来小问题:目标类不能声明成final,因为final类不能被继承,无法生成代理;目标方法也不能声明成final,final方法不能被重写,无法得到处理

    在实际使用中cglib的应用更加广泛,效率更有优势

    实例代码地址:https://github.com/ctxsdhy/cnblogs-example

  • 相关阅读:
    JSON的序列化与还原
    正则表达式的一些基础
    跨域二三事
    与Web服务器通信
    与 Web 服务器通信
    代码重构
    构造函数与各种继承方法特点
    this指向问题——严格、非严格模式,事件处理程序
    《JavaScript设计模式与开发实践》学习笔记——单例模式
    Git常用命名及常见操作流程
  • 原文地址:https://www.cnblogs.com/ctxsdhy/p/5815492.html
Copyright © 2020-2023  润新知