本文内容:
1、前置增强
@Before("execution(int mul (..))")
public void before(JoinPoint joinPoint) {//前置增强;目标类的方法执行之前,会先执行此方法
Object object = joinPoint.getTarget();
Object [] args = joinPoint.getArgs();
String name = joinPoint.getSignature().getName();
System.out.println(object.getClass().getName()+":The "+name+" method begins.");
System.out.println(object.getClass().getName()+":Parameters of the "+name+" method: ["+args[0]+","+args[1]+"]");
}
joinPoint.getTarget()获取对象;(在本例中结果为:com.jd.calculator.CalculatorService@2ac478)
joinPoint.getArgs()获取方法中传入的参数值(在本例中是mul中传入的参数值)
joinPoint.getSignature().getName();获取对应方法名称(在本例中是mul)
下面后置增强,返回增强,异常增强中的这些语句,意义也一样。
2、后置增强
@After("execution(int mul (int ,int))")
public void after(JoinPoint joinPoint) {//后置增强;目标方法执行之后,执行此方法
Object object = joinPoint.getTarget();
String name = joinPoint.getSignature().getName();
System.out.println(object.getClass().getName()+":The "+name+" method ends.");
}
3、返回增强
@AfterReturning(value="execution(int mul (..))",returning="result")
public void afterReturning(JoinPoint joinPoint, Object result) {//返回增强;当执行目标类的方法抛出异常时,此方法不执行
Object object = joinPoint.getTarget();
String name = joinPoint.getSignature().getName();
System.out.println(object.getClass().getName()+":Result of the "+name+" method:"+result);
}
在这里,returning="result"中的result,要和afterReturning(JoinPoint joinPoint, Object result)中的result名称一致,名称可以随便取,只需要保持一致就行。有一点需要注意:当目标类中的方法抛出异常时,返回增强不执行
4、异常增强
@AfterThrowing(value="execution(int mul (..))",throwing="exception")
public void afterThrowing(JoinPoint joinPoint,Exception exception) {//异常增强;Exception要和抛出的异常相同或者比它大,否则不会执行这个方法(比如,如果抛出异常是RuntimeException,那么这里的Exception改成RuntimeException也可以)
Object object = joinPoint.getTarget();
String name = joinPoint.getSignature().getName();
System.out.println(object.getClass().getName()+":The "+name+" method have a exception:"+exception);
}
在这里,throwing="exception"中的exception要和afterThrowing(JoinPoint joinPoint,Exception exception)中的exception名称一致,同返回增强一样,名称可以随意取,但是要保持一致。有一点需要注意:afterThrowing(JoinPoint joinPoint,Exception exception)中的Exception,要和目标类中方法抛出的异常相同,或者比目标类中方法抛出的异常大。
5、环绕增强
@Around(value="execution(int * (..))")
public Object around(ProceedingJoinPoint joinPoint) {
Object result=null;
Object [] args = joinPoint.getArgs();
Object object = joinPoint.getTarget();
String methodName = joinPoint.getSignature().getName();
try {
try {//前置增强
System.out.println(object.getClass().getName()+":The "+methodName+" method begins.");
System.out.println(object.getClass().getName()+":Parameters of the "+methodName+" method: ["+args[0]+","+args[1]+"]");
result = joinPoint.proceed();//得到目标方法返回的值
}finally {//后置增强
System.out.println(object.getClass().getName()+":The "+methodName+" method ends.");
}//返回增强
System.out.println(object.getClass().getName()+":Result of the "+methodName+" method:"+result);
} catch (Throwable exception) {//异常增强
System.out.println(object.getClass().getName()+":The "+methodName+" method have a exception:"+exception.getMessage());
}
return result;
}
环绕增强同时实现了,以上四种增强。这里面的args,object,和前置增强中的介绍一样,methodName相当于前置增强中的name,result是目标类中方法执行后的返回值。
在这里可以解释,前面四种增强同时执行后出现的一种现象:
输出结果:
com.jd.calculator.CalculatorService:The mul method begins.
com.jd.calculator.CalculatorService:Parameters of the mul method: [1,1]
com.jd.calculator.CalculatorService:The mul method ends.
com.jd.calculator.CalculatorService:Result of the mul method:1
————>1
现象是:由输出结果可以发现,后置增强得到的结果,输出顺序在返回增强之前。
代码解释:在环绕增强中,下面这段代码相当于前置增强
System.out.println(object.getClass().getName()+":The "+methodName+" method begins.");
System.out.println(object.getClass().getName()+":Parameters of the "+methodName+" method: ["+args[0]+","+args[1]+"]");
下面这段代码相当于后置增强
System.out.println(object.getClass().getName()+":The "+methodName+" method ends.");
下面这段代码相当于返回增强
System.out.println(object.getClass().getName()+":Result of the "+methodName+" method:"+result);
下面这段代码相当于异常增强
System.out.println(object.getClass().getName()+":The "+methodName+" method have a exception:"+exception.getMessage());
执行过程:在执行环绕增强中的代码时,会先执行相当于前置增强的那段代码,然后执行result = joinPoint.proceed(); 得到了目标方法的返回值。此时如果目标方法中抛出异常的话,会执行finally中的相当于后置增强的代码,接着就会执行catch中相当于异常增强的代码,而相当于返回增强的那部分代码不会执行。如果目标方法没有抛出异常的话,按照顺序执行,就会出现上面提到的现象:后置增强输出结果顺序在返回增强之前,而且无论目标类方法是否抛出异常,相当于后置增强的代码都会执行。
6、Spring AOP支持如下三种通配符:
1、*:匹配任何数量字符,用于参数列表表示参数可以是任意数据类型,但是必须有参数,
2、..:方法中表示任意数量参数,在包中表示当前包及其子包,
3、+:匹配指定类型的子类型(不是子类);仅能作为后缀放在类型模式后边,(了解即可)
前两种通配符的使用,比如环绕增强中的注释,@Around(value="execution(int * (..))"),可匹配目标类中的任意一个方法。
7、切入点表达式:
execution:语法:execution([修饰符] 返回值类型 [包名.类名/接口名.]方法名([参数])[异常]),说明:a、该表达式用于指定匹配的方法;b、修饰符包括访问权限和static、final以及synchronized;c、红色中括号框起的部分可以省略。
@annotation:
语法:@annotation(注解全名),说明:该表达式用于匹配任意指定注解修饰的方法;
例子:@annotation(org.springframework.transaction.annotation.Transactional):匹配org.springframework.transaction.annotation.Transactional修饰的任意方法;
8、@Pointcut注解:
通过观察发现CalculatorAspect类中@Before,@After,@AfterReturning、@AfterThrowing和@Around注解中切入点表达式相同,为了简化代码,可以单独自定义一个@Pointcut注解修饰的空方法,通过该方法可以简化@Before,@After,@AfterReturning、@AfterThrowing和@Around注解中的切入点表达式,具体代码如下:
@Aspect
@Component
public class BcalculatorTest {
@Pointcut("execution(int * (..))")
public void pointCut() {
}
@Before("pointCut()")
public void before(JoinPoint joinPoint) {
Object object = joinPoint.getTarget();
Object [] args = joinPoint.getArgs();
String name = joinPoint.getSignature().getName();
System.out.println(object.getClass().getName()+":The "+name+" method begins.");
System.out.println(object.getClass().getName()+":Parameters of the "+name+" method: ["+args[0]+","+args[1]+"]");
}
}
在这个例子中,@Before("pointCut()")中的pointCut()要与@Pointcut标记的方法名一致;@Pointcut()中的值,就是表达式的值
如果需要增强的方法很多,而且表达式比较长时,需要修改表达式的时候,会非常耗费时间和精力。如果使用@PointCut注解,只需要修改@PointCut中表达式的值就可以,这样会节省很多时间和精力。
9、切面优先级:
如果一个方法匹配多个切面中相同类型增强方法,那么必须明确指定各个切面优先级,否则切面优先级不确定,切面优先级的确定既可以通过在切面类添加@Order注解或实现Ordered接口实现,也可以在XML配置aop:aspect标签的order属性来实现。
下面用例子来介绍:这里有两个类BcalculatorTest和AcalculatorTest,这两个类中各有前置增强中的部分内容:
BcalculatorTest.java
package com.jd.calculator;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;
@Aspect
@Component
public class BcalculatorTest {
@Before("execution(int * (..))")
public void before(JoinPoint joinPoint) {
Object object = joinPoint.getTarget();
String name = joinPoint.getSignature().getName();
System.out.println(object.getClass().getName()+":The "+name+" method begins.");
}
}
AcalculatorTest.java
package com.jd.calculator;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;
@Aspect
@Component
public class AcalculatorTest {
@Before("execution(int * (..))")
public void before(JoinPoint joinPoint) {
Object object = joinPoint.getTarget();
Object [] args = joinPoint.getArgs();
String name = joinPoint.getSignature().getName();
System.out.println(object.getClass().getName()+":Parameters of the "+name+" method: ["+args[0]+","+args[1]+"]");
}
}
两个类都有@Aspect注解;那么问题来了,当程序执行时会先执行哪个类中的before方法呢?
默认情况下:会先执行类名首字母在26个字母中靠前的那个类中的方法。这个例子执行结果为:
com.jd.calculator.CalculatorService:Parameters of the mul method: [1,1]
com.jd.calculator.CalculatorService:The mul method begins.
————>1
这和我们想要的结果不一致,下面使用@Order注解来实现我们想要的结果:我们设置先执行BcalculatorTest.java中的方法
@Aspect
@Component
@Order(2)//值越大,优先级越低,后执行
public class AcalculatorTest
@Aspect
@Component
@Order(1)//值越小,优先级越高,先执行
public class BcalculatorTest
这时执行的结果为:
com.jd.calculator.CalculatorService:The mul method begins.
com.jd.calculator.CalculatorService:Parameters of the mul method: [1,1]
————>1