Spring全家桶(八)AOP核心思想与AspectJ 5种类型通知

来源:互联网 发布:flask sql session 编辑:程序博客网 时间:2024/06/02 16:20

一、AOP核心思想

AOP是Aspect-Oriented Programming的缩写,翻译为面向切面编程。我个人理解切面就是一个方面。
例子,一个接口里面有增删改查四个方法:

package com.stuspring.aop.impl;/** * Created by bee on 17/5/15. */public interface ArithmeticCalculator {     int add(int i,int j);     int sub(int i,int j);     int mul(int i,int j);     int div(int i,int j);}

实现类:

package com.stuspring.aop.impl;import org.springframework.stereotype.Component;/** * Created by bee on 17/5/15. */@Component("arithmeticCalculator")public class ArithmeticCalculatorImpl implements ArithmeticCalculator {    @Override    public int add(int i, int j) {        int result=i+j;        return result;    }    @Override    public int sub(int i, int j) {        int result=i-j;        return result;    }    @Override    public int mul(int i, int j) {        int result=i*j;        return result;    }    @Override    public int div(int i, int j) {        int result=i/j;        return result;    }}

如果想给这四个方法分别加上前置日志功能,以其中一个为例,add方法变这样:

  @Override    public int add(int i, int j) {        System.out.println("The method add begins with " +i+","+j);        int result=i+j;        return result;    }

手动给每个方法都加上固然可行,但是维护起来过于麻烦,面向切面编程就是为了解决这一问题。AOP的好处就是每个逻辑位于一个位置,代码不分散便于维护和升级,业务模块更简洁。

加一个日志切面:

package com.stuspring.aop.impl;import org.aspectj.lang.JoinPoint;import org.aspectj.lang.annotation.After;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;/** * Created by bee on 17/5/15. */@Aspect@Componentpublic class LoggingAspect {    /**     * 前置通知 方法开始之前执行     * @param joinPoint     */    @Before("execution(public int com.stuspring.aop.impl.ArithmeticCalculatorImpl.*(int,int))")    public void beforeMethod(JoinPoint joinPoint) {        String methodName = joinPoint.getSignature().getName();        List<Object> agrs = Arrays.asList(joinPoint.getArgs());        System.out.println("The method " + methodName + " begins with " + agrs);    }    /**     * 后置通知,方法执行完之后执行,不论方法是否出现异常     * 后置通知中不能访问目标方法的执行结果     */    @After("execution(public int com.stuspring.aop.impl.ArithmeticCalculatorImpl.*(int,int))")    public void afterMethod(JoinPoint joinPoint) {        String methodName = joinPoint.getSignature().getName();        List<Object> args = Arrays.asList(joinPoint.getArgs());        System.out.println("The method " + methodName + " ends with " + args);    }}

新建spring配置文件beans-aspect.xml:

<?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"       xmlns:content="http://www.springframework.org/schema/context"       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-4.3.xsd       http://www.springframework.org/schema/aop       http://www.springframework.org/schema/aop/spring-aop-4.3.xsd">    <content:component-scan base-package="com.stuspring.aop.impl"/>    <aop:aspectj-autoproxy/></beans>

附maven依赖:

        <dependency>            <groupId>org.springframework</groupId>            <artifactId>spring-context</artifactId>            <version>4.3.8.RELEASE</version>        </dependency>        <dependency>            <groupId>org.springframework</groupId>            <artifactId>spring-webmvc</artifactId>            <version>4.3.8.RELEASE</version>        </dependency>        <dependency>            <groupId>org.springframework</groupId>            <artifactId>spring-aop</artifactId>            <version>4.3.8.RELEASE</version>        </dependency>        <dependency>            <groupId>org.springframework</groupId>            <artifactId>spring-aspects</artifactId>            <version>4.3.8.RELEASE</version>        </dependency>        <dependency>            <groupId>org.aspectj</groupId>            <artifactId>aspectjrt</artifactId>            <version>1.8.10</version>        </dependency>        <dependency>            <groupId>org.aspectj</groupId>            <artifactId>aspectjweaver</artifactId>            <version>1.8.10</version>        </dependency>

二、五种类型通知

  1. @Before前置通知:方法开始之前执行。

     /**     * 前置通知 方法开始之前执行     * @param joinPoint     */    @Before("execution(public int com.stuspring.aop.impl.ArithmeticCalculatorImpl.*(int,int))")    public void beforeMethod(JoinPoint joinPoint) {        String methodName = joinPoint.getSignature().getName();        List<Object> agrs = Arrays.asList(joinPoint.getArgs());        System.out.println("The method " + methodName + " begins with " + agrs);    }
  2. @After后置通知:方法开始之后执行,不论方法是否出现异常。

       /**     * 后置通知,方法执行完之后执行,不论方法是否出现异常     * 后置通知中不能访问目标方法的执行结果     */    @After("execution(public int com.stuspring.aop.impl.ArithmeticCalculatorImpl.*(int,int))")    public void afterMethod(JoinPoint joinPoint) {        String methodName = joinPoint.getSignature().getName();        List<Object> args = Arrays.asList(joinPoint.getArgs());        System.out.println("The method " + methodName + " ends with " + args);    }
  3. @AfterRunning:返回通知,在方法返回结果之后执行。

        /**     * 返回通知,在方法正常结束之后执行的代码     * 返回通知可以访问方法的返回值     */    @AfterReturning(value="execution(public int com.stuspring.aop.impl.ArithmeticCalculatorImpl.*(int,int))",returning = "result")    public void afterReturning(JoinPoint joinPoint,Object result){        String methodName=joinPoint.getSignature().getName();        System.out.println("The method "+methodName+" ends with "+result);    }
  4. @AfterThrowing:异常通知,在方法抛出异常之后。

        /**     * 异常通知:在方法抛出异常之后执行     * @param joinPoint     * @param e     */    @AfterThrowing(value="execution(public int com.stuspring.aop.impl.ArithmeticCalculatorImpl.*(int,int))",throwing = "e")    public void afterThrowing(JoinPoint joinPoint,Exception e){        String methodName=joinPoint.getSignature().getName();        System.out.println("The method "+methodName+" occurs execution: "+e);    }
  5. @Around:环绕通知,围绕方法执行。

        /**     * 环绕通知需要携带ProceedingJoinPoint类型的参数     * 环绕通知类似于动态代理的全过程:ProceedingJoinPoint类型的参数可以决定是否执行目标方法     * 环绕通知必须有返回值,返回值即为目标方法的返回值     *     * @param pjd     */    @Around("execution(public int com.stuspring.aop.impl.ArithmeticCalculatorImpl.*(..))")    public Object aroundMethod(ProceedingJoinPoint pjd) {        Object result = null;        String methodName = pjd.getSignature().getName();        try {            //前置通知            System.out.println("--->The method " + methodName + " begins with " + Arrays.asList(pjd.getArgs()));            //执行目标方法            result = pjd.proceed();            //后置通知            System.out.println("--->The method " + methodName + " ends with " + result);        } catch (Throwable e) {            //异常通知            System.out.println("The method "+methodName+" occurs exception : "+e);        }        //后置通知        System.out.println("The Method "+methodName+" ends!");        return result;    }

三、指定切面的优先级

切面的优先级可以用@Order注解指定,传入的整数值越小,优先级越高。
这里写图片描述

四、复用切点表达式

   /**     *  定义一个方法用于声明切入点表达式。一般地,该方法不需要再写其它代码。     */    @Pointcut("execution(public int com.stuspring.aop.impl.ArithmeticCalculatorImpl.*(int,int))")    public void declareJoinPointExpression(){    }    /**     * 前置通知 方法开始之前执行     * @param joinPoint     */    @Before("declareJoinPointExpression()")    public void beforeMethod(JoinPoint joinPoint) {        String methodName = joinPoint.getSignature().getName();        List<Object> agrs = Arrays.asList(joinPoint.getArgs());        System.out.println("The method " + methodName + " begins with " + agrs);    }

外部类引用可以使用报名加方法名的方法。

五、配置文件方式配置AOP

接口:

package com.stuspring.aop.fileimpl;/** * Created by bee on 17/5/16. */public interface ArithmeticCalculator {    int add(int i,int j);    int sub(int i,int j);    int mul(int i,int j);    int div(int i,int j);}

实现类:

package com.stuspring.aop.fileimpl;/** * Created by bee on 17/5/16. */public class ArithmeticCalculatorImpl implements ArithmeticCalculator {    @Override    public int add(int i, int j) {        int result=i+j;        return result;    }    @Override    public int sub(int i, int j) {        int result=i-j;        return result;    }    @Override    public int mul(int i, int j) {        int result=i*j;        return result;    }    @Override    public int div(int i, int j) {        int result=i/j;        return result;    }}

日志切面:

package com.stuspring.aop.fileimpl;import org.aspectj.lang.JoinPoint;/** * Created by bee on 17/5/16. */public class LoggingAspect {    public void beforeMethod(JoinPoint joinPoint){        String methodName=joinPoint.getSignature().getName();        System.out.println("The method begins with "+methodName);    }}

参数验证切面:

package com.stuspring.aop.fileimpl;import org.aspectj.lang.JoinPoint;/** * Created by bee on 17/5/16. */public class ValidationAspect {    public void validateArgs(JoinPoint joinPoint){        System.out.println("validationMethod......");    }}

Spring配置文件:

<?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-4.3.xsd">    <!--配置bean-->    <bean id="arithmeticCalculator" class="com.stuspring.aop.fileimpl.ArithmeticCalculatorImpl"/>    <bean id="loggingAspect" class="com.stuspring.aop.fileimpl.LoggingAspect"/>    <bean id="validationAspect" class="com.stuspring.aop.fileimpl.ValidationAspect"/>    <aop:config>        <!--配置切面表达式-->        <aop:pointcut id="pointcut" expression="execution(public int com.stuspring.aop.fileimpl.ArithmeticCalculatorImpl.*(int,int))"/>        <!--配置切面通知-->        <aop:aspect ref="loggingAspect" order="2">            <aop:before method="beforeMethod" pointcut-ref="pointcut"/>        </aop:aspect>        <aop:aspect ref="validationAspect" order="1">            <aop:before method="validateArgs" pointcut-ref="pointcut"/>        </aop:aspect>    </aop:config></beans>

Main测试方法:

package com.stuspring.aop.fileimpl;import org.springframework.context.ApplicationContext;import org.springframework.context.support.ClassPathXmlApplicationContext;/** * Created by bee on 17/5/16. */public class Main {    public static void main(String[] args) {        ApplicationContext ctx=new ClassPathXmlApplicationContext("beans-aspectfile.xml");        ArithmeticCalculator calculator= (ArithmeticCalculator) ctx.getBean("arithmeticCalculator");        System.out.println(calculator.add(2,4));    }}
原创粉丝点击