springboot下的spring aop注解实现和execution正则实现

先上maven依赖

<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-aop</artifactId>
</dependency>

一个简单的增删改查service(被切入的类)

package com.fchan.layui.service;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fchan.layui.aspect.CheckUser;
import com.fchan.layui.entity.AopTestEntity;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

@Service
@Slf4j
public class SpringAopTestService {

    private static final ObjectMapper objectMapper = new ObjectMapper();

    public String insert(AopTestEntity aopTestEntity) throws JsonProcessingException {
        String result = objectMapper.writeValueAsString(aopTestEntity);
        log.info("插入一条数据:{}",result);
        return result;
    }

    @CheckUser
    public void delete(AopTestEntity aopTestEntity) throws JsonProcessingException {
        String result = objectMapper.writeValueAsString(aopTestEntity);
        log.info("删除一条数据:{}",result);
    }


}

AspectJ提供不同的通知类型

  • @Before:前置通知,在执行方法之前进入前置通知
  • @AfterReturning:后置通知,可以获取到方法的返回值,但是修改不了返回值,方法抛异常后不会执行这个切入点的后置通知方法.
  • @Around:环绕通知,可以阻止目标方法执行.同时有AroundBefore的时候优先执行Around,然后再执行Before.同时有AroundAfterReturning的时候先执行AfterReturning再执行Around.Around.要注意Around可以拿到方法的返回值,并且可以修改返回值.
  • @AfterThrowing:异常抛出通知,只能捕获方法的异常,如果是在切面中抛出的异常是捕获不到的.
  • @After:最终final通知,不管被切入方法是否抛异常,这个通知都会执行.和Around一起用的时候After优先级高
  • @DeclareParents:引介通知

aop切面的注解形式和execution正则形式

注解形式的需要先声明一个注解接口

package com.fchan.layui.aspect;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface CheckUser {
}
`切面demo代码`

在切面中使用根据注解切的时候的各种方式,如根据注解切整个controller类的方法。(当前这个注解要声明未可以加在类上)

//@Around("@annotation(自定义注解)")//自定义注解标注在方法上的方法执行aop方法
如:@Around("@annotation(org.springframework.transaction.annotation.Transactional)")

//@Around("@within(自定义注解)")//自定义注解标注在的类上;该类的所有方法(不包含子类方法)执行aop方法
如:@Around("@within(org.springframework.transaction.annotation.Transactional)")

//@Around("within(包名前缀.*)")//com.aop.within包下所有类的所有的方法都会执行(不包含子包) aop方法
如:@Around("within(com.aop.test.*)")

//@Around("within(包名前缀..*)")//com.aop.within包下所有的方法都会执行(包含子包)aop 方法
如:@Around("within(com.aop.test..*)")

//@Around("this(java类或接口)")//实现了该接口的类、继承该类、该类本身的类---的所有方法(包括不是接口定义的方法,但不包含父类的方法)都会执行aop方法
如:@Around("this(com.aop.service.TestService)")

//@Around("target(java类或接口)")//实现了该接口的类、继承该类、该类本身的类---的所有方法(包括不是接口定义的方法,包含父类的方法)
如:@Around("this(com.aop.service.TestService)")
package com.fchan.layui.aspect;

import lombok.extern.slf4j.Slf4j;
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.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;

@Aspect
@Component
@Slf4j
public class TestAspect {


    @Pointcut(value = "execution(* com.fchan.layui.service.SpringAopTestService.insert(..))")
    public void insertPointcut(){}

    @Before(value = "insertPointcut()")
    public String beforeInsert(JoinPoint joinPoint){
        if(!"hello".equals(CurrentUserHolder.get())){
            return "not allow";
        }
        return "allow";
    }

    @Pointcut("@annotation(CheckUser)")
    public void CheckUser(){

    }

    @After(value = "CheckUser()")
    public void afterDelete(JoinPoint joinPoint){
        log.info("删除一条数据之后:{}",joinPoint.getArgs());
    }

	 /**
     * 要判断传入的参数类型是Map或者List时需要写包名全路径
     */
    @Pointcut("execution(String com.fchan.layui.service.SpringAopTestService.*(java.util.Map)))")
    public void testArgOnlyReturnStringAndMap(){}


}

@Pointcut的几种匹配包的方式

within
..代表匹配包以及子包

//匹配 ProductService类里的的所有方法
@Pointcut("within(com.imooc.service.ProductService)")
public void testWithin(){}

//匹配com.imooc包以及子包下所有类的方法
@Pointcut("within(com.imooc..*)")
public void testWithin(){}

匹配对象

/**
 * 匹配AOP对象的目标对象为指定类型的方法,即 SpringAopTestService 的aop代理对象的方法
 */
@Pointcut(value = "this(com.fchan.layui.service.SpringAopTestService)")
public void testThis(){}

/**
 * 匹配实现 SpringAopTestInterface 接口的目标对象(而不是aop代理后的对象)的方法,这里即SpringAopTestService的方法
 */
@Pointcut(value = "target(com.fchan.layui.service.SpringAopTestInterface)")
public void testTarget(){}


/**
 * 匹配所有以service结尾的bean里头的方法
 */
@Pointcut(value = "bean(*Service)")
public void testBean(){}

匹配参数

/**
  * 匹配任何以find开头而且只有一个Long参数的方法
  */
 @Pointcut("execution(* *..find*(Long))")
 public void testArgs(){}


/**
 * 匹配任何只有一个Long参数的方法
 */
@Pointcut("args(Long)")
public void testArgOne(){}

/**
 * 匹配任何以find开头的而且第一个参数为Long型的方法
 */
@Pointcut("execution( * *..find*(Long, ..))")
public void testArgsOne(){}


/**
 * 匹配第一个参数为Long型的所有方法
 */
@Pointcut("args(Long, ..)")
public void testArgFirstOne(){}
/**
 * 匹配路径在com.fchan.layui.service下的SpringAopTestService中只有一个Long型参数的所有方法
 */
@Pointcut("args(Long) && within(com.fchan.layui.service.SpringAopTestService))")
public void testArgAndPackage(){}

execution的格式
springboot下的spring aop注解实现和execution正则实现_第1张图片
修饰符
返回值类型
包名
方法名和方法形参
匹配方法抛出的异常

图中后面带"?"的可以省略,其他的不能省略

/**
 * 匹配路径在com.fchan.layui.service下的SpringAopTestService中返回值为String的所有方法
 */
@Pointcut("execution(String com.fchan.layui.service.SpringAopTestService.*(..)))")
public void testArgAndPackageOnlyReturnString(){}

使用通知的时候获取传入方法的形参,下面以Before通知为例

springboot下的spring aop注解实现和execution正则实现_第2张图片

你可能感兴趣的:(JAVA,spring)