五种增强和部分AOP知识

本文内容:

1、前置增强

2、后置增强

3、返回增强

4、异常增强

5、环绕增强

6、Spring AOP支持如下三种通配符:

7、切入点表达式:

8、@Pointcut注解:

9、切面优先级:


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

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值