【Spring源码】Spring中的AOP底层原理分析

简介: 【Spring源码】Spring中的AOP底层原理分析

AOP中的几个概念

Advisor 和 Advice

Advice,我们通常都会把他翻译为通知,其实很不好理解,其实他还有另外一个意思,就是“建议”,我觉得把Advice理解为“建议”会更好。

比如,我们已经完成了一个功能,这时客户跟我们说,我建议在这个功能之前可以再增加一些逻辑,再之后再增加一些逻辑。

在Spring中,Advice分为:

  1. 前置Advice:MethodBeforeAdvice
  2. 后置Advice:AfterReturningAdvice
  3. 环绕Advice:MethodInterceptor
  4. 异常Advice:ThrowsAdvice

在利用Spring AOP去生成一个代理对象时,我们可以设置这个代理对象的Advice。

而对于Advice来说,它只表示了“建议”,它没有表示这个“建议”可以用在哪些方面。

就好比,我们已经完成了一个功能,客户给这个功能提了一个建议,但是这个建议也许也能用到其他功能上。

这时,就出现了Advisor,表示一个Advice可以应用在哪些地方,而“哪些地方”就是Pointcut(切点)。

Pointcut

切点,表示我想让哪些地方加上我的代理逻辑。

比如某个方法,

比如某些方法,

比如某些方法名前缀为“find”的方法,

比如某个类下的所有方法,等等。

在Pointcut中,有一个MethodMatcher,表示方法匹配器。

使用ProxyFactory通过编程创建AOP代理

定义一个MyAdvisor

public class MyAdvisor implements PointcutAdvisor {    @Override    public Pointcut getPointcut() {        NameMatchMethodPointcut methodPointcut = new NameMatchMethodPointcut();        methodPointcut.addMethodName("test");        return methodPointcut;    }    @Override    public Advice getAdvice() {        MethodBeforeAdvice methodBeforeAdvice = new MethodBeforeAdvice() {            @Override            public void before(Method method, Object[] args, Object target) throws Throwable {                System.out.println("执行方法前"+method.getName());            }        };        return methodBeforeAdvice;    }    @Override    public boolean isPerInstance() {        return false;    }}

定义一个UserService

public class UserService {    public void test() {        System.out.println("111");    }}


ProxyFactory factory = new ProxyFactory();factory.setTarget(new UserService());factory.addAdvisor(new MyAdvisor());UserService userService = (UserService) factory.getProxy();userService.test();

ProxyFactory的工作原理

ProxyFactory就是一个代理对象生产工厂,在生成代理对象之前需要对代理工厂进行配置。

ProxyFactory在生成代理对象之前需要决定到底是使用JDK动态代理还是CGLIB技术:

// config就是ProxyFactory对象// optimize为true,或proxyTargetClass为true,或用户没有给ProxyFactory对象添加interfaceif (config.isOptimize() || config.isProxyTargetClass() || hasNoUserSuppliedProxyInterfaces(config)) {    Class<?> targetClass = config.getTargetClass();    if (targetClass == null) {        throw new AopConfigException("TargetSource cannot determine target class: " +                "Either an interface or a target is required for proxy creation.");    }    // targetClass是接口,直接使用Jdk动态代理    if (targetClass.isInterface() || Proxy.isProxyClass(targetClass)) {        return new JdkDynamicAopProxy(config);    }    // 使用Cglib    return new ObjenesisCglibAopProxy(config);}else {    // 使用Jdk动态代理    return new JdkDynamicAopProxy(config);}

JdkDynamicAopProxy创建代理对象过程

  1. 获取生成代理对象所需要实现的接口集合
  1. 获取通过ProxyFactory.addInterface()所添加的接口,如果没有通过ProxyFactory.addInterface()添加接口,那么则看ProxyFactory.setTargetClass()所设置的targetClass是不是一个接口,把接口添加到结果集合中
  2. 同时把SpringProxy、Advised、DecoratingProxy这几个接口也添加到结果集合中去
  1. 确定好要代理的集合之后,就利用Proxy.newProxyInstance()生成一个代理对象

JdkDynamicAopProxy创建的代理对象执行过程

  1. 如果通过ProxyFactory.setExposeProxy()把exposeProxy设置为了true,那么则把代理对象设置到一个ThreadLocal(currentProxy)中去。
  2. 获取通过ProxyFactory所设置的target,如果设置的是targetClass,那么target将为null
  3. 根据当前所调用的方法对象寻找ProxyFactory中所添加的并匹配的Advisor,并且把Advisor封装为MethodInterceptor返回,得到MethodInterceptor链叫做chain
  4. 如果chain为空,则直接执行target对应的当前方法,如果target为null会报错
  5. 如果chain不为空,则会依次执行chain中的MethodInterceptor
  1. 如果当前MethodInterceptor是MethodBeforeAdviceInterceptor,那么则先执行Advisor中所advice的before()方法,然后执行下一个MethodInterceptor
  2. 如果当前MethodInterceptor是AfterReturningAdviceInterceptor,那么则先执行下一个MethodInterceptor,拿到返回值之后,再执行Advisor中所advice的afterReturning()方法

ObjenesisCglibAopProxy创建代理对象过程

  1. 创建Enhancer
  2. 设置Enhancer的superClass为通过ProxyFactory.setTarget()所设置的对象的类
  3. 设置Enhancer的interfaces为通过ProxyFactory.addInterface()所添加的接口,以及SpringProxy、Advised接口
  4. 设置Enhancer的Callbacks为DynamicAdvisedInterceptor
  5. 最后通过Enhancer创建一个代理对象

ObjenesisCglibAopProxy创建的代理对象执行过程

执行过程主要就看DynamicAdvisedInterceptor中的实现,执行逻辑和JdkDynamicAopProxy中是一样的。

使用“自动代理(autoproxy)”功能

"自动代理"表示,只需要在Spring中添加某个Bean,这个Bean是一个BeanPostProcessor,那么Spring在每创建一个Bean时,都会经过这个BeanPostProcessor的判断,去判断当前正在创建的这个Bean是不是需要进行AOP。

我们可以在项目中定义很多个Advisor,定义方式有两种:

  1. 通过实现PointcutAdvisor接口
  2. 通过@Aspect、@Pointcut、@Before等注解

在创建某个Bean时,会根据当前这个Bean的信息,比如对应的类,以及当前Bean中的方法信息,去和定义的所有Advisor进行匹配,如果匹配到了其中某些Advisor,那么就会把这些Advisor给找出来,并且添加到ProxyFactory中去,在利用ProxyFactory去生成代理对象

BeanNameAutoProxyCreator

@Beanpublic BeanNameAutoProxyCreator creator(){    BeanNameAutoProxyCreator beanNameAutoProxyCreator = new BeanNameAutoProxyCreator();    beanNameAutoProxyCreator.setBeanNames("userService");      beanNameAutoProxyCreator.setInterceptorNames("myAdvisor");    return beanNameAutoProxyCreator;}

定义的这个bean,相当于一个“自动代理”器,有了这个Bean之后,可以自动的对setBeanNames中所对应的bean进行代理,代理逻辑为所设置的interceptorNames

DefaultAdvisorAutoProxyCreator

DefaultAdvisorAutoProxyCreator这个更加强大,只要添加了这个Bean,它就会自动识别所有的Advisor中的PointCut进行代理

AbstractAutoProxyCreator实现了SmartInstantiationAwareBeanPostProcessor接口,是一个BeanPostProcessor

  1. 在某个Bean实例化之前,查看该AbstractAutoProxyCreator中是不是设置了CustomTargetSource,如果设置了就查看当前Bean是不是需要创建一个TargetSource,如果需要就会创建一个TargetSource对象,然后进行AOP创建一个代理对象,并返回该代理对象
  2. 如果某个Bean出现了循环依赖,那么会利用getEarlyBeanReference()方法提前进行AOP
  3. 在某个Bean初始化之后,会调用wrapIfNecessary()方法进行AOP
  4. 在这个类中提供了一个抽象方法:getAdvicesAndAdvisorsForBean(),表示对于某个Bean匹配了哪些Advices和Advisors

AbstractAdvisorAutoProxyCreator继承了AbstractAutoProxyCreator,AbstractAdvisorAutoProxyCreator中实现了getAdvicesAndAdvisorsForBean()方法,实现逻辑为:

  1. 调用findEligibleAdvisors()
  1. 调用findCandidateAdvisors,得到所有Advisor类型的Bean
  2. 按当前正在进行Bean的生命周期的Bean进行过滤

@EnableAspectJAutoProxy

这个注解主要是添加了一个AnnotationAwareAspectJAutoProxyCreator类型的BeanDefinition

AspectJAwareAdvisorAutoProxyCreator继承了AbstractAdvisorAutoProxyCreator,重写了shouldSkip(Class<?> beanClass, String beanName)方法,表示某个bean需不需要进行AOP,在shouldSkip()方法中:

  1. 拿到所有的Advisor
  2. 遍历所有的Advisor,如果当前bean是AspectJPointcutAdvisor,那么则跳过

AnnotationAwareAspectJAutoProxyCreator继承了AspectJAwareAdvisorAutoProxyCreator,重写了findCandidateAdvisors()方法,它即可以找到Advisor类型的bean,也能把所有@Aspect注解标注的类扫描出来并生成Advisor

注解和源码对应关系

  1. @Before对应的是AspectJMethodBeforeAdvice,直接实现MethodBeforeAdvice,在进行动态代理时会把AspectJMethodBeforeAdvice转成MethodBeforeAdviceInterceptor,也就转变成了MethodBeforeAdviceInterceptor
  1. 先执行advice对应的方法
  2. 再执行MethodInvocation的proceed(),会执行下一个Interceptor,如果没有下一个Interceptor了,会执行target对应的方法
  1. @After对应的是AspectJAfterAdvice,直接实现了MethodInterceptor
  1. 先执行MethodInvocation的proceed(),会执行下一个Interceptor,如果没有下一个Interceptor了,会执行target对应的方法
  2. 再执行advice对应的方法
  1. @Around对应的是AspectJAroundAdvice,直接实现了MethodInterceptor
  1. 直接执行advice对应的方法
  1. @AfterThrowing对应的是AspectJAfterThrowingAdvice,直接实现了MethodInterceptor
  1. 先执行MethodInvocation的proceed(),会执行下一个Interceptor,如果没有下一个Interceptor了,会执行target对应的方法
  2. 如果上面抛了Throwable,那么则会执行advice对应的方法
  1. @AfterReturning对应的是AspectJAfterReturningAdvice,实现了AfterReturningAdvice,在进行动态代理时会把AspectJAfterReturningAdvice转成AfterReturningAdviceInterceptor,也就转变成了MethodInterceptor
  1. 先执行MethodInvocation的proceed(),会执行下一个Interceptor,如果没有下一个Interceptor了,会执行target对应的方法
  2. 执行上面的方法后得到最终的方法的返回值
  3. 再执行Advice对应的方法

Spring中AOP原理流程图

Introduction

Spring的文档上对Introduction这个概念和相关的注解@DeclareParents作了如下介绍:


Introductions (known as inter-type declarations in AspectJ) enable an aspect to declare that advised objects implement a given interface, and to provide an implementation of that interface on behalf of those objects.An introduction is made using the @DeclareParents annotation. This annotation is used to declare that matching types have a new parent (hence the name).

在这段介绍之后还给出了一个例子,对于初学者要理解这段话以及后面的例子还是蛮困难的,因此下面用一个简单的例子告诉大家什么是Introduction以及如何使用@DeclareParents注解。


对于Introduction这个词,个人认为理解成引入是最合适的,其目标是对于一个已有的类引入新的接口(有人可能会问:有什么用呢?简单的说,你可以把当前对象转型成另一个对象,那么很显然,你就可以调用另一个对象的方法了),看一个例子就全明白了。


假设已经有一个UserService类提供了保存User对象的服务,但是现在想增加对User进行验证的功能,只对通过验证的User提供保存服务,在不修改UserService类代码的前提下就可以通过Introduction来解决。


首先定义一个Verifier接口,里面定义了进行验证的方法validate(),如下所示:

package com.jackfrued.aop;
import com.jackfrued.models.User;
public interface Verifier {
    public boolean validate(User user);}

接下来给出该接口的一个实现类BasicVerifier,如下所示:


package com.jackfrued.aop;
import com.jackfrued.models.User;
public class BasicVerifier implements Verifier {
    @Override    public boolean validate(User user) {        if(user.getUsername().equals("jack") && user.getPassword().equals("1234")) {            return true;        }        return false;    }}

如何才能为UserService类增加验证User的功能呢,如下所示定义Aspect:

package com.jackfrued.aop;
import org.aspectj.lang.annotation.Aspect;import org.aspectj.lang.annotation.DeclareParents;import org.springframework.stereotype.Component;
@Aspect@Componentpublic class MyAspect {    @DeclareParents(value="com.tsinghuait.services.UserService",             defaultImpl=com.tsinghuait.aop.BasicVerifier.class)    public Verifier verifer;}

接下来就可以将UserService对象转型为Verifier对象并对用户进行验证了,如下所示:

package com.jackfrued.main;
import org.springframework.context.ApplicationContext;import org.springframework.context.support.ClassPathXmlApplicationContext;
import com.jackfrued.aop.Verifier;import com.jackfrued.models.User;import com.jackfrued.services.Service;
class Test {
    public static void main(String[] args) {        User user1 = new User();        user1.setUsername("abc");        user1.setPassword("def");                ApplicationContext factory = new ClassPathXmlApplicationContext("config.xml");        Service s = (Service) factory.getBean("service");        Verifier v = (Verifier) s;        if(v.validate(user1) {            System.out.println("验证成功");            s.serve(user1);        }            }}

这样,上面代码中的user1是不会被服务的,当然是因为没有通过验证啦!


这样一说,是不是大概明白什么是Introduction了呢,其实@DeclareParents用起来也很简单吧!

相关文章
|
4月前
|
监控 Java 应用服务中间件
高级java面试---spring.factories文件的解析源码API机制
【11月更文挑战第20天】Spring Boot是一个用于快速构建基于Spring框架的应用程序的开源框架。它通过自动配置、起步依赖和内嵌服务器等特性,极大地简化了Spring应用的开发和部署过程。本文将深入探讨Spring Boot的背景历史、业务场景、功能点以及底层原理,并通过Java代码手写模拟Spring Boot的启动过程,特别是spring.factories文件的解析源码API机制。
151 2
|
4月前
|
XML Java 开发者
Spring Boot开箱即用可插拔实现过程演练与原理剖析
【11月更文挑战第20天】Spring Boot是一个基于Spring框架的项目,其设计目的是简化Spring应用的初始搭建以及开发过程。Spring Boot通过提供约定优于配置的理念,减少了大量的XML配置和手动设置,使得开发者能够更专注于业务逻辑的实现。本文将深入探讨Spring Boot的背景历史、业务场景、功能点以及底层原理,并通过Java代码手写模拟Spring Boot的启动过程,为开发者提供一个全面的理解。
73 0
|
16天前
|
存储 监控 数据可视化
SaaS云计算技术的智慧工地源码,基于Java+Spring Cloud框架开发
智慧工地源码基于微服务+Java+Spring Cloud +UniApp +MySql架构,利用传感器、监控摄像头、AI、大数据等技术,实现施工现场的实时监测、数据分析与智能决策。平台涵盖人员、车辆、视频监控、施工质量、设备、环境和能耗管理七大维度,提供可视化管理、智能化报警、移动智能办公及分布计算存储等功能,全面提升工地的安全性、效率和质量。
|
2月前
|
监控 JavaScript 数据可视化
建筑施工一体化信息管理平台源码,支持微服务架构,采用Java、Spring Cloud、Vue等技术开发。
智慧工地云平台是专为建筑施工领域打造的一体化信息管理平台,利用大数据、云计算、物联网等技术,实现施工区域各系统数据汇总与可视化管理。平台涵盖人员、设备、物料、环境等关键因素的实时监控与数据分析,提供远程指挥、决策支持等功能,提升工作效率,促进产业信息化发展。系统由PC端、APP移动端及项目、监管、数据屏三大平台组成,支持微服务架构,采用Java、Spring Cloud、Vue等技术开发。
122 7
|
3月前
|
NoSQL Java Redis
Spring Boot 自动配置机制:从原理到自定义
Spring Boot 的自动配置机制通过 `spring.factories` 文件和 `@EnableAutoConfiguration` 注解,根据类路径中的依赖和条件注解自动配置所需的 Bean,大大简化了开发过程。本文深入探讨了自动配置的原理、条件化配置、自定义自动配置以及实际应用案例,帮助开发者更好地理解和利用这一强大特性。
251 14
|
3月前
|
XML Java 数据格式
Spring Core核心类库的功能与应用实践分析
【12月更文挑战第1天】大家好,今天我们来聊聊Spring Core这个强大的核心类库。Spring Core作为Spring框架的基础,提供了控制反转(IOC)和依赖注入(DI)等核心功能,以及企业级功能,如JNDI和定时任务等。通过本文,我们将从概述、功能点、背景、业务点、底层原理等多个方面深入剖析Spring Core,并通过多个Java示例展示其应用实践,同时指出对应实践的优缺点。
81 14
|
3月前
|
存储 缓存 Java
Spring面试必问:手写Spring IoC 循环依赖底层源码剖析
在Spring框架中,IoC(Inversion of Control,控制反转)是一个核心概念,它允许容器管理对象的生命周期和依赖关系。然而,在实际应用中,我们可能会遇到对象间的循环依赖问题。本文将深入探讨Spring如何解决IoC中的循环依赖问题,并通过手写源码的方式,让你对其底层原理有一个全新的认识。
89 2
|
4月前
|
Java 开发者 Spring
Spring AOP 底层原理技术分享
Spring AOP(面向切面编程)是Spring框架中一个强大的功能,它允许开发者在不修改业务逻辑代码的情况下,增加额外的功能,如日志记录、事务管理等。本文将深入探讨Spring AOP的底层原理,包括其核心概念、实现方式以及如何与Spring框架协同工作。
|
4月前
|
前端开发 Java 开发者
Spring生态学习路径与源码深度探讨
【11月更文挑战第13天】Spring框架作为Java企业级开发中的核心框架,其丰富的生态系统和强大的功能吸引了无数开发者的关注。学习Spring生态不仅仅是掌握Spring Framework本身,更需要深入理解其周边组件和工具,以及源码的底层实现逻辑。本文将从Spring生态的学习路径入手,详细探讨如何系统地学习Spring,并深入解析各个重点的底层实现逻辑。
107 9
|
Java Spring
Spring原理学习系列之五:IOC原理之Bean加载
其实很多同学都想通过阅读框架的源码以汲取框架设计思想以及编程营养,Spring框架其实就是个很好的框架源码学习对象。我们都知道Bean是Spring框架的最小操作单元,Spring框架通过对于Bean的统一管理实现其IOC以及AOP等核心的框架功能,那么Spring框架是如何把Bean加载到环境中来进行管理的呢?本文将围绕这个话题进行详细的阐述,并配合Spring框架的源码解析。
Spring原理学习系列之五:IOC原理之Bean加载

热门文章

最新文章