JDK动态代理 vs CGLIB:一场经纪人之战,谁才是你的最佳选择?

JDK动态代理 vs CGLIB:一场经纪人之战,谁才是你的最佳选择?_第1张图片

目录

    • 一、 啥是代理?为啥要有代理?(明星经纪人版)
    • 二、 JDK动态代理:接口的守护者(接口明星的专属经纪人)
    • 三、 CGLIB代理:类的守护者(全能经纪人)
    • 四、 JDK动态代理 vs CGLIB代理:谁更胜一筹?(经纪人PK)
    • 五、 在SpringBoot中,它们是如何应用的?(SpringBoot的智能经纪人系统)
    • 六、 总结

如果喜欢作者的讲解方式,关注作者不迷路,同时也可以看看我的其他文章! 感谢!!!
被重复代码逼疯?AOP来当“舔狗”!日志/事务/权限,随叫随到!

咱今儿个来聊聊JDK动态代理和CGLIB代理,保证你听得懂,记得住,还能用得上!

一、 啥是代理?为啥要有代理?(明星经纪人版)

想象一下,你是个超级巨星,每天行程排得满满当当,签合同、拍广告、跑通告,忙到飞起!你哪有时间事事亲力亲为?这时候,就需要一个经纪人来帮你打理一切!这个经纪人就是你的代理

  • 代理:就是代替你去做一些事情的人或事物。就像你的经纪人,帮你处理各种琐事。
  • 为啥要有代理?
    • 解耦:明星不用直接面对各种烦人的事情,可以专心提升演技,唱好歌!
    • 增强功能:经纪人可以帮你谈更高的片酬,安排更合理的行程,让你更火!
    • 控制访问:经纪人可以帮你挡掉一些不必要的骚扰,保护你的隐私️!

在编程世界里,代理也是一样的道理!它可以让你在不修改原有代码的情况下,增强对象的功能,控制对象的访问。简直是程序员的福音!

二、 JDK动态代理:接口的守护者(接口明星的专属经纪人)

JDK动态代理,就像一个只服务于有接口的明星的经纪人。它要求你的明星(也就是你的类)必须有接口,才能为你保驾护航!
JDK动态代理 vs CGLIB:一场经纪人之战,谁才是你的最佳选择?_第2张图片

  • 原理:JDK动态代理是利用Java反射机制,在运行时动态地生成一个代理类。这个代理类实现了你的接口,并且可以拦截对接口方法的调用。就像经纪人时刻关注着你的行程,随时准备为你处理突发状况!

  • 通俗易懂的解释

    1. 你定义一个接口,比如Star(明星)。就像你和经纪公司签的合同,规定了你的权利和义务。
    2. 你有一个实现了这个接口的类,比如RealStar(真正的明星)。你就是那个闪耀的明星!✨
    3. 你创建一个InvocationHandler(经纪人),它知道如何处理对Star接口方法的调用。经纪人熟知你的所有行程,知道如何帮你处理各种事务。
    4. 通过Proxy.newProxyInstance()方法,JDK会动态地生成一个代理类,这个代理类也实现了Star接口。就像经纪公司为你配备了一个专属的代理人!
    5. 当你调用代理对象的sing()方法时,实际上是调用了InvocationHandlerinvoke()方法。在这个方法里,你可以做一些额外的操作,比如记录日志、权限校验等等。经纪人会在你唱歌前后记录日志,确保一切顺利!
  • 例子

    • 简单例子:日志记录(经纪人帮你记录行程)
    // 1. 定义接口
    interface Star {
        void sing(String song);
    }
    
    // 2. 实现类
    class RealStar implements Star {
        @Override
        public void sing(String song) {
            System.out.println("RealStar singing: " + song);
        }
    }
    
    // 3. InvocationHandler
    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.Method;
    
    class StarInvocationHandler implements InvocationHandler {
        private Object target; // 被代理的对象
    
        public StarInvocationHandler(Object target) {
            this.target = target;
        }
    
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            System.out.println("Before singing, record the log."); // 增强功能:记录日志
            Object result = method.invoke(target, args); // 调用真实对象的方法
            System.out.println("After singing, record the log."); // 增强功能:记录日志
            return result;
        }
    }
    
    
    // 4. 使用代理
    import java.lang.reflect.Proxy;
    
    public class Main {
        public static void main(String[] args) {
            RealStar realStar = new RealStar();
            StarInvocationHandler handler = new StarInvocationHandler(realStar);
            Star proxy = (Star) Proxy.newProxyInstance(
                    realStar.getClass().getClassLoader(), //获取目标类对象的类加载器
                    realStar.getClass().getInterfaces(), //获取目标类对象实现的接口
                    handler   //增强功能的类
            );
            proxy.sing("Hello World"); // 调用代理对象的方法
        }
    }
    
    • 稍微复杂点的例子:权限校验(经纪人帮你挡掉不必要的邀约)
    // 假设有一个权限校验的接口
    interface PermissionChecker {
        boolean hasPermission(String user, String operation);
    }
    
    // 实现类
    class AdminPermissionChecker implements PermissionChecker {
        @Override
        public boolean hasPermission(String user, String operation) {
            // 模拟权限校验逻辑
            if ("admin".equals(user) && "delete".equals(operation)) {
                return true;
            }
            return false;
        }
    }
    
    // 修改InvocationHandler
    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.Method;
    
    class StarInvocationHandler implements InvocationHandler {
        private Object target;
        private PermissionChecker permissionChecker;
    
        public StarInvocationHandler(Object target, PermissionChecker permissionChecker) {
            this.target = target;
            this.permissionChecker = permissionChecker;
        }
    
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            // 权限校验
            if (method.getName().equals("sing")) {
                if (!permissionChecker.hasPermission("user", "sing")) {
                    System.out.println("No permission to sing!");
                    return null;
                }
            }
    
            System.out.println("Before singing, record the log.");
            Object result = method.invoke(target, args);
            System.out.println("After singing, record the log.");
            return result;
        }
    }
    
    // 使用代理
    import java.lang.reflect.Proxy;
    
    public class Main {
        public static void main(String[] args) {
            RealStar realStar = new RealStar();
            AdminPermissionChecker permissionChecker = new AdminPermissionChecker();
            StarInvocationHandler handler = new StarInvocationHandler(realStar, permissionChecker);
            Star proxy = (Star) Proxy.newProxyInstance(
                    realStar.getClass().getClassLoader(),
                    realStar.getClass().getInterfaces(),
                    handler
            );
            proxy.sing("Hello World"); // 调用代理对象的方法
        }
    }
    
  • 好处

    • 简单易用,JDK自带,不需要引入额外的依赖。就像你天生自带光环!✨
    • 基于接口,符合面向接口编程的思想。就像你和经纪公司签的合同,一切按规矩办事!
  • 缺点

    • 只能代理实现了接口的类。如果你的类没有实现接口,就没法用JDK动态代理了。就像你没有和经纪公司签约,就没法享受经纪人的服务!

三、 CGLIB代理:类的守护者(全能经纪人)

CGLIB代理,就像一个更厉害的经纪人,它不需要你的明星有接口,直接就能代理你的类!简直是全能经纪人!
JDK动态代理 vs CGLIB:一场经纪人之战,谁才是你的最佳选择?_第3张图片

  • 原理:CGLIB(Code Generation Library)是一个强大的高性能的代码生成库。它通过生成目标类的子类来实现代理。就像经纪人直接接管了你的生活,为你安排一切!️

  • 通俗易懂的解释

    1. CGLIB会创建一个目标类的子类(代理类)。就像经纪人为你打造了一个全新的形象!
    2. 这个子类会重写目标类的方法。就像经纪人为你重新设计了你的表演风格!
    3. 当你调用代理对象的方法时,实际上是调用了子类重写后的方法。就像你按照经纪人的安排,进行表演!
    4. 在重写后的方法里,你可以做一些额外的操作,比如记录日志、权限校验等等。经纪人会在你表演前后记录日志,确保一切顺利!
  • 例子(由易到难)

    • 简单例子:日志记录(经纪人帮你记录行程)
    // 1. 定义类(不需要接口)
    class Singer {
        public void sing(String song) {
            System.out.println("Singer singing: " + song);
        }
    }
    
    // 2. MethodInterceptor
    import net.sf.cglib.proxy.MethodInterceptor;
    import net.sf.cglib.proxy.MethodProxy;
    
    import java.lang.reflect.Method;
    
    class SingerMethodInterceptor implements MethodInterceptor {
        @Override
        public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
            System.out.println("Before singing, record the log."); // 增强功能:记录日志
            Object result = proxy.invokeSuper(obj, args); // 调用父类(目标类)的方法
            System.out.println("After singing, record the log."); // 增强功能:记录日志
            return result;
        }
    }
    
    // 3. 使用代理
    import net.sf.cglib.proxy.Enhancer;
    
    public class Main {
        public static void main(String[] args) {
            Enhancer enhancer = new Enhancer();
            enhancer.setSuperclass(Singer.class); // 设置父类(目标类)
            enhancer.setCallback(new SingerMethodInterceptor()); // 设置回调
            Singer proxy = (Singer) enhancer.create(); // 创建代理对象
            proxy.sing("Hello World"); // 调用代理对象的方法
        }
    }
    
    • 稍微复杂点的例子:性能监控(经纪人帮你分析表演效果)
    // 1. 定义类(不需要接口)
    class Calculator {
        public int add(int a, int b) {
            return a + b;
        }
    }
    
    // 2. MethodInterceptor
    import net.sf.cglib.proxy.MethodInterceptor;
    import net.sf.cglib.proxy.MethodProxy;
    
    import java.lang.reflect.Method;
    
    class PerformanceMonitorInterceptor implements MethodInterceptor {
        @Override
        public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
            long startTime = System.nanoTime();
            Object result = proxy.invokeSuper(obj, args);
            long endTime = System.nanoTime();
            System.out.println(method.getName() + " execution time: " + (endTime - startTime) + " ns");
            return result;
        }
    }
    
    // 3. 使用代理
    import net.sf.cglib.proxy.Enhancer;
    
    public class Main {
        public static void main(String[] args) {
            Enhancer enhancer = new Enhancer();
            enhancer.setSuperclass(Calculator.class);
            enhancer.setCallback(new PerformanceMonitorInterceptor());
            Calculator proxy = (Calculator) enhancer.create();
            proxy.add(1, 2);
        }
    }
    
  • 好处

    • 可以代理没有实现接口的类。即使你没有和经纪公司签约,也能找到全能经纪人为你服务!
  • 缺点

    • 需要引入额外的依赖(CGLIB库)。就像你需要支付经纪人一定的费用!
    • CGLIB是通过生成子类来实现代理的,所以不能代理final类,因为final类不能被继承。就像你已经和别的经纪公司签约了,就不能再找别的经纪人了!
    • CGLIB代理的效率相对JDK动态代理来说,稍微低一些(但通常可以忽略不计)。就像全能经纪人虽然厉害,但处理事情的速度可能稍微慢一点!

四、 JDK动态代理 vs CGLIB代理:谁更胜一筹?(经纪人PK)

特性 JDK动态代理 CGLIB代理
接口要求 必须实现接口 不需要实现接口
实现方式 基于反射,动态生成代理类 基于字节码生成,生成目标类的子类
依赖 JDK自带,无需额外依赖 需要引入CGLIB库
代理final类 可以代理 不能代理
效率 相对较高 相对较低(但通常可以忽略不计)

总结

  • 如果你的类实现了接口,优先选择JDK动态代理。就像你已经和经纪公司签约了,就优先选择经纪公司的服务!
  • 如果你的类没有实现接口,只能选择CGLIB代理。就像你没有和经纪公司签约,只能找全能经纪人为你服务!

五、 在SpringBoot中,它们是如何应用的?(SpringBoot的智能经纪人系统)

在SpringBoot中,AOP(面向切面编程)是代理模式的典型应用场景。SpringBoot默认使用JDK动态代理,如果目标类没有实现接口,则会自动切换到CGLIB代理。就像SpringBoot有一个智能经纪人系统,会自动为你选择合适的经纪人!

  • 例子:使用AOP记录方法执行时间(SpringBoot的智能经纪人帮你记录表演时间)

    import org.aspectj.lang.ProceedingJoinPoint;
    import org.aspectj.lang.annotation.Around;
    import org.aspectj.lang.annotation.Aspect;
    import org.springframework.stereotype.Component;
    
    @Aspect
    @Component
    public class TimeAspect {
    
        @Around("@annotation(com.example.demo.annotation.TimeLog)") // 拦截带有@TimeLog注解的方法
        public Object logExecutionTime(ProceedingJoinPoint joinPoint) throws Throwable {
            long start = System.currentTimeMillis();
            Object proceed = joinPoint.proceed();
            long executionTime = System.currentTimeMillis() - start;
            System.out.println(joinPoint.getSignature() + " executed in " + executionTime + "ms");
            return proceed;
        }
    }
    
    import java.lang.annotation.ElementType;
    import java.lang.annotation.Retention;
    import java.lang.annotation.RetentionPolicy;
    import java.lang.annotation.Target;
    
    @Retention(RetentionPolicy.RUNTIME)
    @Target(ElementType.METHOD)
    public @interface TimeLog {
    }
    
    import org.springframework.stereotype.Service;
    
    @Service
    public class MyService {
    
        @TimeLog
        public void doSomething() {
            try {
                Thread.sleep(1000); // 模拟耗时操作
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
    

    在这个例子中,TimeAspect就是一个切面,它使用@Around注解来拦截带有@TimeLog注解的方法,并在方法执行前后记录时间。SpringBoot会自动使用代理模式来实现AOP,如果MyService实现了接口,则使用JDK动态代理,否则使用CGLIB代理。就像SpringBoot的智能经纪人系统会自动为你选择合适的经纪人,并帮你记录表演时间!⏱️

六、 总结

JDK动态代理和CGLIB代理都是代理模式的实现方式,它们可以让你在不修改原有代码的情况下,增强对象的功能,控制对象的访问。选择哪种代理方式,取决于你的类的具体情况。在SpringBoot中,AOP是代理模式的典型应用场景,SpringBoot会自动选择合适的代理方式来实现AOP。

希望这篇丰富、生动有趣的文章讲解能让你彻底理解JDK动态代理和CGLIB代理! 祝你早日成为编程界的超级巨星!

你可能感兴趣的:(SpringBoot,java,开发语言)