JDK动态代理与CGLib动态代理

一、什么是动态代理?

动态代理(Dynamic Proxy)是在程序运行时给目标对象创建代理对象,代理对象可以对方法进行增强、控制等。在生成代理对象的过程中,目标对象不变,代理对象中方法对目标对象方法进行增强。代理对象执行目标方法时会被拦截,转而调用写好的代理方法。

动态代理的实现方式有两种:JDK代理、CGLib代理

二、JDK动态代理

特点:基于 接口 实现(implement)的动态代理,即 生成的代理对象必须实现指定的接口

生成代理对象方法Proxy类中的静态方法newProxyInstance

public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces,InvocationHandler h));
  • ClassLoader loader:要代理对象的类加载器

  • Class<?>[] interfaces:代理对象实现的接口

  • InvocationHandler h:调用处理器,代理对象每次执行方法都会被拦截,将相关信息传递给调用处理器的invoke方法

特别注意在调用处理器的 invoke方法中尽量别调用代理对象的任何方法 ,因为在任何地方执行代理对象的方法都会被拦截,然后将调用信息传递给调用处理器的invoke方法,在内部调用代理对象的方法,很容易造成无限递归(包括不要打印代理对象,因为会隐式调用代理对象的toString方法)

实现jdk动态代理核心步骤:

1)定义任务处理器,实现 InvocationHandler 接口,实现invoke方法

2)通过 Proxy.newProxyInstance(...) 创建代理对象

具体jdk动态代理实现:

① 声明接口类

import java.util.List;

public interface IUserService {
    /* 获取所有用户的名字*/
    List<String> getUserNames() ;
}

② 定义实现类(即将被代理的类)

import java.util.Arrays;
import java.util.List;

public class UserServiceImpl implements IUserService{
    @Override
    public List<String> getUserNames() {
        return Arrays.asList("超人强", "GGB", "小呆呆");
    }
}

③ 实现调用处理器(定义代理行为)

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

public class UserInvocationHandler implements InvocationHandler {
    //  目标对象
    private Object target;
    //    接收目标对象
    public UserInvocationHandler(Object target) {
        this.target = target;
    }
    
    /**
     * @param proxy  代理对象
     * @param method 执行方法
     * @param args   方法参数
     * @return  返回方法的执行结果
     */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        //    ======     !!! 尽量不要再该方法中调用proxy对象的方法,不然很容易无限递归 !!!    ======
        //  1.记录方法的开始时间
        long startTime = System.currentTimeMillis();
        //  2.执行方法
        Object result = method.invoke(target, args);
        //  3.记录方法的结束时间
        long endTime = System.currentTimeMillis();
        //  4.打印方法耗时
        System.out.printf("======  %s 方法执行耗时:%d ms ======\n", method.getName(), endTime- startTime);
        //    5.返回方法的执行结果
        return result;
    }
}

④ 创建代理对象,调用代理方法

import java.lang.reflect.Proxy;
import java.util.List;

public class Main {

    public static void main(String[] args) {
        //  生成代理对象
        IUserService userService = (IUserService) Proxy.newProxyInstance(
                        UserServiceImpl.class.getClassLoader(), //  需要代理对象类的加载器
                        UserServiceImpl.class.getInterfaces(),  //  代理类实现的接口
                        new UserInvocationHandler(new UserServiceImpl())    //  调用处理器
                );
        //  通过代理对象执行方法
        List<String> userNames = userService.getUserNames();
        System.out.println(userNames);
    }
}

执行结果:

======  getUserNames 方法执行耗时:0 ms ======
[超人强, GGB, 小呆呆]
 
进程已结束,退出代码为 0    

三、CGLib动态代理

特点:基于类 继承(extend)的动态代理,即代理类必须实现目标类,无需提前写好接口,属于第三方依赖,需要导入

工作原理

  1. 生成子类:CGLIB通过字节码技术在运行时生成目标类的子类(代理类)。

  2. 方法拦截:在生成的子类中,方法可以被重写以添加额外的逻辑,例如前置处理、后置处理等,这些逻辑组成了代理的行为。

  3. 创建代理对象:通过创建代理类对象,得到一个与目标对象有相同的方法名的对象

导入CGLib依赖

方式一:maven导入

<dependency>
	<groupId>cglib</groupId>
	<artifactId>cglib</artifactId>
	<version>3.3.0</version>
</dependency>

方式二:从远程仓库下载

下载地址:Maven Repository: cglib » cglib (mvnrepository.com)

生成代理对象方法Enhancer类中的静态方法create

public static Object create(Class type, Callback callback);
  • Class type:要代理类的Class对象

  • Callback callback:代理类实现行为的接口,其主要实现是MethodInterceptor

实现CGLib动态代理的核心步骤:

1)定义代理类的行为,实现方法拦截器MethodInterceptor,实现其中的intercept方法

2)通过Enhancer.create(...)创建代理对象

具体GGLib动态代理实现:

① 定义目标类

public class Person {

    public void marry() {
        System.out.println("Person.marry()+++");
    }
}

② 实现方法拦截器(定义代理行为)

intercept方法介绍

public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy);
  • Object proxy:代理对象,与jdk代理逻辑类似,不要在intercept方法内部直接调用代理对象的方法,不然容易形成无限递归

  • Method method:要执行的方法

  • Object[] args:方法参数

  • MethodProxy methodProxy:方法的代理,这个类中重点关注两个方法:

    • invoke(Object obj, Object[] args)

      与Method中的invoke方法效果一样,都是执行特定对象中的方法

    • invokeSuper(Object obj, Object[] args)

      执行obj父类对象中的方法,这里可以将代理对象proxy传递过去,这样就会直接执行proxy父类中的同名方法,

      invokeSuper(proxy, args) 等价于 ==> invoke(target, args) ,target的需要被代理的对象

方法拦截器具体实现

import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
/*
    代理对象方法拦截器
 */
public class ProcedureHandler implements MethodInterceptor {

    //  目标对象
    private Object target;

    public ProcedureHandler(Object target) {
        this.target = target;
    }

    /**
     * @param proxy 代理对象
     * @param method  调用方法
     * @param args  方法参数
     * @param methodProxy   代理方法
     * @return  调用方法的返回值
     * @throws Throwable
     */
    @Override
    public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
        //  --- 记录开始时间
        long startTime = System.currentTimeMillis();
        System.out.println("方法开始时间:" + startTime);

        //  ======   执行方法,这里方便对比,将方法执行了三遍  ======
        //      执行当前类的当前方法,以下两行代码效果相同
        Object result = method.invoke(target, args);		
        Object result2 = methodProxy.invoke(target, args);
        //      执行父类同名方法
        Object result3 = methodProxy.invokeSuper(proxy, args);

        //  --- 记录结束时间
        long endTime = System.currentTimeMillis();
        System.out.println("方法开始时间:" + endTime);

        //  --  打印方法耗时
        System.out.printf("====== 方法总耗时:%d ms ======\n", endTime - startTime);
        return result;
    }
}

③ 创建代理对象,调用代理方法

import net.sf.cglib.proxy.*;

public class Main {
    public static void main(String[] args) {
        Person person = (Person) Enhancer.create(Person.class, new ProcedureHandler(new Person()));
        person.marry();
    }
}

执行结果:

方法开始时间:1722225835558
Person.marry()+++
Person.marry()+++
Person.marry()+++
方法开始时间:1722225835568
====== 方法总耗时:10 ms ======

进程已结束,退出代码为 0

动态代理使用推荐

当有代理类的接口时,可以选择jdk动态代理;没有接口就用CGLib动态代理,毕竟CGLib动态代理太好用了

四、问题解决

我这里使用的是jdk8版本,如果大于8可能会出现一些反射相关的异常(由于反射需要大量性能,所以一些使用频率不高的方法就被关闭了,所以从 JDK1.8 后大量包的可访问反射属性都被关闭了,lang 就是其中之一。)。

如:“module java.base does not “opens java.lang“ to unnamed module”。

只需要在启动虚拟机选项中加上以下参数即可:

--add-opens java.base/java.lang=ALL-UNNAMED

  • 47
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
JDK动态代理CGLib动态代理都是实现AOP编程的方式,它们的使用上有以下异同点: 1. JDK动态代理只能代理实现了接口的类,而CGLib动态代理可以代理没有实现接口的类。 2. JDK动态代理是通过反射来实现的,而CGLib动态代理使用的是继承。 3. JDK动态代理在生成代理对象时,需要传入一个InvocationHandler对象,而CGLib动态代理在生成代理对象时,需要继承MethodInterceptor类,并重写intercept()方法。 4. JDK动态代理生成的代理对象性能比CGLib动态代理生成的代理对象性能要低。 下面是一个JDK动态代理的例子: ```python import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; interface Subject { void request(); } class RealSubject implements Subject { public void request() { System.out.println("RealSubject request"); } } class DynamicProxy implements InvocationHandler { private Object subject; public DynamicProxy(Object subject) { this.subject = subject; } public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("before calling " + method); method.invoke(subject, args); System.out.println("after calling " + method); return null; } } public class Main { public static void main(String[] args) { RealSubject realSubject = new RealSubject(); InvocationHandler handler = new DynamicProxy(realSubject); Subject subject = (Subject) Proxy.newProxyInstance(handler.getClass().getClassLoader(), realSubject.getClass().getInterfaces(), handler); subject.request(); } } ``` 下面是一个CGLib动态代理的例子: ```python import net.sf.cglib.proxy.Enhancer; import net.sf.cglib.proxy.MethodInterceptor; import net.sf.cglib.proxy.MethodProxy; class RealSubject { public void request() { System.out.println("RealSubject request"); } } class DynamicProxy implements MethodInterceptor { private Object subject; public DynamicProxy(Object subject) { this.subject = subject; } public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable { System.out.println("before calling " + method); proxy.invoke(subject, args); System.out.println("after calling " + method); return null; } } public class Main { public static void main(String[] args) { Enhancer enhancer = new Enhancer(); enhancer.setSuperclass(RealSubject.class); enhancer.setCallback(new DynamicProxy(new RealSubject())); RealSubject subject = (RealSubject) enhancer.create(); subject.request(); } } ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值