当前位置: 首页 > news >正文

Spring-SpringAOP的实现

对Spring AOP的理解

OOP表示面向对象编程,是一种编程思想,AOP表示面向切面编程,也是一种编程思想

Spring AOP:Spring为了让程序员更加方便的做到面向切面编程所提供的技术支持

Spring提供的一套机制,让我们更容易的进行AOP,这套机制就是Spring AOP

扩展:用注解的方式来定义Pointcut和Advice,Spring并不是首创,首创是 AspectJ。JBoss 4.0、aspectwerkz 等技术也提供了对于AOP的支持。

Spring是依赖了AspectJ的,Spring觉得AspectJ中的@Before、@Around等注解比较好用,所以把这些注解直接拿过来用,但是注解底层的解析是由Spring自己做的。

所以我们在用 Spring时,如果你想用@Before、@Around等注解,是需要单独引入aspecj相关jar包的:

compile group: 'org.aspectj', name: 'aspectjrt', version: '1.9.5'
compile group: 'org.aspectj', name: 'aspectjweaver', version: '1.9.5'

注意:AspectJ(它自己也是一个项目)是在编译时对字节码进行了修改,可以理解为是在编译时就会去解析@Before这些注解,然后得到代理逻辑,加入到被代理类的字节码中去,所以如果想用AspectJ技术来生成代理对象 ,是需要用单独的AspectJ编译器的。项目中很少用AspectJ编译器,只是用了@Before这些注解,在启动Spring的过程中会去解析这些注解,然后利用动态代理机制生成代理对象。

AOP中的概念

Spring官网:

Let us begin by defining some central AOP concepts and terminology. These terms are not Spring-specific. Unfortunately, AOP terminology is not particularly intuitive. However, it would be even more confusing if Spring used its own terminology

翻译:AOP中的这些概念不是Spring特有的,不幸的是,AOP中的概念不是特别直观的,但是,如果Spring重新定义自己的那可能会导致更加混乱

1、Aspect:表示切面,比如被@Aspect注解的类就是切面,可以在切面中去定义Pointcut、Advice等等

2、Join point:表示连接点,表示一个程序在执行过程中的一个点,比如一个方法的执行,比如一个异常的处理,在Spring AOP中,一个连接点通常表示一个方法的执行

3、Advice:表示通知,表示在一个特定连接点上所采取的动作。很多AOP框架中,包括Spring,会用Interceptor拦截器来实现Advice,并且在连接点周围维护一个Interceptor链

4、Pointcut:表示切点,用来匹配一个或多个连接点,Advice与切点表达式是关联在一起的,Advice将会执行在和切点表达式所匹配的连接点上

5、Introduction:可以使用@DeclareParents来给所匹配的类添加一个接口,并指定一个默认实现

6、Target object:目标对象,被代理对象

7、AOP proxy:表示代理工厂,用来创建代理对象的,在Spring Framework中,要么是JDK动态代理,要么是CGLIB代理

8、Weaving:表示织入,表示创建代理对象的动作,这个动作可以发生在编译时期(比如Aspejctj),或者运行时,比如Spring AOP

Advice在Spring AOP中对应的API

Aspject中用五个注解来定义Advice,表示代理逻辑,以及执行时机;Spring有提供类似执行时机的实现类:

Aspject注解(代理逻辑、执行时机)Spring实现类(类似执行时机)Spring解析注解为对应的Advice类
@Before接口MethodBeforeAdvice, 继承了接口BeforeAdviceAspectJMethodBeforeAdvice(实际上是MethodBeforeAdvice)
@AfterReturning接口AfterReturningAdviceAspectJAfterReturningAdvice(实际上是AfterReturningAdvice)
@AfterThrowing接口ThrowsAdviceAspectJAfterThrowingAdvice(实际上是MethodInterceptor)
@After接口AfterAdviceAspectJAfterAdvice(实际上是MethodInterceptor)
@Around接口MethodInterceptorAspectJAroundAdvice(实际上是MethodInterceptor)

TargetSource的使用

日常的AOP中,被代理对象就是Bean对象,是由BeanFactory创建出来的。

Spring AOP中提供了TargetSource机制,可以用自定义逻辑来创建被代理对象。

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

/*** Complete implementation of the* {@link org.springframework.beans.factory.support.AutowireCandidateResolver} strategy* interface, providing support for qualifier annotations as well as for lazy resolution* driven by the {@link Lazy} annotation in the {@code context.annotation} package.** @author Juergen Hoeller* @since 4.0*/
public class ContextAnnotationAutowireCandidateResolver extends QualifierAnnotationAutowireCandidateResolver {@Override@Nullablepublic Object getLazyResolutionProxyIfNecessary(DependencyDescriptor descriptor, @Nullable String beanName) {// 判断是不是懒注入(@Autowired+@Lazy)// 如果是则会在注入时生成一个代理对象注入给属性,所以懒注入并不代表属性为nullreturn (isLazy(descriptor) ? buildLazyResolutionProxy(descriptor, beanName) : null);}protected boolean isLazy(DependencyDescriptor descriptor) {for (Annotation ann : descriptor.getAnnotations()) {Lazy lazy = AnnotationUtils.getAnnotation(ann, Lazy.class);if (lazy != null && lazy.value()) {return true;}}MethodParameter methodParam = descriptor.getMethodParameter();if (methodParam != null) {Method method = methodParam.getMethod();if (method == null || void.class == method.getReturnType()) {Lazy lazy = AnnotationUtils.getAnnotation(methodParam.getAnnotatedElement(), Lazy.class);if (lazy != null && lazy.value()) {return true;}}}return false;}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() {@Overridepublic Class<?> getTargetClass() {return descriptor.getDependencyType();}@Overridepublic boolean isStatic() {return false;}@Overridepublic 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;}@Overridepublic void releaseTarget(Object target) {}};// ProxyFactory生成代理对象,并使用了TargetSource// 所以代理对象在执行某个方法时,调用TargetSource的getTarget()方法实时得到一个被代理对象ProxyFactory pf = new ProxyFactory();pf.setTargetSource(ts);Class<?> dependencyType = descriptor.getDependencyType();if (dependencyType.isInterface()) {pf.addInterface(dependencyType);}return pf.getProxy(dlbf.getBeanClassLoader());}
}

Introduction(@DeclareParents)简介

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注解实现

如何使用@DeclareParents注解?

// 第一步,添加一个接口
package com.gax.aop;
public interface Verifier
{boolean validate(User user);
}// 第二步,给接口添加一个实现类
package com.gax.aop;
public class BasicVerifier implements Verifier
{@Overridepublic boolean validate(User user){if (user.getName().equals("gc") && user.getPass().equals("6174")){return true;}return false;}
}// 第三步,使用@DeclareParents注解关联新增接口和原来的类
@Aspect
@Component
public class MyAspect
{@DeclareParents(value = "com.gax.service.UserService",defaultImpl = com.gax.aop.BasicVerifier.class)public Verifier verifer;
}// 第四步,测试效果
public class Test
{public static void main(String[] args){User user = new User();user.setPass("6174888");user.setName("gc");// 创建一个Spring容器AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(AppConfig.class);UserService userService = (UserService) applicationContext.getBean("userService");// 注意,这里Verifier是一个接口Verifier verifier = (Verifier) userService;// 验证通过才能提供服务if(verifier.validate(user)){userService.service();}}
}// AppConfig指定扫描包
@ComponentScan(value = "com.gax")
//@EnableAspectJAutoProxy
@Import(AnnotationAwareAspectJAutoProxyCreator.class)
public class AppConfig
{
}@Data
public class User
{private String name;private String pass;
}

@EnableAspectJAutoProxy

@Import(AnnotationAwareAspectJAutoProxyCreator.class)

某些情况下,上面这两种写法等价。@EnableAspectJAutoProxy注解内部其实就是注册了一个AnnotationAwareAspectJAutoProxyCreator

AnnotationAwareAspectJAutoProxyCreator其实就是一个BeanPostProcessor,在Spring启动过程中可以去解析AspectJ的注解

参考文章:https://www.cnblogs.com/powerwu/articles/5170861.html

LoadTimeWeaver

参考文章:https://www.cnblogs.com/davidwang456/p/5633609.html

相关文章:

Spring-SpringAOP的实现

对Spring AOP的理解 OOP表示面向对象编程&#xff0c;是一种编程思想&#xff0c;AOP表示面向切面编程&#xff0c;也是一种编程思想 Spring AOP&#xff1a;Spring为了让程序员更加方便的做到面向切面编程所提供的技术支持 Spring提供的一套机制&#xff0c;让我们更容易的…...

js:Browserslist用特定语句查询浏览器列表的工具与Babel和Postcss配置使用

目录 BrowserslistBabel 和 BrowserslistPostcss 和 Browserslist推荐配置参考文章 Browserslist Browserslist 是一个用特定语句查询浏览器列表的工具 文档 https://www.npmjs.com/package/browserslisthttps://github.com/browserslist/browserslist#full-list 安装 pnp…...

odoo16前端框架源码阅读——boot.js

odoo16前端框架源码阅读——boot.js 从名字就能看出来&#xff0c;这个文件是一个启动文件。 odoo前端将所有的js打包成了两个文件&#xff0c;一个是common.js,另一个是backend.js, 而common.js 是最先加载的 在common.js打包的js文件中最先加载的是下面的文件&#xff0c;看…...

使用MybatisPlus时出现的java.lang.NullPointerException异常~

错误描述如下所示&#xff1a; 错误原因&#xff1a;Junit的导包错误 单元测试的包有如下所示两个 我们应该根据springboot的版本进行选择&#xff0c; 在Spring Boot 2.2.X以后使用import org.junit.jupiter.api.Test Junit5 在Spring Boot 2.2.x之前使用import org.junit.T…...

27 微服务配置拉取

1&#xff09;引入nacos-config依赖 首先&#xff0c;在user-service服务中&#xff0c;引入nacos-config的客户端依赖&#xff1a; <!--nacos配置管理依赖--> <dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-sta…...

hutool ExcelUtil导出excel二级表头

简介&#xff1a;Hutool是一款十分好用的开发工具集&#xff0c;里面包含了大部分日常开发常用的工具&#xff0c;使用简单方便&#xff0c;可以大大提升日常开发效率&#xff0c;十分推荐大家使用。这里简单总结一下基于Hutool的Excel使用。 一、Hutool依赖 <!-- Excel导出…...

《开箱元宇宙》:认识香港麦当劳通过 The Sandbox McNuggets Land 的 Web3 成功经验

McNuggets Land 是 The Sandbox 于 2023 年发布的最受欢迎的体验之一。在本期的《开箱元宇宙》系列中&#xff0c;我们采访了香港麦当劳数位顾客体验暨合作伙伴资深总监 Kai Tsang&#xff0c;来了解这一成功案例背后的策略。 在不断发展的市场营销和品牌推广领域&#xff0c;不…...

基于python+TensorFlow+Django卷积网络算法+深度学习模型+蔬菜识别系统

欢迎大家点赞、收藏、关注、评论啦 &#xff0c;由于篇幅有限&#xff0c;只展示了部分核心代码。 文章目录 一项目简介 二、功能三、系统四. 总结 一项目简介 介绍了TensorFlow在图像识别分类中的应用&#xff0c;并通过相关代码进行了讲解。通过TensorFlow提供的工具和库&am…...

Python异常处理:三种不同方法的探索与最佳实践

Python异常处理&#xff1a;三种不同方法的探索与最佳实践 前言 本文旨在探讨Python中三种不同的异常处理方法。通过深入理解各种异常处理策略&#xff0c;我们可以更好地应对不同的编程场景&#xff0c;选择最适合自己需求的方法。 异常处理在编程中扮演着至关重要的角色。合…...

一文图解爬虫(spider)

—引导语 互联网&#xff08;Internet&#xff09;进化到今天&#xff0c;已然成为爬虫&#xff08;Spider&#xff09;编制的天下。从个体升级为组合、从组合联结为网络。因为有爬虫&#xff0c;我们可以更迅速地触达新鲜“网事”。 那么爬虫究竟如何工作的呢&#xff1f;允许…...

腾讯云3年期轻量应用服务器优惠(薅羊毛教程)

腾讯云轻量应用服务器特价是有新用户限制的&#xff0c;所以阿腾云建议大家选择3年期轻量应用服务器&#xff0c;一劳永逸&#xff0c;免去续费困扰。腾讯云轻量应用服务器3年优惠可以选择2核2G4M和2核4G5M带宽&#xff0c;3年轻量2核2G4M服务器540元&#xff0c;2核4G5M轻量应…...

多个div横向排列的几种方法

以下面这组 div 为例&#xff0c;group的高度由内容撑开 <div id"group"><div id"div1">div1</div><div id"div2">div2</div><div id"div3">div3</div> </div>显示结果如下为上下排…...

【编程语言发展史】Go语言的发展历史

目录 Go的起源 Go语言发展时间轴 logo Go的起源 Go 语言起源 2007 年&#xff0c;并于 2009 年正式对外发布。它从 2009 年 9 月 21 日开始作为谷歌公司 20% 兼职项目&#xff0c;即相关员工利用 20% 的空余时间来参与 Go 语言的研发工作。该项目的三位领导者均是著名的 …...

深入理解JVM虚拟机第二十三篇:详解JVM当中的栈顶缓存技术

大神链接:作者有幸结识技术大神孙哥为好友,获益匪浅。现在把孙哥视频分享给大家。 孙哥链接:孙哥个人主页 作者简介:一个颜值99分,只比孙哥差一点的程序员 本专栏简介:话不多说,让我们一起干翻JVM 本文章简介:话不多说,让我们讲清楚JVM当中与操作数栈相关的字节码指令…...

通过easyexcel导出数据到表格

这篇文章简单介绍一下怎么通过easyexcel做数据的导出&#xff0c;使用之前easyui构建的歌曲列表crud应用&#xff0c;添加一个导出按钮&#xff0c;点击的时候直接连接后端接口地址&#xff0c;在后端的接口完成数据的导出功能。 前端页面完整代码 let editingId; let request…...

C++入门学习(4)引用 (讲解拿指针比较)

上期回顾 在学习完函数重载之后&#xff0c;我们可以使用多个重名函数进行操作&#xff0c;会发现C真的是弥补了好多C语言的不足之处&#xff0c;真的不禁感概一下&#xff0c;时代的进步是需要人去做出改变的&#xff0c;而不是一味的使用啊&#xff01;所以我们今天继续学一下…...

温度采集DS18B20

/******************************************************************** * 描述 : 该文件实现了用温度传感器件DS18B20对温度的采集&#xff0c;并在数码管上显示出来。 ***********************************************************************/ #include<reg52.h&…...

同城跑腿服务预约小程序的作用如何

无论是互联网服务化加快还是前几年疫情冲击&#xff0c;在同城生活服务场景中出现了很多商机&#xff0c;如外卖跑腿、校园跑腿、代买代送等&#xff0c;无论公司还是个人都借势不断提升自己品牌的影响力&#xff0c;并且依赖朋友圈不断提升生意营收。 同城跑腿品牌不少&#…...

前后端开发迭代

要创建一个具有登录和注册功能的前端网页&#xff0c;并使用Go语言编写后端来支持它&#xff0c;你需要分两部分来进行&#xff1a;前端开发和后端开发。下面我将提供一个基本的指导方案。 前端开发 前端部分主要涉及HTML、CSS和JavaScript。你可以使用框架如React或Vue来简化…...

Git可视化界面的操作,SSH协议的以及IDEA集成Git

目录 一. Git可视化界面的操作 二. gitee的ssh key 2.1 SSH协议 2.2 ssh key 三. IDEA集成Git 3.1 分享项目 3.2 下载项目 一. Git可视化界面的操作 上一篇博客只用到了git的命令窗口&#xff0c;现在就来看看可视化窗口要怎么操作。 点击Git GUI Here GUI界面 在g…...

微信小程序 - 手机震动

一、界面 <button type"primary" bindtap"shortVibrate">短震动</button> <button type"primary" bindtap"longVibrate">长震动</button> 二、js逻辑代码 注&#xff1a;文档 https://developers.weixin.qq…...

【算法训练营Day07】字符串part1

文章目录 反转字符串反转字符串II替换数字 反转字符串 题目链接&#xff1a;344. 反转字符串 双指针法&#xff0c;两个指针的元素直接调转即可 class Solution {public void reverseString(char[] s) {int head 0;int end s.length - 1;while(head < end) {char temp …...

Python爬虫(二):爬虫完整流程

爬虫完整流程详解&#xff08;7大核心步骤实战技巧&#xff09; 一、爬虫完整工作流程 以下是爬虫开发的完整流程&#xff0c;我将结合具体技术点和实战经验展开说明&#xff1a; 1. 目标分析与前期准备 网站技术分析&#xff1a; 使用浏览器开发者工具&#xff08;F12&…...

EtherNet/IP转DeviceNet协议网关详解

一&#xff0c;设备主要功能 疆鸿智能JH-DVN-EIP本产品是自主研发的一款EtherNet/IP从站功能的通讯网关。该产品主要功能是连接DeviceNet总线和EtherNet/IP网络&#xff0c;本网关连接到EtherNet/IP总线中做为从站使用&#xff0c;连接到DeviceNet总线中做为从站使用。 在自动…...

【python异步多线程】异步多线程爬虫代码示例

claude生成的python多线程、异步代码示例&#xff0c;模拟20个网页的爬取&#xff0c;每个网页假设要0.5-2秒完成。 代码 Python多线程爬虫教程 核心概念 多线程&#xff1a;允许程序同时执行多个任务&#xff0c;提高IO密集型任务&#xff08;如网络请求&#xff09;的效率…...

NFT模式:数字资产确权与链游经济系统构建

NFT模式&#xff1a;数字资产确权与链游经济系统构建 ——从技术架构到可持续生态的范式革命 一、确权技术革新&#xff1a;构建可信数字资产基石 1. 区块链底层架构的进化 跨链互操作协议&#xff1a;基于LayerZero协议实现以太坊、Solana等公链资产互通&#xff0c;通过零知…...

ios苹果系统,js 滑动屏幕、锚定无效

现象&#xff1a;window.addEventListener监听touch无效&#xff0c;划不动屏幕&#xff0c;但是代码逻辑都有执行到。 scrollIntoView也无效。 原因&#xff1a;这是因为 iOS 的触摸事件处理机制和 touch-action: none 的设置有关。ios有太多得交互动作&#xff0c;从而会影响…...

Rapidio门铃消息FIFO溢出机制

关于RapidIO门铃消息FIFO的溢出机制及其与中断抖动的关系&#xff0c;以下是深入解析&#xff1a; 门铃FIFO溢出的本质 在RapidIO系统中&#xff0c;门铃消息FIFO是硬件控制器内部的缓冲区&#xff0c;用于临时存储接收到的门铃消息&#xff08;Doorbell Message&#xff09;。…...

什么是Ansible Jinja2

理解 Ansible Jinja2 模板 Ansible 是一款功能强大的开源自动化工具&#xff0c;可让您无缝地管理和配置系统。Ansible 的一大亮点是它使用 Jinja2 模板&#xff0c;允许您根据变量数据动态生成文件、配置设置和脚本。本文将向您介绍 Ansible 中的 Jinja2 模板&#xff0c;并通…...

React---day11

14.4 react-redux第三方库 提供connect、thunk之类的函数 以获取一个banner数据为例子 store&#xff1a; 我们在使用异步的时候理应是要使用中间件的&#xff0c;但是configureStore 已经自动集成了 redux-thunk&#xff0c;注意action里面要返回函数 import { configureS…...