springBoot-aop

springBoot-aop

简介

AOP(Aspect OrientedProgramming):面向切面编程,面向切面编程(也叫面向方面编程),是目前软件开发中的一个热点,也是Spring框架中的一个重要内容。利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。

使用场景

利用AOP可以对我们边缘业务进行隔离,降低无关业务逻辑耦合性。提高程序的可重用性,同时提高了开发的效率。一般用于日志记录性能统计安全控制权限管理事务处理异常处理资源池管理。使用场景

技术要点

  1. 通知(Advice):包含了需要用于多个应用对象的横切行为,完全听不懂,没关系,通俗一点说就是定义了“什么时候”和“做什么”。
  2. 连接点(Join Point):是程序执行过程中能够应用通知的所有点。
  3. 切点(Pointcut):是定义了在“什么地方”进行切入,哪些连接点会得到通知。显然,切点一定是连接点。
  4. 切面(Aspect):是通知和切点的结合。通知和切点共同定义了切面的全部内容——是什么,何时,何地完成功能。
  5. 引入(Introduction):允许我们向现有的类中添加新方法或者属性。
  6. 织入(Weaving):是把切面应用到目标对象并创建新的代理对象的过程,分为编译期织入、类加载期织入和运行期织入。

在springBoot中运用

  1. 导入依赖
        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjweaver</artifactId>
        </dependency>

        <!--aop-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-aop</artifactId>
        </dependency>
  1. 编写测试的TestController
@RestController
public class TestController {

    @GetMapping("/test01")
    public String test01(@RequestParam(value = "str")String str){
        return str + "我是test01";
    }

    @GetMapping("/test02")
    public String test02(){
        int a = 10/0;
        return "出异常了";
    }

    @Wlog(value = "test")
    @PostMapping("/test03")
    public String test03(){

        return "test03";
    }
}

  1. 定义切面
@Aspect  //使用@Aspect注解表示这是一个切面类
@Component
public class LogAspect {

    /**
     * @Pointcut 定义切点
     * 由于Spring切面粒度最小是达到方法级别,而execution表达式可以用于明确指定方法返回类型,类名,方法名和参数名等与方法相关的部件,并且实际中,
     * 大部分需要使用AOP的业务场景也只需要达到方法级别即可,因而execution表达式的使用是最为广泛的。如下是execution表达式的语法
     */
    @Pointcut("execution(public * com.cxf.controller.*.*(..))")
    public void webLog(){}

    //前置通知
    @Before(value = "webLog()")
    public void doBefore(JoinPoint joinPoint){
        System.out.println("在方法执行前通知");
        ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = requestAttributes.getRequest();
        System.out.println("url:"+request.getRequestURL().toString());
        System.out.println("requestMethod:"+request.getMethod());
        System.out.println("content-Type:"+request.getContentType());
        System.out.println("remoteHost:"+request.getRemoteHost());
        System.out.println("args:"+ Arrays.toString(joinPoint.getArgs()));

    }
    
    //后置通知,相当于是finally的增强,不管是异常还是正常退出都会执行
    @After(value = "webLog()")
    public void doAfter(JoinPoint joinPoint){
        System.out.println("在方法执行后");

    }
    
    //环绕通知,环绕增强,相当于MethodInterceptor
    @Around("webLog()")
    public Object arround(ProceedingJoinPoint pjp) throws Throwable{
        System.out.println("方法环绕start.....");
        try {
            Object o =  pjp.proceed();
            System.out.println("方法环绕proceed,结果是 :" + o);
            return o;
        } catch (Throwable e) {
            throw e;
        }
    }

    //方法的返回值
    @AfterReturning(returning = "ret",pointcut = "webLog()")
    public void returnArgs(Object ret){
        System.out.println("return:"+ret.toString());

    }

    //有异常时触发
    @AfterThrowing(value = "webLog()")
    public void afterThrow(JoinPoint joinPoint){
        System.out.println("触发了异常");
    }

}
  1. 测试结果

    aopconsole

自定义注解

在日常开发中,有一些相同的功能,但是又不想编写重复的代码,此时自定义一个注解,并且赋予他功能,只需在需要使用到的地方加上这个注解就方便很多。

如何自定义注解

  1. 定义注解 -- 相当于定义标记
  2. 配置注解 -- 把标记打在需要用到的程序代码中
  3. 解析注解 -- 在编译期或运行时检测到标记,并运行特殊操作

基本语法

  • 所有定义的注解都会自动继承java.lang.annotation,Annotation接口
  • 关键字: @interface

定义注解类型元素

  1. 访问修饰符必须为public,不写默认为public
  2. 类型为基本数据类型、String、Class、枚举类型、注解类型、以及上述类型的一维数组
  3. 元素名称一般为名词,如果注解中只有一个元素,请命名为value(方便后续操作)
  4. ()内不放任何参数,只是特殊语法
  5. default为默认值
  6. 若没有默认值,则后续使用时必须赋值

元注解

  1. @Target 限制注解的使用范围 TYPE--类、FIELD--属性、METHOD--方法、PARAMETER--方法形式参数、LOCAL_VARIABLE--局部变量、ANNOTATION--注解类型、PACKAGE--包
  2. @Retention 用来修饰自定义注解的生命力 SOURCE--注解将被编译器忽略掉、CLASS--注解将被编译器记录在class文件中,但在运行时不会被虚拟机保留,
    这是一个默认的行为、RUNTIME--注解将被编译器记录在class文件中,而且在运行时会被虚拟机保留,因此它们能通过反射被读取到
  3. @Documented 是被用来指定自定义注解是否能随着被定义的java文件生成到JavaDoc文档当中
  4. @Inherited 是指定某个自定义注解如果写在了父类的声明部分,那么子类的声明部分也能自动拥有该注解。只对那些@Target被定义为ElementType.TYPE的自定义注解起作用。

示例

@Target({ElementType.TYPE,ElementType.METHOD})
@Retention(java.lang.annotation.RetentionPolicy.RUNTIME)
@Documented
public @interface Wlog {

    String value() default "";

}

定义该注解切面

@Aspect
@Component
public class MyWlogAspect {

    @Pointcut(value = "@annotation(com.cxf.annotation.Wlog)")
    public void myMethod() {}
    
    @Around(value = "@annotation(wlog)")
    public Object aroundAdvice(ProceedingJoinPoint pjp, Wlog wlog){
        System.out.println("获取注解属性的值:"+wlog.value());
        try{
            return pjp.proceed();
        } catch (Throwable throwable) {
            throwable.printStackTrace();
            return null;
        }
    }
}

测试结果

testResult

posted @ 2023-03-10 14:52  cxf0616  阅读(42)  评论(0编辑  收藏  举报