AOP需要另外加入的jar包(Idea中)
com.springsource.org.aopalliance-1.0.0.jar 提取码:y5i5
面向切面编程(AOP):在一个工程中,我们想要在执行每一个方法之前打印一串日志,在方法结束后打印一串日志。最笨的方法是在每个方法中去添加这些日志代码,但是这样不仅会影响原本的逻辑代码,还会出现大量的重复代码。AOP可以解决这样的问题,我们可以把每个方法执行前作为一个切面,把它从系统中复制出来,加上日志代码,方法结束同样可以这样做。
AOP中的术语
切面:横切关注点,我们要添加在方法前和方法后的日志方法都在切面中
通知:切面要完成的工作
目标:被通知的对象(原业务逻辑)
代理:通知+目标
连接点:某个方法执行的前后或者抛出异常点或返回点
切点:具体到方法,指定要对哪些方法进行切面编程,通过切点可定位多个连接点
基于AspectJ注解的AOP
1.使用aop命名空间,在xml文件中加入说明,使AspectJ注解起作用,会自动为匹配到的bean创建代理
2.在要声明为切面的类前加上注解@Aspect,另外加上@Component使IOC容器为其生成bean
4.声明切点:由于不同的类型的通知可能使用的是同一个切点,所以可以声明一个方法对切点进行重用。这个方法不需要实现代码,只使用它的方法名。切点的声明:exexution(返回类型+包名+方法名)
/*
* 定义一个声明切入点表达式的方法
* 一般该方法中不需要添入其他方法
* 后面的其他通知使用该方法名引用当前切入点表达式
* */
@Pointcut("execution(public int com.ustc.yi.aop.impl.ArithmeticCalculator.*(int,int))")
public void declareJointPointExpression(){}
3.在切面中加入各种通知,通知以方法的形式存在,在方法前加上各种注解可声明为不同类型的通知
①前置通知(@Before):在目标方法开始之前执行,切点声明在@Before后的括号中(value)
使用JointPoint变量可以来访问连接细节,如方法名和方法使用的变量
//声明该方法是一个前置通知:在目标方法开始之前执行
@Before("declareJointPointExpression()")
public void beforeMethod(JoinPoint joinPoint) {
//通过连接点获取方法的参数
System.out.println(joinPoint.getTarget());
String methodName = joinPoint.getSignature().getName();
List
②后置通知(@After):无论方法是否异常都会执行的通知,在后置通知中不能访问目标方法执行的结果(可能方法异常无法正常返回)
//后置通知:在目标方法执行后(无论是否发生异常都会执行),执行的通知
//在后置通知中还不能访问目标方法执行的结果
@After("declareJointPointExpression()")
public void afterMethod(JoinPoint joinPoint) {
String methodName = joinPoint.getSignature().getName();
System.out.println("后置通知");
System.out.println("The method " + methodName + " ends");
}
③返回通知(@AfterReturning):方法正常结束后执行的通知,可以访问返回值,方法异常时该通知不执行。可以在前面注解中将返回值赋给一个变量result,然后可以获取到返回值
//在方法正常结束后执行的代码
//返回通知可以访问方法的返回值
@AfterReturning(value = "declareJointPointExpression()", returning = "result")
public void afterReturning(JoinPoint joinPoint, Object result) {
String methodName = joinPoint.getSignature().getName();
System.out.println("返回通知");
System.out.println("The method " + methodName + " ends with " + result);
}
④异常通知(@AfterThrowing):方法异常时执行的通知,可以在注解中指定一个变量获取到发生的异常,另外可以在方法变量中指定发生何种异常时才执行该通知,如下面只有在发生空指针异常时才会执行该通知。
//目标方法出现异常时执行
//可以访问到异常对象,可以指定出现特定异常时再执行通知代码
@AfterThrowing(value = "declareJointPointExpression()", throwing = "ex")
//指定只有在发生空指针异常时执行
public void afterReturning(JoinPoint joinPoint, NullPointerException ex) {
String methodName = joinPoint.getSignature().getName();
System.out.println("异常通知");
System.out.println("The method " + methodName + " occurs Exception " + ex);
}
⑤环绕通知(@Around):环绕通知可以决定方法是否执行,需要携带ProceedingJoinPoint参数,这个参数可以决定方法的执行,在这条执行语句之前的通知即为前置通知,在执行结束后的即为返回通知,发生异常的通知即为异常通知,在最后面的通知为后置通知。
@Around(value = "execution(* *.*(int,int))")
public Object aroundMethod(ProceedingJoinPoint proceedingJoinPoint) {
Object result=null;
String methodName=proceedingJoinPoint.getSignature().getName();
try {
//前置通知
System.out.println("method "+methodName+" begins");
//执行目标方法
result=proceedingJoinPoint.proceed();
//返回通知
System.out.println("method "+methodName+" returns");
} catch (Throwable throwable) {
System.out.println("Exception "+throwable);
throwable.printStackTrace();
}
//后置通知
System.out.println("method "+methodName+" ends");
return result;
}
4.切面的优先级,当有多个切面时,可以用Order(n)注解来确定切面的优先级,n越小优先级越高。
@Order(1)
@Aspect
@Component
public class VlidationAspect {
@Before(value = "LoggingAspect.declareJointPointExpression()")
public void VlidationAspect(){
System.out.println("数据验证");
}
}