【Spring】AOP的AspectJ开发

AOP基础不了解可以阅读:【Spring】AOP原来如此-CSDN博客

AspectJ是一个居于JAVA开发的AOP框架

基于XML的声明式AspectJ

        基于XML的声明式AspectJ是通过XML文件来定义切面,切入点及通知,所有的切面、切入点和通知必须定义在内,

元素及其子元素如下图所示

        上图中,Spring配置文件中元素下包含多个元素,一个元素又包含属性和子元素,它的子元素在配置时必须按照此顺序来定义,在元素下,同样包含了属性和多个子元素,通过使用元素及其子元素可以在xml文件中配置切面、切入点和通知

1、配置切面

        配置文件中用的是元素,将一个定义好的SpringBean转换为切面Bean,所以要先定义好一个Bean,完成后ref引用即可

        常用属性id和ref

package com.aqiuo.aspectj.xml;import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
//切面类
public class MyAspect {//前置通知public void myBefore(JoinPoint joinPoint) {System.out.println("前置通知,模拟执行权限检查");System.out.println("前置类是:"+joinPoint.getClass());System.out.println("被植入增强处理的目标方法是"+joinPoint.getSignature().getName());}//后置通知public void myAfterReturning(JoinPoint joinPoint) {System.out.println("后置通知,模拟日志记录...");System.out.println("被置入增强处理的目标方法为"+joinPoint.getSignature().getName());}/*** 环绕通知* 返回值必须是Object* 必须接受一个参数,参数类型必须是ProceedingJoinPoint* 方法必须抛异常*/public Object myAround(ProceedingJoinPoint joinPoint)throws Throwable {System.out.println("环绕开始,执行方法前开启事务...");Object object=joinPoint.proceed();System.out.println("环绕结束,执行方法后关闭事务...");return object;}/*** 异常通知*/public void myAfterThrowing(JoinPoint joinPoint,Throwable e) {System.out.println("异常通知出错了"+e.getMessage());}/*** 最终通知*/public void myAfter(JoinPoint joinPoint) {System.out.println("最终通知。,模拟方法结束后释放资源...");}
}

2、配置切入点

        在Spring的配置文件中,切入点是通过元素来定义的。当它作为的子元素定义时,它是全局切入点,可以被多个切面共享。当它作为元素的子元素时,表示切入点只对当前切面有效

        常用属性id和expression,值如下

        execution(modifiers-pattern? ret-type-pattern declaring-type-pattern? name-pattern(param-pattern) throw-pattern?)

例子:execution(public String com.aqiuo.jdk.*.*(..))

表达式解释:
  • modifier:匹配修饰符,public, private 等,省略时匹配任意修饰符
  • ret-type:匹配返回类型,使用 * 匹配任意类型
  • declaring-type:匹配目标类,省略时匹配任意类型
  • .. 匹配包及其子包的所有类
  • name-pattern:匹配方法名称,使用 * 表示通配符
  • () 匹配没有参数的方法
  • (..) 匹配有任意数量参数的方法
  • (,String) 匹配有两个参数的方法,并且第一个为任意类型,第二个为 String 类型
  • throws-pattern:匹配抛出异常类型,省略时匹配任意类型

3、配置通知

通知常用的属性及其描述

pointcut

该属性用于指定一个切入点,Spring将在匹配该表达式的连接点时织入该通知

pointcut-ref

该属性指定一个已经存在的切入点名称,如配置代码中的myPointCut,通常pointcut与pointcut-ref二选一

method

该属性指定一个方法名,指定将切面bean中的该方法转换为增强处理

throwing

该属性只对元素有效,用于指定一个形参名,异常通知方法可以通过该形参访问目标方法所输出的异常

returning

该属性只对元素有效,用于指定一个形参名,后置通知方法可以通过该形参访问目标方法的返回值

示例: 


//xml文件<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"xmlns:context="http://www.springframework.org/schema/context"xmlns:aop="http://www.springframework.org/schema/aop"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-4.3.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.3.xsd  " > <bean id="myAspect" class="com.aqiuo.demo.MyAspect"></bean><aop:config><aop:aspect id="myAspect" ref="myAspect"><aop:pointcut id="point1" expression="execution(public * com.aqiuo.service.impl.*.*(..))"></aop:pointcut><!--前置方法:第一个执行--><aop:before method="before" pointcut-ref="point1"></aop:before><!--异常方法第三个执行--><aop:after-throwing method="after_throwing" pointcut-ref="point1"></aop:after-throwing><!--后置方法:第三个执行--><aop:after-returning method="after_returning" pointcut-ref="point1"></aop:after-returning><!--最终方法第二个执行--><aop:after method="after" pointcut-ref="point1"></aop:after><!--环绕方法--></aop:aspect></aop:config></beans>//测试

注意标签的摆放位置会导致执行的顺序出错,要按一定的顺序排放

基于注解的声明式AspectJ

与基于代理类的AOP实现相比,XML的声明式AspectJ要便捷很多,但也存在Spring文件中配置大量代码信息,为了解决这个问题,AspectJ框架为AOP的实现提供了一套注解,用来取代Spring配置文件中为实现AOP功能所配置的 臃肿代码

AspectJ的注解及其描述

@Aspect

用来定义一个切面类

@Pointcut

用来定义切入点表达式,使用时还需要定义一个包含名字和任意参数的方法签名来表示切入点名称,实际上,这个方法签名就是一个返回值为void,且方法体为空的普通方法

@Before

用于定义前置通知,

相当于BeforeAdvice,在使用时,通常需要指定一个value属性值,该属性值用于指定一个切入点表达式(可以是已经有的切入点,也可以是切入点表达式)

在目标方法执行之前执行执行的通知。无论何时都第一个执行

@AfterReturning

用于定义后置通知,相当于AfterReturningAdvice.在使用时可以指定 pointcut/value和returning属性,其中,pointcut/value两个属性的作用一样,都是作用于指定切入点表达式,returning属性值用于表示Advice方法中可以定义与此同名的形参。该形参用于访问目标方法值的返回值

在目标方法执行之后执行的通知。正常执行时第三个执行

@Around

用于定义环绕通知,相当于MethodInterceptor。在使用时需要指定一个value属性,该属性用于指定该通知被植入的切入点

@AfterThrowing

用于定义异常通知处理程序中未处理的异常,相当于ThrowAdvice.在使用时可以指定pointcut/value和throw属性。pointcut/value用于指定切入点表达式,throwing属性值用于指定一个形参名表示Advice方法中可定义与此同名的形参,该形参可用于访问目标方法抛出的异常

在目标方法抛出异常时执行的通知。出现异常时第三个执行

@After

最终通知,不管是否异常都会执行

是在目标方法执行之后执行的通知。无论何时都第二个执行

@DeclareParents

无需了解

 配置切面:

这个切面要加上@Aspect :标注为切面类 @Component:加入spring容器


@Aspect
@Component
public class MyAspect01 {@Pointcut(value = "execution(public * com.aqiuo.service.impl.*.*(..))")public void pointCut(){}@Before(value = "pointCut()")public void before(JoinPoint joinPoint){System.out.println("注解前置通知");}@AfterReturning(value ="pointCut()" )public void afterReturning(JoinPoint joinPoint){System.out.println("注解后置通知");}@After(value = "pointCut()")public void after(JoinPoint joinPoint){System.out.println("注解最终通知");}@AfterThrowing(value ="pointCut()",throwing = "e")public void afterThrowing(JoinPoint joinPoint,Throwable e){System.out.println("注解异常处理通知");e.printStackTrace();}@Around(value = "pointCut()")public Object around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {System.out.println("环绕注解前置");Object res = proceedingJoinPoint.proceed();System.out.println("环绕注解后置");return res;}}

在配置文件中加扫描标签和AOP的驱动注解标签 
<context:component-scan base-package="com.aqiuo.service.impl"></context:component-scan>
 <aop:aspectj-autoproxy></aop:aspectj-autoproxy>

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"xmlns:context="http://www.springframework.org/schema/context"xmlns:tx="http://www.springframework.org/schema/tx"xmlns:aop="http://www.springframework.org/schema/aop"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/contexthttp://www.springframework.org/schema/context/spring-context.xsdhttp://www.springframework.org/schema/aophttp://www.springframework.org/schema/aop/spring-aop.xsdhttp://www.springframework.org/schema/txhttp://www.springframework.org/schema/tx/spring-tx.xsd"><context:component-scan base-package="com.aqiuo.service.impl"></context:component-scan><aop:aspectj-autoproxy></aop:aspectj-autoproxy></beans>

注意:

  • 如果同一个连接点有多个通知需要执行,那在同一个切面中,目标方法之前的前置通知和环绕通知的执行顺序是未知的,目标方法之后的后置通知和环绕通知的执行顺序也是未知的
  • 注解才加
  • 用spring的配置自动完成创建代理织入切面的工作。必须要有,否则没有增强
  • 有一个proxy-target-class属性,默认为false,表示使用jdk动态代理织入增强
  • 当配为时,表示使用CGLib动态代理技术织入增强。
  • 不过即使proxy-target-class设置为false,如果目标类没有声明接口,则spring将自动使用CGLib动态代理。

 纯注解开发:

配置注解类

@EnableAspectJAutoProxy()这个是替代<aop:aspectj-autoproxy>

放在配置类上

package com.aqiuo.config;import com.aqiuo.aspect.MyAspect01;
import com.aqiuo.demo.MyAspect;
import org.springframework.beans.factory.annotation.Configurable;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
import org.springframework.context.annotation.Import;@Configurable
@ComponentScan("com")
@EnableAspectJAutoProxy()
@Import(DaoConfig.class)
public class SpringConfig {}

由于我dao层要用到数据源,所以额外写了一个DAO的配置类(初学,不用可以不写)

package com.aqiuo.config;import com.alibaba.druid.pool.DruidDataSource;
import org.springframework.context.annotation.Bean;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Component;import javax.sql.DataSource;public class DaoConfig {@Beanpublic DataSource getDataSource(){DruidDataSource druidDataSource=new DruidDataSource();druidDataSource.setDriverClassName("com.mysql.jdbc.Driver");druidDataSource.setUrl("jdbc:mysql://localhost:3306/ssm?characterEncoding=utf-8");druidDataSource.setUsername("root");druidDataSource.setPassword("3.14159265358");return druidDataSource;}@Beanpublic JdbcTemplate getJdbcTemplate(DataSource ds){JdbcTemplate jdbcTemplate=new JdbcTemplate();jdbcTemplate.setDataSource(ds);return jdbcTemplate;}}

编写配置类 

package com.aqiuo.aspect;import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;@Aspect
@Component
public class MyAspect01 {@Pointcut(value = "execution(public * com.aqiuo.service.impl.*.*(..))")public void pointCut(){}@Before(value = "pointCut()")public void before(JoinPoint joinPoint){System.out.println("注解前置通知");}@AfterReturning(value ="pointCut()" )public void afterReturning(JoinPoint joinPoint){System.out.println("注解后置通知");}@After(value = "pointCut()")public void after(JoinPoint joinPoint){System.out.println("注解最终通知");}@AfterThrowing(value ="pointCut()",throwing = "e")public void afterThrowing(JoinPoint joinPoint,Throwable e){System.out.println("注解异常处理通知");e.printStackTrace();}@Around(value = "pointCut()")public Object around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {System.out.println("环绕注解前置");Object res = proceedingJoinPoint.proceed();System.out.println("环绕注解后置");return res;}}

 被增强的方法:


@Service
public class AccountServiceImpl implements AccountService {@AutowiredAccountMapper accountMapper;public Boolean pay(Integer money, Integer produce, Integer customer) throws SQLException {accountMapper.addMoney(money, produce);System.out.println("AccountService执行啦...");accountMapper.subMoney(money, customer);return false;}}

 测试类

    @Testpublic void run2() throws SQLException {ApplicationContext applicationContext=new AnnotationConfigApplicationContext(SpringConfig.class);AccountService accountService= (AccountService) applicationContext.getBean(AccountService.class);accountService.pay(100,1,2);}

总结:

        1.纯xml配置文件方式:

写一个类,再xml文件中配置bean,和即可

        2.配置加注解:

写一个切面类(加上注解@Aspect @Component @Before等)

再配置文件中写: 注解驱动

        3.纯注解方式

在配置类上添加@EnableAspectJAutoProxy()

其他不变,注意扫描一定要都扫描到

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

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

相关文章

【SpringBoot】常用注解

RequestBody&#xff1a;自动将请求体中的 json 数据转换为实体类对象。 这个例子凑巧传入的json属性键名和User键名一致&#xff0c;可以直接使用User实体类对象&#xff0c;如果键名不一致则需要用一个Map 类接收参数&#xff1a; PutMapping("/update")public R…

给多行文本的每行添加指定的前缀textwrap.indent()

【小白从小学Python、C、Java】 【计算机等考500强证书考研】 【Python-数据分析】 给多行文本的每行添加指定的前缀 textwrap.indent() [太阳]选择题 请问以下代码输出的第一行结果是&#xff1f; import textwrap text welcome to China! print("【显示】text\n&quo…

Head First Design Patterns - 观察者模式

观察者模式 观察者模式定义了对象之间的一对多依赖&#xff0c;当一个对象改变状态时&#xff0c;它的所有依赖者都会收到通知并自动更新。观察者模式是一种对象行为型模式。 场景 很多用户都订阅了某一公众号&#xff0c;当该公众号更新时&#xff0c;所以用户都会收到消息…

C++17中的内联变量

在C11中&#xff1a; (1).声明为constexpr的函数隐式地是内联函数; (2).deleted函数隐式地是一个内联函数。 在内联函数中&#xff1a; 1.所有函数定义中的函数局部静态对象(function-local static object)在所有翻译单元之间共享(它们都引用一个翻译单…

【c++————————构造函数和析构函数】

【c————————构造函数和析构函数】 欢迎阅读新一期的c模块————构造函数和析构函数 ✒️个人主页&#xff1a;-Joker- &#x1f3f7;️专栏&#xff1a;C &#x1f4dc;代码仓库&#xff1a;c_code &#x1f339;&#x1f339;欢迎大佬们的阅读和三连关注&#xff0c…

Dependency Track:智能组件分析平台。

Dependency Track:智能组件分析平台。 ############################# 免责声明:工具本身并无好坏,希望大家以遵守《网络安全法》相关法律为前提来使用该工具,支持研究学习,切勿用于非法犯罪活动,对于恶意使用该工具造成的损失,和本人及开发者无关。 ################…

Linux | 解决问题Ubuntu重启无法进入系统以及网络无法连接【图文详解】

Ubuntu18.04重启无法进入系统&#xff0c;重开后如图 一直在加载系统内核4.15.0-213-generic,无法加载 错误原因 原本的系统是Ubuntu16.04,使用命令升级到Ubuntu18.04版本&#xff0c;升级重启后&#xff0c;远程无法连接&#xff01; 错误解决 第一步&#xff1a;进入GRUB…

AIGC入门系列1:感性的认识扩散模型

1、序言 大家好&#xff0c;欢迎来到AI手工星的频道&#xff0c;我是专注AI领域的手工星。AIGC已经成为AI又一个非常爆火的领域&#xff0c;并且与之前的AI模型不同&#xff0c;AIGC更适合普通人使用&#xff0c;我们不仅可以与chatgpt对话&#xff0c;也能通过绘画模型生成想…

使用ASP.NET MiniAPI 调试未匹配请求路径

本文将介绍如何在使用ASP.NET MiniAPI时调试未匹配到的请求路径。我们将详细讨论使用MapFallback方法、中间件等工具来解决此类问题。 1. 引言 ASP.NET MiniAPI是一个轻量级的Web API框架&#xff0c;它可以让我们快速地构建和部署RESTful服务。然而&#xff0c;在开发过程中如…

PACC:数据中心网络的主动 CNP 生成方案

PACC&#xff1a;数据中心网络的主动 CNP 生成方案 文章目录 PACC&#xff1a;数据中心网络的主动 CNP 生成方案PACC算法CNP数据结构PACC参数仿真结果参考文献 PACC算法 CNP数据结构 PACC参数 仿真结果 PACC Hadoop Load0.2 的情况&#xff1a; PACC Hadoop Load0.4 的情况&a…

go slice源码探索(切片、copy、扩容)和go编译源码分析

文章目录 概要一、数据结构二、初始化2.1、字面量2.2、下标截取2.2.1、截取原理 2.3、make关键字2.3.1、编译时 三、复制3.1、copy源码 四、扩容4.1、append源码 五&#xff1a;切片使用注意事项六&#xff1a;参考 概要 Go语言的切片&#xff08;slice&#xff09;是对数组的…

axios的使用及说明

目录 1.说明 2.直接使用 3.封装使用 4.注意 1.说明 官网&#xff1a;Axios 实例 | Axios中文文档 | Axios中文网 Axios 是一个基于 promise 网络请求库&#xff0c;作用于node.js 和浏览器中。 它是 isomorphic 的(即同一套代码可以运行在浏览器和node.js中)。在服务端它使…

Java超高精度无线定位技术--UWB (超宽带)人员定位系统源码

UWB室内定位技术是一种全新的、与传统通信技术有极大差异的通信新技术。它不需要使用传统通信体制中的载波&#xff0c;而是通过发送和接收具有纳秒或纳秒级以下的极窄脉冲来传输数据&#xff0c;从而具有GHz量级的带宽。 UWB&#xff08;超宽带&#xff09;高精度定位系统是一…

java零拷贝zero copy MappedByteBuffer

目录 调用操作系统的 mmap 未使用 mmap 的文件通过网络传输的过程 使用 mmap 的文件通过网络传输的过程 使用例子 调用操作系统的 sendfile() 在 java 中的具体实现 mmap的优劣 mmap 的不足 mmap 的优点 mmap 的使用场景 对于零拷贝&#xff08;zero copy&#xff09…

C语言实验4:指针

目录 一、实验要求 二、实验原理 1. 指针的基本概念 1.1 指针的定义 1.2 取地址运算符&#xff08;&&#xff09; 1.3 间接引用运算符&#xff08;*&#xff09; 2. 指针的基本操作 2.1 指针的赋值 2.2 空指针 3. 指针和数组 3.1 数组和指针的关系 3.2 指针和数…

【Linux】内核编译 镜像制作

文章目录 一、Ubuntu内核编译1.1 为什么自己编译内核1.2 Ubuntu 内核源码下载1.21 内核的作用1.22 Linux内核与ubuntu内核1.23 Ubuntu内核源码获取 1.3 在Windows系统下编译ubuntu内核1.4 在Linux系统下编译ubuntu内核 二、镜像制作 一、Ubuntu内核编译 1.1 为什么自己编译内核…

用LCD循环右移显示“Welcome to China“

#include<reg51.h> //包含单片机寄存器的头文件 #include<intrins.h> //包含_nop_()函数定义的头文件 sbit RSP2^0; //寄存器选择位&#xff0c;将RS位定义为P2.0引脚 sbit RWP2^1; //读写选择位&#xff0c;将RW位定义为P2.1引脚 sbit EP2^2; //使能…

Debezium日常分享系列之:向 Debezium 连接器发送信号

Debezium日常分享系列之&#xff1a;向 Debezium 连接器发送信号 一、概述二、激活源信号通道三、信令数据集合的结构四、创建信令数据集合五、激活kafka信号通道六、数据格式七、激活JMX信号通道八、自定义信令通道九、Debezium 核心模块依赖项十、部署自定义信令通道十一、信…

【C# 技术】 C# 常用排序方式——自定义数据排序

C# 常用排序方式——自定义数据排序 前言 在最近的项目中经常会对C#中的数据进行排序&#xff0c;对于基本数据类型&#xff0c;其排序方式比较简单&#xff0c;只需要调用内置算法即可实现&#xff0c;但对于自定义数据类型以及自定义排序规则的情况实现起来就比较麻烦&…

区分LR(0),SLR(1),LR(1)和LALR(1)

目录 对于LR(0)文法&#xff1a; 对于SLR(1)文法&#xff1a; 对于LR(0)和SLR(1)文法&#xff1a; 对于LR(1)和SLR(1)文法&#xff1a; 对于LALR(1)文法&#xff1a; 例题1&#xff1a; 例题2&#xff1a; 例题3&#xff1a; 例题4&#xff1a; 这几个文法大致的步骤都…