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, 继承了接口BeforeAdvice | AspectJMethodBeforeAdvice(实际上是MethodBeforeAdvice) |
| @AfterReturning | 接口AfterReturningAdvice | AspectJAfterReturningAdvice(实际上是AfterReturningAdvice) |
| @AfterThrowing | 接口ThrowsAdvice | AspectJAfterThrowingAdvice(实际上是MethodInterceptor) |
| @After | 接口AfterAdvice | AspectJAfterAdvice(实际上是MethodInterceptor) |
| @Around | 接口MethodInterceptor | AspectJAroundAdvice(实际上是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表示面向对象编程,是一种编程思想,AOP表示面向切面编程,也是一种编程思想 Spring AOP:Spring为了让程序员更加方便的做到面向切面编程所提供的技术支持 Spring提供的一套机制,让我们更容易的…...
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 从名字就能看出来,这个文件是一个启动文件。 odoo前端将所有的js打包成了两个文件,一个是common.js,另一个是backend.js, 而common.js 是最先加载的 在common.js打包的js文件中最先加载的是下面的文件,看…...
使用MybatisPlus时出现的java.lang.NullPointerException异常~
错误描述如下所示: 错误原因:Junit的导包错误 单元测试的包有如下所示两个 我们应该根据springboot的版本进行选择, 在Spring Boot 2.2.X以后使用import org.junit.jupiter.api.Test Junit5 在Spring Boot 2.2.x之前使用import org.junit.T…...
27 微服务配置拉取
1)引入nacos-config依赖 首先,在user-service服务中,引入nacos-config的客户端依赖: <!--nacos配置管理依赖--> <dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-sta…...
hutool ExcelUtil导出excel二级表头
简介:Hutool是一款十分好用的开发工具集,里面包含了大部分日常开发常用的工具,使用简单方便,可以大大提升日常开发效率,十分推荐大家使用。这里简单总结一下基于Hutool的Excel使用。 一、Hutool依赖 <!-- Excel导出…...
《开箱元宇宙》:认识香港麦当劳通过 The Sandbox McNuggets Land 的 Web3 成功经验
McNuggets Land 是 The Sandbox 于 2023 年发布的最受欢迎的体验之一。在本期的《开箱元宇宙》系列中,我们采访了香港麦当劳数位顾客体验暨合作伙伴资深总监 Kai Tsang,来了解这一成功案例背后的策略。 在不断发展的市场营销和品牌推广领域,不…...
基于python+TensorFlow+Django卷积网络算法+深度学习模型+蔬菜识别系统
欢迎大家点赞、收藏、关注、评论啦 ,由于篇幅有限,只展示了部分核心代码。 文章目录 一项目简介 二、功能三、系统四. 总结 一项目简介 介绍了TensorFlow在图像识别分类中的应用,并通过相关代码进行了讲解。通过TensorFlow提供的工具和库&am…...
Python异常处理:三种不同方法的探索与最佳实践
Python异常处理:三种不同方法的探索与最佳实践 前言 本文旨在探讨Python中三种不同的异常处理方法。通过深入理解各种异常处理策略,我们可以更好地应对不同的编程场景,选择最适合自己需求的方法。 异常处理在编程中扮演着至关重要的角色。合…...
一文图解爬虫(spider)
—引导语 互联网(Internet)进化到今天,已然成为爬虫(Spider)编制的天下。从个体升级为组合、从组合联结为网络。因为有爬虫,我们可以更迅速地触达新鲜“网事”。 那么爬虫究竟如何工作的呢?允许…...
腾讯云3年期轻量应用服务器优惠(薅羊毛教程)
腾讯云轻量应用服务器特价是有新用户限制的,所以阿腾云建议大家选择3年期轻量应用服务器,一劳永逸,免去续费困扰。腾讯云轻量应用服务器3年优惠可以选择2核2G4M和2核4G5M带宽,3年轻量2核2G4M服务器540元,2核4G5M轻量应…...
多个div横向排列的几种方法
以下面这组 div 为例,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 年,并于 2009 年正式对外发布。它从 2009 年 9 月 21 日开始作为谷歌公司 20% 兼职项目,即相关员工利用 20% 的空余时间来参与 Go 语言的研发工作。该项目的三位领导者均是著名的 …...
深入理解JVM虚拟机第二十三篇:详解JVM当中的栈顶缓存技术
大神链接:作者有幸结识技术大神孙哥为好友,获益匪浅。现在把孙哥视频分享给大家。 孙哥链接:孙哥个人主页 作者简介:一个颜值99分,只比孙哥差一点的程序员 本专栏简介:话不多说,让我们一起干翻JVM 本文章简介:话不多说,让我们讲清楚JVM当中与操作数栈相关的字节码指令…...
通过easyexcel导出数据到表格
这篇文章简单介绍一下怎么通过easyexcel做数据的导出,使用之前easyui构建的歌曲列表crud应用,添加一个导出按钮,点击的时候直接连接后端接口地址,在后端的接口完成数据的导出功能。 前端页面完整代码 let editingId; let request…...
C++入门学习(4)引用 (讲解拿指针比较)
上期回顾 在学习完函数重载之后,我们可以使用多个重名函数进行操作,会发现C真的是弥补了好多C语言的不足之处,真的不禁感概一下,时代的进步是需要人去做出改变的,而不是一味的使用啊!所以我们今天继续学一下…...
温度采集DS18B20
/******************************************************************** * 描述 : 该文件实现了用温度传感器件DS18B20对温度的采集,并在数码管上显示出来。 ***********************************************************************/ #include<reg52.h&…...
同城跑腿服务预约小程序的作用如何
无论是互联网服务化加快还是前几年疫情冲击,在同城生活服务场景中出现了很多商机,如外卖跑腿、校园跑腿、代买代送等,无论公司还是个人都借势不断提升自己品牌的影响力,并且依赖朋友圈不断提升生意营收。 同城跑腿品牌不少&#…...
前后端开发迭代
要创建一个具有登录和注册功能的前端网页,并使用Go语言编写后端来支持它,你需要分两部分来进行:前端开发和后端开发。下面我将提供一个基本的指导方案。 前端开发 前端部分主要涉及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的命令窗口,现在就来看看可视化窗口要怎么操作。 点击Git GUI Here GUI界面 在g…...
超短脉冲激光自聚焦效应
前言与目录 强激光引起自聚焦效应机理 超短脉冲激光在脆性材料内部加工时引起的自聚焦效应,这是一种非线性光学现象,主要涉及光学克尔效应和材料的非线性光学特性。 自聚焦效应可以产生局部的强光场,对材料产生非线性响应,可能…...
stm32G473的flash模式是单bank还是双bank?
今天突然有人stm32G473的flash模式是单bank还是双bank?由于时间太久,我真忘记了。搜搜发现,还真有人和我一样。见下面的链接:https://shequ.stmicroelectronics.cn/forum.php?modviewthread&tid644563 根据STM32G4系列参考手…...
CMake控制VS2022项目文件分组
我们可以通过 CMake 控制源文件的组织结构,使它们在 VS 解决方案资源管理器中以“组”(Filter)的形式进行分类展示。 🎯 目标 通过 CMake 脚本将 .cpp、.h 等源文件分组显示在 Visual Studio 2022 的解决方案资源管理器中。 ✅ 支持的方法汇总(共4种) 方法描述是否推荐…...
docker 部署发现spring.profiles.active 问题
报错: org.springframework.boot.context.config.InvalidConfigDataPropertyException: Property spring.profiles.active imported from location class path resource [application-test.yml] is invalid in a profile specific resource [origin: class path re…...
Vite中定义@软链接
在webpack中可以直接通过符号表示src路径,但是vite中默认不可以。 如何实现: vite中提供了resolve.alias:通过别名在指向一个具体的路径 在vite.config.js中 import { join } from pathexport default defineConfig({plugins: [vue()],//…...
解析奥地利 XARION激光超声检测系统:无膜光学麦克风 + 无耦合剂的技术协同优势及多元应用
在工业制造领域,无损检测(NDT)的精度与效率直接影响产品质量与生产安全。奥地利 XARION开发的激光超声精密检测系统,以非接触式光学麦克风技术为核心,打破传统检测瓶颈,为半导体、航空航天、汽车制造等行业提供了高灵敏…...
Ubuntu系统复制(U盘-电脑硬盘)
所需环境 电脑自带硬盘:1块 (1T) U盘1:Ubuntu系统引导盘(用于“U盘2”复制到“电脑自带硬盘”) U盘2:Ubuntu系统盘(1T,用于被复制) !!!建议“电脑…...
k8s从入门到放弃之HPA控制器
k8s从入门到放弃之HPA控制器 Kubernetes中的Horizontal Pod Autoscaler (HPA)控制器是一种用于自动扩展部署、副本集或复制控制器中Pod数量的机制。它可以根据观察到的CPU利用率(或其他自定义指标)来调整这些对象的规模,从而帮助应用程序在负…...
[USACO23FEB] Bakery S
题目描述 Bessie 开了一家面包店! 在她的面包店里,Bessie 有一个烤箱,可以在 t C t_C tC 的时间内生产一块饼干或在 t M t_M tM 单位时间内生产一块松糕。 ( 1 ≤ t C , t M ≤ 10 9 ) (1 \le t_C,t_M \le 10^9) (1≤tC,tM≤109)。由于空间…...
Vue 3 + WebSocket 实战:公司通知实时推送功能详解
📢 Vue 3 WebSocket 实战:公司通知实时推送功能详解 📌 收藏 点赞 关注,项目中要用到推送功能时就不怕找不到了! 实时通知是企业系统中常见的功能,比如:管理员发布通知后,所有用户…...
