Spring-AOP

来源:互联网 发布:塔吊桩基础计算软件 编辑:程序博客网 时间:2024/06/15 10:07

一. AOP术语

  • 横切关注点: 对哪些方法进行拦截,拦截后怎么处理,这些关注点称之为横切关注点。

  • 切面(aspect): 类是对物体特征的抽象,切面就是对横切关注点的抽象。

  • 通知(advice): 切面拦截到连接点之后要执行的代码,通知分为前置、后置、异常、最终、环绕通知五类。

  • 目标(target): 被代理(通知)的对象。

  • 代理(proxy): 代理对象。向目标对象应用通知之后创建的对象。

  • 连接点(joinpoint):是切面类和业务类的连接点,其实就是封装了业务方法的一些基本属性,作为通知的参数来解析。

  • 切点(pointcut):业务类中指定的方法,作为切面切入的点。其实就是指定某个方法作为切面切的地方。相当于查询条件,在哪些业务方法中需要加入通知,spring自动生成新的代理对象。

通知:

  • 前置通知:在切入点之前执行。

  • 后置通知:在切入点执行完成后,执行通知。

  • 返回通知: 返回通知, 在方法返回结果之后执行。

  • 环绕通知(around advice):包围切入点,调用方法前后完成自定义行为。

  • 异常通知(after throwing advice):在切入点抛出异常后,执行通知。

二. AspectJ注解

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

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

三. 导入Spring AOP jar包

使用maven pom.xml导入依赖jar。

<properties>    <spring.version>4.3.8.RELEASE</spring.version>      <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding></properties><dependencies>       <!-- https://mvnrepository.com/artifact/org.springframework/spring-webmvc -->    <dependency>        <groupId>org.springframework</groupId>        <artifactId>spring-context</artifactId>        <version>${spring.version}</version>    </dependency>    <!-- https://mvnrepository.com/artifact/org.springframework/spring-aop -->        <dependency>            <groupId>org.springframework</groupId>            <artifactId>spring-aop</artifactId>            <version>${spring.version}</version>        </dependency>    <!-- https://mvnrepository.com/artifact/org.springframework/spring-aspects -->    <dependency>        <groupId>org.springframework</groupId>        <artifactId>spring-aspects</artifactId>        <version>${spring.version}</version>    </dependency><dependencies>   

四.启用AspjectJ注解

<?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-4.3.xsd        http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.3.xsd">    <!--自动扫描-->    <context:component-scan base-package="com.cd.aop.impl"></context:component-scan>    <!-- 启用AspjectJ 注解: 自动为匹配的类生成代理对象 -->    <aop:aspectj-autoproxy></aop:aspectj-autoproxy></beans>

五.前置、后置、 返回、异常通知

Calculator.java

package com.cd.spring.bean;public interface Calculator {    public int add(int num1, int num2);    public int div(int num1, int num2);}

CalculatorImpl.java

package com.cd.spring.bean;import org.springframework.stereotype.Component;@Componentpublic class CalculatorImpl implements Calculator{    public int add(int num1, int num2){        System.out.println("Calculator add...");        return num1 + num2;    }    public int div(int num1, int num2){        System.out.println("Calculator div...");        return num1 / num2;    }}

切面类
CalculatorAspect.java

package com.cd.spring.bean;import java.util.Arrays;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;// 使用注解@Aspect声明一个切面类@Aspect@Componentpublic class CalculatorAspect {    // 通知    @Before(value="execution(* com.cd.spring.bean.*(..))")    public void beforeMethod(JoinPoint jp){  // JoinPoint切入点        String methodName = jp.getSignature().getName();        System.out.println(methodName);        System.out.println("before method execute,args are "+Arrays.toString(jp.getArgs()));    }    @After("execution(* com.cd.spring.bean.*(..))")    public void afterMethod(JoinPoint jp){        System.out.println("after method execute,args are "+Arrays.toString(jp.getArgs()));    }    @AfterThrowing(value="execution(* com.cd.spring.bean.*(..))",throwing="ex")    public void afterThrow(Exception ex){        System.out.println("afterThrow"+ex.getMessage());        }    @AfterReturning(value="execution(* com.cd.spring.bean.*(..))",returning="result")    public void afterReturn(Object result){        System.out.println("the result is "+result);    }}

调用:

ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");Calculator calc = (Calculator) context.getBean("calculatorImpl");calc.add(50, 50); // calc.div(100, 0); // 异常通知

调用结果:
add
before method execute,args are [50, 50]
Calculator add…
after method execute,args are [50, 50]
the result is 100

div
before method execute,args are [100, 0]
after method execute,args are [100, 0]
afterThrow/ by zero

六. 环绕通知

package com.cd.spring.bean;import java.util.Arrays;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;@Aspect@Componentpublic class CalculatorAspectAround {    @Around(value="execution(* execution(* com.cd.spring.bean.*(..))")    public Object aroundMethod(ProceedingJoinPoint pjp){        Object result = null;        String methodName = pjp.getSignature().getName();        try {            System.out.println(methodName + "前置通知...);            result = jointPoint.proceed();            System.out.println(methodName + "后置通知...");        } catch (Throwable e) {            System.out.println(methodName + "异常通知...");            throw new RuntimeException(e);        }        System.out.println(methodName + "返回通知.....");        return result;    }}


注意:@Around环绕通知,必须携带对象ProceedingJoinPoint参数

环绕通知类似于动态代理的全部过程,可以决定是否执行目标方法

七. 指定切面的优先级

在同一个连接点上应用不止一个切面时, 除非明确指定, 否则它们的优先级是不确定的.
切面的优先级可以通过实现 Ordered 接口或利用 @Order 注解指定。
使用@Order注解,值越小,优先级越高。

@Order(1)@Aspect@Componentpublic class LoggingAspect {}

八. 重用切入点定义

在 AspectJ 切面中, 可以通过 @Pointcut注解将一个切入点声明成简单的方法。切入点的方法体通常是空的。
切入点方法的访问控制符同时也控制着这个切入点的可见性。
如果切入点要在多个切面中共用, 最好将它们集中在一个公共的类中. 在这种情况下, 它们必须被声明为 public。 在引入这个切入点时, 必须将类名也包括在内。 如果类没有与这个切面放在同一个包中, 还必须包含包名。
其他通知可以通过方法名称引入该切入点。

CalculatorAspect.java

package com.cd.spring.bean;import java.util.Arrays;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;// 使用注解@Aspect声明一个切面类@Aspect@Componentpublic class CalculatorAspect {    /**     * 定义一个方法, 用于声明切入点表达式。     * 使用 @Pointcut 来声明切入点表达式。     */    @Pointcut("execution(public int com.cd.spring.bean.*(..))")    public void declareJointPointExpression(){}    // 通知    @Before(value="declareJointPointExpression()")    public void beforeMethod(JoinPoint jp){  // JoinPoint切入点        String methodName = jp.getSignature().getName();        System.out.println(methodName);        System.out.println("before method execute,args are "+Arrays.toString(jp.getArgs()));    }    @After("com.cd.spring.bean.CalculatorAspect.declareJointPointExpression()")    public void afterMethod(JoinPoint jp){        System.out.println("after method execute,args are "+Arrays.toString(jp.getArgs()));    }    @AfterThrowing(value="declareJointPointExpression()",throwing="ex")    public void afterThrow(Exception ex){        System.out.println("afterThrow"+ex.getMessage());        }    @AfterReturning(value="declareJointPointExpression()",returning="result")    public void afterReturn(Object result){        System.out.println("the result is "+result);    }}

九. 基于xml配置切面

  1. 将Calculator、CalculatorImpl、CalculatorAspect类移到其他包里面,去掉类以及方法中的所有注解。

  2. 定义新的配置文件applicationContext-xml.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:context="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.0.xsd        http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.0.xsd">    <!-- 配置业务bean -->    <bean id="calculator" class="com.cd.spring.xml.CalculatorImpl"></bean>    <!-- 配置切面bean -->    <bean id="calculatorAspect" class="com.cd.spring.xml.CalculatorAspect"></bean>    <!-- 配置AOP -->    <aop:config>        <!--配置切点表达式-->        <aop:pointcut            expression="execution(* com.cd.spring.xml.Calculator.*(int,int))" id="pointCut" />        <!--配置切面及通知-->        <aop:aspect ref="calculatorAspect" order="1">            <aop:before method="beforeMethod" pointcut-ref="pointCut" />            <aop:after method="afterMethod" pointcut-ref="pointcut"/>            <aop:after-throwing method="afterThrowing" pointcut-ref="pointcut" throwing="e"/>            <aop:after-returning method="afterReturning" pointcut-ref="pointcut" returning="result"/>        </aop:aspect>    </aop:config></beans>
原创粉丝点击