spring学习-AOP切面编程

1.引入包

--aop相关包
spring-aop-4.0.0.RELEASE.jar
spring-aspects-4.0.0.RELEASE.jar
com.springsource.org.aopalliance-1.0.0.jar
com.springsource.org.aspectj.weaver-1.6.8.RELEASE.jar
--log日志相关包
commons-logging-1.1.3.jar
--Spring核心包
spring-beans-4.0.0.RELEASE.jar
spring-context-4.0.0.RELEASE.jar
spring-core-4.0.0.RELEASE.jar
spring-expression-4.0.0.RELEASE.jar

2.配置文件中引入aop命名空间(红色部分),基于注解的方式需要在配置文件中添加以下代码(蓝色字体)

 1 <?xml version="1.0" encoding="UTF-8"?>
 2 <beans xmlns="http://www.springframework.org/schema/beans"
 3     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 4     xmlns:context="http://www.springframework.org/schema/context"
 5     xmlns:aop="http://www.springframework.org/schema/aop"
 6     xsi:schemaLocation="http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.0.xsd
 7         http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
 8         http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd">
 9 
10     <!-- 配置自动扫描的包 -->
11     <context:component-scan base-package="com.springstudy.aop"></context:component-scan>
12     
13     <!-- 配置自动为匹配 aspectJ 注解的 Java 类生成代理对象 -->
14     <aop:aspectj-autoproxy></aop:aspectj-autoproxy>
15     
16 </beans>

3.将横切关注点的代码抽象到切面的类中

(1)切面首先是一个 IOC 中的 bean,即添加注解@Component

(2)切面类也需要添加注解  @Aspect

4.AspectJ 支持 5 种类型的通知注解

通知是标注有某种注解的简单的 Java 方法.

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

5.在aop的方法中要声明代理的类(下图红色字体)

package com.springstudy.aop;

import java.util.Arrays;
import java.util.List;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;

@Aspect  //声明为切面类
@Component   //配置bean实例
public class AuthLoggingAspect {

    // 声明该方法是在目标方法之前执行
    @Before("execution(public int com.springstudy.aop.Auth.*(..))")
    public void befordmethod(JoinPoint jp) {
        String method = jp.getSignature().getName();
List<Object> args = Arrays.asList(jp.getArgs());
        System.out.println(method+"Before..." + args+";"+jp.getKind());
    }

    public void aftermethod() {

    }

}

 6.最典型的切入点表达式时根据方法的签名来匹配各种方法:

2 execution * com.atguigu.spring.ArithmeticCalculator.*(..): 匹配 ArithmeticCalculator 中声明的所有方法,
  第一个 * 代表任意修饰符及任意返回值.
  第二个 * 代表任意方法. .. 匹配任意数量的参数.
  若目标类与接口与该切面在同一个包中, 可以省略包名.
3 execution public * ArithmeticCalculator.*(..): 匹配 ArithmeticCalculator 接口的所有公有方法. 4 execution public double ArithmeticCalculator.*(..): 匹配 ArithmeticCalculator 中返回 double 类型数值的方法 5 execution public double ArithmeticCalculator.*(double, ..): 匹配第一个参数为 double 类型的方法, .. 匹配任意数量任意类型的参数 6 execution public double ArithmeticCalculator.*(double, double): 匹配参数类型为 double, double 类型的方法.

7.让通知访问当前连接点的细节

  示例: 5 中红色字体黄色背景部分代码。类型为 JoinPoint 的参数,并通过该参数访问链接细节。

8.其它五个方法以及通过@order(数字)指定切面优先级,即切面程序执行先后

package com.atguigu.spring.aop;

import java.util.Arrays;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;

/**
 * 可以使用 @Order 注解指定切面的优先级, 值越小优先级越高
 */
@Order(2)
@Aspect
@Component
public class LoggingAspect {
    
    /**
     * 定义一个方法, 用于声明切入点表达式. 一般地, 该方法中再不需要添入其他的代码. 
     * 使用 @Pointcut 来声明切入点表达式. 
     * 后面的其他通知直接使用方法名来引用当前的切入点表达式. 
     */
    @Pointcut("execution(public int com.atguigu.spring.aop.ArithmeticCalculator.*(..))")
    public void declareJointPointExpression(){}


  /** * 在 com.atguigu.spring.aop.ArithmeticCalculator 接口的每一个实现类的每一个方法开始之前执行一段代码 */ @Before("declareJointPointExpression()") public void beforeMethod(JoinPoint joinPoint){ String methodName = joinPoint.getSignature().getName(); Object [] args = joinPoint.getArgs(); System.out.println("The method " + methodName + " begins with " + Arrays.asList(args)); } /** * 在方法执行之后执行的代码. 无论该方法是否出现异常 */ @After("declareJointPointExpression()") public void afterMethod(JoinPoint joinPoint){ String methodName = joinPoint.getSignature().getName(); System.out.println("The method " + methodName + " ends"); } /** * 在方法法正常结束受执行的代码 * 返回通知是可以访问到方法的返回值的! */ @AfterReturning(value="declareJointPointExpression()", returning="result") public void afterReturning(JoinPoint joinPoint, Object result){ String methodName = joinPoint.getSignature().getName(); System.out.println("The method " + methodName + " ends with " + result); } /** * 在目标方法出现异常时会执行的代码. * 可以访问到异常对象; 且可以指定在出现特定异常时在执行通知代码 */ @AfterThrowing(value="declareJointPointExpression()", throwing="e") public void afterThrowing(JoinPoint joinPoint, Exception e){ String methodName = joinPoint.getSignature().getName(); System.out.println("The method " + methodName + " occurs excetion:" + e); } /** * 环绕通知需要携带 ProceedingJoinPoint 类型的参数. * 环绕通知类似于动态代理的全过程: ProceedingJoinPoint 类型的参数可以决定是否执行目标方法. * 且环绕通知必须有返回值, 返回值即为目标方法的返回值 */ @Around("execution(public int com.atguigu.spring.aop.ArithmeticCalculator.*(..))") 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); throw new RuntimeException(e); } //后置通知 System.out.println("The method " + methodName + " ends"); return result; } }
<?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.0.xsd">

    <!-- 配置 bean -->
    <bean id="arithmeticCalculator" 
        class="com.atguigu.spring.aop.xml.ArithmeticCalculatorImpl"></bean>

    <!-- 配置切面的 bean. -->
    <bean id="loggingAspect"
        class="com.atguigu.spring.aop.xml.LoggingAspect"></bean>

    <bean id="vlidationAspect"
        class="com.atguigu.spring.aop.xml.VlidationAspect"></bean>

    <!-- 配置 AOP -->
    <aop:config>
        <!-- 配置切点表达式 -->
        <aop:pointcut expression="execution(* com.atguigu.spring.aop.xml.ArithmeticCalculator.*(int, int))" 
            id="pointcut"/>
        <!-- 配置切面及通知 -->
        <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="e"/>
            <aop:after-returning method="afterReturning" pointcut-ref="pointcut" returning="result"/>
            <!--  
            <aop:around method="aroundMethod" pointcut-ref="pointcut"/>
            -->
        </aop:aspect>    
        <aop:aspect ref="vlidationAspect" order="1">
            <aop:before method="validateArgs" pointcut-ref="pointcut"/>
        </aop:aspect>
    </aop:config>

</beans>
原文地址:https://www.cnblogs.com/yuntianblog/p/13425109.html