Spring AOP使用记录

通知

  • @Before:前置通知
  • @After:后置通知,无论是否发生异常都会执行
  • @AfterReturning:后置通知,正常返回时执行
  • @AfterThrowing:后置通知,异常返回时执行
  • @Aroud:环绕通知
    各种通知使用例子
// 定义切点  
@Pointcut("execution(* com.wangtao.springInAction.service.IDBService.*(..))")  
public void pointCut() {  

}  

/**
 * 定义通知, 并引用切点  
 * 在方法执行前执行  
 */  
@Before("pointCut()")  
public void doBefore() {  
    System.out.println("开始事务");  
}  

@After("pointCut()")  
public void doAfter() {  
    System.out.println("在方法之后执行, 无论方法是否抛出异常");  
}  

/**  
 * 在方法正常返回后执行  
 * returning: 指定目标方法返回结果的参数名
 */  
@AfterReturning(value = "pointCut()", returning = "result")  
public void doAfterReturning(Object result) {  
    System.out.println(result);  
    System.out.println("提交事务");  
}  

/**
 * 在抛出异常时执行
 * throwing: 目标方法异常参数名字, 方便将异常对象绑定到通知方法参数中
 * 这里无须再将异常抛出去, 内部自己会将异常抛出去, 也就是说异常不会被吞掉
 */  
@AfterThrowing(value = "pointCut()", throwing = "e")  
public void doAfterThrowing(Exception e) {  
    System.out.println(e.getClass());  
    System.out.println("出现异常, 回滚");  
}  

/**
 * 环绕通知
 */
@Around(value = "pointCut()")  
public Object doRound(ProceedingJoinPoint jp) {  
    try {
        // 执行目标方法, 必须将结果返回回去, 不然调用目标方法会拿不到执行结果  
        return jp.proceed();  
    } catch (Throwable e) {  
        throw new RuntimeException(e);  
    }  
}

切点表达式

@annotation

方法存在指定的注解,将会被拦截
使用例子:
创建一个自定义注解

package com.wangtao.aop.annotation;

@Documented  
@Retention(RetentionPolicy.RUNTIME)  
@Target({ElementType.METHOD, ElementType.TYPE})  
public @interface MyTransactional {  

}

创建一个切面

package com.wangtao.aop.aspect;

@Aspect
@Component
public class AnnotationAspect {

    /**
     * 使用方式1
     * 通知方法中将注解作为参数,@annotation使用参数名称来推断具体的注解
     * 此方式可以直接获取到拦截方法的注解信息
     * 注: 此方式无法在@Pointcut切点注解使用, 因为没有注解参数信息,无法推断具体注解,运行时      * 会报错
     */
    @Around("@annotation(transactional)")
    public Object doRound(ProceedingJoinPoint jp, MyTransactional transactional) throws Throwable {
        Object result;
        System.out.println(transactional);
        try {
            result = jp.proceed();
            System.out.println("=====commit=====");
            return result;
        } catch (Throwable e) {
            System.out.println("=====rollback=====");
            throw e;
        }
    }
    
    /**
     * 使用方式2
     * @annotation指定注解完全限定名
     * 该方式无法在通知方法中直接获取到拦截方法的注解信息,需要先拿到拦截方法,再拿注解信息
     * 注: @annotation如果只写类名, 会自动加上切面所在的包名来进行推断
     */
    @Around("@annotation(com.wangtao.aop.aspect.MyTransactional)")
    public Object doRound(ProceedingJoinPoint jp) throws Throwable {
        Object result;
        MyTransactional transactional = getGoalMethod(jp).getAnnotation(MyTransactional.class);
        System.out.println(transactional);
        try {
            result = jp.proceed();
            System.out.println("=====commit=====");
            return result;
        } catch (Throwable e) {
            System.out.println("=====rollback=====");
            throw e;
        }
    }
    
    private Method getGoalMethod(ProceedingJoinPoint jp) {  
        MethodSignature signature = (MethodSignature)jp.getSignature();  
        return signature.getMethod();  
    }
}

技巧

获取拦截方法

public Method getGoalMethod(ProceedingJoinPoint jp) {  
    MethodSignature signature = (MethodSignature)jp.getSignature();  
    return signature.getMethod();  
}

拿到拦截方法之后,便可以拿到各种信息了,如注解,参数类型,参数个数,返回值类型等。

posted on 2023-06-30 23:55  wastonl  阅读(41)  评论(0编辑  收藏  举报