目录
1. 场景模拟
1.1. 声明接口
1.2. 创建实现类
1.3. 创建带日志功能的实现类
1.4. 提出问题
2. 代理模式
2.1. 概述
2.2. 静态代理
2.3. 动态代理
3. AOP切面编程
3.1. 概述
3.2. 相关术语
3.2.1. 横切关注点
3.2.2. 通知(增强)
3.2.3. 切面
3.2.4. 目标
3.2.5. 代理
3.2.6. 连接点
3.2.7. 切入点
3.3. 作用
1. 场景模拟
搭建子模块:spring6-aop
1.1. 声明接口
声明计算器接口Calculator,包含加减乘除的抽象方法
package com.sakurapaid.spring6.aop.demo;
/**
* 计算器接口,定义了基本的四则运算方法。
*/
public interface Calculator {
/**
* 加法操作。
*
* @param i 第一个操作数。
* @param j 第二个操作数。
* @return 两个操作数相加的结果。
*/
int add(int i, int j);
/**
* 减法操作。
*
* @param i 第一个操作数。
* @param j 第二个操作数。
* @return 两个操作数相减的结果。
*/
int sub(int i, int j);
/**
* 乘法操作。
*
* @param i 第一个操作数。
* @param j 第二个操作数。
* @return 两个操作数相乘的结果。
*/
int mul(int i, int j);
/**
* 除法操作。
*
* @param i 第一个操作数,作为被除数。
* @param j 第二个操作数,作为除数。
* @return 两个操作数相除的结果。
*/
int div(int i, int j);
}
1.2. 创建实现类
package com.sakurapaid.spring6.aop.demo;
/**
* 实现Calculator接口的计算类,提供基本的四则运算功能。
*/
public class CalculatorImpl implements Calculator {
/**
* 实现加法操作。
*
* @param i 第一个操作数。
* @param j 第二个操作数。
* @return 两个操作数相加的结果。
*/
@Override
public int add(int i, int j) {
int result = i + j; // 计算加法结果
System.out.println("方法内部 result = " + result); // 打印结果
return result; // 返回结果
}
/**
* 实现减法操作。
*
* @param i 第一个操作数。
* @param j 第二个操作数。
* @return 两个操作数相减的结果。
*/
@Override
public int sub(int i, int j) {
int result = i - j; // 计算减法结果
System.out.println("方法内部 result = " + result); // 打印结果
return result; // 返回结果
}
/**
* 实现乘法操作。
*
* @param i 第一个操作数。
* @param j 第二个操作数。
* @return 两个操作数相乘的结果。
*/
@Override
public int mul(int i, int j) {
int result = i * j; // 计算乘法结果
System.out.println("方法内部 result = " + result); // 打印结果
return result; // 返回结果
}
/**
* 实现除法操作。
*
* @param i 第一个操作数。
* @param j 第二个操作数。
* @return 两个操作数相除的结果。
*/
@Override
public int div(int i, int j) {
int result = i / j; // 计算除法结果
System.out.println("方法内部 result = " + result); // 打印结果
return result; // 返回结果
}
}
1.3. 创建带日志功能的实现类
package com.sakurapaid.spring6.aop.demo;
/**
* 实现Calculator接口的计算类,提供基本的四则运算功能,并通过日志记录方法的调用过程。
*/
public class CalculatorLogImpl implements Calculator {
@Override
/**
* 实现加法运算。
* @param i 第一个加数。
* @param j 第二个加数。
* @return 两个数的和。
*/
public int add(int i, int j) {
System.out.println("[日志] add 方法开始了,参数是:" + i + "," + j);
int result = i + j; // 计算结果
System.out.println("方法内部 result = " + result);
System.out.println("[日志] add 方法结束了,结果是:" + result);
return result;
}
@Override
/**
* 实现减法运算。
* @param i 被减数。
* @param j 减数。
* @return 两个数的差。
*/
public int sub(int i, int j) {
System.out.println("[日志] sub 方法开始了,参数是:" + i + "," + j);
int result = i - j; // 计算结果
System.out.println("方法内部 result = " + result);
System.out.println("[日志] sub 方法结束了,结果是:" + result);
return result;
}
@Override
/**
* 实现乘法运算。
* @param i 第一个乘数。
* @param j 第二个乘数。
* @return 两个数的积。
*/
public int mul(int i, int j) {
System.out.println("[日志] mul 方法开始了,参数是:" + i + "," + j);
int result = i * j; // 计算结果
System.out.println("方法内部 result = " + result);
System.out.println("[日志] mul 方法结束了,结果是:" + result);
return result;
}
@Override
/**
* 实现除法运算。
* @param i 被除数。
* @param j 除数。
* @return 两个数的商。
*/
public int div(int i, int j) {
System.out.println("[日志] div 方法开始了,参数是:" + i + "," + j);
int result = i / j; // 计算结果
System.out.println("方法内部 result = " + result);
System.out.println("[日志] div 方法结束了,结果是:" + result);
return result;
}
}
1.4. 提出问题
- 现有代码缺陷
针对带日志功能的实现类,我们发现有如下缺陷:
- 对核心业务功能有干扰,导致程序员在开发核心业务功能时分散了精力
- 附加功能分散在各个业务功能方法中,不利于统一维护
- 解决思路
解决这两个问题,核心就是:解耦。我们需要把附加功能从业务功能代码中抽取出来。
- 困难
解决问题的困难:要抽取的代码在方法内部,靠以前把子类中的重复代码抽取到父类的方式没法解决。所以需要引入新的技术。
2. 代理模式
2.1. 概述
代理模式的核心思想是为某个对象(称为目标对象或真实对象)创建一个替代品(称为代理对象)。当客户端请求与目标对象交互时,实际上是与代理对象打交道。代理对象可以透明地转发请求到目标对象,也可以在转发请求前或后添加额外的操作,如权限检查、日志记录、缓存、延迟加载、数据预处理等。这些附加功能通常不属于目标对象的核心业务逻辑,但对系统的整体功能或性能有重要影响。
代理模式的运作机制:
- 代理对象与目标对象实现了相同的接口或继承自同一基类,这意味着它们对外部世界(客户端)呈现出相同的接口,即客户端可以以相同的方式与两者交互。
- 当客户端调用代理对象的方法时,代理对象可以选择直接转发该调用给目标对象,或者在转发前/后执行一些额外的操作。这些操作可以包括但不限于:
-
- 访问控制:如检查调用者的权限,决定是否允许访问目标对象的方法。
- 功能增强:如在方法调用前后添加日志记录、统计计数、性能监控等。
- 资源管理:如缓存目标对象的结果以提高性能,或者延迟加载目标对象直到真正需要时。
- 复杂逻辑封装:将与目标对象交互的复杂流程(如网络通信、事务管理)封装在代理内,简化客户端代码。
通俗例子说明:
- 广告商找大明星拍广告需要经过经纪人:在这个场景中,大明星是目标对象,拥有实际拍摄广告的能力。经纪人则是代理对象,他代表明星与广告商进行谈判、签订合同等工作。广告商并不直接与明星交流,而是通过经纪人这个代理来间接访问明星的服务。经纪人可能还会负责处理非核心演艺事务,如协调档期、管理酬劳等,这些都是明星业务逻辑之外的辅助工作。
- 合作伙伴找大老板谈合作要约见面时间需要经过秘书:大老板是目标对象,拥有决策权和进行商业洽谈的能力。秘书作为代理对象,负责过滤和安排合作伙伴的会面请求。合作伙伴不直接联系大老板,而是通过秘书预约时间,秘书可能会先进行初步筛选,确保会面符合大老板的日程安排和其他要求。
- 房产中介是买卖双方的代理:卖方和买方分别是各自交易过程中的目标对象,他们拥有房源或购房需求。房产中介作为代理,帮助双方寻找合适的交易对象,处理看房、议价、签约、过户等复杂流程。买卖双方无需直接对接所有细节,而是通过中介这一代理进行沟通和交易,中介在此过程中提供了诸如市场分析、法律咨询、手续代办等附加服务。
相关术语解释:
- 代理:在代理模式中,代理是一个类或对象,它代表或封装了对目标对象的访问。代理负责接收客户端的请求,并根据需要执行额外的操作,最终将请求转发给目标对象或返回结果。代理可以增加、改变或限制对目标对象的访问。
- 目标(或真实对象):目标对象是代理所代表的实际对象,它包含了代理所要提供服务的核心业务逻辑。客户端对目标对象的访问通常是由代理对象间接提供的,目标对象本身并不知晓代理的存在,专注于执行其核心职责。
总结起来,代理模式通过引入代理对象来控制对目标对象的访问,实现了职责分离和功能增强,有助于提高代码的可维护性和系统的灵活性。代理对象封装了非核心逻辑,使得这些逻辑可以独立于目标对象进行管理和扩展,同时保持了对客户端的透明性,即客户端无需关心是直接与目标对象交互还是通过代理进行交互。
2.2. 静态代理
静态代理是指在编写代码时,手动创建一个代理类来实现对目标对象的代理。这个代理类通常会遵循以下特点:
- 明确指定代理对象:静态代理类需要提前知道它要代理的是哪个具体的目标类,因此代理类通常会与某个已知接口(或抽象类)关联,确保代理类和目标类具有相同的接口。
- 手动编码代理逻辑:代理类中的每个方法都会包含两部分逻辑:一部分是针对该方法的额外功能(如日志记录、权限检查等),另一部分是调用目标对象相应方法以执行核心业务逻辑。
- 硬编码限制:由于代理类是在编译时就已经确定的,它的行为和功能也是固定的。如果需要为不同的目标类添加类似的代理逻辑,或者调整代理逻辑,就需要为每个目标类编写对应的代理类。这种情况下,代码的重复性高,难以灵活应对变化。
创建静态代理类:
package com.sakurapaid.spring6.aop.demo;
public class CalculatorStaticProxy implements Calculator {
// 将被代理的目标对象声明为成员变量
private final Calculator target;
public CalculatorStaticProxy(Calculator target) {
this.target = target;
}
@Override
public int add(int i, int j) {
// 附加功能由代理类中的代理方法来实现
System.out.println("[日志] add 方法开始了,参数是:" + i + "," + j);
// 通过目标对象来实现核心业务逻辑
int addResult = target.add(i, j);
System.out.println("[日志] add 方法结束了,结果是:" + addResult);
return addResult;
}
@Override
public int sub(int i, int j) {
return 0;
}
@Override
public int mul(int i, int j) {
return 0;
}
@Override
public int div(int i, int j) {
return 0;
}
}
静态代理虽然实现了解耦,即将附加功能(如日志)与核心业务逻辑分离,但它存在明显的局限性:
- 缺乏灵活性:对于不同的目标类或同一目标类的不同方法,如果都需要添加类似日志这样的附加功能,必须为每个类或方法分别编写代理类和代理方法。这会导致代码重复,且随着需求变化,需要不断修改或新增代理类。
- 无法集中管理:如果需要在系统中全局地应用某种通用的代理逻辑(如日志),静态代理方式会让这些逻辑分散在各个代理类中,不易于统一管理和维护。
基于上述问题,提出了使用动态代理技术的需求。动态代理允许在运行时根据需要动态地创建代理对象,其优势在于:
- 无需手动编写代理类:动态代理通常通过编程框架(如 Java 的 JDK 动态代理或 CGLIB 等第三方库)在运行时自动为给定的目标对象生成代理类及其代理方法,无需手动编写大量代理类代码。
- 通用性与集中管理:动态代理可以为任意符合一定条件(如实现特定接口)的目标对象提供代理,且附加功能(如日志)可以在一个集中位置定义和配置,适用于多种场景和对象,大大减少了代码重复。
- 易于扩展和调整:由于代理逻辑是在运行时动态生成的,可以根据运行时条件或配置动态调整代理行为,使得系统更易于适应需求变化。
举个通俗易懂的例子就是:
静态代理
想象一下,你正在经营一家小店,雇了一位员工(目标对象)专门负责算账。为了监督员工的工作,你找了一个朋友(代理对象)帮忙。朋友的任务是:
- 记录日志:每当员工开始计算或结束计算时,朋友都要记下时间和计算的详情。
- 传递工作:朋友不亲自计算,而是让员工去做实际的计算,然后把结果告诉客户。
在这个例子中,你的朋友就是静态代理。编写了一个名为 CalculatorStaticProxy
的类,就像你朋友的角色一样。这个类实现了 Calculator
接口(就像你的朋友知道如何“算账”),并包含一个指向员工(target
)的引用。代理类的方法(如 add()
)包含了两部分任务:记录日志(附加功能)和调用员工的对应方法(核心业务逻辑)。
局限性
现在,问题来了:
- 不够灵活:假如你开了更多的分店,每家店都需要监督员工算账,你得为每家店找一个朋友做代理。这意味着你需要为每个分店编写一个单独的代理类(就像为每个店找不同的朋友)。如果以后要更改日志格式或添加其他监控功能(如记录计算耗时),你得逐一修改所有代理类的代码,工作量大且容易出错。
- 难以集中管理:每个朋友(代理类)有自己的日志记录方式,没有统一的标准。如果你想查看所有分店的日志,得分别向每个朋友要,不方便且效率低。如果要更改日志处理规则,比如统一上传到云端,你需要逐个通知每个朋友,非常麻烦。
动态代理
为了解决这些问题,你决定雇佣一个专业的审计团队(动态代理)。他们承诺:
- 自动化代理:不论你有多少家分店,只要告诉他们哪位员工需要监督,他们会立即安排一个“虚拟朋友”去工作,无需你手动找人或编写代理类。
- 集中处理日志:所有的“虚拟朋友”都按照审计团队的标准流程记录日志,然后统一上报到审计团队的系统中,便于你集中查看和管理。
- 灵活调整:如果你需要更改日志规则或添加新功能,只需告诉审计团队一次,他们会自动更新所有“虚拟朋友”的行为,无需你一个个通知。
在编程世界里,动态代理就像这个审计团队。它通过编程框架(如 Java 的 JDK 动态代理)在运行时自动为你生成代理对象。你只需要指定目标对象(员工),框架会自动生成一个代理对象(虚拟朋友),这个代理对象能执行附加功能(日志记录)并调用目标对象的方法(算账)。这样,无论有多少个目标对象(分店员工),都可以用同一个动态代理机制来处理,日志逻辑也集中在一个地方管理,大大提高了代码的复用性和可维护性。
总结来说,静态代理就像手动找朋友帮忙监督员工,虽然实现了功能,但扩展困难、管理分散。而动态代理就像雇佣专业团队,自动、集中且灵活地处理代理任务,更适合应对复杂多变的需求。
2.3. 动态代理
生产代理对象的工厂类:
package com.sakurapaid.spring6.aop.demo;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.Arrays;
/**
* 动态代理工厂类,用于创建目标对象的动态代理实例。
*/
public class ProxyFactory {
private Object target;
/**
* 构造函数,初始化目标对象。
* @param target 目标对象,将为此对象创建动态代理。
*/
public ProxyFactory(Object target) {
this.target = target;
}
/**
* 获取目标对象的动态代理实例。
* @return 返回代理对象,该对象实现了目标对象的所有接口。
*/
public Object getProxy(){
// 获取目标对象的类加载器和实现的接口数组
ClassLoader classLoader = target.getClass().getClassLoader(); // 获取目标对象的类加载器
Class<?>[] interfaces = target.getClass().getInterfaces(); // 获取目标对象的实现的接口数组
// 设置代理对象的行为,即在调用任何方法时执行的逻辑
InvocationHandler invocationHandler = new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 在方法执行前后打印日志
Object result = null;
try {
System.out.println("[动态代理][日志] "+method.getName()+",参数:"+ Arrays.toString(args));
result = method.invoke(target, args); // 调用目标对象的方法
System.out.println("[动态代理][日志] "+method.getName()+",结果:"+ result);
} catch (Exception e) {
e.printStackTrace();
System.out.println("[动态代理][日志] "+method.getName()+",异常:"+e.getMessage());
} finally {
System.out.println("[动态代理][日志] "+method.getName()+",方法执行完毕");
}
return result;
}
};
// 创建并返回代理实例
return Proxy.newProxyInstance(classLoader, interfaces, invocationHandler);
}
}
测试输出
package com.sakurapaid.spring6.aop.demo;
import org.junit.jupiter.api.Test;
/**
* 计算器测试类,用于测试动态代理功能。
*/
public class CaculatorTest {
/**
* 测试使用动态代理进行方法拦截。
* 该测试方法不接受参数,也不返回任何值。
* 主要流程是创建一个代理工厂,针对CalculatorLogImpl实例生成一个动态代理对象,
* 然后通过该代理对象调用add方法,以验证动态代理的拦截功能是否正常。
*/
@Test
public void testDynamicProxy(){
// 创建代理工厂,并设置目标对象为CalculatorLogImpl的实例
ProxyFactory factory = new ProxyFactory(new CalculatorLogImpl());
// 通过代理工厂获取代理对象
Calculator proxy = (Calculator) factory.getProxy();
// 使用代理对象调用add方法,验证动态代理是否生效
proxy.add(1,2);
}
}
3. AOP切面编程
3.1. 概述
AOP是什么?
想象你开了一家餐厅,餐厅里有很多事情要做:厨师负责烹饪美食(这是主业务,就像程序中的核心业务逻辑),服务员接待客人、点菜、上菜(这也是业务的一部分),还有清洁工打扫卫生、收银员结账等。除此之外,还有一些贯穿整个运营过程的“边缘任务”,比如:
- 记账:每次卖出一道菜,都需要记录销售额和成本,以便月底算账。
- 消毒:每接待一批客人后,都要对桌面和餐具进行消毒,保证食品安全。
- 提醒:当某种食材快要用完时,需要及时提醒采购员补货。
这些“边缘任务”(我们称它们为“切面”)虽然不是直接做菜或服务客人,但对餐厅的正常运营至关重要。而且,它们会涉及到餐厅的各个环节,不是只针对某一特定角色或环节。
传统的做法
以前,你可能让厨师、服务员、清洁工等人在忙完自己的本职工作后,额外花时间去记账、消毒、提醒采购。这样做的问题是:
- 分散注意力:每个人都得操心额外的事情,可能会影响他们做好本职工作。
- 重复劳动:比如每道菜售出时,每个服务员都得记一笔账,很浪费时间。
- 难于管理:记账、消毒、提醒采购的规则可能经常变动,要通知所有人并确保他们按新规则执行,很麻烦。
AOP的做法
AOP就像是请了一个全能助手,专门负责这些“边缘任务”。助手站在一旁观察餐厅的运营情况,按照设定好的规则自动执行:
- 记账助手:每道菜卖出时,助手自动记账,月底直接给你报表。
- 消毒助手:客人离开后,助手立刻去清理桌面、消毒餐具,无需服务员操心。
- 提醒助手:食材快用完时,助手直接通知采购员,无需厨师分心。
这样做的好处:
- 专注主业:厨师、服务员可以专心烹饪和服务,不用分心做其他事。
- 高效执行:记账、消毒等任务由专人专职处理,速度快、准确率高。
- 易于管理:修改记账规则、消毒频率、提醒条件时,只需告诉助手一个人即可,省时省力。
映射到编程领域
在软件开发中,AOP就是那个全能助手。它能在程序运行时,自动在合适的地方插入额外的代码(比如记录日志、开启事务、权限检查等),而不用修改原来的业务代码。这样:
- 业务代码更纯粹:程序员只需关注业务逻辑本身,不用在代码中混杂各种“边缘任务”。
- 功能复用:比如日志记录逻辑,可以统一定义一次,然后在需要的地方自动应用,无需重复编写。
- 易于维护:如果需要调整某个“边缘任务”(如日志格式),只需改动一处,不影响业务代码。
总结来说,AOP是一种编程思想,它让程序能够自动在执行主业务逻辑的同时,处理好那些与业务逻辑交织但又相对独立的“边缘任务”,使得代码更清晰、更易维护,同时也提高了开发效率。在Spring框架中,AOP提供了具体的工具和技术实现这一思想。
AOP(Aspect Oriented Programming)是一种设计思想,是软件设计领域中的面向切面编程,它是面向对象编程的一种补充和完善,它以通过预编译方式和运行期动态代理方式实现,在不修改源代码的情况下,给程序动态统一添加额外功能的一种技术。利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。
3.2. 相关术语
3.2.1. 横切关注点
比喻: 想象你经营一家餐厅,每天有许多日常事务要处理。其中有一些事务是贯穿整个餐厅运营的,就像一根根“红线”贯穿于各个部门之间。例如:
- 食品安全检查:无论是厨房烹饪、服务员上菜还是收银结账,都需要确保食物安全。
- 员工考勤:所有员工上班时都需要打卡,下班时也需要记录工时。
- 顾客满意度调查:在顾客用餐结束后,可能需要让他们填写问卷,收集反馈。
这些贯穿于餐厅各个角落、各部门都需处理的共同问题,就像“红线”一样横跨各个业务模块,我们称之为“横切关注点”。
解释: 在软件开发中,横切关注点指的是那些在不同业务模块中反复出现、与业务逻辑本身关系不大,但又不可或缺的处理逻辑。比如用户验证、日志记录、事务管理、数据缓存等。它们并非某一个具体功能的核心部分,而是与整个系统的行为规范、运行环境、管理需求等相关。
3.2.2. 通知(增强)
比喻: 假设你想提升餐厅的服务质量,于是决定给餐厅添加一些“增强功能”。比如:
- 餐前欢迎词:顾客入座后,服务员送上一句温馨的欢迎词。
- 餐后感谢语:顾客结账离开时,收银员表达一声真诚的感谢。
- 限时优惠提醒:在特定时段,主动告知顾客当前正在进行的优惠活动。
这些额外的互动环节就像给餐厅原有的服务流程加上了“增强功能”,让顾客体验更佳。
解释: 在AOP中,通知(增强)就是指那些在目标方法执行前后(或过程中)插入的额外操作,用来实现横切关注点所需的功能。比如:
- 前置通知:在厨师开始炒菜(目标方法)之前,先检查食材新鲜度(附加功能)。
- 返回通知:顾客成功付款(目标方法执行完毕)后,系统自动发送电子发票(附加功能)。
- 异常通知:若结账时发生支付失败(目标方法异常),则触发退款流程(附加功能)。
- 后置通知:无论结账是否成功,都会记录本次交易信息到数据库(附加功能)。
- 环绕通知:整个点餐到结账的过程,如同包裹在try...catch...finally结构中,确保无论是否发生异常,都能执行必要的清理工作(如释放资源、关闭连接等)。
3.2.3. 切面
比喻: 为了让餐厅更好地运营,你专门设立了一个“服务质量提升小组”,这个小组负责策划并实施所有的“增强功能”,如欢迎词、感谢语、优惠提醒等。这个小组就是所有“增强功能”的集合体。
解释: 在AOP中,切面就是一个封装了多个通知(增强)的类,它定义了哪些地方需要执行哪些增强逻辑。切面就像一个“服务质量提升小组”,集中管理与特定横切关注点相关的所有通知。
3.2.4. 目标
比喻: 在餐厅里,厨师专心炒菜、服务员热情待客,他们是各自岗位上的“目标”,他们的主要任务是完成本职工作。
解释: 在AOP中,目标就是指那些被增强的对象或方法,即我们想要在其周围添加通知的原始业务逻辑。比如某个处理订单的类或方法,它们专注于处理订单的核心业务,是AOP要进行增强的“目标”。
3.2.5. 代理
比喻: 如果你聘请了一位餐厅经理,他并不直接炒菜或服务顾客,但他会指导厨师和服务员如何更好地工作,并在必要时介入处理一些特殊情况。这位经理就像是厨师和服务员的“代理”,他们对外展示的是经理协调下的综合服务能力。
解释: 在AOP中,代理就是指为目标对象生成的一个替代者,它在保留原对象功能的基础上,包含了切面中定义的通知逻辑。客户端代码实际上与代理对象交互,代理对象在调用目标方法时会适时执行相应的通知。
3.2.6. 连接点
比喻: 在餐厅运营过程中,任何一个可以插入“增强功能”的时机都可以看作一个“连接点”。比如顾客刚坐下时、点完菜后、结账时、离开时等都是可以插入欢迎词、感谢语、优惠提醒等“增强功能”的“连接点”。
解释: 在AOP中,连接点是一个抽象概念,它表示在程序执行过程中能够插入通知的所有可能位置。具体来说,通常是某个方法的执行前、执行后、抛出异常时、正常返回后等时刻。连接点就像一张餐厅运营流程的时间线图,图上标注了所有可以插入“增强功能”的时间节点。
3.2.7. 切入点
比喻: 如果你决定只在顾客入座时和结账时进行“增强功能”(欢迎词、感谢语),那么这两个特定的“连接点”就是你要瞄准的“切入点”。
解释: 在AOP中,切入点就是用来精确指定应该在哪些连接点上应用通知的表达式或规则。它就像一个过滤器,从众多连接点中选出真正需要插入通知的具体位置。比如,可能只选择所有以“save
”开头的方法作为切入点,对这些方法执行事务管理的增强。切入点就像一份详细的“增强功能”实施计划,指定了在哪些具体场景(方法调用)中启用哪些“增强功能”。
3.3. 作用
- 简化代码:把方法中固定位置的重复的代码抽取出来,让被抽取的方法更专注于自己的核心功能,提高内聚性。
- 代码增强:把特定的功能封装到切面类中,看哪里有需要,就往上套,被套用了切面逻辑的方法就被切面给增强了。