Java中的代理模式与CGLIB的应用!

举报
喵手 发表于 2025/07/18 21:37:21 2025/07/18
【摘要】 开篇语哈喽,各位小伙伴们,你们好呀,我是喵手。运营社区:C站/掘金/腾讯云/阿里云/华为云/51CTO;欢迎大家常来逛逛  今天我要给大家分享一些自己日常学习到的一些知识点,并以文字的形式跟大家一起交流,互相学习,一个人虽可以走的更快,但一群人可以走的更远。  我是一名后端开发爱好者,工作日常接触到最多的就是Java语言啦,所以我都尽量抽业余时间把自己所学到所会的,通过文章的形式进行输出,...

开篇语

哈喽,各位小伙伴们,你们好呀,我是喵手。运营社区:C站/掘金/腾讯云/阿里云/华为云/51CTO;欢迎大家常来逛逛

  今天我要给大家分享一些自己日常学习到的一些知识点,并以文字的形式跟大家一起交流,互相学习,一个人虽可以走的更快,但一群人可以走的更远。

  我是一名后端开发爱好者,工作日常接触到最多的就是Java语言啦,所以我都尽量抽业余时间把自己所学到所会的,通过文章的形式进行输出,希望以这种方式帮助到更多的初学者或者想入门的小伙伴们,同时也能对自己的技术进行沉淀,加以复盘,查缺补漏。

小伙伴们在批阅的过程中,如果觉得文章不错,欢迎点赞、收藏、关注哦。三连即是对作者我写作道路上最好的鼓励与支持!

前序

在软件设计中,代理模式是一种结构型设计模式,旨在通过代理对象来控制对目标对象的访问。代理模式通常被用于为目标对象提供某种功能增强,例如延迟加载、访问控制、日志记录、性能监控等。在Java中,代理模式的应用非常广泛,尤其是在实现面向切面编程(AOP)缓存日志框架等功能时。

Java提供了多种方式来实现代理模式,包括静态代理动态代理。其中,CGLIB(Code Generation Library)是一种常用的字节码生成库,它提供了一种基于字节码操作的动态代理方式,可以通过继承目标类的方式生成代理对象。在很多框架中,CGLIB被广泛用于实现代理和AOP功能,尤其是Spring框架中。

本文将深入探讨Java中的代理模式,包括静态代理、动态代理,以及CGLIB的应用。我们将通过具体的案例分析,展示如何使用这些技术来解决常见的开发问题,如日志记录、性能监控和缓存等。

前言

代理模式是一种常用的设计模式,它通过为对象提供一个代理来控制对目标对象的访问。代理模式有很多应用场景,例如日志记录、缓存管理、权限控制等。在Java中,代理模式的实现可以通过静态代理和动态代理两种方式来实现。静态代理通常通过手动创建代理类来完成,而动态代理则通过反射机制在运行时动态创建代理对象,极大地提高了灵活性。

在实际开发中,CGLIB是一种常用的字节码生成技术,它提供了一种基于继承的动态代理方式。CGLIB的优势在于,它不需要目标对象实现接口,因此在没有接口的情况下也可以创建代理对象。

本文将通过对代理模式的深入剖析,帮助开发者理解如何在Java中有效应用代理模式,提升代码的灵活性、可维护性和扩展性。

代理模式的基本概念

1. 代理模式的定义

代理模式(Proxy Pattern)是一种结构型设计模式,它为其他对象提供一种代理以控制对该对象的访问。代理对象可以在访问目标对象之前或之后执行一些额外的操作,如权限检查、缓存、延迟加载等。代理模式有两种主要形式:

  • 静态代理:通过手动创建代理类来实现目标对象的代理。静态代理的缺点是代理类和目标类之间紧密耦合,且需要为每个目标类创建代理类,维护成本较高。
  • 动态代理:在运行时动态生成代理类,通常利用反射或字节码生成技术实现。动态代理能够解耦目标对象和代理对象,提供更大的灵活性。

2. 代理模式的应用场景

  • AOP(面向切面编程):通过代理模式来增强目标对象的功能,常用于日志记录、权限检查、事务管理等场景。
  • 缓存:代理模式可以用于缓存管理,代理对象可以在访问目标对象之前先检查缓存,避免重复计算。
  • 延迟加载:在需要时才加载目标对象的内容,常用于数据库连接、文件操作等场景,提升系统性能。

静态代理

1. 静态代理的实现

静态代理是通过在编译时创建代理类来实现的。代理类需要实现与目标类相同的接口,并在代理类中调用目标对象的方法。

示例:静态代理的代码实现

假设有一个UserService接口,它定义了addUser方法,UserServiceImpl实现了这个接口。我们创建一个UserServiceProxy代理类,用来在执行addUser方法时添加日志。

// UserService接口
public interface UserService {
    void addUser(String user);
}

// UserServiceImpl实现类
public class UserServiceImpl implements UserService {
    @Override
    public void addUser(String user) {
        System.out.println("User added: " + user);
    }
}

// UserServiceProxy代理类
public class UserServiceProxy implements UserService {
    private UserServiceImpl userService;

    public UserServiceProxy(UserServiceImpl userService) {
        this.userService = userService;
    }

    @Override
    public void addUser(String user) {
        System.out.println("Logging: addUser method is called");
        userService.addUser(user);
    }
}

使用静态代理:

public class Main {
    public static void main(String[] args) {
        UserServiceImpl userServiceImpl = new UserServiceImpl();
        UserServiceProxy proxy = new UserServiceProxy(userServiceImpl);
        proxy.addUser("John Doe");  // 输出:Logging: addUser method is called
                                    // 输出:User added: John Doe
    }
}

在这个例子中,UserServiceProxy代理类在调用addUser方法时,先打印一条日志,之后再调用实际的addUser方法。静态代理的主要问题是,如果有多个接口或者多个目标类,需要为每个接口和目标类创建一个代理类,维护成本高。

动态代理

1. 动态代理的实现

动态代理的好处在于,它不需要手动编写代理类,而是通过Java的反射机制或者字节码生成技术,在运行时动态生成代理类。Java标准库中提供了两种实现动态代理的方式:

  • JDK动态代理:要求目标类实现接口,使用java.lang.reflect.Proxy类和InvocationHandler接口来生成代理类。
  • CGLIB动态代理:基于字节码生成技术,代理类不需要目标类实现接口,可以代理没有接口的类。

JDK动态代理示例

JDK动态代理通过实现InvocationHandler接口和Proxy.newProxyInstance方法来生成代理对象。

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

public class JdkDynamicProxyExample {
    public static void main(String[] args) {
        UserService userService = new UserServiceImpl();
        UserService proxy = (UserService) Proxy.newProxyInstance(
                userService.getClass().getClassLoader(),
                userService.getClass().getInterfaces(),
                new InvocationHandler() {
                    @Override
                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                        System.out.println("Logging: " + method.getName() + " method is called");
                        return method.invoke(userService, args);
                    }
                }
        );
        proxy.addUser("Jane Doe");  // 输出:Logging: addUser method is called
                                    // 输出:User added: Jane Doe
    }
}

在这个例子中,Proxy.newProxyInstance创建了一个代理类,并通过InvocationHandler拦截addUser方法的调用,执行自定义的逻辑(如日志记录),然后调用目标类的addUser方法。

主要步骤:

  1. Proxy.newProxyInstance():创建代理对象。
  2. InvocationHandler.invoke():拦截所有方法的调用,在调用目标方法之前执行自定义逻辑。

CGLIB字节码生成与代理

1. CGLIB简介

CGLIB(Code Generation Library)是一个基于字节码的代理生成库,它通过继承目标类的方式动态生成代理类。CGLIB不要求目标类实现接口,因此可以对没有接口的类进行代理。CGLIB的优势在于能够对没有接口的类进行代理,而JDK动态代理只能代理接口。

2. CGLIB的实现

CGLIB通过继承目标类并重写其方法来实现代理。CGLIB的代理类是目标类的子类,目标类的方法在代理类中被重写,执行代理逻辑。

CGLIB代理示例

首先需要添加CGLIB依赖:

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

然后,使用CGLIB来代理目标类:

import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;

public class CglibProxyExample {
    public static void main(String[] args) {
        UserServiceImpl userServiceImpl = new UserServiceImpl();
        
        // 创建CGLIB代理对象
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(UserServiceImpl.class);
        enhancer.setCallback(new MethodInterceptor() {
            @Override
            public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
                System.out.println("Logging: " + method.getName() + " method is called");
                return proxy.invokeSuper(obj, args);  // 调用目标类的方法
            }
        });
        
        UserServiceImpl proxy = (UserServiceImpl) enhancer.create();
        proxy.addUser("Sam Smith");  // 输出:Logging: addUser method is called
                                    // 输出:User added: Sam Smith
    }
}

CGLIB的核心步骤:

  1. Enhancer.setSuperclass():设置代理类的父类。
  2. Enhancer.setCallback():设置拦截器,定义方法的拦截逻辑。
  3. enhancer.create():创建代理对象。

代理模式的应用场景

1. AOP(面向切面编程)

代理模式在AOP(面向切面编程)中应用广泛。在AOP中,代理对象用于在不修改原始类的情况下,动态地为方法添加额外功能(例如日志记录、事务管理等)。Spring AOP就是利用JDK动态代理或CGLIB代理实现的。

AOP中的代理示例:

Spring AOP使用代理模式,在目标方法前后插入额外的逻辑(如日志记录或事务处理)。

@Aspect
@Component
public class LoggingAspect {

    @Before("execution(* com.example.service.UserService.addUser(..))")
    public void logBefore(JoinPoint joinPoint) {
        System.out.println("Logging: Method " + joinPoint.getSignature().getName() + " is called");
    }
}

2. 缓存管理

代理模式可用于缓存管理。在访问目标对象时,代理对象会先检查缓存是否存在该数据,如果存在直接返回缓存中的数据;如果不存在,则调用目标对象并将结果存入缓存。

缓存管理的代理示例:

public class CacheProxy implements UserService {
    private final UserServiceImpl userService;
    private final Map<String, String> cache = new HashMap<>();

    public CacheProxy(UserServiceImpl userService) {
        this.userService = userService;
    }

    @Override
    public void addUser(String user) {
        if (!cache.containsKey(user)) {
            userService.addUser(user);
            cache.put(user, "Cached data for " + user);
        } else {
            System.out.println("Returning cached data: " + cache.get(user));
        }
    }
}

3. 日志框架

日志记录是代理模式的一个常见应用场景。通过代理,我们可以在方法调用前后自动记录日志,而无需手动在每个方法中插入日志代码。

日志记录的代理示例:

public class LogProxy implements UserService {
    private final UserServiceImpl userService;

    public LogProxy(UserServiceImpl userService) {
        this.userService = userService;
    }

    @Override
    public void addUser(String user) {
        System.out.println("Logging: Before calling addUser method");
        userService.addUser(user);
        System.out.println("Logging: After calling addUser method");
    }
}

总结

通过代理模式,Java开发者能够在不修改目标类代码的情况下,为目标对象添加额外的功能,如日志记录、权限控制、缓存管理等。静态代理和动态代理各有优缺点,静态代理需要为每个目标类手动创建代理类,而动态代理通过反射和字节码生成技术,能够在运行时生成代理类,提供更大的灵活性。CGLIB作为一种字节码生成工具,通过继承目标类的方式生成代理对象,适用于没有接口的类的代理。

代理模式在实际开发中有着广泛的应用,尤其是在AOP缓存管理日志框架等场景中。掌握代理模式的实现和应用,能够帮助开发者编写出更加灵活、可维护的代码,提高系统的扩展性和可重用性。

… …

文末

好啦,以上就是我这期的全部内容,如果有任何疑问,欢迎下方留言哦,咱们下期见。

… …

学习不分先后,知识不分多少;事无巨细,当以虚心求教;三人行,必有我师焉!!!

wished for you successed !!!


⭐️若喜欢我,就请关注我叭。

⭐️若对您有用,就请点赞叭。
⭐️若有疑问,就请评论留言告诉我叭。


版权声明:本文由作者原创,转载请注明出处,谢谢支持!

【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。