(未完成)【Spring专题】SringAOP底层原理解析——阶段三(AOP)

目录

前言

前置知识

代理范式

什么是代理模式?代理模式的解释:为其他对象提供一种代理以控制对这个对象的访问,增强一个类中的某个方法,对程序进行扩展。

少年可曾听闻【代理范式】?它的伪代码一般如下:(Cglib动态代理采用的方案)

java 复制代码
class 被代理类 {
	function 方法A();
}

class 代理类 <<继承>> 被代理类 {
	被代理类 targetObject;
	
	@Override
	function 方法A() {
		// 这里写被加强的逻辑
		targetObject.方法A();
		// 也可以在这里写
	}
}

或者用java代码表示如下:

java 复制代码
// 被代理对象
public class ProxyTarget {
    public void run() {
        System.out.println("这是普通对象的run");
    }
}


// 代理对象
public class ProxyModel extends ProxyTarget {
    private ProxyTarget proxyTarget;

    public void setProxyTarget(ProxyTarget proxyTarget) {
        this.proxyTarget = proxyTarget;
    }

    @Override
    public void run() {
        System.out.println("我代理对象可以在这里做加强---1");
        super.run();
        System.out.println("我代理对象也可以在这里做加强---2");
    }
}

但是,代理模式的实现其实还有一种,但前提是被代理的类存在接口。它的代理范式伪代码如下:(JDK动态代理采用的方案)

java 复制代码
class 被代理类 <<实现>> 接口A {
	function 实现自接口A的方法();
}

class 代理类 <<实现>> 接口A {
	被代理类 targetObject;
	
	@Override
	function 实现自接口A的方法() {
		// 这里写被加强的逻辑
		targetObject.方法A();
		// 也可以在这里写
	}
}

SpringAOP的理解

OOP表示面向对象编程,是一种编程思想,AOP表示面向切面编程,也是一种编程思想,而Spring为了让程序员更加方便的做到面向切面编程所提供的技术支持,于是提供了一套机制,可以让我们更加容易的来进行AOP,所以这套机制我们也可以称之为Spring AOP。

Spring动态代理的实现

我把它放到【课程内容】里面了,因为我怕大家不重视

课程内容

一、动态代理的实现

我相信有经验的朋友,应该都知道,我们Java里面通常使用的代理方式有两种,分别为:JDK动态代理,以及Cglib动态代理。下面就给大家简单演示一下,这两种动态代理的简单使用示例。

在言是两种代理之前,需要一个基础类。如下:

java 复制代码
@Component
public class UserService {

    public void test() {
        System.out.println("这是UserService的一个普通方法");
    }
}

在开始代理之前,再次重申一下为什么需要代理?
代理模式的解释:为其他对象提供一种代理以控制对这个对象的访问,增强一个类中的某个方法,对程序进行扩展。

就是,当我们需要增强某个方法的,又不想修改原有代码的时候(遵守设计原则里面的:开闭原则),一般就会考虑使用代理。在Spring里面就使用了下面两种方案来实现这个代理模式。

1.1 Cglib动态代理

在上面的UserService,如果我们new一个UserService对象,然后执行test()方法,结果是显而易见的。如果我们现在想在不修改UserService类的源码前提下,给test()增加额外逻辑,那么就可以使用Cglib动态代理机制来创建UserService对象了,比如:

java 复制代码
 public static void main(String[] args) throws IOException {
        UserService target = new UserService();
        target.test();


        System.out.println("下面开始是cglib之后的逻辑了");

        // cglib
        {
            // 使用cglib增强类
            Enhancer enhancer = new Enhancer();
            enhancer.setSuperclass(UserService.class);

            // 定义额外的增强逻辑,增强对象
            enhancer.setCallbacks(new Callback[]{new MethodInterceptor() {
                @Override
                public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
                    System.out.println("before...");
                    Object result = methodProxy.invoke(target, objects);
                    System.out.println("after...");
                    return result;
                }
            }});

            // 动态代理所创建出来的UserService对象
            UserService userService = (UserService) enhancer.create();

            // 执行这个userService的test方法时,就会额外会执行一些其他逻辑
            userService.test();
        }
    }

得到的都是UserService对象,但是执行test()方法时的效果却不一样了,这就是代理所带来的效果。
上面是通过cglib来实现的代理对象的创建,是基于父子类的,被代理类(UserService)是父类,代理类是子类,代理对象就是代理类的实例对象,代理类是由cglib创建的,对于程序员来说不用关心。

1.2 JDK动态代理

除开cglib技术,jdk本身也提供了一种创建代理对象的动态代理机制,但是它只能代理接口,也就是UserService得先有一个接口才能利用jdk动态代理机制来生成一个代理对象,比如:

java 复制代码
public interface UserInterface {
    void test();
}

public class UserService implements UserInterface{

    @Override
    public void test() {
        System.out.println("这是UserService的一个普通方法");
    }
}

利用JDK动态代理来生成一个代理对象的示例如下:

java 复制代码
 public static void main(String[] args) throws IOException {
        UserService target = new UserService();
        target.test();

        System.out.println("下面开始是cglib之后的逻辑了");

        // jdk代理
        {
            // UserInterface接口的代理对象
            Object proxy = Proxy.newProxyInstance(UserService.class.getClassLoader(), new Class[]{UserInterface.class}, new InvocationHandler() {

                @Override
                public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                    System.out.println("before...");
                    Object result = method.invoke(target, args);
                    System.out.println("after...");
                    return result;
                }
            });

            UserInterface userService = (UserInterface) proxy;
            userService.test();
        }
    }

如果你把new Class\[\]{UserInterface.class},替换成new Class\[\]{UserService.class},代码会直接报错:(这也证明了JDK采用的是第二种代理范式)

bash 复制代码
java.lang.IllegalArgumentException: UserService is not an interface

由于这个限制,所以产生的代理对象的类型是UserInterface,而不是UserService,这是需要注意的。

1.3 ProxyFactory:Spring对两种代理的封装

上面我们介绍了两种动态代理技术,那么在Spring中对它们进行了封装,封装出来的类叫做ProxyFactory,表示是创建代理对象的一个工厂,使用起来会比上面的更加方便,比如:

java 复制代码
    public static void main(String[] args) throws IOException {
        UserService target = new UserService();

        ProxyFactory proxyFactory = new ProxyFactory();
        proxyFactory.setTarget(target);
        proxyFactory.addAdvice(new MethodInterceptor() {
            @Override
            public Object invoke(MethodInvocation invocation) throws Throwable {
                System.out.println("before...");
                Object result = invocation.proceed();
                System.out.println("after...");
                return result;
            }
        });

        UserInterface userService = (UserInterface) proxyFactory.getProxy();
        userService.test();
    }

通过ProxyFactory,我们可以不再关心到底是用cglib还是jdk动态代理了,ProxyFactory会帮我们去判断,如果UserService实现了接口,那么ProxyFactory底层就会用jdk动态代理;如果没有实现接口,就会用cglib技术,上面的代码,就是由于UserService实现了UserInterface接口,所以最后产生的代理对象是UserInterface类型。

二、AOP基础知识

AOP基础概念回顾

SpringAOP大家都不陌生,但是在说AOP之前,我觉得再梳理一下其中的概念,对我们进一步研究有很大的帮助。而且,我相信,就算大家SpringAOP已经有过实际生产经验了,下面的一些概念也不一定懂。另外,有一些概念会出现在我们后面【发展历程】中,看似一字之差,但其实是两码事。

  • 切面(AspectJ): 在Aop中切面指的就是【切面类】。切面类中管理着增强的公共行为代码【即:通知】 和 切入方式【即:切点】
  • 通知(Advice):在切面的某个特定的连接点上执行的【动作】。通知有多种类型,包括【around】、【before】和【after】等等。许多AOP框架,包括Spring在内,都是以【拦截器】做通知模型的,并维护着一个以连接点为中心的【拦截器链】
  • 切点(PointCut):匹配连接点的断言。【Advice通知】和【切点】表达式相关联,并在满足这个【切点】的【连接点】上运行(如,当执行某个特定名称的方法时)。切点表达式何和连接点匹配是AOP的核心:。Spring默认使用AspectJ切点语义
  • 连接点(JoinPoint): 在Spring AOP中,一个连接点总是代表一个方法的执行,其实就代表被增强的【方法】
  • 目标对象(Target):目标对象指将要被增强的对象。即包含主业务逻辑的类的对象
  • 顾问(Advisor):顾问是Advice的一种包装体现。Advisor是Pointcut以及Advice的一个结合,用来管理Advice和Pointcut,应用无需关心(但是研究源码需要知道)
  • 织入(Weaving):将通知切入连接点的过程叫做织入
  • 引入(Introductions):可以将其他接口和实现动态引入到targetClass中

(PS:特别声明一下,根据我的经验,好多人会把【通知Advice】跟【顾问Advisor】给混淆,毕竟这俩的英文太像了。特别是在读源码的过程中,一下子就蒙查查了)。

通知Advice的分类

相信大家看了上面的概念之后,油然升起一种熟悉的感觉,没错,他就是我们平时用的那些注解嘛。通常,它包含以下这些分类:

  1. Before Advice:方法之前执行。对应SpringAOP的@Before注解。另外也有对应的API接口:MethodBeforeAdvice继承了接口BeforeAdvice
  2. After returning advice:方法return后执行。对应SpringAOP的@AfterReturning注解。另外也有对应的API接口:AfterReturningAdvice
  3. After throwing advice:方法抛异常后执行。对应SpringAOP的@AfterThrowing注解。另外也有对应的API接口:ThrowsAdvice
  4. After (finally) advice:方法执行完finally之后执行,这是最后的,比return更后。对应SpringAOP的@After注解。另外也有对应的API接口:AfterAdvice
  5. Around advice:这是功能最强大的Advice,可以自定义执行顺序。对应SpringAOP的@Around注解。另外也有对应的API接口:MethodInterceptor

Spring会把五个注解解析为对应的Advice类:

  1. @Before:AspectJMethodBeforeAdvice,实际上就是一个MethodBeforeAdvice
  2. @AfterReturning:AspectJAfterReturningAdvice,实际上就是一个AfterReturningAdvice
  3. @AfterThrowing:AspectJAfterThrowingAdvice,实际上就是一个MethodInterceptor
  4. @After:AspectJAfterAdvice,实际上就是一个MethodInterceptor
  5. @Around:AspectJAroundAdvice,实际上就是一个MethodInterceptor

Advisor的理解

跟Advice类似的还有一个Advisor的概念,一个Advisor是有一个Pointcut和一个Advice组成的,通过Pointcut可以指定要需要被代理的逻辑,比如一个UserService类中有两个方法,按上面的例子,这两个方法都会被代理,被增强,那么我们现在可以通过Advisor,来控制到具体代理哪一个方法,比如:

java 复制代码
	public static void main(String[] args) {
        PointcutAdvisor pointcutAdvisor = new PointcutAdvisor() {

            @Override
            public Pointcut getPointcut() {
                return new StaticMethodMatcherPointcut() {
                    @Override
                    public boolean matches(Method method, Class<?> targetClass) {
                        return method.getName().equals("test");
                    }
                };
            }

            @Override
            public Advice getAdvice() {
                return new MethodInterceptor() {
                    @Override
                    public Object invoke(MethodInvocation invocation) throws Throwable {
                        System.out.println("before...");
                        Object result = invocation.proceed();
                        System.out.println("after...");
                        return result;
                    }
                };
            }

            @Override
            public boolean isPerInstance() {
                return false;
            }
        };

        // 注入advisor
        UserService target = new UserService();
        ProxyFactory proxyFactory = new ProxyFactory();
        proxyFactory.setTarget(target);
        proxyFactory.addAdvisor(pointcutAdvisor);
        UserInterface userService = (UserInterface) proxyFactory.getProxy();
        userService.test();
    }

    // 系统输出如下:
    // before...
    // 这是UserService的一个普通方法
    // after...

上面代码表示,产生的代理对象,只有在执行test这个方法时才会被增强,会执行额外的逻辑,而在执行其他方法时是不会增强的。

三、创建代理对象的方式

上面介绍了Spring中所提供了ProxyFactory、Advisor、Advice、PointCut等技术来实现代理对象的创建,但是我们在使用Spring时,我们并不会直接这么去使用ProxyFactory的。比如说,我们希望ProxyFactory所产生的代理对象能直接就是Bean,能直接从Spring容器中得到UserSerivce的代理对象,而这些,Spring都是支持的,只不过,作为开发者的我们肯定得告诉Spring,那些类需要被代理,代理逻辑是什么。

3.1 ProxyFactoryBean

我们创建一个UserService的代理bean,会这么做:

java 复制代码
    @Bean
    public ProxyFactoryBean userServiceProxy() {
        UserService userService = new UserService();

        ProxyFactoryBean proxyFactoryBean = new ProxyFactoryBean();
        proxyFactoryBean.setTarget(userService);
        proxyFactoryBean.addAdvice(new MethodInterceptor() {

            @Override
            public Object invoke(MethodInvocation invocation) throws Throwable {
                System.out.println("before...1111");
                Object result = invocation.proceed();
                System.out.println("after...1111");
                return result;
            }
        });
        return proxyFactoryBean;
    }

通过这种方法来定义一个UserService的Bean,并且是经过了AOP的。但是这种方式只能针对某一个Bean,意味着,如果我们想要切入更多的Bean,需要给每一个Bean都创建一个对应的FactoryBean。这显然是我们无法接受的。

ProxyFactoryBean还有额外的功能,比如可以把某个Advise或Advisor定义成为Bean,然后在ProxyFactoryBean中进行设置:

java 复制代码
@Bean
    public MethodInterceptor zhouyuAroundAdvice() {
        return new MethodInterceptor() {
            @Override
            public Object invoke(MethodInvocation invocation) throws Throwable {
                System.out.println("before...");
                Object result = invocation.proceed();
                System.out.println("after...");
                return result;
            }
        };
    }

    @Bean
    public ProxyFactoryBean userService() {
        UserService userService = new UserService();

        ProxyFactoryBean proxyFactoryBean = new ProxyFactoryBean();
        proxyFactoryBean.setTarget(userService);
        proxyFactoryBean.setInterceptorNames("zhouyuAroundAdvise");
        return proxyFactoryBean;
    }

3.2 BeanNameAutoProxyCreator

ProxyFactoryBean得自己指定被代理的对象,那么我们可以通过BeanNameAutoProxyCreator来通过指定某个bean的名字,来对该bean进行代理。

java 复制代码
    @Bean
    public BeanNameAutoProxyCreator beanNameAutoProxyCreator() {
        BeanNameAutoProxyCreator beanNameAutoProxyCreator = new BeanNameAutoProxyCreator();
        beanNameAutoProxyCreator.setBeanNames("userSe*");
        beanNameAutoProxyCreator.setInterceptorNames("zhouyuAroundAdvice");
        beanNameAutoProxyCreator.setProxyTargetClass(true);
        return beanNameAutoProxyCreator;
    }

通过BeanNameAutoProxyCreator可以对批量的Bean进行AOP,并且指定了代理逻辑,指定了一个InterceptorName,也就是一个Advice,前提条件是这个Advice也得是一个Bean,这样Spring才能找到的,但是BeanNameAutoProxyCreator的缺点很明显,它只能根据beanName来指定想要代理的Bean。

3.3 DefaultAdvisorAutoProxyCreator

java 复制代码
   @Bean
    public DefaultPointcutAdvisor defaultPointcutAdvisor() {
        NameMatchMethodPointcut pointcut = new NameMatchMethodPointcut();
        pointcut.addMethodName("test");

        DefaultPointcutAdvisor defaultPointcutAdvisor = new DefaultPointcutAdvisor();
        defaultPointcutAdvisor.setPointcut(pointcut);
        defaultPointcutAdvisor.setAdvice(new ZhouyuAfterReturningAdvice());

        return defaultPointcutAdvisor;
    }

    @Bean
    public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator() {
        DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator = new DefaultAdvisorAutoProxyCreator();
        return defaultAdvisorAutoProxyCreator;
    }

通过DefaultAdvisorAutoProxyCreator会直接去找所有Advisor类型的Bean,根据Advisor中的PointCut和Advice信息,确定要代理的Bean以及代理逻辑。

但是,我们发现,通过这种方式,我们得依靠某一个类来实现定义我们的Advisor,或者Advise,或者Pointcut,那么这个步骤能不能更加简化一点呢?

对的,通过注解!比如我们能不能只定义一个类,然后通过在类中的方法上通过某些注解,来定义PointCut以及Advice,可以的,比如:

java 复制代码
@Aspect
@Component
public class ZhouyuAspect {
    @Before("execution(public void com.zhouyu.service.UserService.test())")
    public void zhouyuBefore(JoinPoint joinPoint) {
        System.out.println("zhouyuBefore");
    }
}

通过上面这个类,我们就直接定义好了所要代理的方法(通过一个表达式),以及代理逻辑(被@Before修饰的方法),简单明了,这样对于Spring来说,它要做的就是来解析这些注解了,解析之后得到对应的Pointcut对象、Advice对象,生成Advisor对象,扔进ProxyFactory中,进而产生对应的代理对象,具体怎么解析这些注解就是@EnableAspectJAutoProxy注解所要做的事情了,后面详细分析。

三、底层源码解析

3.1 概念回顾

3.2 核心方法讲解

学习总结

相关推荐
树若逝花若殇4 分钟前
基于Spring AOP优雅记录请求响应日志
java·spring boot
Yyyyyy~30 分钟前
【java】数组的定义和使用
java
麻辣布丁31 分钟前
java集合篇面试模拟
java·开发语言·面试
niucloud-admin33 分钟前
JAVA V6 多商户商城 开发文档——插件开发规范
java·开发语言
嘟哩DuliDuli1 小时前
创作 Agent 的任务状态该如何保存
android·java·javascript
wdfk_prog1 小时前
lely-canopen-rtt:为什么推荐在 RT-Thread 上使用 Lely CANopen 构建主站
java·运维·缓存·docker·容器
海盗12341 小时前
DSH 接入 OpenCode Go 报 400 MissingSessionID:从 LLM 语义层退到 fetch 传输层的完整排障
开发语言·后端·golang
Token掘金室1 小时前
JSON模式结构化输出报错
java·服务器·json
秋饼1 小时前
Spring AI 2.0 接入 DeepSeek V4.1 Flash 生产级实战
java·ai·技术分享·后端开发
IT枫斗者枫哥1 小时前
Java 分批导出仍然 OOM?用 32 MiB 堆复现三种 CSV 写法
java