Spring-AOP

一. 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;

@Component
public 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
@Component
public 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
@Component
public 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
@Component
public 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
@Component
public 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>
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值