Spring AOP
AOP应用场景(横切性问题):
日志记录、权限校验、事务控制、效率检查(统计函数执行时间),散落在各个地方,AOP实现解耦,对这些问题进行统一管理。
基本概念:
- 连接点(JoinPoint):描述了目标对象上的方法。
- 切入点(Pointcut):是连接点的集合,描述了满足特定条件的方法的集合。
- 目标对象(Target):被代理的对象、被增强的对象、原始对象
- 通知(Advice):在方法执行的什么时间(分为前置、后置、环绕、异常、最终五类)以及做什么(增强的功能)
- 切面(Aspect):切面 = 切入点 + 通知,通俗点就是:在什么时机,什么地方,做什么增强!
- 织入(Weaving):把切面应用到目标对象,并创建出代理对象的过程。只要把切面声明为一个bean,放入到容器中,spring就会自动完成织入过程。
使用示例
启用@AspectJ支持,以下注解参数默认值为false,可以指定为true,意味着后续的aop会强制使用cgLib增强
@Configuration @EnableAspectJAutoProxy(proxyTargetClass = false) public class AppConfig {}
或
<aop:aspectj-autoproxy/>
声明一个切面,交由spring来管理
@Component @Aspect public class UserAspect { /** * 定义切入点,匹配UserDao所有方法调用 */ @Pointcut("execution(* com.yao.dao.UserDao.*(..))") public void pintCut(){ System.out.println("point cut"); } /** * 添加 Advice。 * 可以不指定JoinPoint参数,也可以指定JoinPoint参数用于获取被增强函数的执行参数 */ @Before("com.yao.aop.UserAspect.pintCut()") public void beforeAdvice(JoinPoint joinPoint){ // 打印函数的执行参数 for(int i = 0; i < joinPoint.getArgs().length; i++) { System.out.println(joinPoint.getArgs()[i]) } System.out.println("before"); } }
springAop和AspectJ的关系
Aop是一种概念。springAop、AspectJ都是Aop的实现,SpringAop有自己的语法,但是语法复杂,所以SpringAop借助了AspectJ的注解,但是底层实现还是自己的。spring AOP提供两种编程风格:
- @AspectJ support:利用aspectj的注解
- Schema-based AOP support:xml aop:config 命名空间
spring底层使用的是JDK或者CGLIB来完成的代理。
连接点JoinPoint
execution
由于Spring切面粒度最小是达到方法级别,而execution表达式可以用于明确指定方法返回类型,类名,方法名和参数名等与方法相关的信息,并且在Spring中,大部分需要使用AOP的业务场景也只需要达到方法级别即可,因而execution表达式的使用是最为广泛的。表达式如下:
execution(modifiers-pattern? ret-type-pattern declaring-type-pattern?name-pattern(param-pattern) throws-pattern?)
这里问号表示当前项可以有也可以没有,其中各项的语义如下
- modifiers-pattern:方法的可见性,如public,protected;
- ret-type-pattern:方法的返回值类型,如int,void等;
- declaring-type-pattern:方法所在类的全路径名,如com.spring.Aspect;
- name-pattern:方法名类型,如buisinessService();
- param-pattern:方法的参数类型,如java.lang.String;
- throws-pattern:方法抛出的异常类型,如java.lang.Exception;
示例:
@Pointcut("execution(* com.chenss.dao.*.*(..))") //匹配com.chenss.dao包下的任意接口和类的任意方法 @Pointcut("execution(public * com.chenss.dao.*.*(..))") //匹配com.chenss.dao包下的任意接口和类的public方法 @Pointcut("execution(public * com.chenss.dao.*.*())") //匹配com.chenss.dao包下的任意接口和类的public 无方法参数的方法 @Pointcut("execution(* com.chenss.dao.*.*(java.lang.String, ..))") //匹配com.chenss.dao包下的任意接口和类的第一个参数为String类型的方法 @Pointcut("execution(* com.chenss.dao.*.*(java.lang.String))") //匹配com.chenss.dao包下的任意接口和类的只有一个参数,且参数为String类型的方法 @Pointcut("execution(* com.chenss.dao.*.*(java.lang.String))") //匹配com.chenss.dao包下的任意接口和类的只有一个参数,且参数为String类型的方法 @Pointcut("execution(public * *(..))") //匹配任意的public方法 @Pointcut("execution(* te*(..))") //匹配任意的以te开头的方法 @Pointcut("execution(* com.chenss.dao.IndexDao.*(..))") //匹配com.chenss.dao.IndexDao接口中任意的方法 @Pointcut("execution(* com.chenss.dao..*.*(..))") //匹配com.chenss.dao包及其子包中任意的方法
within
表达式的最小粒度为类,与execution相比,粒度更大,仅能实现到包和接口、类级别。而execution可以精确到方法的返回值,参数个数、修饰符、参数类型等。示例
@Pointcut("within(com.chenss.dao.*)")//匹配com.chenss.dao包中的任意方法 @Pointcut("within(com.chenss.dao..*)")//匹配com.chenss.dao包及其子包中的任意方法
args
匹配指定参数类型和指定参数数量的方法,与包名和类名无关。args匹配的是运行时传递给方法的参数类型
@Pointcut("args(java.io.Serializable)")//匹配运行时传递的参数类型为指定类型的、且参数个数和顺序匹配 @Pointcut("@args(com.chenss.anno.Chenss)")//接受一个参数,并且传递的参数的运行时类型具有@Classified
this
作用在某个类的所有方法上,当代理对象是目标类或子类时,可以匹配到
JDK代理时,指向接口和代理类proxy,cglib代理时 指向接口和子类(不使用proxy)
@Pointcut("this(com.chenss.dao.IndexDaoImpl)")//当前对象,也就是代理对象,代理对象时通过代理目标对象的方式获取新的对象,与原值并非一个
target
作用在某个类的所有方法上,当被代理对象是目标类或子类时,可以匹配到
@Pointcut("target(com.chenss.dao.IndexDaoImpl)")//目标对象,也就是被代理的对象。限制目标对象为com.chenss.dao.IndexDaoImpl类 @Pointcut("@target(com.chenss.anno.Chenss)")//具有@Chenss的目标对象中的任意方法
测试:IndexDao实现了Dao接口,外部通过getBean(Dao.class)来获取bean。
不强制使用cgLib,即注解参数设置为false时,意味着此时是jdk代理:
使用target(com.dao.IndexDao):能匹配到
使用this:匹配不到,因为代理对象基于接口,与接口Dao实现类IndexDao没有关系
强制使用cgLib:
使用target:匹配不到
使用this:能匹配到,cgLib基于继承,即代理对象是继承了IndexDao的
@annotation
@Pointcut("@annotation(com.chenss.anno.Chenss)")//匹配带有com.chenss.anno.Chenss注解的方法
bean
匹配bean的名字
@Pointcut("bean(dao1)")//名称为dao1的bean上的任意方法 @Pointcut("bean(dao*)")
注意:上述所有的表达式可以混合使用,||、 &&、 !,如:
@Before("pointCut1()&&!pointCut2()")
Proceedingjoinpoint 和JoinPoint的区别
Proceedingjoinpoint 继承了JoinPoint,添加了proceed()方法,这个是aop代理链执行的方法,用于执行被增强函数。JoinPoint仅能获取相关参数,无法执行被增强函数。JoinPoint的方法
- java.lang.Object[] getArgs():获取连接点方法运行时的入参列表;
- Signature getSignature() :获取连接点的方法签名对象;
- java.lang.Object getTarget() :获取连接点所在的目标对象;
- java.lang.Object getThis() :获取代理对象本身;
Proceedingjoinpoint的proceed()方法有重载,有个带参数的方法,可以修改目标方法的的参数。通过这个process方法来实现环绕advice,仅当使用Around环绕通知时,才可以注入这个类型的参数
@Around("point()") public Object fn1(ProceedingJoinPoint pjp) throws Throwable { // 环绕通知开始 Object retVal = pjp.proceed(); // 环绕通知结束 return retVal; }
1
原文地址:https://www.cnblogs.com/hellohello/p/12209510.html
- java教程
- Java快速入门
- Java 开发环境配置
- Java基本语法
- Java 对象和类
- Java 基本数据类型
- Java 变量类型
- Java 修饰符
- Java 运算符
- Java 循环结构
- Java 分支结构
- Java Number类
- Java Character类
- Java String类
- Java StringBuffer和StringBuilder类
- Java 数组
- Java 日期时间
- Java 正则表达式
- Java 方法
- Java 流(Stream)、文件(File)和IO
- Java 异常处理
- Java 继承
- Java 重写(Override)与重载(Overload)
- Java 多态
- Java 抽象类
- Java 封装
- Java 接口
- Java 包(package)
- Java 数据结构
- Java 集合框架
- Java 泛型
- Java 序列化
- Java 网络编程
- Java 发送邮件
- Java 多线程编程
- Java Applet基础
- Java 文档注释