Java代理系列-代理的起源

本文从AOP入手,逐步深入解析代理模式的概念与应用场景。通过具体示例介绍了如何在不改动原有代码的基础上添加新功能,并探讨了静态与动态代理的区别及其实现方式。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

前言:这是一篇有关代理的系列文章,记录了我对代理由浅入深的认识和理解。代理是一种编程思想,它试图做到如何在不修改或尽量少修改现有代码的情况下,改变对象的行为。而且代理绝非Java专属,它是一种使用非常广泛的编程思想,你会在很多语言和框架上看到代理的影子。既然代理无处不在,那么掌握它是非常有必要的。

 

初识代理

我对代理的认识是从AOP(Aspect Oriented Programming,面向切面编程)开始的。写一个切面,加一个注解,就能神不知鬼不觉在方法调用的前后增加很多行为。当时对这种技术的底层实现非常着迷,就迫不及待找了很多博客,做了很多试验,看了很多源码,终于对代理有了更深入的理解,想写一个系列文章来介绍代理。中间由于换工作等诸多原因,断掉了,现在打算重新把这个系列写完,让这个世界留下一抹我思考过的痕迹。

 

问题驱动

先看一段简单的代码

public class UserDao {
    public boolean login(String account, String password){
        return "wonking".equals(account) && "root".equals(password);
    }
}

假如现在不允许修改UserDao的源码,让你在userDao.login()调用前打印一下参数,在调用后打印结果。你会怎么做?

我们很自然的会想到,新增一个类,继承UserDao,重写login方法,就像这样

public class UserDaoProxy extends UserDao {
    private UserDao target;
    public UserDaoProxy(){
        target=new UserDao();
    }

    @Override
    public boolean login(String account, String password) {
        System.out.println(String.format("account->%s, password->%s", account, password));
        boolean result=target.login(account, password);
        System.out.println(String.format("result->%s", result));
        return result;
    }
}

然后在实例化UserDao的地方,用UserDao userDao=new UserDaoProxy()代替,这样就神不知,鬼不觉的完成了日志的打印。这就是设计模式-代理模式的手法,UserDao,我们称为目标类(官方叫委托类),UserDaoProxy,我们称为代理类。

 

问题又来了,现在有成百上千个类的方法,我想给他们全部都加上打印日志的功能。如果你不嫌麻烦,可以按照同样的模式复制粘贴上千个代理类。聪明人(或者说喜欢偷懒的人)一定会试图寻找一种自动化生成代理类的解决方案。所以任何问题,我们把它放大来看,它就变成了另外一个问题。

 

代理的特征

自动化生成代理类的方法,将在下篇介绍。正因为书写代理类是有一个固定的模式的,所以将书写代理类的工作自动化是存在解决方案的。为了了解这个模式,我们先来总结一下,动态代理类有什么特征

1.透明性
代理类与目标类,必须有一个共同的父类型,这样才能使代理类对客户端透明。
---对应到实现方案中来,要么双方实现共同的接口,要么代理类继承目标类,前者是JDK代理框架采用的方案,后者是cglib框架采用的方案。

2.动态性
动态性有两层含义。一,代理类生成的动态性,代理类是在运行期间动态生成字节码;二,代理逻辑的动态性,代理逻辑是抽象的,不确定的逻辑。
---对应到实现方案中来,首先,代理逻辑不确定,很容易想到应该抽象一个接口,表示对代理逻辑的封装,作为参数动态传递给代理类。

 

代理分类

1.静态代理
静态代理就是在编译前,代理类就存在了。这种方法很不灵活,实际编程中很少会用到,不多展开。

2.动态代理
动态代理实现方式非常多,下表大概是最全的一个动态代理技术方案总结了

类别机制原理优点缺点技术
静态AOP静态织入在编译期,切面直接以字节码的形式编译到目标字节码文件中对系统无性能影响灵活性不够AspectJ
动态AOP动态代理在运行期,目标类加载后,为接口动态生成代理类,将切面植入到代理类中相对于静态AOP更加灵活切入的关注点需要实现接口。对系统有一点性能影响JDK dynamic proxy
动态字节码生成在运行期,目标类加载后,动态构建字节码文件生成目标类的子类,将切面逻辑加入到子类中 没有接口也可以织入扩展类的实例方法为final时,则无法进行织入cglib
自定义类加载器在运行期,目标加载前,将切面逻辑加到目标字节码里 可以对绝大部分类进行织入代码中如果使用了其他类加载器,则这些类将不会被织入 
字节码转换在运行期,所有类加载器加载字节码前,前进行拦截 可以对所有类进行织入  

后面几个严格来说已经超出了代理的范畴了,因为他们根本就没有生成代理类,而是直接修改目标类的字节码,改变其行为,目标类代理类合二为一,可以说是仙法了~~

 

代理应用

---添加日志切面
---添加事务特性
---加入缓存代理
---系统性能监控
---方法拦截
---权限控制
---流量控制
---负载均衡

形式千变万化,内涵只有一个。掌握真理所在,万变不离其宗^_^

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值