zoukankan      html  css  js  c++  java
  • SpringAOP 前置通知、后置通知

    AspectJ:Java社区中最完整、最流行的AOP框架。

    在Spring2.0以上版本中,可以使用基于AspectJ注解基于XML配置的AOP。

    在Spring中2启用AspectJ注解支持:

    1、要在 Spring应用中使用AspectJ注解,需要添加spring-aspect、aspectj-weaver、aopalliance依赖

    2、将aop Schema添加到<beans>根元素

    3、要在SpringIOC容器中启用AspectJ注解支持,只要在Bean配置文件中定义一个空的XML元素:<aop:aspectj-autoproxy>

    4、在Spring IOC容器侦测到Bean配置文件中的<aop:aspectj-autoproxy>元素时,会自动与AspectJ切面匹配的Bean创建代理。

    一、基于注解的方式

    1、依赖

    2、在配置中加入aop的命名空间

    3、基于注解的方式:

    ①在配置文件中加入:<aop:aspectj-autoproxy>

    配置文件:

    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xmlns:context="http://www.springframework.org/schema/context"
           xmlns:aop="http://www.springframework.org/schema/aop"
           xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">
    
        <!--配置自动扫描的包-->
        <context:component-scan base-package="aopImpl"/>
    
        <!--使AspectJ注解起作用:自动为匹配的类生成代理对象-->
        <aop:aspectj-autoproxy></aop:aspectj-autoproxy>
    </beans>

    ②把横切关注点的代码抽象到切面的类中

    切面首先是一个IOC容器中的Bean,即加入@Component注解

    切面还需要加入@AspectJ注解

    ③在类中声明各种通知:

    1、声明一个方法

    2、在方法前加入通知

    @AspectJ支持的5中通知:

    —@Before:前置通知在方法执行前执行

    —@After:后置通知,在方法执行后执行

    —@AfterReturning:返回通知,在方法返回结果之后执行

    —@AfterThrowing:异常通知,在方法抛出异常后执行

    —@Around:环绕通知,围绕着方法执行

    ④我们可以在通知方法中声明一个类型为JoinPoint参数,然后就能访问链接细节,如方法名称和参数值

    切面类:

    package aopImpl;
    
    import org.aspectj.lang.JoinPoint;
    import org.aspectj.lang.annotation.Aspect;
    import org.aspectj.lang.annotation.Before;
    import org.springframework.stereotype.Component;
    
    import java.util.Arrays;
    import java.util.List;
    
    /**
     * 把这个类声明为一个切面:
     * 1、需要把该类放入到容器中(就是加上@Component注解)
     * 2、再声明为一个切面(加上@AspectJ注解)
     *
     * @author chenpeng
     * @date 2018/6/3 23:20
     */
    @Aspect
    @Component
    public class LoggingAspect {
    
        //声明该方法为一个前置通知:在目标方法开始之前执行
        //execution中是AspectJ表达式
        @Before(value = "execution(* aopImpl.ArithmeticCalculatorImpl.*(int ,int ))")
        public void beforeMethod(JoinPoint joinPoint){
            String methodName = joinPoint.getSignature().getName();
            List<Object> args = Arrays.asList(joinPoint.getArgs());
            System.out.println("beforeMethod "+methodName+" and "+args);
        }
    }
    

    在使用的时候报错:因为动态代理不能用接口的实现类来转换Proxy的实现类,它们是同级,应该用共同的接口来转换。


    改为这样:

    import aopImpl.ArithmeticCalculator;
    import aopImpl.ArithmeticCalculatorImpl;
    import org.springframework.context.ApplicationContext;
    import org.springframework.context.support.ClassPathXmlApplicationContext;
    
    /**
     * @author chenpeng
     * @date 2018/6/3 23:16
     */
    public class aopImplTest {
        public static void main(String[] args) {
            //1、创建SpringIOC容器
            ApplicationContext context = new ClassPathXmlApplicationContext("aopImpl.xml");
            //2、从IOC容器中国获取Bean的实例
            ArithmeticCalculator arithmeticCalculator = (ArithmeticCalculator) context.getBean("ArithmeticCalculatorImpl");
            //3、使用Bean
            int result = arithmeticCalculator.add(3,6);
            System.out.println(result);
        }
    }


    其他通知:

    package aopImpl;
    
    import org.aspectj.lang.JoinPoint;
    import org.aspectj.lang.ProceedingJoinPoint;
    import org.aspectj.lang.annotation.*;
    import org.springframework.stereotype.Component;
    
    import java.util.Arrays;
    import java.util.List;
    
    /**
     * 把这个类声明为一个切面:
     * 1、需要把该类放入到容器中(就是加上@Component注解)
     * 2、再声明为一个切面(加上@AspectJ注解)
     *
     * @author chenpeng
     * @date 2018/6/3 23:20
     */
    @Aspect
    @Component
    public class LoggingAspect {
    
        //声明该方法为一个前置通知:在目标方法开始之前执行
        //execution中是AspectJ表达式
        @Before(value = "execution(* aopImpl.ArithmeticCalculatorImpl.*(int ,int ))")
        public void beforeMethod(JoinPoint joinPoint){
            String methodName = joinPoint.getSignature().getName();
            List<Object> args = Arrays.asList(joinPoint.getArgs());
            System.out.println("beforeMethod "+methodName+" start with "+args);
        }
    
        //后置通知,就是在目标方法执行之后(无论是否发生异常)执行的通知
        //后置通知中不能访问目标方法的返回结果
        @After(value = "execution(* aopImpl.ArithmeticCalculatorImpl.*(int ,int ))")
        public void afterMethod(JoinPoint joinPoint){
            String methodName = joinPoint.getSignature().getName();
            List<Object> args = Arrays.asList(joinPoint.getArgs());
            System.out.println("afterMethod "+methodName+" end with "+args);
        }
    
        //返回通知,在方法正常结束之后执行的代码
        //返回通知是可以访问到方法的返回值的
        @AfterReturning(value = "execution(* aopImpl.ArithmeticCalculatorImpl.*(int ,int ))",returning = "result")
        public void afterReturning(JoinPoint joinPoint,Object result){
            String methodName = joinPoint.getSignature().getName();
            List<Object> args = Arrays.asList(joinPoint.getArgs());
            System.out.println("afterReturning "+methodName+" end with "+result);
        }
    
        //返回异常通知,返回抛出异常的时候执行的通知,可以获得返回的异常
        //可以访问到异常对象,且可以指定在出现特定异常的时候再执行通知代码
        @AfterThrowing(value = "execution(* aopImpl.ArithmeticCalculatorImpl.*(int ,int ))",throwing = "ex")
        public void afterThrowing(JoinPoint joinPoint,Exception ex){
            String methodName = joinPoint.getSignature().getName();
            List<Object> args = Arrays.asList(joinPoint.getArgs());
            System.out.println("afterThrowing "+methodName+" end with "+ ex );
        }
    
        //环绕通知需要携带ProceedingJoinPoint类型的参数
        //环绕通知类似于动态代理的全过程,这个类型ProceedingJoinPoint的参数可以决定是否执行目标方法
        //且环绕通知必须有返回值,返回值即为目标方法返回值
        @Around(value = "execution(* aopImpl.ArithmeticCalculatorImpl.*(int ,int ))")
        public  void around(ProceedingJoinPoint proceedingJoinPoint){
            Object result = null;
            String methodName = proceedingJoinPoint.getSignature().getName();
            Object[] args = proceedingJoinPoint.getArgs();
    
            //执行目标方法
            try {
                //前置通知
                System.out.println("beforeMethod "+methodName+" start with "+args);
    
                result = proceedingJoinPoint.proceed();
    
                //返回通知
                System.out.println("afterMethod "+methodName+" end with "+result);
            } catch (Throwable throwable) {
                //异常通知
                System.out.println("afterThrowing "+methodName+" exception with "+ throwable );
                throwable.printStackTrace();
            }
    
            //后置通知
            System.out.println("afterMethod "+methodName+" end with "+args);
            //System.out.println("around "+proceedingJoinPoint);
            return;
        }
    
    }


    对于切面的优先级

    可以在类上使用注解@Order(1),括号中的数字越小,优先级越高

    @Order(1)

    重用切面的切点表达式:使用@Pointcut 


    /**
     * 定义一个方法,用于声明切入点表达式
     *一般的,该方法中不需要添加其他的代码
     * 使用@Pointcut来声明切入点表达式
     * 后面的其他通知直接使用方法名来引用切入点表达式
     */
    @Pointcut(value = "execution(* aopImpl.ArithmeticCalculatorImpl.*(int ,int ))")
    public void declareJoinPointExpress(){ }
    
    //声明该方法为一个前置通知:在目标方法开始之前执行
    //execution中是AspectJ表达式
    @Before(value = "declareJoinPointExpress()")
    public void beforeMethod(JoinPoint joinPoint){
        String methodName = joinPoint.getSignature().getName();
        List<Object> args = Arrays.asList(joinPoint.getArgs());
        System.out.println("beforeMethod "+methodName+" start with "+args);
    }

    其他切面类中使用:

    package aopImpl;
    
    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;
    
    import java.util.Arrays;
    
    /**
     * @author chenpeng
     * @date 2018/6/4 8:09
     */
    //可以使用@Order指定切面的优先级,值越小优先级越高
    @Order(1)
    @Component
    @Aspect
    public class ValidationAspect {
    
        @Before(value = "LoggingAspect.declareJoinPointExpress()")
        public void validateArgs(JoinPoint joinPoint){
            System.out.println("validateArgs"+ Arrays.asList(joinPoint.getArgs()));
        }
    }
    

    二、基于XML配置文件的方式

    除了使用@AspectJ注解声明切面,Spring还支持在Bean的配置文件中声明切面,这种声明是通过aop Schema中的XML元素完成的。

    正常情况下,基于注解的声明要优于基于XML的声明,通过@AspectJ注解,可以与Aspect切面相兼容,而基于XML配置则是Spring专有的。优于@AspectJ得到越来越多AOP框架支持,所以用的更多。

    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop"
           xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">
    
        <!--配置bean-->
        <bean id="arithmeticCalculatorImpl" class="XML.ArithmeticCalculatorImpl"/>
    
        <!--配置切面的bean-->
        <bean id="LoggingAspect" class="XML.LoggingAspect"/>
        <bean id="ValidationAspect" class="XML.ValidationAspect"/>
    
        <!--配置AOP-->
        <aop:config>
            <!--配置切点表达式-->
            <aop:pointcut id="pointcut" expression="execution(* XML.ArithmeticCalculatorImpl.*(..))"/>
    
            <!--配置切面及通知-->
            <aop:aspect ref="LoggingAspect" order="2">
                <aop:before method="beforeMethod" pointcut-ref="pointcut"/>
                <aop:after method="afterMethod" pointcut-ref="pointcut"/>
                <aop:after-throwing method="afterThrowing" pointcut-ref="pointcut" throwing="ex"/>
                <aop:after-returning method="afterReturning" pointcut-ref="pointcut" returning="result"/>
    
            </aop:aspect>
    
            <aop:aspect ref="ValidationAspect" order="1">
                <aop:before method="validateArgs" pointcut-ref="pointcut"/>
            </aop:aspect>
        </aop:config>
    
    </beans>


  • 相关阅读:
    理解angularJs中的$on,$broadcast,$emit
    ionic项目上划刷新和下拉刷新
    写在开始
    Django查询结果以时间正序或者倒序排列
    Django把现在时间写入数据库,模板渲染在页面中
    《易中天品三国》———— 六、一错再错
    《易中天品三国》———— 五、何去何从
    《易中天品三国》———— 四、能臣之路
    《易中天品三国》———— 三、奸雄之谜
    《易中天品三国》———— 二、真假曹操
  • 原文地址:https://www.cnblogs.com/huangzhe1515023110/p/9276055.html
Copyright © 2011-2022 走看看