SpringBoot基于注解的面向切面编程

AOP 中,模块化单位是方面,可以将一个方面想像为在独立实体中实现了软件系统中(横切)部分的常见功能,其主要目的是通过分离这些横切关注点而增加模块化 ,同时也符合DRY (不要重复你自己)原则,以避免代码重复
spring aop 是为了解耦 ,让一组类共享相同的行为,使用 切面来满足跨应用程序的横切关注点,从而提高应用程序的模块化
定义AOP 的术语:

  1. 通知:(advice)
    切面的目标 – 必须完成的工作 ,切面的工作称为通知 通知定义了切面是什么以及何时使用, 除了描述切面要完成的工作,通知还解决了何时执行这个工作的问题

spring 切面有五种类型的通知
前置通知(before)在目标方法被调用前调用通知功能

后置通知(after) 在目标方法完成后调用通知 ,不管接合点是否执行,都会执行通知,接合点的执行可能正常放回或者抛出异常,但不管发现,通知代码都会被执行

返回通知(after-returning) 在目标方法成功执行之后调用通知 ,如果从一个通知方法中抛出一个异常,则不会执行该通知

异常通知 (after-throwing )在目标方法抛出异常后被调用

环绕通知(around)在被通知的方法调用之前和调用之后执行自定义的行为 实际上通过围绕结合点而在其周围 当调用之前执行完成,并没有错误,可以使用point.proceed(); 进入调用之后
2. 切点(pointcut)
切点的定义会匹配通知所需要织入一个或多个连接点,一般使用明确的类和方法名称,或者利用正则表达式定义所匹配的可和方法的名称来指定这些切点 。可以将切点看作是用来缩小切面所通知的连接点的范围

  1. 连接点(join point)
    连接点是在应用执行过程中能够插入切面的一个点,这个点可以是调用方法时,抛出异常时,切面代码可以利用这些点插入到应用的正常流程之中,并且添加新的行为
  2. 切面
    切面是通知和切点的结合,通知和切点共同定义了切面的全部内容 ----是什么,在何时和何处完成其功能

spring 支持aspectj 的注解式切面编程
1.使用@aspectJ 声明是一个切面
2.使用@after @before @around 定义 advice ,可直接将拦截规则(切入点)作为参数
3.为了使切点服用 可使用@pointcut 专门定义拦截规则,然后在@after @before @around 的参数中调用
4.其中符合条件的每一个拦截处为来连接点(joinpoint) 通过反射可获得注解上的属性,然后做日志记录相关的操

@Component
@Aspect
public class AdviceAspect {
    protected final static Logger LOGGER = LoggerFactory.getLogger(AdviceAspect.class);

    @Pointcut("execution(public * com.voicecyber.controller.AdviceController..*(..))")
    public void advice(){
    }
    @Before("advice()")
    public void beforeAdvice(JoinPoint joinPoint){
        String name = joinPoint.getSignature().getName();
        Object[] args = joinPoint.getArgs();
        LOGGER.info("method name:"+name);
        LOGGER.info("参数:"+args.toString());
        LOGGER.info("before enter controller");
    }
    @AfterReturning(pointcut = "advice()" ,returning = "obj")
    public void afterAdvice(Object obj){
        LOGGER.info("controller return msg" + obj);
    }
    @AfterThrowing(pointcut = "advice()",throwing = "t")
    public void afterThrowing(JoinPoint joinPoint,Throwable t){
        Signature signature = joinPoint.getSignature();
        Object[] args = joinPoint.getArgs();
        LOGGER.info("method name:"+signature.getName());
        LOGGER.info("参数:"+args.toString());
        LOGGER.error("error message:"+t.getMessage());
    }

}
@Aspect
@Component
public class AroundAspect {
    @Pointcut(" execution(public * com.voicecyber.controller.AroundController..*(..))")
    public void around(){
    }
    @Around("around()" )
    public Object aroundAdvice(ProceedingJoinPoint joinPoint){
        Object proceed=null;
        Method method = ((MethodSignature) joinPoint.getSignature()).getMethod();
        System.out.println("访问的方法"+method.getName());
        Object[] args = joinPoint.getArgs();
        System.out.println("访问的参数"+args[0]);
        if ("test".equals(args[0].toString())){
            args[0]="sean";
        }
        System.out.println("访问controller 的前置通知结束  ,进入后置通知");
        try {
            proceed = joinPoint.proceed(args); //around  方法最主要的一点在于这个地方,当前置通知结束后,需要手动调用proceed 发方法,使其进入后置通知。 这样就可以控制访问的前置数据和后置数据。
            System.out.println("controller 返回"+proceed);
        } catch (Throwable throwable) {
            throwable.printStackTrace();
        }
        return proceed;
    }
}

你可能感兴趣的:(springboot)