【Java】动态代理

动态代理

动态代理实际上是JVM在运行期动态创建class字节码并加载的过程

与静态代理同为实现代理模式的方式

动态代理是通过Proxy创建代理对象,然后将接口方法“代理”给InvocationHandler完成的。

动态代理的源码是在程序运行期间由JVM根据反射等机制动态的生成,在运行前并不存在代理类的字节码文件

代理对象的作用就是代理原本的Bean对象,代理对象在执行某个方法时,会在该方法的基础上增加一些切面逻辑,使得可以利用AOP来实现一些诸如登录校验、权限控制、日志记录等统一功能。

可以非常灵活地在某个类,某个方法,某个代码点上切入想要的内容(代理中的内容),可以在运行的时候才切入改变类的方法,而不需要预先定义它。

原理

Java虚拟机类加载过程主要分为五个阶段:加载、验证、准备、解析、初始化。其中加载阶段需要完成以下3件事情:

  1. 通过一个类的全限定名来获取定义此类的二进制字节流
  2. 将这个字节流所代表的静态存储结构转化为方法区的运行时数据结构
  3. 在内存中生成一个代表这个类的 java.lang.Class 对象,作为方法区这个类的各种数据访问入口

Java虚拟机在 类加载过程中的加载阶段 获取类的class字节码(二进制字节流)时,允许加载 运行时计算生成的class字节码,这就是动态代理技术的实现基础

动态代理原理是 根据接口或目标对象,计算出代理类的字节码,然后再加载到JVM中使用。(但是计算和生成的方式非常复杂,需要借助现有方案实现)

常见的字节码操作类库:

  • Apache BCEL (Byte Code Engineering Library):是Java classworking广泛使用的一种框架,它可以深入到JVM汇编语言进行类操作的细节。
  • ObjectWeb ASM:是一个Java字节码操作框架。它可以用于直接以二进制形式动态生成stub根类或其他代理类,或者在加载时动态修改类。
  • CGLIB(Code Generation Library):是一个功能强大,高性能和高质量的代码生成库,用于扩展JAVA类并在运行时实现接口。
  • Javassist:是Java的加载时反射系统,它是一个用于在Java中编辑字节码的类库; 它使Java程序能够在运行时定义新类,并在JVM加载之前修改类文件。

参考:Open Source ByteCode Libraries in Java (java-source.net)

实现

实现动态代理的两种常见方式:

  • JDK动态代理:通过实现接口的方式
  • CGLIB动态代理:通过继承类的方式

比较:

  • 性能
    • Java动态代理:使用 Java 原生的反射 API 进行操作(运行期),在生成类上比较高效
    • CGLIB动态代理:相比于 JDK 动态代理更加强大,CGLIB使用ASM框架直接对字节码进行操作(编译期),在类的执行过程中比较高效
  • 限制
    • JDK动态代理:被代理的对象需实现至少一个接口,而且只能对 实现的接口中的方法 进行代理
    • CGLIB动态代理:被代理对象不能用final修饰

JDK动态代理

Java标准库提供的一种动态代理(Dynamic Proxy)的机制,允许在运行期动态创建某个interface的实例。

JDK动态代理虽然简单易用,但是其有一个致命缺陷是,只能对接口进行代理

生成的代理类继承了Proxy,由于java是单继承的,所以JDK动态代理只能代理接口

需要对对象进行代理时,被代理的对象需实现至少一个接口,而且只能对 实现的接口中的方法 进行代理

优点:

  • 最小化依赖关系,减少依赖意味着简化开发和维护,JDK 本身的支持,可能比 cglib 更加可靠。
  • 平滑进行 JDK 版本升级,而字节码类库通常需要进行更新以保证在新版 Java 上能够使用。
  • 代码实现简单。
核心对象
Proxy

专门完成代理的操作类,是所有动态代理类的父类,通过此类为一个或多个接口动态地生成实现类

在 java.lang.reflect.Proxy 类中,使用 ProxyGenerator.generateProxyClass 来为特定接口生成形式为 *$Proxy 的代理类的二进制字节流

常用方法:

1、创建代理类实例

构造实现指定接口的代理类的一个新实例,所有方法会调用给定处理器对象的 invoke 方法

/**
* loder,选用的类加载器
* interfaces,被代理的类所实现的接口,接口至少一个
* h,绑定代理类的一个方法
*/
static Object newProxylnstance(ClassLoader loader, Class<?>[] interfaces, InvocatonHandler h)

被代理后生成的对象,是通过接口的字节码增强方式创建的类而构造出来的,它是一个临时构造的实现类的对象。

loder和interfaces基本就是决定了这个类到底是个怎么样的类,而h是InvocationHandler,决定了这个代理类到底是多了什么功能

InvocationHandler 是具体的装饰类,主要用来包裹业务类的某个方法,起到修饰(过滤、拦截、校验等)作用。

源码:

public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) throws IllegalArgumentException {  
    if (h == null) {  
        throw new NullPointerException();  
    }  
  
    /* 
     * Look up or generate the designated proxy class. 
     */  
    Class cl = getProxyClass(loader, interfaces);  
  
    /* 
     * Invoke its constructor with the designated invocation handler. 
     */  
    try {  
           /* 
            * Proxy源码开始有这样的定义: 
            * private final static Class[] constructorParams = { InvocationHandler.class }; 
            * cons即是形参为InvocationHandler类型的构造方法 
           */  
        Constructor cons = cl.getConstructor(constructorParams);  
        return (Object) cons.newInstance(new Object[] { h });  
    } catch (NoSuchMethodException e) {  
        throw new InternalError(e.toString());  
    } catch (IllegalAccessException e) {  
        throw new InternalError(e.toString());  
    } catch (InstantiationException e) {  
        throw new InternalError(e.toString());  
    } catch (InvocationTargetException e) {  
        throw new InternalError(e.toString());  
    }  
}

过程:

  1. 根据参数loaderinterfaces调用方法getProxyClass(loader, interfaces)创建代理类$Proxy0类,实现了interfaces的接口,并继承了Proxy

  2. 实例化 $Proxy0 并在构造方法中把InvocationHandler实例传过去,接着 $Proxy0 调用父Proxy的构造器,为h赋值

  3. 接着把得到的$Proxy0实例强制转换成Subject,并将引用赋给subject。当执行subject.request()方法时,就调用了$Proxy0类中的request()方法,进而调用父类Proxy中的hinvoke()方法,即InvocationHandler.invoke()

细节:

  • $Proxy0的源码可以看出,动态代理类不仅代理了显示定义的接口中的方法,而且还代理了java的根类Object中的继承而来的equals()、hashcode()、toString()这三个方法,并且仅此三个方法

2、返回处理器

用于获取指定代理对象所关联的调用处理器

static InvocationHandler getInvocationHandler(Object proxy)

3、返回指定接口的代理类

static Class<?> getProxyClass(ClassLoader loader, Class<?>... interfaces)

4、判断是否为代理类

返回 cl 是否为一个代理类

static boolean isProxyClass(Class<?> cl)
InvocatonHandler

一个通用的接口类,可以在指定类某个方法的外层添加自己的拦截、过虑、校验代码

常用方法:

1、定义代理逻辑

定义了代理对象调用方法时希望执行的动作,用于集中处理在动态代理类对象上的方法调用

/**
* proxy,代理后的实例对象。
* method,对象被调用方法。
* args,调用时的参数。
*/
Object invoke(Object proxy, Method method, Object[] args)

在使用动态代理时,需要在InvocatonHandler.invoke()方法MethodInterceptor.intercept()方法中使用原方法时,调用该方法的是被代理的对象。如果使用的是代理对象,则会不停地循环调用,这是因为通过代理对象调用该方法时,会触发代理,然后代理中又会通过代理对象调用该方法,又触发代理,以此类推。

在整个动态代理机制中,并没有用到InvocationHandler中invoke方法的proxy参数,而传入的这个参数实际是代理类的一个实例,是为了让程序员在invoke方法中使用反射来获取关于代理类的一些信息。

当代理对象的原本方法被调用的时候,会绑定执行InvocationHandler.invoke()方法,同时将返回结果替代原本方法的结果返回。

实现

先定义了接口,但是并不编写实现类,而是直接通过JDK提供的一个Proxy.newProxyInstance()创建了一个接口对象(代理对象)

过程:

  1. 定义被代理的接口
  2. 定义一个InvocationHandler实例,,它必须实现invoke方法, 以完成代理的具体操作。
  3. 通过Proxy.newProxyInstance()创建interface实例(代理对象),它需要3个参数:
    • ClassLoader:代理对象的类加载器,通常就是接口类的ClassLoader
    • interfaces:被代理对象的接口,需要实现的接口数组,至少需要一个接口
    • h:用来处理接口方法调用的InvocationHandler实例
  4. 将返回的代理对象Object强制转型为接口
  5. 通过代理对象调用方法
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

public class Main {
    public static void main(String[] args) {
        InvocationHandler handler = new InvocationHandler() {
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                System.out.println(method);
                if (method.getName().equals("morning")) {
                    System.out.println("Good morning, " + args[0]);
                }
                return null;
            }
        };
        
        /**
         * 创建对象的代理对象
         * 参数一:类加载器
         * 参数二:对象的接口
         * 参数三:调用处理器,代理对象中的方法被调用,都会在执行方法。对所有被代理对象的方法进行拦截
         */
        Hello hello = (Hello) Proxy.newProxyInstance(
            Hello.class.getClassLoader(), // 传入ClassLoader
            new Class[] { Hello.class }, // 传入要实现的接口
            handler); // 传入处理调用方法的InvocationHandler
        
        hello.morning("Bob");
    }
}

interface Hello {
    void morning(String name);
}

CGLIB

CGLIB是一个功能强大,高性能的代码生成包,主要通过对字节码的操作,为对象引入间接级别,以控制对象的访问。它为没有实现接口的类提供代理,为JDK的动态代理提供了很好的补充。通常可以使用Java的动态代理创建代理,但当要代理的类没有实现接口或者为了更好的性能,CGLIB是一个好的选择。

动态生成一个要代理类的子类,子类重写要代理的类的所有不是final的方法。在子类中采用方法拦截的技术拦截所有父类方法的调用,顺势织入横切逻辑。它比使用java反射的JDK动态代理要快。

第三方提供

被代理对象不能用final修饰

原理:

使用字节码处理框架ASM来转换字节码并生成业务类的子类作为代理类。

生成目标类的子类,通过调用父类(目标类)的方法实现,在调用父类方法时再代理中进行增强。

优点:

  • 无需实现接口,达到代理类无侵入
  • 只操作我们关心的类,而不必为其他相关类增加工作量。
  • 高性能

过程:

  1. 查找目标类上的所有非final 的public类型的方法定义;
  2. 将这些方法的定义转换成字节码;
  3. 将组成的字节码转换成相应的代理的class对象;
  4. 实现 MethodInterceptor接口,用来处理对代理类上所有方法的请求
核心对象
Enhancer

使用的是Enhancer类创建代理对象

import net.sf.cglib.proxy.Enhancer;
public class CglibTest {
    public static void main(String[] args) {
        LogInterceptor logInterceptor = new LogInterceptor();
        
        Enhancer enhancer = new Enhancer();
        // 设置超类,cglib是通过继承来实现的
        enhancer.setSuperclass(UserDao.class);   
        // 设置多个拦截器,NoOp.INSTANCE是一个空拦截器,不做任何处理
        enhancer.setCallbacks(new Callback[]{logInterceptor, NoOp.INSTANCE});   
        enhancer.setCallbackFilter(new DaoFilter());

        UserDao proxy = (UserDao) enhancer.create();   // 创建代理类
        proxy.select();
        proxy.update();
    }
}
MethodInterceptor

必须实现MethodInterceptor(方法拦截器)接口

常用方法:

1、代理逻辑

    /**
     * @param object 表示要进行增强的对象
     * @param method 表示拦截的方法
     * @param objects 数组表示参数列表,基本数据类型需要传入其包装类型,如int-->Integer、long-Long、double-->Double
     * @param methodProxy 表示对方法的代理,invokeSuper方法表示对被代理对象方法的调用
     * @return 执行结果
     * @throws Throwable
     */
    public Object intercept(Object object, Method method, Object[] objects, MethodProxy methodProxy)
CallbackFilter

过滤筛选

// 回调过滤器: 在CGLib回调时可以设置对不同方法执行不同的回调逻辑,或者根本不执行回调。
public class DaoFilter implements CallbackFilter {
    @Override
    public int accept(Method method) {
        if ("select".equals(method.getName())) {
            return 0;   // Callback 列表第1个拦截器
        }
        return 1;   // Callback 列表第2个拦截器,return 2 则为第3个,以此类推
    }
}
实现
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import proxy.UserService;
import java.lang.reflect.Method;

/**
 * 创建代理类的工厂 该类要实现 MethodInterceptor 接口。
 * 该类中完成三样工作:
 * (1)声明目标类的成员变量,并创建以目标类对象为参数的构造器。用于接收目标对象
 * (2)定义代理的生成方法,用于创建代理对象。方法名是任意的。代理对象即目标类的子类
 * (3)定义回调接口方法。对目标类的增强这在这里完成
 */
public class CGLibFactory implements MethodInterceptor {
    // 声明目标类的成员变量
    private UserService target;

    public CGLibFactory(UserService target) {
        this.target = target;
    }
    // 定义代理的生成方法,用于创建代理对象
    public UserService myCGLibCreator() {
        Enhancer enhancer = new Enhancer();
        // 为代理对象设置父类,即指定目标类
        enhancer.setSuperclass(UserService.class);
        
        /**
         * 设置回调接口对象 注意,只所以在setCallback()方法中可以写上this,
         * 是因为MethodIntecepter接口继承自Callback,是其子接口
         */
        enhancer.setCallback(this);
        return (UserService) enhancer.create();// create用以生成CGLib代理对象
    }
    @Override
    public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
        System.out.println("start invoke " + method.getName());
        Object result = method.invoke(target, args);
        // 调用 invokeSuper 而不是 invoke,否则死循环,proxy.invokesuper执行的是原始类的方法,method.invoke执行的是子类的方法
        // Object result = proxy.invokeSuper(object, objects);   
        System.out.println("end invoke " + method.getName());
        return result;
    }
}

源码

生成

工具类

import sun.misc.ProxyGenerator;
import java.io.FileOutputStream;
import java.io.IOException;

public class ProxyUtils {
    /**
     * 将根据类信息动态生成的二进制字节码保存到硬盘中,默认的是clazz目录下
     * params: clazz 需要生成动态代理类的类
     * proxyName: 为动态生成的代理类的名称
     */
    public static void generateClassFile(Class clazz, String proxyName) {
        // 根据类信息和提供的代理类名称,生成字节码
        byte[] classFile = ProxyGenerator.generateProxyClass(proxyName, clazz.getInterfaces());
        String paths = clazz.getResource(".").getPath();
        System.out.println(paths);
        FileOutputStream out = null;
        try {
            //保留到硬盘中
            out = new FileOutputStream(paths + proxyName + ".class");
            out.write(classFile);
            out.flush();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                out.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

代理处理器

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.util.Date;

public class LogHandler implements InvocationHandler {
    Object target;  // 被代理的对象,实际的方法执行者

    public LogHandler(Object target) {
        this.target = target;
    }
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        before();
        Object result = method.invoke(target, args);  // 调用 target 的 method 方法
        after();
        return result;  // 返回方法的执行结果
    }
    // 调用invoke方法之前执行
    private void before() {
        System.out.println(String.format("log start time [%s] ", new Date()));
    }
    // 调用invoke方法之后执行
    private void after() {
        System.out.println(String.format("log end time [%s] ", new Date()));
    }
}

客户端

import proxy.UserService;
import proxy.UserServiceImpl;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;

public class Client2 {
    public static void main(String[] args) throws IllegalAccessException, InstantiationException {
        // 设置变量可以保存动态代理类,默认名称以 $Proxy0 格式命名
        // 通过设置环境变量sun.misc.ProxyGenerator.saveGeneratedFiles=true也可以保存代理类
        System.getProperties().setProperty("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");
        
        // 1. 创建被代理的对象,UserService接口的实现类
        UserServiceImpl userServiceImpl = new UserServiceImpl();
        // 2. 获取对应的 ClassLoader
        ClassLoader classLoader = userServiceImpl.getClass().getClassLoader();
        // 3. 获取所有接口的Class,这里的UserServiceImpl只实现了一个接口UserService,
        Class[] interfaces = userServiceImpl.getClass().getInterfaces();
        // 4. 创建一个将传给代理类的调用请求处理器,处理所有的代理对象上的方法调用
        //     这里创建的是一个自定义的日志处理器,须传入实际的执行对象 userServiceImpl
        InvocationHandler logHandler = new LogHandler(userServiceImpl);
        /*
		   5.根据上面提供的信息,创建代理对象 在这个过程中,
               a.JDK会通过根据传入的参数信息动态地在内存中创建和.class 文件等同的字节码
               b.然后根据相应的字节码转换成对应的class,
               c.然后调用newInstance()创建代理实例
		 */
        UserService proxy = (UserService) Proxy.newProxyInstance(classLoader, interfaces, logHandler);
        // 调用代理的方法
        proxy.select();
        proxy.update();
        
        // 保存JDK动态代理生成的代理类,类名保存为 UserServiceProxy
        ProxyUtils.generateClassFile(userServiceImpl.getClass(), "UserServiceProxy");
    }
}

IDEA 再次运行之后就可以在 target 的类路径下找到 UserServiceProxy.class,双击后IDEA的反编译插件会将该二进制class文件反编译

在这里插入图片描述

分析

代理类源码:

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;
import proxy.UserService;

public final class UserServiceProxy extends Proxy implements UserService {
    private static Method m1;
    private static Method m2;
    private static Method m4;
    private static Method m0;
    private static Method m3;

    public UserServiceProxy(InvocationHandler var1) throws  {
        super(var1);
    }

    public final boolean equals(Object var1) throws  {
        // 省略...
    }

    public final String toString() throws  {
        // 省略...
    }

    public final void select() throws  {
        try {
            super.h.invoke(this, m4, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    public final int hashCode() throws  {
        // 省略...
    }

    public final void update() throws  {
        try {
            super.h.invoke(this, m3, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    static {
        try {
            m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
            m2 = Class.forName("java.lang.Object").getMethod("toString");
            m4 = Class.forName("proxy.UserService").getMethod("select");
            m0 = Class.forName("java.lang.Object").getMethod("hashCode");
            m3 = Class.forName("proxy.UserService").getMethod("update");
        } catch (NoSuchMethodException var2) {
            throw new NoSuchMethodError(var2.getMessage());
        } catch (ClassNotFoundException var3) {
            throw new NoClassDefFoundError(var3.getMessage());
        }
    }
}

在这里插入图片描述

  • UserServiceProxy 继承了 Proxy 类,并且实现了被代理的所有接口,以及equals、hashCode、toString等方法
  • 由于 UserServiceProxy 继承了 Proxy 类,所以每个代理类都会关联一个 InvocationHandler 方法调用处理器
  • 类和所有方法都被 public final 修饰,所以代理类只可被使用,不可以再被继承
  • 每个方法都有一个 Method 对象来描述,Method 对象在static静态代码块中创建,以 m + 数字 的格式命名
  • 调用方法的时候通过 super.h.invoke(this, m1, (Object[])null); 调用,其中的 super.h.invoke 实际上是在创建代理的时候传递给 Proxy.newProxyInstance 的 LogHandler 对象,它继承 InvocationHandler 类,负责实际的调用处理逻辑

应用

1、自定义注解

使用自定义注解给不同的接口增加权限

@permission("添加分类")
/*添加分类*/ void addCategory(Category category);

/*查找分类*/
void findCategory(String id);

@permission("查找分类")
/*查看分类*/ List<Category> getAllCategory();

返回一个代理的Service对象来处理自定义注解:

public class ServiceDaoFactory {

    private static final ServiceDaoFactory factory = new ServiceDaoFactory();

    private ServiceDaoFactory() {
    }

    public static ServiceDaoFactory getInstance() {
        return factory;
    }

    //需要判断该用户是否有权限
    public <T> T createDao(String className, Class<T> clazz, final User user) {
        try {
            //得到该类的实例
            final T t = (T) Class.forName(className).newInstance();
            //返回一个动态代理对象出去
            return (T) Proxy.newProxyInstance(ServiceDaoFactory.class.getClassLoader(), 
                                              t.getClass().getInterfaces(), 
                                              new InvocationHandler() {
                @Override
                public Object invoke(Object proxy, Method method, Object[] args) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException, PrivilegeException {
                    //得到用户调用的真实方法
                    Method method1 = t.getClass().getMethod(method.getName(),method.getParameterTypes());

                    //查看方法上有没有注解
                    permission permis = method1.getAnnotation(permission.class);

                    //如果注解为空,那么表示该方法并不需要权限,直接调用方法即可
                    if (permis == null) {
                        return method.invoke(t, args);
                    }

                    //如果注解不为空,得到注解上的权限
                    String privilege = permis.value();

                    //设置权限【后面通过它来判断用户的权限有没有自己】
                    Privilege p = new Privilege();
                    p.setName(privilege);

                    //到这里的时候,已经是需要权限了,那么判断用户是否登陆了
                    if (user == null) {
                        //抛出的异常是代理对象抛出的,sun公司会自动转换成运行期异常抛出,于是在Servlet上可以根据getCause()来判断是不是该异常,从而做出相对应的提示。
                        throw new PrivilegeException("对不起请先登陆");
                    }

                    //执行到这里用户已经登陆了,判断用户有没有权限
                    Method m = t.getClass().getMethod("findUserPrivilege", String.class);
                    List<Privilege> list = (List<Privilege>) m.invoke(t, user.getId());

                    //看下权限集合中有没有包含方法需要的权限。使用contains方法,在Privilege对象中需要重写hashCode和equals()
                    if (!list.contains(p)) {
                        //抛出的异常是代理对象抛出的,sun公司会自动转换成运行期异常抛出,于是在Servlet上我们根据getCause()来判断是不是该异常,从而做出相对应的提示。
                        throw new PrivilegeException("您没有权限,请联系管理员!");
                    }

                    //执行到这里的时候,已经有权限了,所以可以放行了
                    return method.invoke(t, args);
                }
            });
        } catch (Exception e) {
            new RuntimeException(e);
        }
        return null;
    }
}

2、日志监控

JDK动态代理实现

public class LogProxy {
    /**
     * 生成对象的代理对象,对被代理对象进行所有方法日志增强
     * 参数:原始对象
     * 返回值:被代理的对象
     *
     * 实现方式:JDK 动态代理
     * 注意:被代理的对象需实现至少一个接口,而且只能对 实现的接口中的方法 进行代理
     */
    public static Object getObject(final Object obj){
        Object proxyInstance = Proxy.newProxyInstance(obj.getClass().getClassLoader(),
                                                      obj.getClass().getInterfaces(), 
                                                      new InvocationHandler() {
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                //方法执行前
                long startTime = System.currentTimeMillis();

                //执行方法的调用
                Object result = method.invoke(obj, args);

                //方法执行后
                long endTime = System.currentTimeMillis();
                
                //打印时间
                SimpleDateFormat sdf = new SimpleDateFormat();
                System.out.printf(String.format("%s方法执行结束时间:%%s ;方法执行耗时:%%d%%n", method.getName()), 
                                  sdf.format(endTime),
                                  endTime - startTime
                                 );
                return result;
            }
        });
        return proxyInstance;
    }
}

CGLIB动态代理实现

public class LogProxy {
    /**
     * 生成对象的代理对象,对被代理对象进行所有方法日志增强
     * 参数:原始对象
     * 返回值:被代理的对象
     *
     * 实现方式:CGLib 动态代理
     * 注意:被代理对象不能用final修饰
     */
    public static Object getObjectByCGLib(final Object obj){
        /**
         * 使用CGLib的Enhancer创建代理对象
         * 参数一:对象的字节码文件
         * 参数二:方法的拦截器
         */
        Object proxyObj = Enhancer.create(obj.getClass(), 
                                          new MethodInterceptor() {
            public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
                //方法执行前
                long startTime = System.currentTimeMillis();

                //执行方法的调用
                Object invokeObject = method.invoke(obj, objects);

                //方法执行后
                long endTime = System.currentTimeMillis();
                SimpleDateFormat sdf = new SimpleDateFormat();
                System.out.printf(String.format("%s方法执行结束时间:%%s ;方法执行耗时:%%d%%n", method.getName()), 
                                  sdf.format(endTime), 
                                  endTime - startTime
                                 );
                return invokeObject;
            }
        });
        return proxyObj;
    }
}

案例

1、Spring Aop

Spring AOP是利用的动态代理机制对目标函数做了扩展,核心处理逻辑由目标函数处理

  • 如果一个Bean实现了接口,那么就会采用JDK动态代理来生成该接口的代理对象
  • 如果一个Bean没有实现接口,那么就会采用CGLIB来生成当前类的一个代理对象

在Spring项目中用的注解,例如依赖注入的@Bean、@Autowired,事务注解@Transactional等都有用到动态代理,即Srping的AOP(切面编程)

参考

动态代理 - 廖雪峰的官方网站 (liaoxuefeng.com)

你真的完全了解Java动态代理吗?看这篇就够了 - 简书 (jianshu.com)

Java 动态代理详解 - 掘金 (juejin.cn)

java动态代理中的invoke方法是如何被自动调用的_invoke0方法是如何映射到动态代理的-优快云博客

java设计模式:动态代理Dynamic_Proxy详解 (使用场景:事务、日志、监控等)_java dynamic-优快云博客

深入分析JDK动态代理为什么只能使用接口_技术派的博客-优快云博客

深入理解cglib动态代理 - 知乎 (zhihu.com)

CGLIB介绍与原理(通过继承的动态代理) - linghu_java - 博客园 (cnblogs.com)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值