>  기사  >  Java  >  Springboot 프로젝트에서 Aop 기능을 빠르게 구현하는 방법

Springboot 프로젝트에서 Aop 기능을 빠르게 구현하는 방법

WBOY
WBOY앞으로
2023-05-16 23:04:151425검색

종속성 소개

Springboot가 AOP 종속성 패키지를 도입한 후에는 일반적으로 다른 구성이 필요하지 않습니다. 낮은 버전이나 다른 구성은 AOP의 관련 기능에 영향을 미치므로 aop 기능이 적용되지 않습니다. @EnableAspectJAutoProxy를 추가해 볼 수 있습니다.

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

코드 구현

1. 사용자 정의 주석 @TestAop

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface TestAop {
}

2.ExampleAop .java

@Component
@Aspect
@Slf4j
public class ExampleAop {
 
    //切入点:增强标有@TestAop注解的方法
    @Pointcut(value = "@annotation(TestAop)")
    //切入点签名
    public void pointcut() {
        System.out.println("pointCut签名。。。");
    }
 
    //前置通知
    @Before("pointcut()")
    public void deBefore(JoinPoint joinPoint) throws Throwable {
        log.info("前置通知被执行");
        //可以joinpoint中得到命中方法的相关信息,利用这些信息可以做一些额外的业务操作;
    }
 
    //返回通知
    @AfterReturning(returning = "ret", pointcut = "pointcut()")
    public void doAfterReturning(Object ret) throws Throwable {
        log.info("返回通知被执行");
        //可以joinpoint中得到命中方法的相关信息,利用这些信息可以做一些额外的业务操作;
    }
 
    //异常通知
    @AfterThrowing(throwing = "ex", pointcut = "pointcut()")
    public void throwss(JoinPoint jp, Exception ex) {
        log.info("异常通知被执行");
        //可以joinpoint中得到命中方法的相关信息,利用这些信息可以做一些额外的业务操作;
        //可以从ex中获取到具体的异常信息
    }
 
    //后置通知
    @After("pointcut()")
    public void after(JoinPoint jp) {
        log.info("后置通知被执行");
        //可以joinpoint中得到命中方法的相关信息,利用这些信息可以做一些额外的业务操作;
    }
 
    @Around("pointcut()")
    public Object around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
        log.info("------环绕通知 start");
        String methodName = proceedingJoinPoint.getSignature().getName();
        String className = proceedingJoinPoint.getTarget().getClass().getName();
        Object[] args = proceedingJoinPoint.getArgs();
        String argsName = null;
        StringBuilder sb = new StringBuilder();
        if (args != null && args.length > 0) {
            for (int i = 0; i < args.length; i++) {
                if (args[i] != null) {
                    sb.append(";").append(args[i].getClass().getName());
                }
            }
            if (sb.toString().length() > 0) {
                argsName = sb.toString().substring(1);
            }
        }
        log.info("命中类:{},方法{},参数{};", className, methodName, argsName);
        Object proceed = proceedingJoinPoint.proceed();
        log.info("------环绕通知 end");
        return proceed;
    }
 
}

핵심 주석 및 클래스

1. 간단히 이해하면 클래스는 진입점과 알림 메서드 간의 대응을 표현하는 추상 캡슐화입니다.

@Before 주석이 표시된 메서드는 메서드가 실행되기 전에 실행됩니다. pre-notification

3. @After, post 알림, 메소드에 사용, @After 주석으로 표시된 메소드는 cut 메소드 실행 후 실행됩니다.

4, @AfterReturning, return 알림, 메소드에 사용, 주석이 추가됩니다. by @AfterReturning 표시된 메서드는 컷인 메서드가 결과를 반환한 후 실행됩니다.

6. @AfterThrowing: 메서드에 사용되는 예외 알림 @AfterThrowing 주석으로 표시된 메서드는 컷인 메서드 후에 실행됩니다. 일반적으로 예외 정보를 얻기 위해 사용됩니다.

7. @Aroud: 서라운드 알림, @Around 주석으로 표시된 메서드는 컷 메서드 실행 전후에 실행됩니다.

8. @Pointcut, on 메소드에 표시되어 있으며, 진입점을 정의하는 데 사용됩니다. 소위 진입점은 Spring에서 구체적으로 어떤 연결점을 의미하는지를 나타냅니다. @Pointcut으로 주석이 달린 표현식은 많이 사용되며, 가장 일반적으로 사용되는 두 가지는 연결 지점이라고 불리는 것입니다. Aop 관점에서 잘라내는 위치 지점을 구체적으로 말합니다.

10, PointCut,

11, 알림이라는 정의를 말합니다. Spring에서 정의된 진입점을 가로채는 후 수행해야 하는 특정 작업은 @Before, @After, @AfterReturning, @AfterThrowing, @Around 주석으로 표시된 메서드를 참조합니다.

진입점을 표시하는 일반적인 방법

1. 실행식

표현방식 : 접근수정자 반환값 패키지명. 패키지명... 클래스명 .메소드(매개변수 목록)

예1 : 모든 com.fanfu 패키지 및 모든 클래스에서 add로 시작하는 모든 메소드와 일치함을 나타냄 하위 패키지 아래. 반환 값과 매개변수는 제한되지 않습니다.

@Pointcut("execution(* com.fanfu..*.*.add*(..))")

예 2: 모든 com과 일치함을 나타냅니다. fanfu 패키지 및 하위 패키지 아래의 모든 클래스는 add로 시작하는 메서드이며 매개변수 유형은 문자열입니다. 반환 값은 제한되지 않습니다.

@Pointcut("execution(* com.fanfu..*.*.add*(String))")

예 3: com.fanfu 패키지 아래의 모든 클래스, 모든 메서드 및 모든 매개변수가 일치함을 나타냅니다.

@Pointcut("execution(* com.fanfu..*.*.*(String))")

execution()은 표현식의 본문입니다. 즉, 반환 값 유형이 제한되지 않음을 나타냅니다.
패키지 뒤의 *는 현재 패키지를 나타내고, 패키지 뒤의 두 연속 ..는 현재 패키지와 하위 패키지를 나타냅니다. .) 모든 매개변수를 나타냅니다.

마지막 *.*(..)는 모든 클래스, 모든 메소드 및 모든 매개변수 일치를 나타냅니다.


2. Annotation

Annotation 구문: @annotation(맞춤형 주석)

예: 나타냅니다. 모든 태그 @TestAop 주석과 일치하는 메소드

@Pointcut("@annotation(com.fanfu.config.TestAop)")

Spring Aop의 팁

각 @Pointcut은 실행 또는 주석을 사용하여 포인트컷을 정의할 수 있으며 로직은 여러 포인트컷 간에도 사용할 수 있습니다. &&, ||,

1, point1()&&point2()는 예제와 같이 point1과 point2에 도달하는 모든 포인트컷의 교차점을 나타냅니다. com.fanfu 패키지의 모든 클래스와 모든 하위 패키지; 이름은 add로 시작하고 매개변수 유형이 문자열인 것은 메소드 이름과 매개변수 유형, 즉 com.fanfu의 모든 클래스에 관계없이 com.fanfu.service 패키지의 모든 메소드 및 모든 하위 패키지의 모든 클래스와 교차합니다. 서비스 패키지 및 모든 하위 패키지, 메서드 또는 add1 또는 add2 메서드는 호출 전후에 around() 메서드의 논리를 실행합니다.

@Component
@Aspect
@Slf4j
public class ExampleAop {
    @Pointcut("execution(* com.fanfu..*.*.add*(String))")
    public void point1() {
    }
    @Pointcut("execution(* com.fanfu.service..*.*(..))")
    public void point2() {
    }
    @Pointcut("point1()&&point2()")
    public void point() {
    }
    @Around("point()")
    public Object around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
        log.info("------around start");
        Object proceed = proceedingJoinPoint.proceed();
        log.info("------around end");
        return proceed;
    }
}

2, point1()&&point2()는 point1과 합집합을 의미합니다. point2의 모든 진입점, 예를 들어 com.fanfu.service 패키지의 모든 클래스와 모든 하위 하위 패키지에서 메서드 이름은 add1이고 매개변수 유형은 String이며 com.fanfu.controller 패키지의 모든 메서드입니다. 모든 하위 하위 패키지의 모든 클래스에서 메서드 이름은 add2이고 매개 변수 유형은 String입니다. 즉, com.fanfu.service 또는 com.fanfu.controller 패키지의 모든 클래스에서 모든 메서드의 통합을 가져옵니다. 및 모든 하위 하위 패키지, 메서드 또는 add1 또는 add2 메서드에서 around() 메서드의 논리는 호출 전후에 실행됩니다. !point()는 모든 진입점의 반전을 의미합니다. 예에서와 같이 point()에 도달합니다. com.fanfu .service 패키지의 모든 클래스와 모든 하위 패키지에서 add로 시작하지 않는 메서드는 add로 시작하지 않는 메서드 전후에 around() 메서드의 논리를 실행합니다. call.Matching 메소드, 현재 호출 체인에서 해당 메소드가 현재 클래스에서 처음으로 일치하면 적중됩니다. 즉, 현재 호출 체인이 종료되지 않은 경우 해당 알림이 실행됩니다. 현재 클래스가 현재 메서드에서 호출되면 다른 진입점과 일치하며 더 이상 적중하지 않습니다. 즉, 진입점과 일치하는 다른 메서드가 실행될 때 다른 관련 알림이 더 이상 트리거되지 않습니다. 예:

当请求http://localhost:8080/example时,ExampleController中的example方法被触发,ExampleController#example()又调用了ExampleService#test(),在ExampleService#test()内部,又顺序调用了ExampleService#test1()和ExampleService#test2();在ExampleAop中,按照execution中的配置,是可以匹配到test()、test1()、test2(),实际是命中的方法只有test();

@Component
@Aspect
@Slf4j
public class ExampleAop {
    @Pointcut("execution(* com.fanfu.service.impl.ExampleServiceImpl.test*(..))")
    public void point2() {
        log.info("切入点匹配");
    }
    @Around("point()")
    public Object around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
        log.info("------around start");
        String methodName = proceedingJoinPoint.getSignature().getName();
        String className = proceedingJoinPoint.getTarget().getClass().getName();
        Object[] args = proceedingJoinPoint.getArgs();
        String argsName=null;
        StringBuilder sb=new StringBuilder();
        if (args!=null&&args.length>0) {
            for (int i = 0; i < args.length; i++) {
                if (args[i] != null) {
                    sb.append(";").append(args[i].getClass().getName());
                }
            }
            if (sb.toString().length()>0) {
                argsName=sb.toString().substring(1);
            }
        }
        log.info("命中类:{},方法{},参数{};",className,methodName,argsName);
        Object proceed = proceedingJoinPoint.proceed();
        log.info("------around end");
        return proceed;
    }
}
@Service
@Slf4j
public class ExampleServiceImpl implements IExampleService {
 
    @Override
    public String test(String msg) {
        log.info("test方法被执行");
        this.test1(msg);
        this.test2(msg);
        return msg;
    }
 
    public String test1(String msg) {
        log.info("test1方法被执行");
        return "msg1";
    }
 
    public String test2(String msg) {
        log.info("test2方法被执行");
        return "msg2";
    }
}
public interface IExampleService {
    public String test(String msg);
    public String test1(String msg);
    public String test2(String msg);
}
@RestController
@Slf4j
public class ExampleController {
    @Autowired
    private IExampleService exampleService;
    @GetMapping("/example")
    public String example() {
        log.info("example start");
        exampleService.test(null);
        log.info("example end");
        return String.valueOf(System.currentTimeMillis());
    }
}

Springboot 프로젝트에서 Aop 기능을 빠르게 구현하는 방법

2、对于上面的问题,如果把execution表达换成注解,会不会结果不一样?再把ExampleAop中的@Pointcut改成注解形式,再在ExampleService#test1()、ExampleService#test2()、ExampleService#test()添加注解@TestAop,验证结果依然是一样的,只有test()会命中,其他不会!所以要注意呀。

@Component
@Aspect
@Slf4j
public class ExampleAop {
    @Pointcut("@annotation(TestAop)")
    public void point() {
    }
    @Around("point()")
    public Object around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
        log.info("------around start");
        String methodName = proceedingJoinPoint.getSignature().getName();
        String className = proceedingJoinPoint.getTarget().getClass().getName();
        Object[] args = proceedingJoinPoint.getArgs();
        String argsName = null;
        StringBuilder sb = new StringBuilder();
        if (args != null && args.length > 0) {
            for (int i = 0; i < args.length; i++) {
                if (args[i] != null) {
                    sb.append(";").append(args[i].getClass().getName());
                }
            }
            if (sb.toString().length() > 0) {
                argsName = sb.toString().substring(1);
            }
        }
        log.info("命中类:{},方法{},参数{};", className, methodName, argsName);
        Object proceed = proceedingJoinPoint.proceed();
        log.info("------around end");
        return proceed;
    }
}
@Service
@Slf4j
public class ExampleServiceImpl implements IExampleService {
 
    @Override
    @TestAop
    public String test(String msg) {
        log.info("test方法被执行");
        this.test1(msg);
        this.test2(msg);
        return msg;
    }
    @Override
    @TestAop
    public String test1(String msg) {
        log.info("test1方法被执行");
        return "msg1";
    }
    @Override
    @TestAop
    public String test2(String msg) {
        log.info("test2方法被执行");
        return "msg2";
    }
   
}

3、那什么情况下,ExampleService#test1()、ExampleService#test2()、ExampleService#test()会同时命中呢?让从ExampleController#example()到ExampleService#test1()、ExampleService#test2()、ExampleService#test()分别在不同的调用链上,那么就可以同时命中了;

@RestController
@Slf4j
public class ExampleController {
    @Autowired
    private IExampleService exampleService;
    @GetMapping("/example")
    public String example() {
        log.info("example start");
        exampleService.test(null);
        exampleService.test1(null);
        exampleService.test2(null);
        log.info("example end");
        return String.valueOf(System.currentTimeMillis());
    }
}

Springboot 프로젝트에서 Aop 기능을 빠르게 구현하는 방법

위 내용은 Springboot 프로젝트에서 Aop 기능을 빠르게 구현하는 방법의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

성명:
이 기사는 yisu.com에서 복제됩니다. 침해가 있는 경우 admin@php.cn으로 문의하시기 바랍니다. 삭제