专业的事情,交给专业的人去干,你只管好好上班 —— 代理模式

大家好,我是徒手敲代码。

今天来介绍一下代理模式。

在日常生活中,当需要完成一项专业性强、涉及复杂流程的任务时,比如:买房,我们会选择委托给专业人士------房地产中介。这个中间人扮演的就是代理角色。他凭借专业知识,帮我们处理各种细节,确保任务顺利完成。但是如果我们省去这些代理,直接与多方交涉,可能会面临信息不对称、流程不熟等问题。

像这种专业的事情,交给专业的人去做的原则,就相当于软件范畴中的代理模式。

假设有一个UserService接口,定义了新增用户insertUser()和更新用户updateUser()的方法。实现类UserServiceImpl,实现了这个接口。

java 复制代码
public interface UserService {
    void insertUser(User user);
    void updateUser(User user);
}

public class UserServiceImpl implements UserService {
    @Override
    public void insertUser(User user) {
        // 实现用户插入逻辑
    }

    @Override
    public void updateUser(User user) {
        // 实现用户更新逻辑
    }

如果我们需要在调用insertUser()updateUser()前打印日志,并且不使用代理模式,直接在方法里面加其他逻辑,就变成这样:

typescript 复制代码
public class UserServiceImpl implements UserService {
    @Override
    public void insertUser(User user) {
        log("Inserting user...");
        // 实现用户插入逻辑 
    }

    @Override
    public void updateUser(User user) {
        log("Updating user...");
        // 实现用户更新逻辑
    }

    private void log(String message) {
        // 打印日志的具体实现
    }
}

过了两天,如果又要在那两个方法前,加个事务,那么原来的方法又要再改一次。虽然这样可以实现功能,但是随着时间的推移,这段代码会越来越复杂。显然,是不符合对扩展开放,对修改关闭这个原则。

这个问题的解决方案是,可以加多一个代理类,负责做业务范畴以外的逻辑。比如,创建一个UserServiceProxy类,同样实现UserService接口,并持有一个UserService对象。代理类负责封装日志和事务管理逻辑,将原本直接对UserServiceImpl的操作转交给代理类

typescript 复制代码
public class UserServiceProxy implements UserService {
    private final UserService target;

    public UserServiceProxy(UserService target) {
        this.target = target;
    }

    @Override
    public void insertUser(User user) {
        beginTransaction();
        log("Inserting user...");
        target.insertUser(user);
        commitTransaction();
    }

    @Override
    public void updateUser(User user) {
        beginTransaction();
        log("Updating user...");
        target.updateUser(user);
        commitTransaction();
    }

    private void log(String message) {...}
    private void beginTransaction() {...}
    private void commitTransaction() {...}
    private void rollbackTransaction() {...}
}

// 使用代理类
UserService userService = new UserServiceProxy(new UserServiceImpl());
userService.insertUser(someUser);

像这种预先定义好代理类结构,并实现相关逻辑的模式,称为静态代理

显然,这种方式有个缺点,就是当需要为很多个类添加相同额外操作的时候,就要对每个类都写一个代理类,会出现大量重复的代码。

此时此刻,动态代理就诞生了。

Java提供了两种实现动态代理的方式:基于**InvocationHandler接口的JDK原生动态代理和基于CGLIB**库的字节码生成技术。

基于接口的动态代理:

typescript 复制代码
public class LoggingAndTransactionalProxy implements InvocationHandler {
    private final Object target;

    public static <T> T createProxy(T target) {
        return (T) Proxy.newProxyInstance(
                target.getClass().getClassLoader(),
                target.getClass().getInterfaces(),
                new LoggingAndTransactionalProxy(target)
        );
    }

    private LoggingAndTransactionalProxy(Object target) {
        this.target = target;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        beginTransaction();
        try {
            log(method.getName() + " invoked...");
            Object result = method.invoke(target, args);
            commitTransaction();
            return result;
        } catch (Exception e) {
            rollbackTransaction();
            log(method.getName() + " failed.");
            throw e;
        }
    }

    private void log(String message) {...}
    private void beginTransaction() {...}
    private void commitTransaction() {...}
    private void rollbackTransaction() {...}
}

// 使用动态代理
UserService userService = LoggingAndTransactionalProxy.createProxy(new UserServiceImpl());
userService.insertUser(someUser);

注意:这种方式,必须要求构造参数的入参对象类型,实现了一个或多个接口。

CGLIB动态代理

typescript 复制代码
public class LoggingAndTransactionalInterceptor implements MethodInterceptor {
    @Override
    public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
        beginTransaction();
        try {
            log(method.getName() + " invoked...");
            Object result = proxy.invokeSuper(obj, args);
            commitTransaction();
            return result;
        } catch (Exception e) {
            rollbackTransaction();
            log(method.getName() + " failed.");
        }
    }

    private void log(String message) {...}
    private void beginTransaction() {...}
    private void commitTransaction() {...}
    private void rollbackTransaction() {...}

    public static <T> T createProxy(T target) {
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(target.getClass());
        enhancer.setCallback(new LoggingAndTransactionalInterceptor());
        return (T) enhancer.create();
    }

    // 使用CGLIB动态代理
    UserService userService = LoggingAndTransactionalInterceptor.createProxy(new UserServiceImpl());
    userService.insertUser(someUser);
}

这两种方式,都是在调用的时候,才传入被代理的对象,所以会灵活很多。

最后,来说一下代理模式和装饰者模式的区别,简单来理解,代理模式,就是多了个外人来增强功能,所以这些功能跟被代理类本身,是没说明关系的。就像房地产中介处理房子买卖、过户很厉害,关你买家什么事,都是中介自己的能力。

而装饰者模式,增强的功能是属于自己的功能,想要更加深入的了解装饰着模式,请期待下一篇的文章。

今天的分享到这里结束了。

关注公众号"徒手敲代码",免费领取腾讯大佬推荐的Java电子书!

相关推荐
易安说AI5 小时前
Claude Opus 4.6 凌晨发布,我体验了一整晚,说说真实感受。
后端
易安说AI5 小时前
Ralph Loop 让Claude无止尽干活的牛马...
前端·后端
易安说AI6 小时前
用 Claude Code 远程分析生产日志,追踪 Claude Max 账户被封原因
后端
JH30736 小时前
SpringBoot 优雅处理金额格式化:拦截器+自定义注解方案
java·spring boot·spring
颜酱7 小时前
图结构完全解析:从基础概念到遍历实现
javascript·后端·算法
Coder_Boy_7 小时前
技术让开发更轻松的底层矛盾
java·大数据·数据库·人工智能·深度学习
invicinble7 小时前
对tomcat的提供的功能与底层拓扑结构与实现机制的理解
java·tomcat
较真的菜鸟8 小时前
使用ASM和agent监控属性变化
java
黎雁·泠崖8 小时前
【魔法森林冒险】5/14 Allen类(三):任务进度与状态管理
java·开发语言
qq_12498707539 小时前
基于SSM的动物保护系统的设计与实现(源码+论文+部署+安装)
java·数据库·spring boot·毕业设计·ssm·计算机毕业设计