Spring Aop学习笔记

news/2024/9/12 23:16:13/ 标签: spring, 学习, 笔记

AOP 简介

AOP的概念

AOP,Aspect Oriented Programming,面向切面编程,是对面向对象编程OOP的升华。OOP是纵向对一个事物的抽象,一个对象包括静态的属性信息,包括动态的方法信息等。而AOP是横向的对不同事物的抽象,属性与属性、方法与方法、对象与对象都可以组成一个切面,而用这种思维去设计编程的方式叫做面向切面编程

AOP思想的实现方案

模拟AOP的基础代码

其实在之前学习BeanPostProcessor时,在BeanPostProcessor的after方法中使用动态代理对Bean进行了增强,实际存储到单例池singleObjects中的不是当前目标对象本身,而是当前目标对象的代理对象Proxy,这样在调用目标对象方法时,实际调用的是代理对象Proxy的同名方法,起到了目标方法前后都进行增强的功能,对该方式进行一下优化,将增强的方法提取出去到一个增强类中,且只对com.itheima.service.impl包下的任何类的任何方法进行增强
// 自定义增强类
public class MyAdvice {
        public void beforeAdvice(){
                System. out .println( "beforeAdvice ..." );
        }
        public void afterAdvice(){
                System. out .println( "afterAdvice ..." );
        }
}
public class MockAopBeanPostProcessor implements BeanPostProcessor, ApplicationContextAware {private ApplicationContext applicationContext;//注入Spring容器对象public Object postProcessAfterInitialization(Object bean, String beanName) throws         BeansException {MyAdvice myAdvice = applicationContext.getBean(MyAdvice.class);//获得Advice对象String packageName = bean.getClass().getPackage().getName();if("com.itheima.service.impl".equals(packageName)){//对Bean进行动态代理,返回的是Proxy代理对象Object proxyBean = Proxy.newProxyInstance(bean.getClass().getClassLoader(),bean.getClass().getInterfaces(),(Object proxy, Method method, Object[] args) -> {myAdvice.beforeAdvice();//执行Advice的before方法Object result = method.invoke(bean, args);//执行目标myAdvice.afterAdvice();//执行Advice的after方法return result; });//返回代理对象return proxyBean; }return bean; }public void setApplicationContext(ApplicationContext applicationContext) throws     BeansException {this.applicationContext = applicationContext; 
}}

AOP相关概念

基于xml配置的AOP

xml方式AOP快速入门

前面我们自己编写的AOP基础代码还是存在一些问题的,主要如下:
        被增强的包名在代码写死了
        通知对象的方法在代码中写死了
通过配置文件的方式去解决上述问题
        配置哪些包、哪些类、哪些方法需要被增强
        配置目标方法要被哪些通知方法所增强,在目标方法执行之前还是之后执行增强
配置方式的设计、配置文件(注解)的解析工作,Spring已经帮我们封装好了
xml方式配置AOP的步骤:
1、导入AOP相关坐标;
2、准备目标类、准备增强类,并配置给Spring管理;
3、配置切点表达式(哪些方法被增强);
4、配置织入(切点被哪些通知方法增强,是前置增强还是后置增强)。
1、导入AOP相关坐标
< dependency >
        < groupId >org.aspectj</ groupId >
        < artifactId >aspectjweaver</ artifactId >
        < version >1.9.6</ version >
</ dependency >
Spring-context坐标下已经包含spring-aop的包了,所以就不用额外导入了
2、准备目标类、准备增强类,并配置给Spring管理
public interface UserService {
        void show1();
        void show2();
}
public class UserServiceImpl implements UserService {
        public void show1() {
                System. out .println( "show1..." );
        }
        public void show2() {
                System. out .println( "show2..." );
        }
}
public class MyAdvice {
        public void beforeAdvice(){
                System. out .println( "beforeAdvice" );
        }
        public void afterAdvice(){
                System. out .println( "afterAdvice" );
        }
}
<!-- 配置目标类 , 内部的方法是连接点 -->
< bean id= "userService" class= "com.itheima.service.impl.UserServiceImpl" />
<!-- 配置通知类 , 内部的方法是增强方法 -->
< bean id= myAdvice" class= "com.itheima.advice.MyAdvice" />
3、配置切点表达式(哪些方法被增强)
4、配置织入(切点被哪些通知方法增强,是前置增强还是后置增强)
< aop :config >
        <!--配置切点表达式 , 对哪些方法进行增强 -->
        < aop :pointcut id= "myPointcut" expression= "execution(void com.itheima.service.impl.UserServiceImpl.show1())" />
        <!--切面 = 切点 + 通知 -->
        < aop :aspect ref= "myAdvice" >
                <!--指定前置通知方法是beforeAdvice-->
                < aop :before method= "beforeAdvice" pointcut-ref= "myPointcut" />
                <!--指定后置通知方法是afterAdvice-->
                < aop :after-returning method= "afterAdvice" pointcut-ref= "myPointcut" />
        </ aop :aspect >
</ aop :config >

xml方式AOP配置详解

xml配置AOP的方式还是比较简单的,下面看一下AOP详细配置的细节:
        1.切点表达式的配置方式
        2.切点表达式的配置语法
        3.通知的类型
        4.AOP的配置的两种方式
切点表达式的配置方式有两种,直接将切点表达式配置在通知上,也可以将切点表达式抽取到外面,在通知上进行引用
< aop :config >
        <!--配置切点表达式 , 对哪些方法进行增强 -->
        < aop :pointcut id= "myPointcut" expression= "execution(void com.itheima.service.impl.UserServiceImpl.show1())" />
        <!--切面 = 切点 + 通知 -->
        < aop :aspect ref= "myAdvice" >
                <!--指定前置通知方法是beforeAdvice-->
                < aop :before method= "beforeAdvice" pointcut-ref= "myPointcut" />
                <!--指定后置通知方法是afterAdvice-->
                < aop :after-returning method= "afterAdvice" pointcut= "execution(void com.itheima.service.impl.UserServiceImpl.show1())" />
        </ aop :aspect >
</ aop :config >
切点表达式是配置要对哪些连接点(哪些类的哪些方法)进行通知的增强,语法如下:
execution([ 访问修饰符 ] 返回值类型 包名 . 类名 . 方法名 ( 参数 ))
配置规则
1.访问修饰符可以省略不写;
2.返回值类型、某一级包名、类名、方法名 可以使用 * 表示任意;
3.包名与类名之间使用单点 . 表示该包下的类,使用双点 .. 表示该包及其子包下的类;
4.参数列表可以使用两个点 .. 表示任意参数。
切点表达式举几个例子方便理解
// 表示访问修饰符为 public 、无返回值、在 com.itheima.aop 包下的 TargetImpl 类的无参方法 show
execution(public void com.itheima.aop.TargetImpl.show())
// 表述 com.itheima.aop 包下的 TargetImpl 类的任意方法
execution(* com.itheima.aop.TargetImpl.*(..))
// 表示 com.itheima.aop 包下的任意类的任意方法
execution(* com.itheima.aop.*.*(..))
// 表示 com.itheima.aop 包及其子包下的任意类的任意方法
execution(* com.itheima.aop..*.*(..))
// 表示任意包中的任意类的任意方法
execution(* *..*.*(..))
AspectJ的通知由以下五种类型
环绕通知
public void around(ProceedingJoinPoint joinPoint) throws Throwable {
        //环绕前
        System. out .println( " 环绕前通知 " );
        //目标方法
        joinPoint.proceed();
        ///环绕后
        System. out .println( " 环绕后通知 " );
}
< aop :around method= "around" pointcut-ref= "myPointcut" />
异常通知,当目标方法抛出异常时,异常通知方法执行,且后置通知和环绕后通知不在执行
public void afterThrowing(){
System. out .println( " 目标方法抛出异常了,后置通知和环绕后通知不在执行 " );
}
< aop :after-throwing method= "afterThrowing" pointcut-ref= "myPointcut" />
最终通知,类似异常捕获中的finally,不管目标方法有没有异常,最终都会执行的通知
public void after(){
        System. out .println( " 不管目标方法有无异常,我都会执行 " );
}
< aop :after method= "after" pointcut-ref= "myPointcut" />
通知方法在被调用时,Spring可以为其传递一些必要的参数
JoinPoint 对象
public void 通知方法名称 (JoinPoint joinPoint){
        //获得目标方法的参数
        System. out .println(joinPoint.getArgs());
        //获得目标对象
        System. out .println(joinPoint.getTarget());
        //获得精确的切点表达式信息
        System. out .println(joinPoint.getStaticPart());
}
ProceedingJoinPoint对象
public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
        System. out .println(joinPoint.getArgs()); // 获得目标方法的参数
        System. out .println(joinPoint.getTarget()); // 获得目标对象
        System. out .println(joinPoint.getStaticPart()); // 获得精确的切点表达式信息
        Object result = joinPoint.proceed(); // 执行目标方法
        return result; // 返回目标方法返回值
}
Throwable对象
public void afterThrowing(JoinPoint joinPoint,Throwable th){
        //获得异常信息
        System. out .println( " 异常对象是: " +th+ " 异常信息是: " +th.getMessage());
}
< aop :after-throwing method= "afterThrowing" pointcut-ref= "myPointcut" throwing= "th" />
AOP的另一种配置方式,该方式需要通知类实现Advice的子功能接口
public interface Advice {
}

注解方式AOP配置详解

Advice的子功能接口
例如:通知类实现了前置通知和后置通知接口
public class Advices implements MethodBeforeAdvice, AfterReturningAdvice {
public void before(Method method, Object[] objects, Object o) throws Throwable {
        System. out .println( "This is before Advice ..." );
        }
        public void afterReturning(Object o, Method method, Object[] objects, Object o1) throws
        Throwable {
        System. out .println( "This is afterReturn Advice ..." );
        }
}
切面使用advisor标签配置
< aop :config >
        <!-- 将通知和切点进行结合 -->
        < aop :advisor advice-ref ="advices" pointcut ="execution(void
        com.itheima.aop.TargetImpl.show())" />
</ aop :config >
又例如:通知类实现了方法拦截器接口
public class MyMethodInterceptor implements MethodInterceptor {
        @Override
        public Object invoke(MethodInvocation methodInvocation) throws Throwable {
        System. out .println( " 前置逻辑功能 ..." );
        //执行目标方法
        Object invoke =
        methodInvocation.getMethod().invoke(methodInvocation.getThis(),methodInvocation.getArguments());
        System. out .println( " 后置逻辑功能 ..." );
        return invoke;}
}
切面使用advisor标签配置
< aop :config >
        <!-- 将通知和切点进行结合 -->
        < aop :advisor advice-ref = myMethodInterceptor" pointcut ="execution(void
        com.itheima.aop.TargetImpl.show())" />
</ aop :config >
使用aspect和advisor配置区别如下:
<!-- 使用 advisor 配置 -->
< aop :config >
        <!-- advice-ref:通知 Bean id -->
        < aop :advisor advice-ref ="advices" pointcut ="execution(void
com.itheima.aop.TargetImpl.show())" />
</ aop :config >
<!-- 使用 aspect 配置 -->
< aop :config >
        <!-- ref:通知 Bean id -->
        < aop :aspect ref ="advices" >
< aop :before method ="before" pointcut ="execution(void
com.itheima.aop.TargetImpl.show())" />
        </ aop :aspect >
</ aop :config >
2)通知类的定义要求不同,advisor 需要的通知类需要实现Advice的子功能接口:
public class Advices implements MethodBeforeAdvice {
        public void before(Method method, Object[] objects, Object o) throws Throwable {
                System. out .println( "This is before Advice ..." );
        }
        public void afterReturning(Object o, Method method, Object[] objects, Object o1) throws
                Throwable {
                System. out .println( "This is afterReturn Advice ..." );
        }
}
aspect 不需要通知类实现任何接口,在配置的时候指定哪些方法属于哪种通知类型即可,更加灵活方便:
public class Advices {
        public void before() {
                System. out .println( "This is before Advice ..." );
        }
        public void afterReturning() {
                System. out .println( "This is afterReturn Advice ..." );        
        }
}
3)可配置的切面数量不同:
一个advisor只能配置一个固定通知和一个切点表达式;
一个aspect可以配置多个通知和多个切点表达式任意组合,粒度更细。
4)使用场景不同:
1.如果通知类型多、允许随意搭配情况下可以使用aspect进行配置;
2.如果通知类型单一、且通知类中通知方法一次性都会使用到的情况下可以使用advisor进行配置;
3.在通知类型已经固定,不用人为指定通知类型时,可以使用advisor进行配置,例如后面要学习的Spring事务控制的配置;
由于实际开发中,自定义aop功能的配置大多使用aspect的配置方式,所以我们后面主要讲解aspect的配置,advisor是为了后面Spring声明式事务控制做铺垫。

xml方式AOP原理剖析

通过xml方式配置AOP时,我们引入了AOP的命名空间,根据讲解的,要去找spring-aop包下的META-INF,在去找spring.handlers文件
http\://www.springframework.org/schema/aop = org.springframework.aop.config.AopNamespaceHandler
最终加载的是 AopNamespaceHandler,该Handler的init方法中注册了config标签对应的解析器
this .registerBeanDefinitionParser( "config" , new ConfigBeanDefinitionParser());
以ConfigBeanDefinitionParser作为入口进行源码剖析,最终会注册一个AspectJAwareAdvisorAutoProxyCreator进入到Spring容器中,那该类作用是什么呢?看一下集成体系图
AspectJAwareAdvisorAutoProxyCreator 的上上级父类AbstractAutoProxyCreator中的
postProcessAfterInitialization方法
// 参数 bean :为目标对象
public Object postProcessAfterInitialization(@Nullable Object bean, String beanName) {
        if (bean != null ) {
                Object cacheKey = this .getCacheKey(bean.getClass(), beanName);
                 if ( this .earlyProxyReferences.remove(cacheKey) != bean) {
                        //如果需要被增强,则wrapIfNecessary方法最终返回的就是一个 Proxy 对象
                        return this .wrapIfNecessary(bean, beanName, cacheKey);
                }
        }
        return bean;
}
通过断点方式观察,当bean是匹配切点表达式时,this.wrapIfNecessary(bean, beanName, cacheKey)返回的是一个JDKDynamicAopProxy
高级软件人才培训专家
2211
ublic class MyAdvice {

http://www.ppmy.cn/news/1516062.html

相关文章

C++第四十一弹---C++11新特性深度解析:让你的代码更现代、更高效(上)

✨个人主页&#xff1a; 熬夜学编程的小林 &#x1f497;系列专栏&#xff1a; 【C语言详解】 【数据结构详解】【C详解】 目录 1. C11简介 2. 统一的列表初始化 2.1 &#xff5b;&#xff5d;初始化 2.2 std::initializer_list 3. 声明 3.1 auto 3.2 decltype 3.3 nu…

css设置三个div宽度占据三分之一

在CSS中&#xff0c;如果你想让一个元素占据其父容器宽度的三分之一&#xff0c;你可以通过设置该元素的width属性为33.333%&#xff08;或更常见的&#xff0c;简化为33.33%或33%&#xff0c;因为百分比在CSS中通常不需要极高的精度&#xff09;来实现。这里是一个简单的例子&…

Python知识点:如何使用Redis与Redis-py进行缓存管理

使用Redis与redis-py进行缓存管理是常见的操作&#xff0c;尤其在处理高频请求或需要快速访问的数据时。以下是一个简单的指南&#xff0c;介绍如何使用redis-py与Redis进行缓存管理。 1. 安装Redis和redis-py 首先&#xff0c;需要确保Redis已经安装并正在运行。然后安装red…

【大数据算法】一文掌握大数据算法之:平面图直径问题的亚线性算法。

平面图直径问题的亚线性算法 1、引言2、平面图直径问题的亚线性算法2.1 定义2.2 核心原理2.3 应用场景2.4 算法公式2.5 代码示例 3、总结 1、引言 小屌丝&#xff1a;鱼哥&#xff0c;时间亚线性算算法的文章&#xff0c;咋感觉你写了一半呢&#xff1f; 小鱼&#xff1a;何出…

多元统计分析——基于R语言的单车使用情况可视化分析

注&#xff1a;基于R语言的单车使用情况可视化分析为实验记录&#xff0c;存在不足&#xff0c;自行改进。 一、提出问题&#xff08;要解决或分析的问题&#xff09; 1 、用户对共享单车的使用习惯&#xff0c;环境对共享单车运营带来的影响&#xff1f; 2 、共享单车的租赁…

单个像素的威胁:微小的变化如何欺骗深度学习系统

深度学习&#xff08;DL&#xff09;是人工智能&#xff08;AI&#xff09;的基本组成部分。它的目标是使机器能够执行需要决策机制的任务&#xff0c;这些决策机制往往近似于人类推理。深度学习模型是许多先进应用的核心&#xff0c;例如医疗诊断和自动驾驶汽车。不幸的是&…

dm8 disql 登录时执行sql 或脚本

dm8 disql 执行sql 或脚本 1 环境说明2 disql 登录同时查询sql2.1 到数据库bin目录登录2.2 使用绝对路径登录数据库 3 disql 登录同时执行sql 脚本3.1 编写sql脚本3.2 到数据库bin目录登录3.3 绝对路径登录数据库 4 达梦数据库学习使用列表 1 环境说明 演示环境 x86 cpu , 银河…

C++设计模式2:代理模式

实际上&#xff0c;代理模式就是委托类通过代理类来控制实际对象的访问权限。 比如老板就是委托类&#xff0c;助理就是代理类&#xff0c;由于不是每一个客户都可以面见老板&#xff0c;所以助理要帮助老板筛选那些可以面见老板的客户。又比如一款游戏&#xff0c;游戏的关卡就…

Java笔试面试题之多线程补充考点总结

常见考点虽然总结涵盖了文档中的大部分考点&#xff0c;但仍然存在一些未在总结中明确提到的具体考点。以下是一些补充的考点&#xff1a; 特定API的使用&#xff1a; Semaphore 的使用及其在多线程环境下的作用。CyclicBarrier 和 CountDownLatch 的详细使用场景及区别&#x…

Python酷库之旅-第三方库Pandas(100)

目录 一、用法精讲 431、pandas.DataFrame.items方法 431-1、语法 431-2、参数 431-3、功能 431-4、返回值 431-5、说明 431-6、用法 431-6-1、数据准备 431-6-2、代码示例 431-6-3、结果输出 432、pandas.DataFrame.keys方法 432-1、语法 432-2、参数 432-3、功…

波导阵列天线学习笔记4 一种用于毫米波通信的新型宽带双圆极化阵列天线

摘要&#xff1a; 在本文中&#xff0c;提出了一种新型的基于间隙波导毫米波双圆极化阵列天线。通过级联膜片极化器和十字转门OMT,简单的馈网被首次提出来实现双圆极化条件。通过膜片圆极化器可以在TE10和TE01模式之间实现90度的相位差&#xff0c;并且十字转门OMT被用于分别分…

秋冬春夏,纪念在CSDN的第365天

目录 时光 收获 工作 生活 憧憬 时光 再次收到创作纪念日的消息时&#xff0c;已在CSDN创作和度过了一年的时光。创作&#xff0c;成了自己的第二工作空间&#xff0c;成为了日常的一种习惯。 每当看到第1篇文章的提醒消息&#xff0c;都会想起当时创作的初衷和情景。是一…

MySQL编译安装-麒麟V10 x86

环境信息 操作系统: Kylin Linux Advanced Server V10 (Sword) 架构&#xff1a;X86 MySQL版本&#xff1a;5.7.44 编译 安装必要的依赖库和编译工具 sudo yum groupinstall Development Toolssudo yum install cmake ncurses-devel openssl-devel boost-devel libtirpc li…

自己DIY组装一台MacBook Pro2019需要多少钱,有高性价比吗

自己DIY组装一台MacBook Pro2019需要多少钱,有高性价比吗 一、初步设想 因为最近拆机拆上隐了,萌生了一个特别有趣的想法,看到小黄鱼市场上有很多卖MacBook Pro各种拆机配件的,五花八门啥都有,于是我就想,那我是不是可以自己购买这些硬件,组装配置一台自己想要配置的M…

ubuntu上cmake3.30.2的安装

引言 安装下载安装包将安装包从windows拷贝到ubuntu解压进入解压后的文件夹执行boostrap编译CMake安装CMake查看是否安装成功 目前的ubuntu系统是20.04.4&#xff0c;用命令行安装了cmake的版本是3.16的&#xff0c;由于项目需要升级cmake到cmake3.22之上&#xff0c;使用命令行…

R语言绘图系列专栏 | 更新中

关于**《R语言绘图专栏》**&#xff0c;此专栏基于R语言绘制图形。每个图形我们会提供对应的R代码、数据和文本文档。此系列将会是一个长期更新的系列。 本系列教程&#xff0c;我们计划发表及收录使用R语言绘制50科研中常用图形。这是个长期的过程&#xff0c;计划花费3-4个的…

您的多个密码是否被泄露有没有解决方法?

当密码在数据泄露中泄露并在暗网上公开时&#xff0c;密码就会被泄露&#xff0c;从而使他人能够未经授权访问您的在线帐户。这种风险不仅来自数据泄露&#xff1b;您的密码还可能在网络钓鱼攻击中被泄露&#xff0c;或者如果您没有安全地存储密码。 处理多个被泄露的密码可能…

【前端】VUE 在线运行 模拟器 通过字符串动态渲染页面 可以灵活使用

【前端】VUE2 在线运行 模拟器 通过字符串动态渲染页面 可以灵活使用 <template><div><!-- 这里是动态组件--><component :is"component"></component><!-- 这里是动态组件--><br /><br /><br />可…

es插件 安装Elasticvue插件

Chrome应用商店&#xff1a;https://chrome.google.com/webstore/detail/elasticvue/hkedbapjpblbodpgbajblpnlpenaebaa?h1zh Edge应用商店&#xff1a;https://microsoftedge.microsoft.com/addons/detail/elasticvue/geifniocjfnfilcbeloeidajlfmhdlgo

浅谈AI+工业视觉检测技术应用的优化

1 高质量替代人眼&#xff0c;助力智能制造 视觉是人类获取信息最主要的渠道&#xff0c;它使人们得以感知和理解周边的世界。通过视觉&#xff0c;人类可以感知外界物体的大小、明暗、颜色、动静&#xff0c;获得对机体生存具有重要意义的各种信息。人类的大脑皮层约有70%都在…