需要思考以下几个点:
01 需要知道在什么地方进行切面操作
02 需要知道切面操作的具体内容
03 如果有多个切面操作,应该得有一个先后执行顺序
通知是织入到目标类连接点上的一段程序代码。 (在连接点上进行的具体操作,如何进行增强处理的)
切面要完成的工作被称为通知,通知定义了切面是什么以及何时使用。Spring 切面有5种类型的通知,分别是:
01 前置通知(Before):在目标方法调用之前调用通知功能;
02 后置通知(After):在目标方法调用之后调用通知功能;
03 异常通知(After-throwing):在目标方法抛出异常后调用通知;
04 返回通知(After-returning):在目标方法成功之后调用通知;
05 环绕通知(Around):通知包裹了被通知的方法,在被通知的方法调用之前和调用之后执行自定义的行为。
程序执行的某个特定位置,比如某个方法调用前、调用后、方法抛出异常后,对类成员的访问以及异常处理程序块的执行等。
连接点就是应用执行过程中能够插入切面的一个点,这个点可以是调用方法时、抛出异常时、修改某个字段时。仅作用在方法上。
如果连接点相当于数据中的记录,那么切点相当于查询条件,一个切点可以匹配多个连接点。所以切点表示一组Joinpoint,这些Jointpoint或是通过逻辑关系组合起来,或是通过通配、正则表达式等方式集中起来,它定义了相应的 Advice 将要发生的地方。
切点是为了缩小切面所通知的连接点的范围,即切面在何处执行。通常使用明确的类和方法名称,或者利用正则表达式定义所匹配的类和方法名称来指定切点。
切面是通知和切点的结合。通知和切点共同定义了切面的全部内容,它是什么、在何时何处完成其功能。
引入允许我们在不修改现有类的基础上,向现有类添加新方法或属性。
织入就是把切面应用到目标对象并创建新的代理对象的过程。Spring 是通过实现后置处理器 BeanPostProcessor 接口来实现织入的。也就是在 bean 完成初始化之后,通过给目标对象生成代理对象,并交由 Spring IOC 容器来接管,这样再去容器中获取到的目标对象就是已经增强过的代理对象。
切面在指定的连接点被织入到目标对象中,在目标对象的生命周期里,有以下几个点可以进行织入:
01 编译期:切面在目标类编译时被织入。这种方式需要特殊的编译器。AspectJ的织入编译器就是以这种方式织入切面的。
02 类加载期:切面在目标类加载到JVM时被织入。这种方式需要特殊的类加载器(ClassLoader),它可以在目标类被引入应用之前增强该目标类的字节码。
03 运行期:切面在应用运行的某个时刻被织入。一般情况下,在织入切面时,AOP容器会为目标对象动态地创建一个代理对象。Spring AOP就是以这种方式织入切面的。
包含连接点的对象。也被称作被通知或被代理对象。
包含了原始对象的代码(是在合适的位置调用目标对象的方法)和增加后的代码(Advice通知的内容)的那个对象。
对上面的知识点,通过下面的图来总结一下,

Spring入门(十):Spring AOP使用讲解 - 掘金 (juejin.cn)
Spring AOP 构建在动态代理之上,也就是说,Spring 运行时会为,目标对象动态创建代理对象。
使用动态代理实现 AOP 需要四个角色:被代理的类、被代理类的接口、织入器(Proxy.newProxyInstance())、InvocationHandler。织入器使用接口反射机制生成一个代理类,然后在这个代理类中织入代码(切入逻辑)。被代理的类是 AOP 里所说的目标,InvocationHandler 是切面,包含了通知(Advice)和切点(Pointcut)。
代理类的核心其实就是代理对象的生成,即Proxy.newProxyInstance(),其中 getProxyClass() 方法用于获取代理类,主要做了三件事:在当前类加载器的缓存里搜索是否有代理类,没有则生成代理类并缓存在本地 JVM 里。
代理的目的是调用目标方法时转而执行 InvocationHandler 类的 invoke() 方法。

生气!面试官你过来,我给你手写一个Spring Aop实现! - 腾讯云开发者社区-腾讯云 (tencent.com)
原理图如下,

这样讲可能还是有点抽象,写个例子来辅助理解,
(1)创建一个 Student 接口
- package com.concert;
-
- public interface Student {
- void eat();
- void sleep();
- }
(2)创建 Student接口的实现类 StudentImpl
- package com.concert;
-
- public class StudentImpl implements Student {
- @Override
- public void eat() {
- System.out.println("吃饭啦");
- }
-
- @Override
- public void sleep() {
- System.out.println("该睡觉了");
- }
- }
(3)大家都知道吃饭前要洗手、吃完饭要洗碗,怎么使用代码进行实现呢?可以定义一个 EatHandler,实现 InvocationHandler,
利用 InvocationHandler 实现一个代理,让它去包含 Student 这个对象,那么在运行期实际上是执行这个代理的方法,然后代理再去执行真正的方法。所以我们得以在执行真正方法的前后做一些手脚。JDK 动态代理是利用反射实现的,
- package com.concert;
-
- import java.lang.reflect.InvocationHandler;
- import java.lang.reflect.Method;
- import java.lang.reflect.Proxy;
-
- public class EatHandler implements InvocationHandler {
-
- // 要代理的目标对象 / 被代理对象
- private Object target;
-
- public EatHandler(Object target){
- this.target = target;
- }
-
- public Object proxyInstance(){
- /**
- * 第一个参数是要加载代理对象的类加载器
- * 第二个参数是要代理类实现的接口
- * 第三个参数是EatHandler
- */
- return Proxy.newProxyInstance(target.getClass().getClassLoader(),target.getClass().getInterfaces(),this);
- }
-
- /**
- * 被代理对象target调用其自身方法时执行invoke方法
- */
- @Override
- public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
- System.out.println("吃饭前要洗手");
- Object result = method.invoke(this.target, args); //调用目标对象的方法,即eat()方法
- System.out.println("吃完饭要洗碗");
- return result;
- }
- }
(4)编写测试类
- package com.concert;
-
- public class ProxyMain {
- public static void main(String[] args) {
- EatHandler eatHandler = new EatHandler(new StudentImpl());
- Student stu1 = (Student) eatHandler.proxyInstance();
- Student student = new StudentImpl();
- stu1.eat();
- student.sleep();
- }
- }
(5)输出结果

可以看到,在“吃饭啦”前后已经插入了“吃饭前要洗手”、“吃饭后要洗碗”的功能。怎么插入的呢?

原理就是 JDK 帮我们创建了目标对象(Student)的代理类(EatHandler),这个代理类实现了我们想要的功能。当我们调用 stu1.eat() 时,操作的是代理类,而不是目标对象,从而插入了想要插入的功能。
代理对象继承了 Proxy 类、实现了 Student 接口。
也就是,新创建一个类,然后在类里面创建一个对应的方法,在新创建的方法里面再具体调用目标对象的方法。
bean 的创建过程中的初始化阶段的后置处理器(postProcessAfterInitialization),在满足条件的情况下会对 bean 进行 AOP 增强。核心实现是 AbstractProxyCreator 的 wraplfNecessary 方法,该方法的主要逻辑实现就是找到容器中能够应用到当前所创建的 bean 的切面,利用切面为 bean 创建代理对象。
Spring 的 AOP 就是把切面织入到满足切点限定条件的连接点的过程。
在 Spring 中,怎么用切面去增强一个类?是 拦截! 我们拦截方法的执行,去添加一些额外的逻辑。那如何进行拦截呢? 当然是使用动态代理!
Spring 如何对方法进行拦截?通过 MethodInterceptor 接口,
- public interface MethodInterceptor extends Interceptor {
-
- /**
- * Implement this method to perform extra treatments before and
- * after the invocation. Polite implementations would certainly
- * like to invoke {@link Joinpoint#proceed()}.
- * @param invocation the method invocation joinpoint
- * @return the result of the call to {@link Joinpoint#proceed()};
- * might be intercepted by the interceptor
- * @throws Throwable if the interceptors or the target object
- * throws an exception
- */
- Object invoke(MethodInvocation invocation) throws Throwable;
- }
也就是说,被增强类(目标对象)的方法执行时,实际是通过 MethodInterceptor#invoke 被调用的。