SpringBoot的AOP切面编程通俗易懂

我这里要演示的就是一个简单的AOP编程的方式,其中使用了自定义注解和正常的切面

直接上代码

1.自定义注解,具体每个注解的解释自己百度

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface SysLog {
    String value() default "";
}

2.定义切面类

使用@Aspect注解的类,才能定义切面以及切面@Pointcut的增强方法@before,@After@around...

@Aspect
@Configuration
public class SysLogAspect {

    /**
     * 这里我们使用注解的形式,只要使用了@SysLog的注解都会调用这个方法,而这个方法是个切面方法,        
     * 会被增强
     * 当然,我们也可以通过切点表达式直接指定需要拦截的package,需要拦截的class 以及 method
     * 切点表达式:   execution(...)
    */
    @Pointcut("@annotation(com.wexin.aop.SysLog)")
    public void logPointCut1(){}
    
    /**
     * 这里就是正常的切面方法,指定到方法和参数,
     * 第一个*通配 权限修饰符和返回参数
     * (..) 表示通配入参
     */
    @Pointcut("execution(* com.wexin.aop.SysLogService.save(..))")
    public void logPointCut2(){}

    /**
     * 环绕通知 @Around  , 当然也可以使用 @Before (前置通知)  @After (后置通知)
     * @param point
     * @return
     * @throws Throwable
     */
    @Around("logPointCut1()")
    public Object around() throws Throwable {
        long beginTime = System.currentTimeMillis();
        System.out.println("环绕之前");
        Object result = point.proceed();
        System.out.println("环绕之后");
        long time = System.currentTimeMillis() - beginTime;
        try {
            saveLog(time);
        } catch (Exception e) {
        }
        return result;
    }

    /**
     * 保存日志
     * @param joinPoint
     * @param time
     */
    public void saveLog(long time) {
        System.out.println("保存日志 :" +time);
    }

    @Before("logPointCut1()")
    public void beforeT(){
        System.out.println("before1111");
    }

    @Before("logPointCut2()")
    public void before2(){
        System.out.println("before2222");
    }

}

3.定义Service层,不需要调用方法,输出一下即可,验证一下执行顺序

@Service
public class SysLogService {

    public boolean save (SysLogBo sysLogBo){
        System.out.println("beginService - save");
        return true;
    }

}

4.定义Controller层,查看验证结果

@RestController
public class TestController2 {

    @Autowired
    private SysLogService sysLogService;

    @SysLog("测试")
    @GetMapping("/test")
    public String test(){
        System.out.println("beginTest");
        sysLogService.save();
        return "over";
    }

}

我们期待的执行顺序应该是这样的:

1.自定义注解先生效,自定义注解被定义在切面中:

@Pointcut("@annotation(com.wexin.aop.SysLog)")
    public void logPointCut1(){}

@Around("logPointCut1()")
    public Object around(ProceedingJoinPoint point) throws Throwable {
        long beginTime = System.currentTimeMillis();
        System.out.println("环绕之前");
        Object result = point.proceed();
        System.out.println("环绕之后");
        long time = System.currentTimeMillis() - beginTime;
        try {
            saveLog(point, time);
        } catch (Exception e) {
        }
        return result;
    }

@Before("logPointCut1()")
    public void beforeT(){
        System.out.println("before1111");
    }

所以,应该会先打印出 @Around 中 调用  point.proceed();  之前的所有方法和日志:环绕之前

然后再打印@Before的日志:before1111,然后输出Controller的日志:beginTest;

然后调用save方法,此时由于save方法也在切面类中定义了:

    @Pointcut("execution(* com.wexin.aop.SysLogService.save(..))")
    public void logPointCut2(){}

    @Before("logPointCut2()")
    public void before2(){
        System.out.println("before2222");
    }

所以,会先输出before2222,然后才输出save方法中所写的:

public boolean save (SysLogBo sysLogBo){
        System.out.println("beginService - save");
        return true;
    }

由于save方法的切面包含在自定义注解的切面中,所以 point.proceed(); 所运行的方法应该是这两个切面,之后才会打印环绕之后,这个顺序如果理解了,能够很快帮你搞清楚这几个注解的使用方法。

最终的输出控制台的日志顺序应该是:

环绕之前
before1111
beginTest
before2222
beginService - save
环绕之后
保存日志 : 2019-04-14)

 

 

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