AOP的主要编程对象是切面(aspect),而切面横切关注点。
概念 | 说明 |
---|---|
切面(Aspect) | 一个横切关注点的模块化,这个关注点可能会横切多个对象。它是横切关注点的另一种表达方式。 |
连接点(Joinpoint) | 在程序执行过程中某个特定的点,比如某方法调用的时候或者处理异常的时候。在Spring AOP中,一个连接点表示一个方法的执行,即所有方法都可以嵌入横切逻辑。 |
切入点(Pointcut) | 匹配连接点的断言。它通常是一个表达式,有专门的语法,用于指明在哪里(或者说在哪些方法调用上)嵌入横切逻辑 |
通知(Advice) | 在切面的某个特定的连接点上执行的动作,也就是我们前面提到的横切逻辑,如日志处理逻辑,事务处理逻辑。切入点致命在哪里嵌入横切逻辑,通知指明切入什么,即干什么。 |
目标对象(Target Object) | 被一个或者多个切面所通知的对象,也被称作被通知对象 |
代理对象(Proxy Object) | AOP框架创建的对象,它和目标对象遵循同样的接口,使用它的方式和使用目标对象的方式是一样的,但是它是目标对象的增强版,“通知”中的代码执行将会被代理对象的方法调用触发。在Spring中,AOP代理可以是JDK动态代理或者CGLIB代理。 |
CalculatorAspect类:
package com.jd.calculator;
import org.aspectj.lang.JoinPoint;
public class CalculatorAspect {
//前置增强
public void before(JoinPoint joinPoint) {
Object target=joinPoint.getTarget();
String methodName = joinPoint.getSignature().getName();
Object[] params = joinPoint.getArgs();
System.out.println(target.getClass().getName()+":The "+methodName+" method begins.");
System.out.println(target.getClass().getName()+":Parameters of the "+methodName+" method: ["+params[0]+","+params[1]+"]");
}
//返回增强
public void afterReturning(JoinPoint joinPoint,Object result) {
Object target=joinPoint.getTarget();
String methodName = joinPoint.getSignature().getName();
System.out.println(target.getClass().getName()+":Result of the "+methodName+" method:"+result);
}
//异常增强
public void afterThrowing(JoinPoint joinPoint,Exception e) {
Object target=joinPoint.getTarget();
String methodName = joinPoint.getSignature().getName();
System.out.println(target.getClass().getName()+":Exception of the method "+methodName+": "+e);
}
//后置增强
public void after(JoinPoint joinPoint) {
Object target=joinPoint.getTarget();
String methodName = joinPoint.getSignature().getName();
System.out.println(target.getClass().getName()+":The "+methodName+" method ends.");
}
}
application.xml
分析:
(1)通过在xml文件中进行配置也可以实现AOP的配置,实现与AspectJ支持的AOP同样的效果。(AspectJ支持的AOP详情请看https://blog.csdn.net/qq_42865575/article/details/96145531)
(2)
该配置作用:如果创建目标对象的目标类中的方法与AspectJ切面中切入点表达式匹配,则自动为该目标对象生成动态代理对象,该代理对象默认使用JDK动态代理,当配置为时,则使用CGLib动态代理。
1、* :匹配任何数量字符,用于参数列表表示参数可以是任意数据类型,但是必须有参数,
例子:
java.*.Date——>匹配java包的下一级子包中的任何Date类型;如匹配java.util.Date、java.sql.Date,但不匹配java.util.sql.Date;
java.lang.*e——>匹配任何java.lang包下的以e结尾的类型,如匹配java.util.Hashtable、java.util.Date等等;
2、. .:方法中表示任意数量参数,在包中表示当前包及其子包,
例子:
java..*——>匹配java包及其任何子包下的任何类型,如匹配java.lang.String、java.lang.annotation.Annotation等等;
3、+:匹配指定类型的子类型(不是子类);仅能作为后缀放在类型模式后边,
例子:
java.lang.Number+——>匹配java.lang包下任何Number的子类型,如匹配java.lang.Integer、java.math.BigInteger等等;
java.util.List+——>匹配java.util.List接口实现类,如匹配java.util.ArrayList,但不匹配java.util.HashMap
execution
语法:execution([修饰符] 返回值类型 [包名.类名/接口名.]方法名([参数])[异常]),
说明:
a、该表达式用于指定匹配的方法;
b、修饰符包括访问权限和static、final以及synchronized;
c、中括号框起的部分可以省略。
举例:
execution(* *..*.*(..)):匹配所有的方法;
execution(public * *(..)):匹配任意公共方法;
execution(* *(..) throws IllegalArgumentException, ArrayIndexOutOfBoundsException):匹配抛出IllegalArgumentException和ArrayIndexOutOfBoundsException异常任何方法;
execution(* set*(..)):匹配任何一个名字以 set 开始的方法;
execution(* com.jd.calculator.ICalculatorService.* (*)):匹配com.jd.calculator.ICalculatorService接口定义的只有一个参数的任意方法;
execution(* com.jd.calculator.ICalculatorService.* (*)):匹配com.jd.calculator.ICalculatorService接口定义的只有一个参数的任意方法;
execution(* com.jd.calculator.ICalculatorService.* (..)):匹配com.jd.calculator.ICalculatorService接口定义的任意方法;
execution(* com.jd.calculator.ICalculatorService+.* ()):匹配com.jd.calculator.ICalculatorService接口及子类型的的任何无参方法;
execution(* com.jd.calculator.Calculator*.m*(com.jd.vo.Data)):匹配以com.jd.calculator.Calculator开头的类或接口中以m开头且只有一个com.jd.vo.Data类型参数的方法
注意:
该匹配根据方法签名的参数类型进行匹配,而不是根据执行时传入的实参类型决定的,即方法中参数数据类型一定要和切入点表达式中参数数据类型完全一致
,如切入点表达式为execution(* com.jd.calculator.Calculator*.m*(com.jd.vo.Data)) ,定义的方法为public int mul(Object object){} ,执行该方法时传入com.jd.vo.Data类型的参数,切入点表达式依然不匹配;args则可以;
@annotation
语法:@annotation(注解全名),说明:该表达式用于匹配任意指定注解修饰的方法;
例子:
@annotation(com.jd.annotation.Secure):匹配com.jd.annotation.Secure修饰的任意方法;
@annotation(org.springframework.transaction.annotation.Transactional):匹配org.springframework.transaction.annotation.Transactional修饰的任意方法;
package com.jd.calculator;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;
@Aspect
@Component
public class CalculatorAspect {
@Pointcut("execution(public int com.jd.calculator.CalculatorService.*(..))")
public void pointCut() {
}
//前置增强
@Before("pointCut()")
public void before(JoinPoint joinPoint) {
Object target=joinPoint.getTarget();
String methodName = joinPoint.getSignature().getName();
Object[] params = joinPoint.getArgs();
System.out.println(target.getClass().getName()+":The "+methodName+" method begins.");
System.out.println(target.getClass().getName()+":Parameters of the "+methodName+" method: ["+params[0]+","+params[1]+"]");
}
//后置增强
@After("pointCut()")
public void after(JoinPoint joinPoint) {
Object target=joinPoint.getTarget();
String methodName = joinPoint.getSignature().getName();
System.out.println(target.getClass().getName()+":The "+methodName+" method ends.");
}
//返回增强
@AfterReturning(pointcut="pointCut()", returning="result")
public void afterReturning(JoinPoint joinPoint,Object result) {
Object target=joinPoint.getTarget();
String methodName = joinPoint.getSignature().getName();
System.out.println(target.getClass().getName()+":Result of the "+methodName+" method:"+result);
}
//异常增强
@AfterThrowing(pointcut="pointCut()", throwing="e")
public void afterThrowing(JoinPoint joinPoint,Exception e) {
Object target=joinPoint.getTarget();
String methodName = joinPoint.getSignature().getName();
System.out.println(target.getClass().getName()+":Exception of the method "+methodName+": "+e);
}
//环绕增强
/*@Around("pointCut()")
public Object around(ProceedingJoinPoint joinPoint) {
Object result = null;
Object target=joinPoint.getTarget();//目标对象
String methodName = joinPoint.getSignature().getName();
Object[] params = joinPoint.getArgs();
try {
try {
//前置增强
System.out.println(target.getClass().getName()+":The "+methodName+" method begins.");
System.out.println(target.getClass().getName()+":Parameters of the "+methodName+" method: ["+params[0]+","+params[1]+"]");
//执行目标对象内的方法
result = joinPoint.proceed();
}finally {
//后置增强
System.out.println(target.getClass().getName()+":The "+methodName+" method ends.");
}
//返回增强
System.out.println(target.getClass().getName()+":Result of the "+methodName+" method:"+result);
} catch (Throwable e) {
//异常增强
System.out.println(target.getClass().getName()+":Exception of the method "+methodName+": "+e);
}
return result;
}*/
}
MethodAspect切面类
package com.jd.calculator;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
@Aspect
@Order(1)//值越小,优先级越高,越先执行
@Component
public class MethodAspect {
@Before("execution(public int com.jd.calculator.CalculatorService.*(..))")
public void before(JoinPoint joinPoint) {
Object target=joinPoint.getTarget();
String methodName = joinPoint.getSignature().getName();
System.out.println(target.getClass().getName()+":The "+methodName+" method begins.");
}
}
ArgumentsAspect切面类
package com.jd.calculator;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
@Aspect
@Order(2)//值越小,优先级越高,越先执行
@Component
public class ArgumentsAspect {
@Before("execution(public int com.jd.calculator.CalculatorService.*(..))")
public void before(JoinPoint joinPoint) {
Object target=joinPoint.getTarget();
String methodName = joinPoint.getSignature().getName();
Object[] params = joinPoint.getArgs();
System.out.println(target.getClass().getName()+":Parameters of the "+methodName+" method: ["+params[0]+","+params[1]+"]");
}
}
说明:
(1)默认优先级是按照切面类类名的第一个首字母为准,按照字母顺序排列优先级。
(2)com.jd.calculator.CalculatorService类中每个public访问权限的方法都匹配MethodAspect切面类与ArgumentsAspect切面类内@Before注解修饰的方法,但由于MethodAspect切面优先级比ArgumentsAspect切面优先级高,所以执行com.jd.calculator.CalculatorService类mul方法时一定是MethodAspect切面类中前置增强先执行,而后ArgumentsAspect切面类中前置增强后执行。
MethodAspect类
package com.jd.calculator;
import org.aspectj.lang.JoinPoint;
public class MethodAspect {
public void before(JoinPoint joinPoint) {
Object target=joinPoint.getTarget();
String methodName = joinPoint.getSignature().getName();
System.out.println(target.getClass().getName()+":The "+methodName+" method begins.");
}
}
ArgumentsAspect类
package com.jd.calculator;
import org.aspectj.lang.JoinPoint;
public class ArgumentsAspect {
public void before(JoinPoint joinPoint) {
Object target=joinPoint.getTarget();
String methodName = joinPoint.getSignature().getName();
Object[] params = joinPoint.getArgs();
System.out.println(target.getClass().getName()+":Parameters of the "+methodName+" method: ["+params[0]+","+params[1]+"]");
}
}
application.xml文件