SpringAOP详解(下)

news2025/7/8 11:01:54

proxyFactory代理对象创建方式和代理对象调用方法过程:

springaop创建动态代理对象和代理对象调用方法过程:

一、TargetSource的使用

@Lazy注解,当加在属性上时,会产生一个代理对象赋值给这个属性,产生代理对象的代码为:

protected Object buildLazyResolutionProxy(final DependencyDescriptor descriptor, final @Nullable String beanName) {
  BeanFactory beanFactory = getBeanFactory();
  Assert.state(beanFactory instanceof DefaultListableBeanFactory,
    "BeanFactory needs to be a DefaultListableBeanFactory");
  final DefaultListableBeanFactory dlbf = (DefaultListableBeanFactory) beanFactory;

  TargetSource ts = new TargetSource() {
   @Override
   public Class<?> getTargetClass() {
    return descriptor.getDependencyType();
   }
   @Override
   public boolean isStatic() {
    return false;
   }
   @Override
   public Object getTarget() {
    Set<String> autowiredBeanNames = (beanName != null ? new LinkedHashSet<>(1) : null);
    Object target = dlbf.doResolveDependency(descriptor, beanName, autowiredBeanNames, null);
    if (target == null) {
     Class<?> type = getTargetClass();
     if (Map.class == type) {
      return Collections.emptyMap();
     }
     else if (List.class == type) {
      return Collections.emptyList();
     }
     else if (Set.class == type || Collection.class == type) {
      return Collections.emptySet();
     }
     throw new NoSuchBeanDefinitionException(descriptor.getResolvableType(),
       "Optional dependency not present for lazy injection point");
    }
    if (autowiredBeanNames != null) {
     for (String autowiredBeanName : autowiredBeanNames) {
      if (dlbf.containsBean(autowiredBeanName)) {
       dlbf.registerDependentBean(autowiredBeanName, beanName);
      }
     }
    }
    return target;
   }
   @Override
   public void releaseTarget(Object target) {
   }
  };

  ProxyFactory pf = new ProxyFactory();
  pf.setTargetSource(ts);
  Class<?> dependencyType = descriptor.getDependencyType();
  if (dependencyType.isInterface()) {
   pf.addInterface(dependencyType);
  }
  return pf.getProxy(dlbf.getBeanClassLoader());
 }

这段代码就利用了ProxyFactory来生成代理对象,以及使用了TargetSource,以达到代理对象在执行某个方法时,调用TargetSource的getTarget()方法实时得到一个被代理对象。 

二、Introduction

需要使用@EnableAspectJAutoProxy注解

@Component
public class UserService {

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

}

public interface UserInterface {

	void test();
}

public class UserInterfaceImpl implements UserInterface {

	@Override
	public void test() {
		System.out.println("UserInterfaceImpl");
	}
}

@Aspect
@Component
public class TestAspect {

	@DeclareParents(value="com.spring.service.UserService", defaultImpl=com.spring.service.UserInterfaceImpl.class)
	public UserInterface userInterface;
}



AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(AppConfig.class);
		UserInterface userService = (UserInterface) applicationContext.getBean("userService");
		userService.test();

spring创建bean初始化后会扫描@DeclareParents注解

三、ProxyFactory选择cglib或jdk动态代理原理

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

// config就是ProxyFactory对象

// optimize为true,或proxyTargetClass为true,或用户没有给ProxyFactory对象添加interface
if (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);
}

四、代理对象创建过程

1、JdkDynamicAopProxy

  1. 在构造JdkDynamicAopProxy对象时,会先拿到被代理对象自己所实现的接口,并且额外的增加SpringProxy、Advised、DecoratingProxy三个接口,组合成一个Class[],并赋值给proxiedInterfaces属性
  2. 并且检查这些接口中是否定义了equals()、hashcode()方法
  3. 执行Proxy.newProxyInstance(classLoader, this.proxiedInterfaces, this),得到代理对象,JdkDynamicAopProxy作为InvocationHandler,代理对象在执行某个方法时,会进入到JdkDynamicAopProxy的**invoke()**方法中

2、ObjenesisCglibAopProxy

  1. 创建Enhancer对象
  2. 设置Enhancer的superClass为通过ProxyFactory.setTarget()所设置的对象的类
  3. 设置Enhancer的interfaces为通过ProxyFactory.addInterface()所添加的接口,以及SpringProxy、Advised、DecoratingProxy接口
  4. 设置Enhancer的Callbacks为DynamicAdvisedInterceptor
  5. 最后创建一个代理对象,代理对象在执行某个方法时,会进入到DynamicAdvisedInterceptor的intercept()方法中

五、代理对象执行过程

  1. 在使用ProxyFactory创建代理对象之前,需要往ProxyFactory先添加Advisor
  2. 代理对象在执行某个方法时,会把ProxyFactory中的Advisor拿出来和当前正在执行的方法进行匹配筛选
  3. 把和方法所匹配的Advisor适配成MethodInterceptor
  4. 把和当前方法匹配的MethodInterceptor链,以及被代理对象、代理对象、代理类、当前Method对象、方法参数封装为MethodInvocation对象
  5. 调用MethodInvocation的proceed()方法,开始执行各个MethodInterceptor以及被代理对象的对应方法
  6. 按顺序调用每个MethodInterceptor的invoke()方法,并且会把MethodInvocation对象传入invoke()方法
  7. 直到执行完最后一个MethodInterceptor了,就会调用invokeJoinpoint()方法,从而执行被代理对象的当前方法

各注解对应的MethodInterceptor

  • @Before对应的是AspectJMethodBeforeAdvice,在进行动态代理时会把AspectJMethodBeforeAdvice转成MethodBeforeAdviceInterceptor
    • 先执行advice对应的方法
    • 再执行MethodInvocation的proceed(),会执行下一个Interceptor,如果没有下一个Interceptor了,会执行target对应的方法
  • @After对应的是AspectJAfterAdvice,直接实现了MethodInterceptor
    • 先执行MethodInvocation的proceed(),会执行下一个Interceptor,如果没有下一个Interceptor了,会执行target对应的方法
    • 再执行advice对应的方法
  • @Around对应的是AspectJAroundAdvice,直接实现了MethodInterceptor
    • 直接执行advice对应的方法,由@Around自己决定要不要继续往后面调用
  • @AfterThrowing对应的是AspectJAfterThrowingAdvice,直接实现了MethodInterceptor
    • 先执行MethodInvocation的proceed(),会执行下一个Interceptor,如果没有下一个Interceptor了,会执行target对应的方法
    • 如果上面抛了Throwable,那么则会执行advice对应的方法
  • @AfterReturning对应的是AspectJAfterReturningAdvice,在进行动态代理时会把AspectJAfterReturningAdvice转成AfterReturningAdviceInterceptor
    • 先执行MethodInvocation的proceed(),会执行下一个Interceptor,如果没有下一个Interceptor了,会执行target对应的方法
    • 执行上面的方法后得到最终的方法的返回值
    • 再执行Advice对应的方法

六、AbstractAdvisorAutoProxyCreator

DefaultAdvisorAutoProxyCreator的父类是AbstractAdvisorAutoProxyCreator。

AbstractAdvisorAutoProxyCreator非常强大以及重要,只要Spring容器中存在这个类型的Bean,就相当于开启了AOP,AbstractAdvisorAutoProxyCreator实际上就是一个BeanPostProcessor,所以在创建某个Bean时,就会进入到它对应的生命周期方法中,比如:在某个Bean初始化之后,会调用wrapIfNecessary()方法进行AOP,底层逻辑是,AbstractAdvisorAutoProxyCreator会找到所有的Advisor,然后判断当前这个Bean是否存在某个Advisor与之匹配(根据Pointcut),如果匹配就表示当前这个Bean有对应的切面逻辑,需要进行AOP,需要产生一个代理对象。

七、@EnableAspectJAutoProxy

这个注解主要就是往Spring容器中添加了一个AnnotationAwareAspectJAutoProxyCreator类型的Bean。

AnnotationAwareAspectJAutoProxyCreator.png

AspectJAwareAdvisorAutoProxyCreator继承了AbstractAdvisorAutoProxyCreator,重写了findCandidateAdvisors()方法,AbstractAdvisorAutoProxyCreator只能找到所有Advisor类型的Bean对象,但是AspectJAwareAdvisorAutoProxyCreator除开可以找到所有Advisor类型的Bean对象,还能把@Aspect注解所标注的Bean中的@Before等注解及方法进行解析,并生成对应的Advisor对象。

所以,我们可以理解@EnableAspectJAutoProxy,其实就是像Spring容器中添加了一个AbstractAdvisorAutoProxyCreator类型的Bean,从而开启了AOP,并且还会解析@Before等注解生成Advisor。

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/945065.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

《向量数据库指南》——大模型时代向量数据库是刚需吗?

目录 从实际应用的角度来看 从技术发展的角度来看 如果你问我,我会毫不犹豫地回答:“是的,向量数据库是刚需。”为什么?听我慢慢给你解释。 首先,我们要理解什么是向量数据库。向量数据库是一种专门用于存储和查询向量数据的数据库。这些向量数据可以是文本、图像、音频…

基础数据结构:数组介绍

程序设计 数据结构算法 基本概念和术语 说到数据结构是什么&#xff0c;我们得先来谈谈什么叫数据。 正所谓"巧妇难为无米之炊’&#xff0c;再强大的计算机&#xff0c;也是要有"米’下锅才可以的&#xff0c;否则就是一堆破铜烂铁 这个"米"就是数据。…

【进程间通信】信号

(꒪ꇴ꒪ )&#xff0c;Hello我是祐言QAQ我的博客主页&#xff1a;C/C语言&#xff0c;数据结构&#xff0c;Linux基础&#xff0c;ARM开发板&#xff0c;网络编程等领域UP&#x1f30d;快上&#x1f698;&#xff0c;一起学习&#xff0c;让我们成为一个强大的攻城狮&#xff0…

vsftpd使用遇到的问题

1.正常创建 安装到配置 yum install -y vsftpd systemctl start vsftpd useradd -d /home/ftpuser ftpuer passwd ftpuser vim /etc/vsftpd/vsftpd.conf i chroot_local_userYES allow_writeable_chrootYES chroot_list_enableYES chroot_list_file/etc/vsftpd/chroot_list2.连…

【计算机网络】序列化与反序列化

文章目录 1. 如何处理结构化数据&#xff1f;序列化 与 反序列化 2. 实现网络版计算器1. Tcp 套接字的封装——sock.hpp创建套接字——Socket绑定——Bind将套接字设置为监听状态——Listen获取连接——Accept发起连接——Connect 2. 服务器的实现 ——TcpServer.hpp初始化启动…

UDP 多播(组播)

前言&#xff08;了解分类的IP地址&#xff09; 1.组播&#xff08;多播&#xff09; 单播地址标识单个IP接口&#xff0c;广播地址标识某个子网的所有IP接口&#xff0c;多播地址标识一组IP接口。单播和广播是寻址方案的两个极端&#xff08;要么单个要么全部&#xff09;&am…

商城系统以拼团、砍价、分销为场景的用户增长,裂变和转化才是关键

其实&#xff0c;用户增长是一个非常大的话题。 用户增长不再是传统互联网意义上&#xff0c;一味地追求用户数量上的增长。用户增长是不断的提出增长假设&#xff0c;通过做实验与数据分析&#xff0c;验证假设的正确性&#xff0c;以此循环往复&#xff0c;反复迭代&#xf…

使用智能电磁流量计的时候有哪些方面要注意的?

在各大工业行业领域&#xff0c;流量计量的技术含量和多元性非常高。科学研究它们对提升产品质量、减少企业经营成本、环保节能和处理生态环境保护具有十分重要的意义。智能电磁流量计具备无摩擦阻力、没压力的优势&#xff0c;充足降低了管道里的摩擦阻力&#xff0c;合乎节能…

【微服务部署】02-配置管理

文章目录 1.ConfigMap1.1 创建ConfigMap方式1.2 使用ConfigMap的方式1.3 ConfigMap使用要点建议 2 分布式配置中心解决方案2.1 什么时候选择配置中心2.2 Apollo配置中心系统的能力2.2.1 Apollo创建配置项目2.2.2 项目使用2.2.3 K8s中使用Apollo 1.ConfigMap ConfigMap是K8s提供…

Yolov8-pose关键点检测:模型轻量化创新 | DCNV3结合c2f | CVPR2023

💡💡💡本文解决什么问题:模型轻量化创新引入DCNV3 DCNV3| GFLOPs从9.6降低至8.6,参数量从6482kb降低至5970kb, mAP50从0.921提升至0.926 Yolov8-Pose关键点检测专栏介绍:https://blog.csdn.net/m0_63774211/category_12398833.html ✨✨✨手把手教你从数据标记到…

Java 集合框架1

一、集合框架 1.概念 二、Collection接口 Collection接口之下有两个子接口:List接口/Set接口 List接口是用来处理有序的单列数据&#xff0c;可以有重复的元素。 Set接口是用来处理无序的单列数据&#xff0c;没有重复的元素,重复的元素算一个 三、List接口 …

Redis 7 第三讲 数据类型 进阶篇

⑥ *位图 bitmap 1. 理论 由0和1 状态表现的二进制位的bit 数组。 说明:用String 类型作为底层数据结构实现的一种统计二值状态的数据类型 位图本质是数组,它是基于String 数据类型的按位操作。该数组由多个二进制位组成,每个二进制位都对应一个偏…

DC/DC开关电源学习笔记(一)开关电源技术概述

&#xff08;一&#xff09;开关电源技术概述 1.什么是开关电源&#xff1f;2.开关电源技术概述2.1 小型化、薄型化、轻量化、高频化2.2 高可靠性2.3 低噪声2.4 采用计算机辅助设计和控制 1.什么是开关电源&#xff1f; 开关模式电源&#xff08;Switch Mode Power Supply&…

[JAVA学习笔记]常用类

String类&#xff1a; 一、存放位置&#xff1a; 字符串对象创建好后不能修改 String是引用数据类型&#xff0c;但是这里作为方法参数传递的时候&#xff0c;效果跟基本数据类型是一样的。也就是说在堆中创建出来的字符串”monkey”是不能被改变的&#xff0c;如果…

hadoop 学习:mapreduce 入门案例一:WordCount 统计一个文本中单词的个数

一 需求 这个案例的需求很简单 现在这里有一个文本wordcount.txt&#xff0c;内容如下 现要求你使用 mapreduce 框架统计每个单词的出现个数 这样一个案例虽然简单但可以让新学习大数据的同学熟悉 mapreduce 框架 二 准备工作 &#xff08;1&#xff09;创建一个 maven 工…

Node爬虫项目精简版 wallhaven网站实操 2023.8.29

练习地址&#xff1a; https://wallhaven.cc/toplist const express require(express); const axios require(axios); const cheerio require(cheerio); const schedule require(node-schedule); const fs require(fs);async function downloadImage(url) {const response…

全国工业和信息化应用人才考试-- 服务外包 软件测试复习整理

试卷 201服务外包软件测试考试样卷(1)【附答案】 单项选择题&#xff08;每题1分&#xff0c;共40题40分&#xff09; 多项选择题&#xff08;每题2分&#xff0c;共10题20分&#xff09; 判断题&#xff08;每题1分&#xff0c;共10题10分&#xff09; 填空题&#xff…

基于stm32的ADS1292R 心电波形采集

一、前言 ADS1292R是TI公司早在几年前出产的一款医用级ADC芯片&#xff0c;它主要应用在医疗仪器(心电图ECG),可以监护患者以及病人护理和健身监视器。ADS1292R集成了心电采集所需要的部件&#xff0c;方便设备小型化。它的功耗极低&#xff0c;使得可以作为长时间监控成为可能…

CausalEGM安装使用

1代码来源 github&#xff1a;https://github.com/SUwonglab/CausalEGM/tree/main/src pip&#xff1a;Tutorial for Python Users — CausalEGM documentation 安装&#xff1a;Installation — CausalEGM documentation 版本&#xff1a; 2原理 关于CausalEGM 根据观察…

[文本挖掘和知识发现] 01.红楼梦主题演化分析——文献可视化分析软件CiteSpace入门

八月太忙&#xff0c;还是写一篇吧&#xff01; 本文是作者2023年8月底新开的专栏——《文本挖掘和知识发现》&#xff0c;主要结合Python、大数据分析和人工智能分享文本挖掘、知识图谱、知识发现、图书情报等内容。此外&#xff0c;这些内容也是作者《文本挖掘和知识发现&…