在Spring中,AOP(面向切面编程)通过代理模式来实现横切关注点的织入。代理模式是AOP的核心机制之一,它允许在目标对象的方法执行前、执行后或异常抛出时插入额外的逻辑,而无需修改目标对象的源代码。
所谓代理模式其实就是二十三种设计模式中的一种,属于结构型模式。它的作用就是通过提供一个代理类,让我们在调用目标方法的时候,不再是直接对目标方法进行调用,而是通过代理类间接调用。让不属于目标方法核心逻辑的代码从目标方法中剥离出来——解耦。调用目标方法时先调用代理对象的方法,减少对目标方法的调用和打扰,同时让附加功能能够集中在一起也有利于统一维护。
使用代理模式之前
使用代理模式之后
代理在我们现实生活中的运用:
- 广告公司找大明星拍广告需要经过经纪人。
- 合作伙伴找老板谈合作约见面时间一般需要经过秘书。
- 房产中介是双方的代理
- 还有入厂中介,外包公司都是中介的一种
相关术语:
代理: 将非核心逻辑剥离出来以后,封装这些非核心逻辑的类,对象,方法。
目标:被代理“套用”了非核心逻辑代码的类、对象、方法。
在AOP中,代理模式主要有两种实现方式:静态代理和动态代理。
静态代理:静态代理是通过手动编写代理类来实现的。代理类和目标类实现相同的接口,代理类中包含目标对象的引用,并在方法调用前后插入额外的逻辑。在Spring中,可以使用ProxyFactoryBean来创建静态代理。
动态代理:动态代理是在运行时动态生成代理类的方式。Spring使用JDK动态代理和CGLIB动态代理两种方式来实现动态代理。
- JDK动态代理:基于接口的代理,通过实现InvocationHandler接口来实现代理逻辑。在运行时,通过Proxy类的静态方法newProxyInstance来创建代理对象。
- CGLIB动态代理:基于类的代理,通过继承目标类来实现代理逻辑。在运行时,通过Enhancer类来创建代理对象。
静态代理的实现
静态代理是通过手动编写代理类来实现的。我们先通过一个简单的静态代理示例来说明什么具体什么是静态代理:
首先我们定义一个接口(Subject) 和一个实现类(RealSubject),代表目标对象和实际业务逻辑
然后,创建一个代理类(ProxySubject),实现与目标对象相同的接口,并在方法调用前后插入额外的逻辑:
在上述示例中,ProxySubject是代理类,它持有一个实际的目标对象(RealSubject)。在doSomething方法中,代理类在调用目标对象的方法之前和之后分别输出额外的日志信息。
最后,可以通过创建代理对象来使用静态代理:
在上述示例中,创建了一个RealSubject对象作为目标对象,然后创建了一个ProxySubject对象作为代理对象。通过调用代理对象的doSomething方法,实际上会触发代理类中的逻辑,包括在调用目标对象的方法之前和之后输出日志信息。
需要注意的是,静态代理的缺点是每个目标对象都需要对应一个代理类,当目标对象较多时,会导致代理类的数量增加,代码复杂度提高。此外,静态代理也无法动态地切换和修改代理逻辑。
实际案例
上述代码,静态代理确实实现了解耦,但是由于代码都写死了,完全不具备任何的灵活性。就拿日志功能来说,将来其他地方也需要附加日志,那还得再声明更多个静态代理类,那就产生了大量重复的代码,日志功能还是分散的,没有统一管理。
提出进一步的需求:将日志功能集中到一个代理类中,将来有任何日志需求,都通过这一个代理类来实现。这就需要使用动态代理技术了。
动态代理
说到动态代理,上面说了两种动态代理一种是JDK动态代理,一种是CGLIB动态代理。
JDK动态代理的相关知识
首先,我们定义一个接口(Subject)和一个实现类(RealSubject),代表我们的目标对象和实际业务逻辑,与静态代理示例中的基本上无差别。
然后创建一个实现InvocationHandler的接口和代理处理器类(ProxyHandler),实现invoke方法,在方法调用前后插入额外的逻辑:
ProxyHandler是代理处理器类,它持有一个实际的目标对象(target)。在invoke方法中,代理处理器在调用目标对象的方法之前和之后分别输出额外的日志信息。
CGLIB实现动态代理
CGLIB动态代理是基于类的代理,通过继承目标类来实现代理逻辑。相比JDK动态代理,CGLIB动态代理不要求目标对象实现接口。
ProxyHandler是代理处理器类,它持有一个实际的目标对象(target)。在intercept方法中,代理处理器在调用目标对象的方法之前和之后分别输出额外的日志信息。
使用Enhancer类来创建代理对象:
在上述示例中,创建了一个RealSubject对象作为目标对象,然后创建了一个ProxyHandler对象作为代理处理器。通过使用Enhancer类,设置目标类和代理处理器,即可动态生成代理对象。
通过调用代理对象的doSomething方法,实际上会触发代理处理器中的intercept方法,包括在调用目标对象的方法之前和之后输出日志信息。
需要注意的是,使用CGLIB动态代理时,目标类不能是final类,且目标方法不能是final或private方法。
案例实现
动态代理实现类:
测试案例