AOP常用七个注解

AOP其实最方便的就是你不需要改动原来的函数代码便能够再函数前函数后等切入时机上做一些你的逻辑处理,比如鉴权、日志、看函数执行耗时等等,就很方便,不需要你再去把代码嵌入到原来的每个函数了

常用的注解

  • @Aspect 切面类注解,放在你用来配置AOP的类上面的
  • @Pointcut(“execution(* com.example.demo2.controller.EchoController.test(…))”) 切入点
  • @Pointcut("@annotation(com.example.demo2.annotation.NeedPassword)") 用注解标识的切入点
  • @Before(“test()”) 在切入点执行之前执行
  • @After(“test()”) 在切入点执行之后执行
  • @Around(“test()”) 环绕切入点做处理
  • @AfterReturning(pointcut = “test()”,returning = “result”) 在切入点的函数执行返回后执行
  • @AfterThrowing(pointcut = “execution(* com.example.demo2.controller..(…))”,throwing = “e”) 在切入点发生异常后执行

代码实践

试着访问,看代码、注释以及控制台打印的去理解
常用注解的案例
http://localhost/
http://localhost/doSomething
鉴权的案例
http://localhost/getSecret?password=1
http://localhost/getSecret?password=123456

  • 目录结构
    SpringBoot Spring AOP 各种注解、自定义注解、鉴权使用示范_aop
  • maven pom.xml
		<dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-aop</artifactId>
        </dependency>
  • EchoController代码
package com.example.demo2.controller;

import com.example.demo2.annotation.NeedPassword;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class EchoController {

    @GetMapping("/")
    public String test(String text){
        System.err.println(String.format("the string you typed in is \"%s\"",text));
        System.err.println("test function executed");
        return "hello,test success!";
    }

    /**
     * 会报错产生异常被AOP捕获的接口
     * @return
     */
    @GetMapping("/doSomething")
    public String error()  {
        //故意制造错误
        int a=1/0;
        return "hello";
    }

    /**
     * 访问这个接口需要密码 123456
     * @param password
     * @return
     */
    @NeedPassword(value = "123456")
    @GetMapping(value = "/getSecret",params = {"password"})
    public String seeSecret(String password){
        return "my secret is not secret!ha ha ";
    }
}


  • NeedPassword 自定义的注解代码
package com.example.demo2.annotation;

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

/**
 * 需要一个密码
 * @author humorchen
 */
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface NeedPassword {
    String value="";
    String value();
}

  • TestAspect测试切面类代码
package com.example.demo2.aop;

import com.example.demo2.annotation.NeedPassword;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;

import java.util.Date;

@Component
@Aspect
public class TestAspect {

    /**
     * 声明一个切入点
     */
    @Pointcut("execution(* com.example.demo2.controller.EchoController.test(..))")
    public void test(){

    }

    /**
     * 声明一个以注解标识的切入点
     * 就是只要被那个注解标识了的就纳入进来
     */
    @Pointcut("@annotation(com.example.demo2.annotation.NeedPassword)")
    public void needPassword(){

    }

    @Before("needPassword()")
    public void beforeNeedPassword(){
        System.out.println("executed beforeNeedPassword");
    }

    /**
     * 对加了需要密码注解的方法做密码验证
     * 密码为注解的时候设置的密码
     * 所以我们在下面环绕函数里去获取那个注解并拿到注解里设置的密码
     * 然后跟函数传入的第一个参数的值进行对比,如果相同则密码正确执行函数,如果密码不正确则不执行被切的函数直接返回密码不正确
     * @param joinPoint
     * @return
     */
    @Around("needPassword()")
    public Object aroundNeedPassword(ProceedingJoinPoint joinPoint){
        String methodName=joinPoint.getSignature().getName();
        Object ret=null;
        try {
            //拿这个注解对象去获取注解上设置的正确的密码
            NeedPassword needPassword=joinPoint.getSignature().getDeclaringType().getDeclaredMethod(methodName,String.class).getAnnotation(NeedPassword.class);
            String correctPwd=needPassword.value();
            Object[] objects=joinPoint.getArgs();
            if(objects!=null&&objects[0]!=null&&objects[0].equals(correctPwd)){
                System.out.println("密码验证成功");
                ret=joinPoint.proceed();
            }else{
                ret="密码不正确";
                System.out.println(ret);
            }
        }catch (Throwable e){
            e.printStackTrace();
        }
        return ret;
    }

    /**
     * 被切的方法方法执行前执行下面方法
     */
    @Before("test()")
    public void beforeTest(){
        System.out.println("before");
    }

    /**
     * 被切的方法方法执行后执行下面方法
     */
    @After("test()")
    public void afterTest(){
        System.out.println("after");
    }

    /**
     * 环绕(你可以控制在哪里执行被切的方法)
     * 使用joinPoint.proceed()执行被切的方法,并得到被切方法执行后的返回值
     * 在我们这个方法里把被切的方法的返回值返回
     * 我们可以在方法前后做一些处理
     * @param joinPoint
     * @return
     */
    @Around("test()")
    public Object aroundTest(ProceedingJoinPoint joinPoint){
        System.out.println("around test before");
        Object obj=null;
        try {
            Date date1=new Date();
            obj=joinPoint.proceed();
            Date date2=new Date();
            long gap=date2.getTime()-date1.getTime();
            System.out.println(String.format("around: execute function test used %s ms",gap));
            //把一些参数信息打印出来看看
            Object[] objects=joinPoint.getArgs();
            if(objects!=null){
                for (int i=0,len=objects.length;i<len;i++){
                    Object o=objects[i];
                    String type="null";
                    String value="null";
                    if(o!=null){
                        type=o.getClass().getTypeName();
                        value=o.toString();
                    }
                    System.out.println(String.format("the type of args[%d] is %s ,value=%s",i,type,value));
                }
            }
            Signature signature=joinPoint.getSignature();
            System.out.println(String.format("signature name=%s,declared type is %s",signature.getName(),signature.getDeclaringTypeName()));
            System.out.println("joinPoint kind ="+joinPoint.getKind());
            System.out.println("target ="+joinPoint.getTarget().toString());
        }catch (Throwable e){
            e.printStackTrace();
        }
        System.out.println("around test after");
        return obj;
    }

    /**
     * 被切的方法返回后的处理
     * @param result
     */
    @AfterReturning(pointcut = "test()",returning = "result")
    public void doAfterReturning(Object result){
        String type="null";
        String value="null";
        if(result!=null){
            type=result.getClass().getTypeName();
            value=result.toString();
        }
        System.out.println(String.format("after returning : the type of returned object is %s ,and value is \"%s\"",type,value));
    }

    /**
     * 抛出异常后处理
     * 这里捕捉controller下面任意类的任意方法
     * @param e
     */
    @AfterThrowing(pointcut = "execution(* com.example.demo2.controller.*.*(..))",throwing = "e")
    public void doWhenThrowException(Throwable e){
        System.err.println(String.format("AOP got throwable: message=\"%s\"",e.getMessage()));
    }
}


  • Demo2Application代码
package com.example.demo2;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class Demo2Application {

    public static void main(String[] args) {
        SpringApplication.run(Demo2Application.class, args);
    }

}