Spring中AOP相关的API及源码解析,原来AOP是这样子的(2)

本文涉及的产品
全局流量管理 GTM,标准版 1个月
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
云解析 DNS,旗舰版 1个月
简介: Spring中AOP相关的API及源码解析,原来AOP是这样子的(2)

关于通知的总结


通过上文的分析我们可以发现,通知总共可以分为这么几类


  1. 普通的通知(前置,后置,异常等,没有实现MethodInterceptor接口)
  2. 环绕通知(实现了MethodInterceptor接口)
  3. 引入通知(需要提供额外的引入的信息,实现了MethodInterceptor接口)


上面的分类并不标准,只是为了方便大家记忆跟理解,虽然我们普通的通知没有直接实现MethodInterceptor接口,但其实它的底层也是依赖于拦截器来完成的,大家可以看看下面这个类

class MethodBeforeAdviceAdapter implements AdvisorAdapter, Serializable {
  @Override
  public boolean supportsAdvice(Advice advice) {
    return (advice instanceof MethodBeforeAdvice);
  }
    // 根据传入的一个前置通知,创建一个对应的拦截器
  @Override
  public MethodInterceptor getInterceptor(Advisor advisor) {
    MethodBeforeAdvice advice = (MethodBeforeAdvice) advisor.getAdvice();
    return new MethodBeforeAdviceInterceptor(advice);
  }
}
public class MethodBeforeAdviceInterceptor implements MethodInterceptor, BeforeAdvice, Serializable {
  private final MethodBeforeAdvice advice;
  public MethodBeforeAdviceInterceptor(MethodBeforeAdvice advice) {
    Assert.notNull(advice, "Advice must not be null");
    this.advice = advice;
  }
  // 实际上还是利用拦截器,在方法执行前调用了通知的before方法完成了前置通知
  @Override
  public Object invoke(MethodInvocation mi) throws Throwable {
    this.advice.before(mi.getMethod(), mi.getArguments(), mi.getThis());
    return mi.proceed();
  }
}

Advisor (绑定通知跟切点)


一个Advisor实际上就是一个绑定在指定切点上的通知。在前面的例子我们可以发现,有两种添加通知的方式

// 一个Advisor代表的是一个已经跟指定切点绑定了的通知
// 在这个例子中意味着环绕通知不会作用到toString方法上
Advisor advisor = new DefaultPointcutAdvisor(new DmzPointcut(), new DmzAroundAdvice());
// 添加一个绑定了指定切点的环绕通知
proxyFactory.addAdvisor(advisor);
// 添加一个返回后的通知
proxyFactory.addAdvice(new DmzAfterReturnAdvice());

一种是直接添加了一个Advisor,还有一种是添加一个Advice,后者也会被转换成一个Advisor然后再进行添加,没有指定切点的通知是没有任何意义的

public void addAdvice(Advice advice) throws AopConfigException {
    int pos = this.advisors.size();
    // 默认添加到集合的最后一个位置
    addAdvice(pos, advice);
}
// 这个方法添加通知
public void addAdvice(int pos, Advice advice) throws AopConfigException {
    Assert.notNull(advice, "Advice must not be null");
    // 如果是一个引入通知,那么构建一个DefaultIntroductionAdvisor
    // DefaultIntroductionAdvisor会匹配所有类
    if (advice instanceof IntroductionInfo) {
        addAdvisor(pos, new DefaultIntroductionAdvisor(advice, (IntroductionInfo) advice));
    }
    // 不能直接添加一个不是IntroductionInfo的DynamicIntroductionAdvice(动态引入通知)
    else if (advice instanceof DynamicIntroductionAdvice) {
        throw new AopConfigException("DynamicIntroductionAdvice may only be added as part of IntroductionAdvisor");
    }
    else {
        // 如果是普通的通知,那么会创建一个DefaultPointcutAdvisor
        // DefaultPointcutAdvisor所定义的切点会匹配所有类以及所有方法
        addAdvisor(pos, new DefaultPointcutAdvisor(advice));
    }
}

ProxyCreatorSupport


这个类的主要作用是为创建一个AOP代理对象提供一些功能支持,通过它的getAopProxyFactory能获取一个创建代理对象的工厂。

// 这里我只保留了这个类中的关键代码
public class ProxyCreatorSupport extends AdvisedSupport { 
    private AopProxyFactory aopProxyFactory;
    // 空参构造,默认会创建一个DefaultAopProxyFactory
    // 通过这个ProxyFactory可以创建一个cglib代理或者jdk代理
    public ProxyCreatorSupport() {
        this.aopProxyFactory = new DefaultAopProxyFactory();
    }
    // 通过这个方法可以创建一个具体的代理对象
    protected final synchronized AopProxy createAopProxy() {
        if (!this.active) {
            activate();
        }
        // 实际就是使用DefaultAopProxyFactory来创建一个代理对象
        // 可以看到在调用createAopProxy方法时,传入的参数是this
        // 这是因为ProxyCreatorSupport本身就保存了创建整个代理对象所需要的配置信息
        return getAopProxyFactory().createAopProxy(this);
    }
}

image.png

另外通过上面的UML类图还能看到,ProxyCreatorSupport继承了AdvisedSupport,AdvisedSupport继承了ProxyConfig。


ProxyConfig


其中ProxyConfig是所有的AOP代理工厂的父类,它包含了创建一个AOP代理所需要的基础的通用的一些配置信息

// 这里省略了一些getter跟setter方法
public class ProxyConfig implements Serializable {
    // 是否开启cglib代理,默认不开启使用jdk动态代理
  private boolean proxyTargetClass = false;
    // 是否启用优化,默认为false,按照官网对这个参数的解释
    // 这个优化是针对cglib,如果设计为true的话,会做一些侵入性的优化
    // 是否开启在jdk代理的情况下没有影响
    // 官网中特地说明了,除非对cglib的优化非常了解,否则不要开启这个参数
  private boolean optimize = false;
    // 生成的代理类是否需要实现Advised接口,这个接口可以向外提供操作通知的方法
    // 如果为false会实现
    // 为true的话,不会实现
  boolean opaque = false;
    // 是否将当前的配置类暴露到一个线程上下文中,如果设置为true的话
    // 可以通过AopContext.currentProxy()来获取到当前的代理对象
  boolean exposeProxy = false;
    // 标志着是否冻结整个配置,如果冻结了,那么配置信息将不允许修改
  private boolean frozen = false;
}

AdvisedSupport


当我们为某个对象创建代理时,除了需要上面的ProxyConfig提供的一些基础配置外,起码还需要知道


  1. 需要执行的通知是哪些?
  2. 目标对象是谁?
  3. 创建出来的代理需要实现哪些接口?

而这些配置信息是由AdvisedSupport提供的,AdvisedSupport本身实现了Advised接口,Advised接口定义了管理通知的方法。


在了解了上面的API后我们来看看Spring提供了几种创建AOP代理的方式


  1. ProxyFactoryBean
  2. ProxyFactory
  3. Auto-proxy


ProxyFactoryBean的方式创建AOP代理


使用示例

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
     xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
  <bean class="com.dmz.spring.initalize.service.DmzService" name="dmzService"/>
  <bean id="aroundAdvice" class="com.dmz.spring.initalize.aop.advice.DmzAroundAdvice"/>
  <bean id="dmzProxy"
      class="org.springframework.aop.framework.ProxyFactoryBean">
        <!---->
    <property name="proxyInterfaces" value="java.lang.Runnable"/>
    <property name="proxyTargetClass" value="true"/>
    <property name="target" ref="dmzService"/>
    <property name="interceptorNames">
      <list>
        <value>aroundAdvice</value>
      </list>
    </property>
  </bean>
</beans>
// 目标类
public class DmzService {
  @Override
  public String toString() {
    System.out.println("dmzService toString invoke");
    return "dmzService";
  }
  public void testAop(){
    System.out.println("testAop invoke");
  }
}
// 通知
public class DmzAroundAdvice implements MethodInterceptor {
  @Override
  public Object invoke(MethodInvocation invocation) throws Throwable {
    System.out.println("aroundAdvice invoked");
    return invocation.proceed();
  }
}
public class SourceMain {
  public static void main(String[] args) {
    ClassPathXmlApplicationContext cc =
        new ClassPathXmlApplicationContext("application-init.xml");
    DmzService dmzProxy = ((DmzService) cc.getBean("dmzProxy"));
    dmzProxy.testAop();
  }
}

ProxyFactoryBean介绍


跟普通的FactoryBean一样,这个类的主要作用就是通过getObject方法能够获取一个Bean,不同的是这个类获取到的是代理后的Bean。

我们查看这个类的继承关系可以发现

image.png

这个类除了实现了FactoryBean接口以及一些Aware接口外,额外还继承了ProxyCreatorSupport类。它是一个factoryBean,所以我们重点就关注它的getObject方法即可。

public Object getObject() throws BeansException {
    // 初始化通知链
    // 这里主要就是将在XML中配置的通知添加到
    // AdvisedSupport管理的配置中去
    initializeAdvisorChain();
    if (isSingleton()) {
        // 如果是单例的,那么获取一个单例的代理对象
        return getSingletonInstance();
    }
    else {
        if (this.targetName == null) {
            logger.warn("Using non-singleton proxies with singleton targets is often undesirable. " +
                        "Enable prototype proxies by setting the 'targetName' property.");
        }
        // 如果是原型的,获取一个原型的代理对象
        return newPrototypeInstance();
    }
}

关于这段代码就不做过多分析了,它其实就两步(不管是哪种方式创建代理,都分为这两步)


1.完善创建代理需要的配置信息

2.创建代理


其中配置信息分为两部分,其一是AppConfig管理的通用的配置信息,其二是AdvisedSupport管理的通知信息。通用的配置信息我们可以直接在XML中配置,例如在上面的例子中我们就配置了proxyTargetClass属性,而通知信息即使我们在XML中配置了也还需要做一层转换,在前面我们也提到过了,所有的Advice都会被转换成Advisor添加到配置信息中。


ProxyFactory的方式创建AOP代理


使用示例(略,见开头)


ProxyFactory介绍


image.png

从上面我们可以看出,ProxyFactory也继承自ProxyCreatorSupport,从之前的例子我们也能感受到,使用它的API来创建一个代理对象也是要先去设置相关的配置信息,最后再调用创建代理的方法


我们之后要分析的自动代理内部就是通过创建了一个ProxyFactory来获取代理对象的。

我们可以对比下ProxyFactoryBean跟ProxyFactory在创建代理对象时的代码


ProxyFactory

public Object getProxy() {
    // 调用了ProxyCreatorSupport的createAopProxy()方法创建一个AopProxy对象
    // 然后调用AopProxy对象的getProxy方法
    return createAopProxy().getProxy();
}

ProxyFactoryBean

private synchronized Object getSingletonInstance() {
    if (this.singletonInstance == null) {
        this.targetSource = freshTargetSource();
        if (this.autodetectInterfaces && getProxiedInterfaces().length == 0 && !isProxyTargetClass()) {
            Class<?> targetClass = getTargetClass();
            if (targetClass == null) {
                throw new FactoryBeanNotInitializedException("Cannot determine target class for proxy");
            }
            setInterfaces(ClassUtils.getAllInterfacesForClass(targetClass, this.proxyClassLoader));
        }
        super.setFrozen(this.freezeProxy);
        // 重点就看这里
        // 这里调用了ProxyCreatorSupport的createAopProxy()方法创建一个AopProxy对象
        // 而getProxy方法就是调用创建的AopProxy的getProxy方法
        this.singletonInstance = getProxy(createAopProxy());
    }
    return this.singletonInstance;
}
protected Object getProxy(AopProxy aopProxy) {
    return aopProxy.getProxy(this.proxyClassLoader);
}

综上,我们可以得出结论,不管是通过哪种方式创建AOP代理,核心代码就一句

createAopProxy().getProxy()
相关文章
|
16天前
|
存储 人工智能 API
AgentScope:阿里开源多智能体低代码开发平台,支持一键导出源码、多种模型API和本地模型部署
AgentScope是阿里巴巴集团开源的多智能体开发平台,旨在帮助开发者轻松构建和部署多智能体应用。该平台提供分布式支持,内置多种模型API和本地模型部署选项,支持多模态数据处理。
145 4
AgentScope:阿里开源多智能体低代码开发平台,支持一键导出源码、多种模型API和本地模型部署
|
15天前
|
设计模式 XML Java
【23种设计模式·全精解析 | 自定义Spring框架篇】Spring核心源码分析+自定义Spring的IOC功能,依赖注入功能
本文详细介绍了Spring框架的核心功能,并通过手写自定义Spring框架的方式,深入理解了Spring的IOC(控制反转)和DI(依赖注入)功能,并且学会实际运用设计模式到真实开发中。
【23种设计模式·全精解析 | 自定义Spring框架篇】Spring核心源码分析+自定义Spring的IOC功能,依赖注入功能
|
15天前
|
存储 设计模式 算法
【23种设计模式·全精解析 | 行为型模式篇】11种行为型模式的结构概述、案例实现、优缺点、扩展对比、使用场景、源码解析
行为型模式用于描述程序在运行时复杂的流程控制,即描述多个类或对象之间怎样相互协作共同完成单个对象都无法单独完成的任务,它涉及算法与对象间职责的分配。行为型模式分为类行为模式和对象行为模式,前者采用继承机制来在类间分派行为,后者采用组合或聚合在对象间分配行为。由于组合关系或聚合关系比继承关系耦合度低,满足“合成复用原则”,所以对象行为模式比类行为模式具有更大的灵活性。 行为型模式分为: • 模板方法模式 • 策略模式 • 命令模式 • 职责链模式 • 状态模式 • 观察者模式 • 中介者模式 • 迭代器模式 • 访问者模式 • 备忘录模式 • 解释器模式
【23种设计模式·全精解析 | 行为型模式篇】11种行为型模式的结构概述、案例实现、优缺点、扩展对比、使用场景、源码解析
|
15天前
|
设计模式 存储 安全
【23种设计模式·全精解析 | 创建型模式篇】5种创建型模式的结构概述、实现、优缺点、扩展、使用场景、源码解析
结构型模式描述如何将类或对象按某种布局组成更大的结构。它分为类结构型模式和对象结构型模式,前者采用继承机制来组织接口和类,后者釆用组合或聚合来组合对象。由于组合关系或聚合关系比继承关系耦合度低,满足“合成复用原则”,所以对象结构型模式比类结构型模式具有更大的灵活性。 结构型模式分为以下 7 种: • 代理模式 • 适配器模式 • 装饰者模式 • 桥接模式 • 外观模式 • 组合模式 • 享元模式
【23种设计模式·全精解析 | 创建型模式篇】5种创建型模式的结构概述、实现、优缺点、扩展、使用场景、源码解析
|
15天前
|
设计模式 存储 安全
【23种设计模式·全精解析 | 创建型模式篇】5种创建型模式的结构概述、实现、优缺点、扩展、使用场景、源码解析
创建型模式的主要关注点是“怎样创建对象?”,它的主要特点是"将对象的创建与使用分离”。这样可以降低系统的耦合度,使用者不需要关注对象的创建细节。创建型模式分为5种:单例模式、工厂方法模式抽象工厂式、原型模式、建造者模式。
【23种设计模式·全精解析 | 创建型模式篇】5种创建型模式的结构概述、实现、优缺点、扩展、使用场景、源码解析
|
1月前
|
PyTorch Shell API
Ascend Extension for PyTorch的源码解析
本文介绍了Ascend对PyTorch代码的适配过程,包括源码下载、编译步骤及常见问题,详细解析了torch-npu编译后的文件结构和三种实现昇腾NPU算子调用的方式:通过torch的register方式、定义算子方式和API重定向映射方式。这对于开发者理解和使用Ascend平台上的PyTorch具有重要指导意义。
|
16天前
|
安全 搜索推荐 数据挖掘
陪玩系统源码开发流程解析,成品陪玩系统源码的优点
我们自主开发的多客陪玩系统源码,整合了市面上主流陪玩APP功能,支持二次开发。该系统适用于线上游戏陪玩、语音视频聊天、心理咨询等场景,提供用户注册管理、陪玩者资料库、预约匹配、实时通讯、支付结算、安全隐私保护、客户服务及数据分析等功能,打造综合性社交平台。随着互联网技术发展,陪玩系统正成为游戏爱好者的新宠,改变游戏体验并带来新的商业模式。
|
4月前
Micronaut AOP与代理机制:实现应用功能增强,无需侵入式编程的秘诀
AOP(面向切面编程)能够帮助我们在不修改现有代码的前提下,为应用程序添加新的功能或行为。Micronaut框架中的AOP模块通过动态代理机制实现了这一目标。AOP将横切关注点(如日志记录、事务管理等)从业务逻辑中分离出来,提高模块化程度。在Micronaut中,带有特定注解的类会在启动时生成代理对象,在运行时拦截方法调用并执行额外逻辑。例如,可以通过创建切面类并在目标类上添加注解来记录方法调用信息,从而在不侵入原有代码的情况下增强应用功能,提高代码的可维护性和可扩展性。
88 1
|
2月前
|
安全 Java 编译器
什么是AOP面向切面编程?怎么简单理解?
本文介绍了面向切面编程(AOP)的基本概念和原理,解释了如何通过分离横切关注点(如日志、事务管理等)来增强代码的模块化和可维护性。AOP的核心概念包括切面、连接点、切入点、通知和织入。文章还提供了一个使用Spring AOP的简单示例,展示了如何定义和应用切面。
220 1
什么是AOP面向切面编程?怎么简单理解?
|
2月前
|
XML Java 开发者
论面向方面的编程技术及其应用(AOP)
【11月更文挑战第2天】随着软件系统的规模和复杂度不断增加,传统的面向过程编程和面向对象编程(OOP)在应对横切关注点(如日志记录、事务管理、安全性检查等)时显得力不从心。面向方面的编程(Aspect-Oriented Programming,简称AOP)作为一种新的编程范式,通过将横切关注点与业务逻辑分离,提高了代码的可维护性、可重用性和可读性。本文首先概述了AOP的基本概念和技术原理,然后结合一个实际项目,详细阐述了在项目实践中使用AOP技术开发的具体步骤,最后分析了使用AOP的原因、开发过程中存在的问题及所使用的技术带来的实际应用效果。
74 5

推荐镜像

更多
下一篇
开通oss服务