java的动态代理

来源:互联网 发布:网络备案申请表 编辑:程序博客网 时间:2024/06/05 15:40

代理模式是一种常用的设计模式,其目的就是为其他对象提供一个代理以控制对某个真实对象的访问。代理类负责为委托类预处理消息,过滤消息并转发消息,以及进行消息被委托类执行后的后续处理.通过代理层这一中间层,有效的控制对于真实委托类对象的直接访问,同时可以实现自定义的控制策略(Spring的AOP机制),设计上获得更大的灵活性。

jdk动态代理和cglib动态代理。两种方法同时存在,各有优劣。jdk动态代理是由java内部的反射机制来实现的,cglib动态代理底层则是借助asm来实现的。总的来说,反射机制在生成类的过程中比较高效,而asm在生成类之后的相关执行过程中比较高效(可以通过将asm生成的类进行缓存,这样解决asm生成类过程低效问题)。还有一点必须注意:jdk动态代理的应用前提,必须是目标类基于统一的接口。如果没有上述前提,jdk动态代理不能应用。由此可以看出,jdk动态代理有一定的局限性,cglib这种第三方类库实现的动态代理应用更加广泛,且在效率上更有优势。。

首先我们先看JDK动态代理:

在java的动态代理机制中,有两个重要的类或接口,一个是 InvocationHandler(Interface)、另一个则是 Proxy(Class),这一个类和接口是实现我们动态代理所必须用到的。首先我们先来看看java的API帮助文档是怎么样对这两个类进行描述的:

1.java.lang.reflect.InvocationHandler:调用处理器接口,自定义invokle方法,用于实现对于真正委托类的代理访问。


API帮助文档描述:InvocationHandler is the interface implemented by the invocation handler of a proxy instance.

Each proxy instance has an associated invocation handler. When a method is invoked on a proxy instance, the method invocation is encoded and dispatched to the invoke method of its invocation handler.
每一个动态代理类都必须要实现InvocationHandler这个接口,并且每个代理类的实例都关联到了一个handler,当我们通过代理对象调用一个方法的时候,这个方法的调用就会被转发为由InvocationHandler这个接口的 invoke 方法来进行调用。我们来看看InvocationHandler这个接口的唯一一个方法 invoke 方法:
Object invoke(Object proxy, Method method, Object[] args) throws Throwable

这三个参数代表什么:
proxy:  指代我们所代理的那个真实对象method:  指代的是我们所要调用真实对象的某个方法的Method对象args:  指代的是调用真实对象某个方法时接受的参数

2.java.lang.reflect.Proxy:动态代理机制的主类,提供一组静态方法为一组接口动态的生成对象和代理类。
API帮助文档描述:Proxy provides static methods for creating dynamic proxy classes and instances, and it is also the superclass of all dynamic proxy classes created by those methods. 
Proxy这个类的作用就是用来动态创建一个代理对象的类,它提供了许多的方法,但是我们用的最多的就是 newProxyInstance 这个方法:
// 方法 1: 该方法用于获取指定代理对象所关联的调用处理器
public static InvocationHandler getInvocationHandler(Object proxy) 
// 方法 2:该方法用于获取关联于指定类装载器和一组接口的动态代理类的类对象
public static Class<?> getProxyClass(ClassLoader loader, Class<?>... interfaces)
// 方法 3:该方法用于判断指定类对象是否是一个动态代理类
public static boolean isProxyClass(Class<?> cl) 
// 方法 4:该方法用于为指定类装载器、一组接口及调用处理器生成动态代理类实例
public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces,InvocationHandler h)
3,java.lang.ClassLoader:类装载器类,将类的字节码装载到 Java 虚拟机(JVM)中并为其定义类对象,然后该类才能被使用。Proxy类与普通类的唯一区别就是其字节码是由 JVM 在运行时动态生成的而非预存在于任何一个 .class 文件中 
每次生成动态代理类对象时都需要指定一个类装载器对象:newProxyInstance()方法第一个参数

java动态代理创建对象的过程为如下步骤: 
1,通过实现 InvocationHandler 接口创建自己的调用处理器;

// InvocationHandlerImpl 实现了 InvocationHandler 接口,并能实现方法调用从代理类到委托类的分派转发// 其内部通常包含指向委托类实例的引用,用于真正执行分派转发过来的方法调用InvocationHandler handler = new InvocationHandlerImpl(..); 
  • 1
  • 2
  • 3

2,通过为 Proxy 类指定 ClassLoader 对象和一组 interface 来创建动态代理类;

// 通过 Proxy 为包括 Interface 接口在内的一组接口动态创建代理类的类对象Class clazz = Proxy.getProxyClass(classLoader, new Class[] { Interface.class, ... }); 
  • 1
  • 2

3,通过反射机制获得动态代理类的构造函数,其唯一参数类型是调用处理器接口类型;

// 通过反射从生成的类对象获得构造函数对象Constructor constructor = clazz.getConstructor(new Class[] { InvocationHandler.class });
  • 1
  • 2

4,通过构造函数创建动态代理类实例,构造时调用处理器对象作为参数被传入。

// 通过构造函数对象创建动态代理类实例Interface Proxy = (Interface)constructor.newInstance(new Object[] { handler });
  • 1
  • 2

为了简化对象创建过程,Proxy类中的newProxyInstance方法封装了2~4,只需两步即可完成代理对象的创建。

// InvocationHandlerImpl 实现了 InvocationHandler 接口,并能实现方法调用从代理类到委托类的分派转发InvocationHandler handler = new InvocationHandlerImpl(..); // 通过 Proxy 直接创建动态代理类实例Interface proxy = (Interface)Proxy.newProxyInstance( classLoader,      new Class[] { Interface.class },      handler );
动态代理的注意点: 
1,包:代理接口是public,则代理类被定义在顶层包(package为空),否则(default),代理类被定义在该接口所在包,

2,生成的代理类为public final,不能被继承,

3,类名:格式是“$ProxyN”,N是逐一递增的数字,代表Proxy被第N次动态生成的代理类,要注意,对于同一组接口(接口的排列顺序也相同),不会重复创建动态代理类,而是返回一个先前已经创建并缓存了的代理类对象。提高了效率。

4,代理类的根类 java.lang.Object 中有三个方法也同样会被分派到调用处理器的 invoke 方法执行,它们是 hashCode,equals 和 toString, 
代码在反编译中 

先写一个例子 (根据例子看源码就很容易)

接口:public interface Subject {    public void rent();    public void hello(String str);}

实现类:public class RealSubject implements Subject {    @Override    public void rent() {        System.out.println("rent() ");    }    @Override    public void hello(String str) {        System.out.println("hello(String str)"+str);    }}
代理类:public class MyProxy implements InvocationHandler {
    private Object subject;    public MyProxy(Object subject) {        this.subject = subject;    }    @Override    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {        before();        method.invoke(subject,args);        after();        return null;    }    public void before(){        System.out.println("之前逻辑");    }    public void after(){        System.out.println("之后逻辑");    }}
public class Main {    public static void main(String[] args) {        //生成$Proxy0class文件        /* 设置此系统属性,JVM生成的Proxy类写入文件.保存路径为:com/sun/proxy(如果不存在请手工创建) */        System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");       // writeProxyClassToHardDisk("D:/$Proxy0.class");        //我们要代理的真实对象        Subject realSubject = new RealSubject();        //我们要代理哪个真实对象,就将该对象传进去,最后是通过该真实对象来调用其方法的        MyProxy myProxy = new MyProxy(realSubject);        Subject subject = (Subject)Proxy.newProxyInstance(myProxy.getClass().getClassLoader(), realSubject                .getClass().getInterfaces(), myProxy);        System.out.println(subject.getClass().getName());        subject.rent();        subject.hello("world");    }    /**     * 把代理类的字节码写到硬盘上     * @param path 保存路径     */    public static void writeProxyClassToHardDisk(String path) {             //生成$Proxy0class文件        /* 设置此系统属性,JVM生成的Proxy类写入文件.保存路径为:com/sun/proxy(如果不存在请手工创建) */        // System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", true);        // 第二种方法        // 获取代理类的字节码        byte[] classFile = ProxyGenerator.generateProxyClass("$Proxy0", RealSubject.class.getInterfaces());        FileOutputStream out = null;        try {            out = new FileOutputStream(path);            out.write(classFile);            out.flush();        } catch (Exception e) {            e.printStackTrace();        } finally {            try {                out.close();            } catch (IOException e) {                e.printStackTrace();            }        }    }}
生成的代理类:
//// Source code recreated from a .class file by IntelliJ IDEA// (powered by Fernflower decompiler)//package com.sun.proxy;import com.springboot1.proxy.Subject;import java.lang.reflect.InvocationHandler;import java.lang.reflect.Method;import java.lang.reflect.Proxy;import java.lang.reflect.UndeclaredThrowableException;public final class $Proxy0 extends Proxy implements Subject {    private static Method m1;    private static Method m3;    private static Method m2;    private static Method m4;    private static Method m0;    public $Proxy0(InvocationHandler var1) throws  {        super(var1);    }    public final boolean equals(Object var1) throws  {        try {            return ((Boolean)super.h.invoke(this, m1, new Object[]{var1})).booleanValue();        } catch (RuntimeException | Error var3) {            throw var3;        } catch (Throwable var4) {            throw new UndeclaredThrowableException(var4);        }    }    public final void rent() throws  {        try {            super.h.invoke(this, m3, (Object[])null);        } catch (RuntimeException | Error var2) {            throw var2;        } catch (Throwable var3) {            throw new UndeclaredThrowableException(var3);        }    }    public final String toString() throws  {        try {            return (String)super.h.invoke(this, m2, (Object[])null);        } catch (RuntimeException | Error var2) {            throw var2;        } catch (Throwable var3) {            throw new UndeclaredThrowableException(var3);        }    }    public final void hello(String var1) throws  {        try {            super.h.invoke(this, m4, new Object[]{var1});        } catch (RuntimeException | Error var3) {            throw var3;        } catch (Throwable var4) {            throw new UndeclaredThrowableException(var4);        }    }    public final int hashCode() throws  {        try {            return ((Integer)super.h.invoke(this, m0, (Object[])null)).intValue();        } catch (RuntimeException | Error var2) {            throw var2;        } catch (Throwable var3) {            throw new UndeclaredThrowableException(var3);        }    }    static {        try {            m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[]{Class.forName("java.lang.Object")});            m3 = Class.forName("com.springboot1.proxy.Subject").getMethod("rent", new Class[0]);            m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);            m4 = Class.forName("com.springboot1.proxy.Subject").getMethod("hello", new Class[]{Class.forName("java.lang.String")});            m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);        } catch (NoSuchMethodException var2) {            throw new NoSuchMethodError(var2.getMessage());        } catch (ClassNotFoundException var3) {            throw new NoClassDefFoundError(var3.getMessage());        }    }}
这样就知道到底是谁调用了InvocationHandler的invoke方法的

cglib动态代理实现

Cglib是一个优秀的动态代理框架,它的底层使用ASM在内存中动态的生成被代理类的子类,使用CGLIB即使代理类没有实现任何接口也可以实现动态代理功能。CGLIB具有简单易用,它的运行速度要远远快于JDK的Proxy动态代理:
 cglib是针对类来实现代理的,原理是对指定的业务类生成一个子类,并覆盖其中业务方法实现代理。因为采用的是继承,所以不能对final修饰的类进行代理。 

CGLIB的核心类:
    net.sf.cglib.proxy.Enhancer – 主要的增强类
    net.sf.cglib.proxy.MethodInterceptor – 主要的方法拦截类,它是Callback接口的子接口,需要用户实现
    net.sf.cglib.proxy.MethodProxy – JDK的java.lang.reflect.Method类的代理类,可以方便的实现对源对象方法的调用,

net.sf.cglib.proxy.MethodInterceptor接口是最通用的回调(callback)类型,它经常被基于代理的AOP用来实现拦截(intercept)方法的调用。这个接口只定义了一个方法
public Object intercept(Object object, java.lang.reflect.Method method,
Object[] args, MethodProxy proxy) throws Throwable;

第一个参数是代理对像,第二和第三个参数分别是拦截的方法和方法的参数

下面来看例子:

 1:首先定义业务类,无需实现接口(当然,实现接口也可以,不影响的)

public class RealSubject  {        public void rent() {        System.out.println("rent() ");    }        public void hello(String str) {        System.out.println("hello(String str)"+str);    }}
2:实现 MethodInterceptor方法代理接口,创建代理类
package com.springboot1.cglibProxy;import org.springframework.cglib.proxy.Enhancer;import org.springframework.cglib.proxy.MethodInterceptor;import org.springframework.cglib.proxy.MethodProxy;import java.lang.reflect.Method;/** * Created by 340092 on 2017/12/15. */public class CglibProxy implements MethodInterceptor {    private Object target;//业务类对象,供代理方法中进行真正的业务方法调用    //相当于JDK动态代理中的绑定(newProxyInstance)    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 obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {        before();        proxy.invokeSuper(obj, args); //调用业务类(父类中)的方法        after();        return null;    }    public void before(){        System.out.println("之前逻辑");    }    public void after(){        System.out.println("之后逻辑");    }}
主类:
public class Main {    public static void main(String[] args) {        RealSubject realSubject = new RealSubject();        CglibProxy cglibProxy = new CglibProxy();        RealSubject subject = (RealSubject) cglibProxy.getInstance(realSubject);        subject.hello("hello");        subject.rent();    }}
运行结果:
之前逻辑hello(String str)hello之后逻辑之前逻辑rent() 之后逻辑

 静态代理是通过在代码中显式定义一个业务实现类一个代理,在代理类中对同名的业务方法进行包装,用户通过代理类调用被包装过的业务方法;

    JDK动态代理是通过接口中的方法名,在动态生成的代理类中调用业务实现类的同名方法,jdk动态代理对实现了接口的类进行代理

    CGlib动态代理是通过继承业务类,生成的动态代理类是业务类的子类,通过重写业务方法进行代理,因为是继承,所以我们的目标最好不要使用使用final声明

spring 默认使用的jdk的动态代理;

1.如果目标对象实现了接口,在默认情况下采用jdk的动态代理实现aop

2.如果目标对象实现了接口,也可以强制使用cglib生成代理实现aop

3.如果目标对象没有实现接口,那么必须引入cglib,spring会在jdk的动态代理和cglib代理之间切换

如何使用cglib强制生成代理;

* 加入cglib类库,cglib/*.jar

* 加入如下配置,表示强制使用cglib代理

<aop:aspectj-autoproxy proxy-target-class="true"/>





原创粉丝点击