spring知识六------AOP五大通知

要在 Spring 中声明 AspectJ 切面, 只需要在 IOC 容器中将切面声明为 Bean 实例. 当在 Spring IOC 容器中初始化 AspectJ 切面之后, Spring IOC 容器就会为那些与 AspectJ 切面相匹配的 Bean 创建代理,AOP的原理其实就是利用了动态代理,将动态代理进行了封装。

在 AspectJ 注解中, 切面只是一个带有 @Aspect 注解的 Java 类,通知则是标注有某种注解的简单的 Java 方法。

AspectJ 支持 5 种类型的通知注解:
@Before: 前置通知, 在方法执行之前执行
@After: 后置通知, 在方法执行之后执行
@AfterRunning: 返回通知, 在方法返回结果之后执行
@AfterThrowing: 异常通知, 在方法抛出异常之后
@Around: 环绕通知, 围绕着方法执行

前置通知

前置通知:在目标 方法开始之前进行执行的通知。
前置通知使用 @Before 注解, 并将切入点表达式的值作为注解值
value属性值:切入点表达式,匹配与之对应的目标。利用【*】可以进行那匹配不同的目标,参数只需要传入类型即可。
方法体JoinPoint 参数:用来连接当前连接点的连接细节,一般包括方法名和参数值。【org.aspectj.lang.JoinPoint】包

@Before( value="execution(public int com.wf.springaopImpl.ArithmeticCalculatorImpl.*(int , int))")
    public void brforelogging(JoinPoint joinPoint){ //注意 JoinPoint 的包       
        String methodName = joinPoint.getSignature().getName();
        List<Object> args = Arrays.asList(joinPoint.getArgs());
        System.out.println("The brforelogging methor"+ methodName+" begin with"+args);
    }

后置通知

后置通知: 在目标方法执行之后,无论是否发生异常,都进行执行的通知。
后置通知使用@After注解, 并将切入点表达式的值作为注解值
在后置通知中,不能访问目标方法的执行结果。原因可能在执行过程中发生异常而无法得到结果。


//  @After("execution(public int com.wf.springaopImpl.ArithmeticCalculatorImpl.*(int , int)))")
    public void afterlogging(JoinPoint joinPoint){
        String methodName = joinPoint.getSignature().getName();
        System.out.println("The afterlogging method "+methodName+" end");
    }

返回通知

返回通知: 在目标方法正常结束时,才执行的通知
返回通知使用@AfterReturning注解,并将切入点表达式的值作为注解值
返回通知可以访问到方法的返回值
returning属性值:声明该方法可以存在返回值,该属性的值即为用来传入返回值的参数名称。
方法体Object参数 :需要使用与returning同名参数名称,用来接收方法的返回值。

@AfterReturning(value="execution(public int com.wf.springaopImpl.ArithmeticCalculatorImpl.*(int , int)))",returning="result")
    public void afterreturninglogging(JoinPoint joinPoint,Object result){
        String methodName = joinPoint.getSignature().getName();
        System.out.println("The afterreturninglogging method "+methodName+" end with "+result);

    }

异常通知

异常通知 :在目标方法出现异常时才会进行执行的代码。
异常通知使用@AfterThrowing注解,并将切入点表达式的值作为注解值
throwing属性:访问连接点抛出的异常。
方法体Exception参数:用来接收连接点抛出的异常。Exception类匹配所有的异常,可以指定为特定的异常 例如NullPointerException类等

@AfterThrowing(value="execution(public int com.wf.springaopImpl.ArithmeticCalculatorImpl.*(int , int)))",throwing="ex")
    public void afterthrowinglogging(JoinPoint joinPoint, Exception ex){
        String methodName = joinPoint.getSignature().getName();
        System.out.println("The afterthrowinglogging method "+methodName+" occurs excetion: "+ex);
    }

环绕通知

环绕通知是所有通知类型中功能最为强大的, 能够全面地控制连接点。甚至可以控制是否执行连接点。类似于动态代理。
环绕通知 连接点的参数类型必须是 ProceedingJoinPoint ,它是 JoinPoint 的子接口,,允许控制何时执行, 是否执行连接点
环绕通知中需要明确调用 ProceedingJoinPoint 的 proceed() 方法来执行被代理的方法,如果忘记这样做就会导致通知被执行了,但目标方法没有被执行
环绕通知的方法需要有返回值,返回目标方法执行之后的结果, 即调用 joinPoint.proceed() 的返回值, 否则会出现空指针异常。
环绕通知虽然功能最为强大的,但是一般我们都不使用这个。

@Around(value="execution(public int com.wf.springaopImpl.ArithmeticCalculatorImpl.*(int , int)))")
    public Object aroundlogging(ProceedingJoinPoint pjd){
        Object result =null;
        String methodName = pjd.getSignature().getName();
          List<Object> args = Arrays.asList(pjd.getArgs()); 

        try {
            // 前置通知

            System.out.println("The aroundlogging methor"+ methodName+" begin with"+args);
            result = pjd.proceed();
            // 返回通知
            System.out.println("The aroundlogging method "+methodName+" end with "+result);

        } catch (Throwable e) {
            System.out.println("The aroundlogging method "+methodName+" occurs excetion: "+e);
            // 异常通知
            e.printStackTrace();
        }
        //后置通知
        System.out.println("The aroundlogging method "+methodName+" end");
        return result;
    }

切入点表达式

在切入点表达式中我们可以利用【*】去进行模糊匹配。
最典型的就是下面的这个,我们可以根据这个进行一系列的变形。

execution(* com.wf.springaopImpl.ArithmeticCalculatorImpl.*(..))
//第一个 * 代表任意修饰符及任意返回值. 第二个 * 代表任意方法. .. 匹配任意数量的参数. 

//若目标类与接口与该切面在同一个包中, 可以省略包名.
execution(public * ArithmeticCalculator.*(..))
// 匹配 ArithmeticCalculator 接口的所有公有方法.

execution (public double ArithmeticCalculator.*(..))
//匹配 ArithmeticCalculator 中返回 double 类型数值的方法

execution (public double ArithmeticCalculator.*(double, ..))
// 匹配第一个参数为 double 类型的方法, .. 匹配任意数量任意类型的参数

切面优先级

当在同一个方法中存在多个切面时,我们可以在注解切面的类中添加新的注解@Order,可以给我们的切面进行优先级排序,@Order(index) 其中index的值越小,所对应的优先级就越高
这里写图片描述

重用切点表达式

有时候一个切点可以有多个通知,就像上面,如果我们将切点表达式写在每一个通知中,会有大量的重复代码。因此我们可以重用切点表达式。

// 定义一个方法,利用注解@Pointcut用于声明切入点达式,然后在其中引入方法名即可。
// 我们可以利用ValidateAspect.declareJoinPointExpression()进行跨类声明【类名.方法名】。
// 甚至我们可以利用【类的全路径.方法名】使切点表达式作用于不同的包中。

public class ValidateAspect {


    @Pointcut(value="execution(public int com.wf.springaopImpl.ArithmeticCalculatorImpl.*(int , int))")
    public void declareJoinPointExpression(){
    }   

    @Before("declareJoinPointExpression()")
    public void brforeValidate(JoinPoint joinPoint){ //注意 JoinPoint 的包      
        String methodName = joinPoint.getSignature().getName();
        List<Object> args = Arrays.asList(joinPoint.getArgs()); 
        System.out.println("----------The Validate methor"+ methodName+" begin with"+args);
    }

    @After("declareJoinPointExpression()")
    public void afterValidate(JoinPoint joinPoint){
        String methodName = joinPoint.getSignature().getName();
        System.out.println("The afterValidate method "+methodName+" end");
    }

}

基于xml的配置通知

对于没有注解的,我们可以将对于切面的相关配置写在我们的配置文件中。具体如下:
1、配置基本bean
2、配置切面bean
3、配置AOP
在配置AOP时,我们进行属性配置
A、配置切面表达式
B、配置切面以及通知
注意:记得要引入aop命名空间。


<!-- 配置基本bean -->
<bean id="arithmeticCalculator" class="com.wf.springaopImplxml.ArithmeticCalculatorImpl"></bean>

<!-- 配置切面bean -->
<bean id="loggingAspect" class="com.wf.springaopImplxml.LoggingAspect"></bean>
<bean id="validateAspect" class="com.wf.springaopImplxml.ValidateAspect"></bean>
<!-- 配置AOP -->

<aop:config>
        <!-- 配置切点表达式  -->
    <aop:pointcut expression="execution(public int com.wf.springaopImplxml.ArithmeticCalculatorImpl.*(int , int))" 
    id="pointcut"/>
    <!-- 配置切面及通知 -->
    <aop:aspect ref="loggingAspect" order="2">
        <aop:before method="brforelogging" pointcut-ref="pointcut"/>
        <aop:after method="afterlogging" pointcut-ref="pointcut"/>
        <aop:after-returning method="afterreturninglogging" pointcut-ref="pointcut" returning="result"/>
        <aop:after-throwing method="afterthrowinglogging" pointcut-ref="pointcut" throwing="ex"/>
        <!-- <aop:around method="aroundlogging" pointcut-ref="pointcut"/> -->
    </aop:aspect>


    <!-- 配置切面及通知 -->
    <aop:aspect ref="validateAspect" order="0">
        <aop:before method="brforeValidate" pointcut-ref="pointcut"/>
    </aop:aspect>

</aop:config>

不再单独上传到博客资源,不容易通过,后期会发出百度云链接或者源码Github地址:
https://github.com/wangfa1994/SpringLearning/tree/spring005

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章