AOP 就是面向切面编程,是 OOP(面向对象编程)的延续。
利用 AOP 可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序可用性,同时也提高了开发效率。
通俗一点说,不用修改原代码,可以给原代码增加新的功能。
AOP 底层原理是使用动态代理。
那代理是什么?有动态代理,那是不是还有静态代理?
就是为一个目标对象提供一个代理对象,并由代理对象控制对目标对象的引用。使用代理对象,是为了在不修改目标对象的基础上,增强目标对象的业务逻辑。
比如目标对象 A,代理对象是 B。
而代理分为静态代理和动态代理,区别是:
静态代理是有实实在在的代理类存在,并且和目标类实现相同的接口。
比如,有一个转账业务,现在希望给它增加功能,使在转账之前确认转账人身份,以及转账之后通知收款人。
(1) 接口 AccountServiceDao :
package com.pingguo.spring5.dao; public interface AccountServiceDao { // 主业务逻辑,转账 void transfer(); }
(2) 接口 AccountServiceDao 的实现类:
package com.pingguo.spring5.dao; public class AccountServiceImpl implements AccountServiceDao { @Override public void transfer() { System.out.println("调用dao层,完成转账主业务."); } }
(3) 代理类 AccountProxy :
package com.pingguo.spring5.proxy; import com.pingguo.spring5.dao.AccountServiceDao; public class AccountProxy implements AccountServiceDao { // 目标对象 private AccountServiceDao target; public AccountProxy(AccountServiceDao target) { this.target = target; } /** * 代理方法,实现对目标方法的增强 */ @Override public void transfer() { before(); target.transfer(); after(); } /** * 增强的功能,转账之前使用 */ private void before() { System.out.println("对转账人身份进行验证."); } /** * 增强的功能,转账之后使用 */ private void after() { System.out.println("转账完成,已通知收款人."); } }
在代理类中:
(4) 运行测试
新建一个测试方法,运行看下结果:
@Test public void testProxy() { // 创建目标对象 AccountServiceDao target = new AccountServiceImpl(); // 创建代理对象 AccountProxy proxy = new AccountProxy(target); proxy.transfer(); }
结果:
对转账人身份进行验证. 调用dao层,完成转账主业务. 转账完成,已通知收款人. Process finished with exit code 0
优点:
缺点:
与静态代理的硬编码方式相比,动态代理支持运行时动态生成代理对象这种方式。换句话说,动态代理并不存在代理类,代理对象直接由代理生成工具动态生成。
优点:
缺点:
对于动态代理,针对于是否存在接口的情况下,又分为 2 种:
使用 JDK 动态代理
使用 JDK 动态代理,创建的是接口实现类的代理对象,以此来实现功能增强。
现在不需要上面创建过的实际代理类了 。
接口,为了后面的一些知识点的说明,里面加个参数,转账的金额:
package com.pingguo.spring5.dao; public interface AccountServiceDao { // 主业务逻辑,转账 void transfer(int amount); }
实现类:
package com.pingguo.spring5.dao; public class AccountServiceImpl implements AccountServiceDao { @Override public void transfer(int amount) { System.out.println("调用dao层,完成转账主业务.金额:" + amount); } }
在测试方法里,直接使用动态代理:
@Test public void testDynamicProxy() { // 创建目标对象 AccountServiceDao target = new AccountServiceImpl(); // 创建代理对象 AccountServiceDao proxy = (AccountServiceDao) Proxy.newProxyInstance( target.getClass().getClassLoader(), // 目标类使用的类加载器 target.getClass().getInterfaces(), // 目标类实现的接口 new InvocationHandler() { // 调用处理器 @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("对转账人身份进行验证."); Object res = method.invoke(target, args); System.out.println("转账完成,已通知收款人."); return res; } } ); // 让代理工作 proxy.transfer(10000); }
运行结果:
对转账人身份进行验证. 调用dao层,完成转账主业务.金额:10000 转账完成,已通知收款人. Process finished with exit code 0
动态代理的过程:
对于 invoke 中的 3 个参数,分别是:
使用 CGLIB 动态代理
CGLIB动态代理的原理是生成目标类的子类,这个子类对象就是代理对象,代理对象是被增强过的。
注意,不管有没有接口都可以使用 CGLIB 动态代理, 而不是只有在无接口的情况下才能使用。
示例就暂时不放了,因为我本地环境问题,有个报错始终未解决,后续再说,不影响继续学习 spring。