【学习笔记】Spring AOP注解使用总结
Spring AOP基本概念
- 是一种动态编译期增强性AOP的实现
- 与IOC进行整合,不是全面的切面框架
- 与动态代理相辅相成
- 有两种实现:基于jdk动态代理、cglib
Spring AOP与AspectJ区别
- Spring的AOP是基于动态代理的,动态增强目标对象,而AspectJ是静态编译时增强,需要使用自己的编译器来编译,还需要织入器
- 使用AspectJ编写的java代码无法直接使用javac编译,必须使用AspectJ增强的ajc增强编译器才可以通过编译,写法不符合原生Java的语法;而Spring AOP是符合Java语法的,也不需要指定编译器去编译,一切都由Spring 处理。
JDK动态代理与Cglib的区别
- jdk的动态代理需要实现接口 InvocationHandler
- cglib无需实现接口,使用字节码技术去修改class文件使继承
- spring默认使用jdk动态代理,如果没有实现接口会使用cglib
使用步骤
- 定义业务组件
- 定义切点(重点)
- 定义增强处理方法(切面方法)
依赖
jar包依赖,除此以外还有spring依赖
- aspectjweaver.jar
- aspectjrt.jar
- aspectj.jar
- aopalliance.jar
maven依赖
<dependencies>
<!-- 有此依赖会远程下载其它相关依赖 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>4.2.9.RELEASE</version>
</dependency>
<!-- aspectJ AOP 织入器 -->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.8.9</version>
</dependency>
</dependencies>
注解方式开发
- 扫描Aspect增强的类
<context:component-scan base-package="">
<context:include-filter type="annotation"
expression="org.aspectj.lang.annotation.Aspect"/>
</context:component-scan>
- 开启@AspectJ支持
<aop:aspectj-autoproxy/>
- 使用@AspectJ注解来标记一个切面类(spring不会将切面注册为Bean也不会增强,但是需要扫描)
- 使用其它注解进行开发(如下)
常用注解的使用
- @Before:在切点方法前执行
- 在增强的方法上
@Before("execution(* 包名.*.*(..))")
- 上述表达式可使用pointcut或切入表达式,效果一致,之后不再赘述
- 切点方法没有形参与返回值
- 在增强的方法上
示例代码
@Aspect
public class AuthAspect {
//定义切点
@Pointcut("execution(* com.cnblogs.hellxz.service.*.*(..))")
public void pointCut() {}
//前置处理
@Before("pointCut()")
public void auth() {
System.out.println("模拟权限检查……");
}
}
- @After:在切点方法后执行
- 用法同@Before
- @Around:在切点方法外环绕执行
- 在增强的方法上
@Around("execution(* 包名.*(..))")
或使用切点@Around("pointcut()")
- 接收参数类型为
ProceedingJoinPoint
,必须有这个参数在切面方法的入参第一位 - 返回值为Object
- 需要执行ProceedingJoinPoint对象的proceed方法,在这个方法前与后面做环绕处理,可以决定何时执行与完全阻止方法的执行
- 返回proceed方法的返回值
- @Around相当于@Before和@AfterReturning功能的总和
- 可以改变方法参数,在proceed方法执行的时候可以传入Object[]对象作为参数,作为目标方法的实参使用。
- 如果传入Object[]参数与方法入参数量不同或类型不同,会抛出异常
- 通过改变proceed()的返回值来修改目标方法的返回值
- 在增强的方法上
示例代码
@Aspect
public class TxAspect {
//环绕处理
@Around("execution(* com.cnblogs.hellxz.service.*.*(..))")
Object auth(ProceedingJoinPoint point) {
Object object = null;
try {
System.out.println("事务开启……");
//放行
object = point.proceed();
System.out.println("事务关闭……");
} catch (Throwable e) {
e.printStackTrace();
}
return object;
}
}
- @AfterRetruning: 在方法返回之前,获取返回值并进行记录操作
- 和上边的方法不同的地方是该注解除了切点,还有一个返回值的对象名
- 不同的两个注解参数:returning与pointcut,其中pointcut参数可以为切面表达式,也可为切点
- returning定义的参数名作为切面方法的入参名,类型可以指定。如果切面方法入参类型指定Object则无限制,如果为其它类型,则当且仅当目标方法返回相同类型时才会进入切面方法,否则不会
- 还有一个默认的value参数,如果指定了pointcut则会覆盖value的值
- 与@After类似,但@AfterReturning只有方法成功完成才会被织入,而@After不管结果如何都会被织入
虽然可以拿到返回值,但无法改变返回值
示例代码
@Aspect
public class AfterReturningAspect {
@AfterReturning(returning="rvt",
pointcut = "execution(* com.cnblogs.hellxz.service.*.*(..))")
//声明rvt时指定的类型会限定目标方法的返回值类型,必须返回指定类型或者没有返回值
//rvt类型为Object则是不对返回值做限制
public void log(Object rvt) {
System.out.println("获取目标返回值:"+ rvt);
System.out.println("假装在记录日志……");
}
/**
* 这个方法可以看出如果目标方法的返回值类型与切面入参的类型相同才会执行此切面方法
* @param itr
*/
@AfterReturning(returning="itr",
pointcut="execution(* com.cnblogs.hellxz.service.*.*(..))")
public void test(Integer itr) {
System.out.println("故意捣乱……:"+ itr);
}
}
- @AfterThrowing: 在异常抛出前进行处理,比如记录错误日志
- 与
@AfterReturning
类似,同样有一个切点和一个定义参数名的参数——throwing - 同样可以通过切面方法的入参进行限制切面方法的执行,e.g. 只打印IOException类型的异常, 完全不限制可以使用Throwable类型
- pointcut使用同
@AfterReturning
- 还有一个默认的value参数,如果指定了pointcut则会覆盖value的值
- 如果目标方法中的异常被try catch块捕获,此时异常完全被catch块处理,如果没有另外抛出异常,那么还是会正常运行,不会进入AfterThrowing切面方法
- 与
示例代码
@Aspect
public class AfterThrowingAspect {
@Pointcut("execution(* com.cnblogs.hellxz.test.*.*(..))")
public void pointcut() {}
/**
* 如果抛出异常在切面中的几个异常类型都满足,那么这几个切面方法都会执行
*/
@AfterThrowing(throwing="ex1",
pointcut="pointcut()")
//无论异常还是错误都会记录
//不捕捉错误可以使用Exception
public void throwing(Throwable ex1) {
System.out.println("出现异常:"+ex1);
}
@AfterThrowing(throwing="ex",
pointcut="pointcut()")
//只管IOException的抛出
public void throwing2(IOException ex) {
System.out.println("出现IO异常: "+ex);
}
}
pointcut定义的切点方法在@Before/@After/@Around需要写在双引号中,e.g. @Before("pointCut()")
JoinPoint的概念与方法说明
概念
- 顾名思义,连接点,织入增强处理的连接点
- 程序运行时的目标方法的信息都会封装到这个连接点对象中
- 此连接点只读
方法说明
Object[] getArgs()
:返回执行目标方法时的参数Signature getSignature()
:返回被增强方法的相关信息,e.g 方法名 etcObject getTarget()
:返回被织入增强处理的目标对象Object getThis()
:返回AOP框架目标对象生成的代理对象
使用
- 在@Before/@After/@AfterReturning/@AfterThrowing所修饰的切面方法的参数列表中加入JoinPoint对象,可以使用这个对象获得整个增强处理中的所有细节
- 此方法不适用于@Around, 其可用ProceedingJoinPoint作为连接点
ProceedingJoinPoint的概念与方法说明
概念
- 是JoinPoint的子类
- 与JoinPoint概念基本相同,区别在于是可修改的
- 使用@Around时,第一个入参必须为ProceedingJoinPoint类型
- 在@Around方法内时需要执行proceed()或proceed(Object[] args)方法使方法继续,否则会一直处于阻滞状态
方法说明
ProceedingJoinPoint是JoinPoint的子类,包含其所有方法外,还有两个公有方法
Object proceed()
:执行此方法才会执行目标方法Object proceed(Object[] args)
:执行此方法才会执行目标方法,而且会使用Object数组参数去代替实参,如果传入Object[]参数与方法入参数量不同或类型不同,会抛出异常
通过修改proceed方法的返回值来修改目标方法的返回值
编入的优先级
优先级最高的会最先被织入,在退出连接点的时候,具有最高的优先级的最后被织入
当不同切面中两个增强处理切入同一连接点的时候,Spring AOP 会使用随机织入的方式
如果想要指定优先级,那么有两种方案:
- 让切面类实现
org.springframework.core.Ordered
接口,实现getOrder方法,返回要指定的优先级 - 切面类使用
@Order
修饰,指定一个优先级的值,值越小,优先级越高
访问目标方法的形参
除了使用JoinPoint或ProceedingJoinPoint来获取目标方法的相关信息外(包括形参),如果只是简单访问形参,那么还有一种方法可以实现
- 在pointcut的execution表达式之后加入
&& args(arg0,arg1)
这种方式
@Aspect
public class AccessInputArgs {
@Before("execution(* com.cnblogs.hellxz.test.*.*(..)) && args(arg0, arg1)")
public void access(String arg0, String arg1){
System.out.println("接收到的参数为arg0="+arg0+",arg1="+arg1);
}
}
注意:通过这种方式会只匹配到方法只有指定形参数量的方法,并且,在切面方法中指定的类型会限制目标方法,不符合条件的不会进行织入增强
定义切入点
通过定义切入点,我们可以复用切点,减少重复定义切点表达式等
切入点定义包含两个部分:
- 切入点表达式
- 包含名字和任意参数的方法签名
使用@Pointcut注解进行标记一个无参无返回值的方法,加上切点表达式
@Pointcut("execution(* com.cnblogs.hellxz.test.*.*(..))")
public void pointcut(){}
切入点指示符
Spring AOP 支持10种切点指示符:execution、within、this、target、args、@target、@args、@within、@annotation、bean下面做下简记(没有写@Pointcut(),请注意):
execution: 用来匹配执行方法的连接点的指示符。
用法相对复杂,格式如下:execution(权限访问符 返回值类型 方法所属的类名包路径.方法名(形参类型) 异常类型)
e.g. execution(public String com.cnblogs.hellxz.test.Test.access(String,String))
权限修饰符和异常类型可省略,返回类型支持通配符,类名、方法名支持*通配,方法形参支持..通配within: 用来限定连接点属于某个确定类型的类。
within(com.cnblogs.hellxz.test.Test)
within(com.cnblogs.hellxz.test.) //包下类
within(com.cnblogs.hellxz.test..) //包下及子包下this和target: this用于没有实现接口的Cglib代理类型,target用于实现了接口的JDK代理目标类型
举例:this(com.cnblogs.hellxz.test.Foo) //Foo没有实现接口,使用Cglib代理,用this
实现了个接口public class Foo implements Bar{...}
target(com.cnblogs.hellxz.test.Test) //Foo实现了接口的情况args: 对连接点的参数类型进行限制,要求参数类型是指定类型的实例。
args(Long)@target: 用于匹配类头有指定注解的连接点
@target(org.springframework.stereotype.Repository)@args: 用来匹配连接点的参数的,@args指出连接点在运行时传过来的参数的类必须要有指定的注解
@Pointcut("@args(org.springframework.web.bind.annotation.RequestBody)")
public void methodsAcceptingEntities() {}
@within: 指定匹配必须包括某个注解的的类里的所有连接点
@within(org.springframework.stereotype.Repository)@annotation: 匹配那些有指定注解的连接点
@annotation(org.springframework.stereotype.Repository)bean: 用于匹配指定Bean实例内的连接点,传入bean的id或name,支持使用*通配符
切点表达式组合
使用&&、||、!、三种运算符来组合切点表达式,表示与或非的关系
execution(* com.cnblogs.hellxz.test.*.*(..)) && args(arg0, arg1)
部分代码可以参考:我的Github
本文为学习总结,如需转载请注明出处
【学习笔记】Spring AOP注解使用总结的更多相关文章
- Spring学习笔记之aop动态代理(3)
Spring学习笔记之aop动态代理(3) 1.0 静态代理模式的缺点: 1.在该系统中有多少的dao就的写多少的proxy,麻烦 2.如果目标接口有方法的改动,则proxy也需要改动. Person ...
- Spring学习笔记--spring+mybatis集成
前言: 技术的发展, 真的是日新月异. 作为javaer, 都不约而同地抛弃裸写jdbc代码, 而用各种持久化框架. 从hibernate, Spring的JDBCTemplate, 到ibatis, ...
- CgLib动态代理学习【Spring AOP基础之一】
如果不了解JDK中proxy动态代理机制的可以先查看上篇文章的内容:Java动态代理学习[Spring AOP基础之一] 由于Java动态代理Proxy.newProxyInstance()的时候会发 ...
- springmvc学习笔记(13)-springmvc注解开发之集合类型參数绑定
springmvc学习笔记(13)-springmvc注解开发之集合类型參数绑定 标签: springmvc springmvc学习笔记13-springmvc注解开发之集合类型參数绑定 数组绑定 需 ...
- Spring AOP注解为什么失效?90%Java程序员不知道
使用Spring Aop注解的时候,如@Transactional, @Cacheable等注解一般需要在类方法第一个入口的地方加,不然不会生效. 如下面几种场景 1.Controller直接调用Se ...
- springmvc学习笔记(12)-springmvc注解开发之包装类型參数绑定
springmvc学习笔记(12)-springmvc注解开发之包装类型參数绑定 标签: springmvc springmvc学习笔记12-springmvc注解开发之包装类型參数绑定 需求 实现方 ...
- springmvc学习笔记(10)-springmvc注解开发之商品改动功能
springmvc学习笔记(10)-springmvc注解开发之商品改动功能 标签: springmvc springmvc学习笔记10-springmvc注解开发之商品改动功能 需求 开发mappe ...
- Spring aop注解失效
问题 在spring 中使用 @Transactional . @Cacheable 或 自定义 AOP 注解时,对象内部方法中调用该对象的其他使用aop机制的方法会失效. @Transactiona ...
- Spring in Action 4th 学习笔记 之 AOP
前提:本文中的AOP仅限于Spring AOP. 先说说为什么需要AOP 最简单的一个例子就是日志记录,如果想记录一些方法的执行情况,最笨的办法就是修改每一个需要记录的方法.但这,真的很笨... 好的 ...
随机推荐
- docker-compose编排
创建并启动容器 docker-compose up -d 备注: -d 后台启动并运行容器 前提是你在执行该命令的时候已经编写好了docker-compose.yml文件,在这个文件的当前目录执行上述 ...
- [转]申瓯 JSY2000-06 程控电话交换机呼叫转移设置
说明:若申瓯程控电话交换机分机有事不在位置上或遇忙分机正忙时为使某些重要来话不丢失,可设置将呼入本机的电话转移至其他分机及公网固定电话或手机.电话交换机使用了本功能不管分机用户在什么地方都能接听到办公 ...
- JavaScript中执行环境和栈
在这篇文章中,我会深入理解JavaScript最根本的组成之一 : "执行环境(执行上下文)".文章结束后,你应该对解释器试图做什么,为什么一些函数/变量在未声明时就可以调用并且他 ...
- Daily Scrum - 12/03
Meeting Minutes 后端基本完成,结束当前Sprint, 开始下一个Sprint.进一步讨论了UI,并完成了任务分配. Burndown Progress part 组 ...
- 腾讯云申请的64位ubuntu服务器配置php环境
腾讯云申请的64位ubuntu服务器配置php环境 一.首先还是安装Lamp组合 Linux+Apache+Mysql+php 直接命令 sudo apt-get install apache2 su ...
- ElasticSearch 2 (34) - 信息聚合系列之多值排序
ElasticSearch 2 (34) - 信息聚合系列之多值排序 摘要 多值桶(terms.histogram 和 date_histogram)动态生成很多桶,Elasticsearch 是如何 ...
- input 清空值。(转载)
ref顾名思义我们知道,其实它就可以被看座是一个组件的参考,也可以说是一个标识.作为组件的属性,其属性值可以是一个字符串也可以是一个函数. 其实,ref的使用不是必须的.即使是在其适用的场景中也不是非 ...
- Alpha冲刺——day10
Alpha冲刺--day10 作业链接 Alpha冲刺随笔集 github地址 团队成员 031602636 许舒玲(队长) 031602237 吴杰婷 031602220 雷博浩 031602634 ...
- 基于SSM的Java Web应用开发原理初探
SSM开发Web的框架已经很成熟了,成熟得以至于有点落后了.虽然如今是SOA架构大行其道,微服务铺天盖地的时代,不过因为仍有大量的企业开发依赖于SSM,本文简单对基于SSM的Java开发做一快速入门, ...
- MyBatis中Mapper的返回值类型
insert.update.delete语句的返回值类型 对数据库执行修改操作时,数据库会返回受影响的行数. 在MyBatis(使用版本3.4.6,早期版本不支持)中insert.update.del ...