Java 代理:深入理解与实际应用

🧑 博主简介:历代文学网(PC端可以访问:https://literature.sinhy.com/#/literature?__c=1000,移动端可微信小程序搜索“历代文学”)总架构师,15年工作经验,精通Java编程高并发设计Springboot和微服务,熟悉LinuxESXI虚拟化以及云原生Docker和K8s,热衷于探索科技的边界,并将理论知识转化为实际应用。保持对新技术的好奇心,乐于分享所学,希望通过我的实践经历和见解,启发他人的创新思维。在这里,我希望能与志同道合的朋友交流探讨,共同进步,一起在技术的世界里不断学习成长。

在这里插入图片描述


在这里插入图片描述

Java 代理:深入理解与实际应用

在 Java 开发中,代理模式是一种非常重要的设计模式。它可以在不修改目标对象代码的情况下,为目标对象添加额外的功能。本文将深入探讨 Java 代理中的静态代理与动态代理,包括常见的动态代理实现 JDK ProxyCGLIB,以及它们之间的对比和在 Spring AOP 中的实际应用。

一、静态代理

(一)概念介绍

静态代理是由程序员创建或工具生成代理类的代码,再对其编译。在程序运行前,代理类的.class 文件就已经存在了。

静态代理中,代理类与目标对象实现相同的接口,并且在代理类中持有一个目标对象的引用。当客户端调用代理对象的方法时,代理对象会将请求转发给目标对象,并在转发前后添加一些额外的功能,比如日志记录、性能统计等。

(二)代码示例及注释

以下是一个静态代理的示例代码:

// 定义接口
interface Subject {
    void doSomething();
}

// 目标对象实现接口
class RealSubject implements Subject {
    @Override
    public void doSomething() {
        System.out.println("目标对象的方法被调用");
    }
}

// 代理对象实现接口
class StaticProxy implements Subject {
    private Subject target;

    public StaticProxy(Subject target) {
        this.target = target;
    }

    @Override
    public void doSomething() {
        System.out.println("在调用目标对象方法前执行一些额外操作");
        target.doSomething();
        System.out.println("在调用目标对象方法后执行一些额外操作");
    }
}

在上述代码中,Subject是一个接口,RealSubject是目标对象,实现了Subject接口。StaticProxy是代理对象,也实现了Subject接口,并在代理对象中持有一个目标对象的引用。当客户端调用代理对象的doSomething方法时,代理对象会先执行一些额外的操作,然后调用目标对象的doSomething方法,最后再执行一些额外的操作。

二、动态代理

(一)概念介绍

动态代理是在程序运行时,通过 JDK 反射机制或 CGLIB 等工具动态生成代理类的字节码,并加载到 JVM 中。与静态代理不同,动态代理不需要为每个目标对象都创建一个代理类,而是可以根据需要动态地创建代理对象。

动态代理可以实现对目标对象的方法进行拦截和增强,而不需要修改目标对象的代码。它通常用于实现 AOP(面向切面编程),可以在不修改业务代码的情况下,为业务方法添加日志记录、性能统计、事务管理等功能。

(二)常见的动态代理实现 JDK Proxy

  1. JDK Proxy 的工作原理

    • JDK 动态代理是基于接口实现的。它使用 Java 的反射机制在运行时创建代理对象。代理对象实现了与目标对象相同的接口,并在代理对象的方法调用中,通过反射机制调用 InvocationHandler 的 invoke 方法来实现对目标对象方法的拦截和增强。
  2. 代码示例及注释

    • 以下是一个使用 JDK Proxy 的示例代码:
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

// 定义接口
interface Subject {
    void doSomething();
}

// 目标对象实现接口
class RealSubject implements Subject {
    @Override
    public void doSomething() {
        System.out.println("目标对象的方法被调用");
    }
}

// 代理对象的调用处理器
class DynamicProxyHandler implements InvocationHandler {
    private Object target;

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

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("在调用目标对象方法前执行一些额外操作");
        Object result = method.invoke(target, args);
        System.out.println("在调用目标对象方法后执行一些额外操作");
        return result;
    }
}

在上述代码中,首先定义了一个接口Subject和一个实现了该接口的目标对象RealSubject。然后定义了一个代理对象的调用处理器DynamicProxyHandler,它实现了InvocationHandler接口。在DynamicProxyHandlerinvoke方法中,可以实现对目标对象方法的拦截和增强。最后,使用Proxy.newProxyInstance方法创建代理对象,并将代理对象转换为Subject接口类型进行调用。

(三)CGLIB

  1. CGLIB 的工作原理

    • CGLIB(Code Generation Library)是一个强大的代码生成库。它通过继承目标对象来创建代理对象。CGLIB 使用字节码生成技术在运行时生成目标对象的子类,并在子类中重写目标对象的方法,从而实现对目标对象方法的拦截和增强。
  2. 代码示例及注释

    • 以下是一个使用 CGLIB 的示例代码:
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;

class RealSubject {
    public void doSomething() {
        System.out.println("目标对象的方法被调用");
    }
}

class CglibProxy implements MethodInterceptor {
    private Object target;

    public Object getProxyInstance(Object target) {
        this.target = target;
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(target.getClass());
        enhancer.setCallback(this);
        return enhancer.create();
    }

    @Override
    public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
        System.out.println("在调用目标对象方法前执行一些额外操作");
        Object result = method.invoke(target, args);
        System.out.println("在调用目标对象方法后执行一些额外操作");
        return result;
    }
}

在上述代码中,首先定义了一个目标对象RealSubject。然后定义了一个代理对象CglibProxy,它实现了MethodInterceptor接口。在CglibProxyintercept方法中,可以实现对目标对象方法的拦截和增强。最后,使用Enhancer类创建代理对象,并将代理对象转换为目标对象的类型进行调用。

三、JDK Proxy 和 CGLIB 的对比

(一)实现方式

  • JDK Proxy 是基于接口实现的,它要求目标对象必须实现一个或多个接口。
  • CGLIB 是通过继承目标对象来实现的,它不要求目标对象实现任何接口。

(二)性能

  • 在 JDK 1.8 及以上版本中,JDK Proxy 的性能与 CGLIB 相当。在某些情况下,JDK Proxy 甚至可能比 CGLIB 更快,因为它不需要生成目标对象的子类。
  • CGLIB 在生成代理对象时需要进行字节码生成,因此在性能上可能会有一些开销。但是,CGLIB 可以对没有实现接口的目标对象进行代理,这在某些情况下可能会更加灵活。

(三)使用场景

  • 如果目标对象实现了接口,并且对性能要求不是非常高,可以使用 JDK Proxy。
  • 如果目标对象没有实现接口,或者需要对目标对象的方法进行深度定制,可以使用 CGLIB。

四、动态代理在Spring AOP的应用

(一)Spring AOP 简介

Spring AOP(Aspect-Oriented Programming)是一种基于 Spring 框架的面向切面编程实现。它使用动态代理技术,在不修改业务代码的情况下,为业务方法添加日志记录、性能统计、事务管理等功能。

Spring AOP 主要由切面(Aspect)、通知(Advice)和切点(Pointcut)组成。切面是一个模块化的横切关注点,它包含了一个或多个通知。通知是在切点处执行的增强代码,比如前置通知、后置通知、环绕通知等。切点是一个表达式,用于指定在哪些地方应用切面。

(二)代码示例及注释

以下是一个使用 Spring AOP 实现日志记录的示例代码:

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

@Aspect
@Component
public class LoggingAspect {
    private static final Logger logger = LoggerFactory.getLogger(LoggingAspect.class);

    // 前置通知,在目标方法执行前执行
    @Before("execution(* com.example.demo.service.*.*(..))")
    public void beforeMethod(JoinPoint joinPoint) {
        logger.info("Before method: " + joinPoint.getSignature().getName());
    }

    // 环绕通知,在目标方法执行前后执行
    @Around("execution(* com.example.demo.service.*.*(..))")
    public Object aroundMethod(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
        logger.info("Before executing method: " + proceedingJoinPoint.getSignature().getName());
        Object result = proceedingJoinPoint.proceed();
        logger.info("After executing method: " + proceedingJoinPoint.getSignature().getName());
        return result;
    }
}

在上述代码中,定义了一个切面类LoggingAspect,它使用@Aspect注解标识为一个切面。在切面类中,定义了两个通知方法:beforeMethodaroundMethodbeforeMethod是前置通知,在目标方法执行前执行;aroundMethod是环绕通知,在目标方法执行前后执行。通知方法使用@Before@Around注解指定切点表达式,用于指定在哪些地方应用通知。

五、总结

本文深入探讨了 Java 代理中的静态代理与动态代理,包括常见的动态代理实现 JDK ProxyCGLIB,以及它们之间的对比和在 Spring AOP 中的实际应用。静态代理需要为每个目标对象都创建一个代理类,代码冗余度高,维护成本大。动态代理可以在运行时动态地创建代理对象,更加灵活和高效。JDK Proxy 是基于接口实现的,而 CGLIB 是通过继承目标对象来实现的。在实际应用中,可以根据具体情况选择合适的代理方式。Spring AOP 是一种基于动态代理的面向切面编程实现,它可以在不修改业务代码的情况下,为业务方法添加额外的功能,提高了代码的可维护性和可扩展性。

六、参考资料文献

  1. Effective Java
  2. Spring in Action
  3. 《Java 核心技术》
  4. 官方 Java 文档https://docs.oracle.com/javase/8/docs/api/
  5. Spring 官方文档https://docs.spring.io/spring/docs/current/spring-framework-reference/index.html
评论 9
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

码踏云端

你的打赏是我精心创作的动力!

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值