365bet体育|www.635288com-365体育手机在线

热门关键词: 365bet体育,www.635288com,365体育手机在线

代理可以看做是对调用目标的一个封装

2019-05-30 作者:365bet体育   |   浏览(187)

["

n 动态代理是一种在运行时动态地创建代理对象,动态地处理代理方法调用的机制。n

n

n 实际上它是一种代理机制。代理可以看做是对调用目标的一个封装,直接通过代理来实现对目标n n 代码nn 的调用n

n

n 与静态代理的比较n

n

n 静态代理n

n

n 提前写好代理类,每个业务类都要对应一个代理类,不灵活n

n

图片 1

n

  • n ISubject,该接口是被访问者或者被访问的对象n
  • n SubjectImpl,被访问者的具体实现类n
  • n Subjectn n Proxynn ,被访问者或被访问资源的代理实现类n
  • n Client:代表访问者的抽象角色,n n clientnn 将会访问Isubject类型的对象或者资源,通过代理类进行访问。n
  • n 这里的SubjectImpl和SubjectProxy都实现了ISubject的接口,SubjectProxy是将请求转发给SubjectImpl,其内部有SubjectImpl的对象,并且可以添加一些限制n

n

n 以上的图解n n 本质nn 上就是代理模式的一个讲解,适用于静态代理和动态代理,n n 区别在于代理对象和代理方法生成的n n 时间nn 和方式不同n n 。n

n

n 动态代理是运行时n n 自动生成nn 代理对象,一个缺点是生成代理对象和调用代理方法需要耗费时间n

n

n 动态代理的实现方式n

n

n 动态代理主要有两种实现方式,一种是JDK动态代理,一种是CGLIBn n 字节码nn 机制,当然还有Javassist或ASM库,这两个在CGLIB那块一并介绍n

n

n JDK动态代理n

n

n 说到JDK动态代理,就不得不提起反射机制,JDK动态代理就是通过反射机制实现的。n

n

n 反射机制n

n

n 反射就是通过Class类和n n javann .lang.reflect类库在运行时获取某个类的信息。比如通过java.lang.reflect类库中Field,Method以及Conn n structnn or类就可以获取类的相关信息n

n

n JDK动态代理的实现n

n

n 下面是通过反射机制实现JDK动态代理的一个简单例子n

n

/**n * 动态代理的实现n *n * @author pjmiken * @create 2018-08-04 17:42n */npublic class DynamicProxy {n    public static void main(String[] args) {n        IHelloImpl hello = new IHelloImpl();n        MyInvocationHandler handler = new MyInvocationHandler(hello);n        //获取目标用户的代理对象n        IHello proxyHello = (IHello) Proxy.newProxyInstance(IHelloImpl.class.getClassLoader(), IHelloImpl.class.getInterfaces(), handler);n        //调用代理方法n        proxyHello.sayHello();n    }n}nn/**n * 被访问者接口n */ninterface IHello{n    void sayHello();n}nn/**n * 被访问者的具体实现类n */nclass IHelloImpl implements IHello {nn    @Overriden    public void sayHello() {n        System.out.println("Hello World");n    }n}nnclass MyInvocationHandler implements InvocationHandler {n    private Object target;nn    /**n     *n     * @param target 被代理的目标对象n     */n    public MyInvocationHandler(Object target) {n        this.target = target;n    }nn    /**n     * 执行目标对象的方法n     *n     * @param proxy 代理对象n     * @param method 代理方法n     * @param args 方法参数n     * @returnn     * @throws Throwablen     */n    @Overriden    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {n        System.out.println("invoke method");n        System.out.println("Method name : " method.getName());n        Object result = method.invoke(target, args);n        return result;n    }n}n复制代码

n

n 从上面的例子中可以看出,代理对象的生成是通过n n Proxy.newProxyInstance()nn 来完成的n

n

public static Object newProxyInstance(ClassLoader loader,n                                          Class<?>[] interfaces,n                                          InvocationHandler h)n复制代码

n

n n newProxyInstance()nn 方法主要以下三个参数n

n

  • n n 类加载器nn (ClassLoader)用来加载动态代理类n
  • n 一个要实现接口的数组,从这点就可以看出,要想n n 使用JDK动态代理,必须要有接口类n n
  • n InvocactionHandler接口的一个实现n

n

图片 2

n

n 动态代理可以将所有调用重定向到调用n n 处理器nn ,因此通常上会向调用处理器的构造器传递一个"实际"对象的引用,从而使得处理器在执行任务时,可以请求转发。n

n

n 利用CGLIB实现动态代理n

n

n n cglibnn 是一种基于ASM的字节码生成库,用于生成和转换Java字节码.n

n

n 而ASM是一个轻量但高性能的字节码操作框架。cgn n libnn 是基于ASM的上层应用,对于代理没有实现接口的类,cglib非常实用。n

n

图片 3

n

n CGLIB动态代理的简单例子n

n

n 本质上说,对于需要被代理的类,它只是动态生成一个子类以覆盖非n n finalnn 的方法,同时绑定钩子回调自定义的拦截器。n

n

n 添加CGLIB依赖n

n

<dependency>n      <groupId>cglib</groupId>n      <artifactId>cglib</artifactId>n      <version>3.2.4</version>n /dependency>n复制代码

n

package com.pjmike.proxy;nnimport net.sf.cglib.proxy.Enhancer;nimport net.sf.cglib.proxy.MethodInterceptor;nimport net.sf.cglib.proxy.MethodProxy;nnimport java.lang.reflect.Method;nn/**n * CGLIB动态代理实现n *n * @author pjmiken * @create 2018-08-06 16:55n */npublic class CglibProxy {n    public static void main(String[] args) {n        //Enhancer是CGLIB的核心工具类,是一个字节码增强器,它可以方便的对类进行扩展n        Enhancer enhancer = new Enhancer();n        enhancer.setSuperclass(PersonService.class);n        //设置回调所需的拦截器n        enhancer.setCallback(new MyMethodInterceptor());n        //通过enhancer.create()方法获取代理对象n        //对代理对象所有非final的方法调用都会转发给MethodInterceptor.intercept方法,n        //作用跟JDK动态代理的InvocationHandler类似n        PersonService personService = (PersonService) enhancer.create();n        System.out.println(personService.sayHello("pjmike"));n    }n}nnclass PersonService {n    public String sayHello(String name) {n        return "Hello, "   name;n    }n}nnclass MyMethodInterceptor implements MethodInterceptor {nn    @Overriden    public Object intercept(Object obj, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {n        return methodProxy.invokeSuper(obj, args);n    }n}n复制代码

n

n 至于上面提到的javassist也是需要直接操作字节码,跟ASM类似,所以这两者使用门槛比较高,一般用于框架的底层实现。比如hibernate底层使用了javassist和cglib.n

n

n javassistn

n

n javassist是一个运行时n n 编译nn 库,可以动态的生成或修改类的字节码。它有两种实现动态代理的方案: javassist提供的动态代理接口和javassist字节码。n

n

n 因为javassist提供动态代理接口比较慢,所以这里主要分析javassist字节码,先不考虑其动态代理接口。至于这几种代理方案的性能比较问题,参考动态代理方案性能对比。n

n

n javassist主要由CtClass,CtMethod,CtField几个类组成,与JDK反射中的Class,Method,Field相似。n

n

n 简单使用n

n

n 添加依赖n

n

<dependency>n      <groupId>org.javassist</groupId>n      <artifactId>javassist</artifactId>n      <version>3.21.0-GA</version>n</dependency>n复制代码

n

n 代码n

n

package com.pjmike.proxy;nnimport javassist.*;nn/**n * javassist字节码n *n * @author pjmiken * @create 2018-08-07 0:07n */npublic class JavassistByteCode {n    public static void main(String[] args) throws IllegalAccessException, CannotCompileException, InstantiationException, NotFoundException {n        ByteCodeAPI byteCodeApi = createJavassistBycodeDynamicProxy();n        System.out.println(byteCodeApi.sayHello());n    }n    public static ByteCodeAPI createJavassistBycodeDynamicProxy() throws CannotCompileException, IllegalAccessException, InstantiationException, NotFoundException {n        //获取运行时类的上下文n        ClassPool pool = ClassPool.getDefault();n        //动态创建类n        CtClass cc = pool.makeClass(ByteCodeAPI.class.getName() "demo");n        cc.addInterface(pool.get(ByteCodeAPI.class.getName()));n        //创建属性n        CtField field = CtField.make("private String a;", cc);n        cc.addField(field);n        //创建方法n        CtMethod method = CtMethod.make("public String sayHello() {return /"hello/";}", cc);n        cc.addMethod(method);n        //添加构造器n        cc.addConstructor(CtNewConstructor.defaultConstructor(cc));n        Class<?> pc = cc.toClass();n        ByteCodeAPI byteCodeApi = (ByteCodeAPI) pc.newInstance();n        return byteCodeApi;n    }n}ninterface ByteCodeAPI {n    public String sayHello();n}nn//结果:输出 hellon复制代码

n

n 动态代理的实际应用n

n

n 动态代理实际上有很多应用,比如n n springnn aop的实现,rpc框架的实现,一些第三方工具库的内部使用等等。这里简单介绍动态代理在spring aop和RPC框架中的应用n

n

n 应用一: Springn n AOPnn 的动态代理实现n

n

n Spring AOP的动态代理实现主要有两种方式,JDK动态代理和CGLIB字节码生成。n

n

n 默认情况下,如果Spring AOP发现目标对象后实现了相应的interface,则采用JDK动态代理机制为其生成代理对象。如果没有发现接口,则采用CGLIB的方式为目标对象生成动态的代理对象n n 实例nn

n

n 应用二: RPC框架中的应用n

n

n RPC即远程过程调用,它的实现中使用到了动态代理,关于RPC的具体原理参照你应该知道的RPC原理等n n 文章nn

n

图片 4

n

n 实际上RPC框架要解决的一个问题就是: 如何调用他人的远程服务?像调用本地服务一样调用远程服务。n

n

n 如何封装n n 数据nn ,通过网络传输给远程服务,使远程接口透明,这就需要动态代理的帮助了。所以透明化远程服务调用就是要利用动态代理,在代理层对数据进行封装,网络传输等操作。n

n

n 小结n

n

n 实际n n 开发nn 中,我们很多时候都是利用现成的框架和n n 开源nn 库,其中就包含动态代理的应用。只有真正了解了动态代理的知识,才能更好地理解其在框架中的设计,也有利于更好的去使用框架。n

n

n 参考资料n

n

  • n JDK dynamic proxyn
  • n Java编程思想n
  • n CGLIB动态代理介绍n
  • n Javassist Tutorial-1n

","原文地址:动态代理的原理及其应用, 感谢原作者分享。"]

本文由365bet体育发布于365bet体育,转载请注明出处:代理可以看做是对调用目标的一个封装

关键词: Action Java ACE cat API

365bet体育推荐