侧边栏壁纸
博主头像
噗呲先生 - 差生工具多

行动起来,活在当下

  • 累计撰写 571 篇文章
  • 累计创建 80 个标签
  • 累计收到 168 条评论

目 录CONTENT

文章目录

👨‍💻Java 利用AOP切面实现自定义注解示例

噗呲先生
2023-01-03 / 0 评论 / 3 点赞 / 482 阅读 / 0 字 / 正在检测是否收录...

什么是AOP?

  • AOP为Aspect Oriented Programming的缩写,意为:面向切面编程
  • AOP是一种编程范式,隶属于软工范畴,指导开发者如何组织程序结构
  • AOP最早由AOP联盟的组织提出的,制定了一套规范.Spring将AOP思想引入到框架中,必须遵守AOP联盟的规范
  • 通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术
  • AOP是OOP的延续,是软件开发中的一个热点,也是Spring框架中的一个重要内容,是函数式编程的一种衍生范型
  • 利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率

1、AOP的作用及优势是什么?

1、作用:
  • AOP采取横向抽取机制,取代了传统纵向继承体系重复性代码(性能监视、事务管理、安全检查、缓存)
  • 在程序运行期间,不修改源码对已有方法进行增强
  • 将业务逻辑和系统处理的代码(关闭连接、事务管理、操作日志记录)解耦
2、优势:
  • 减少重复代码
  • 提高开发效率
  • 维护方便

2、AOP相关术语介绍

  1. Joinpoint(连接点) – 所谓连接点是指那些被拦截到的点。在spring中,这些点指的是方法,因为spring只支持方法类型的连接点
  2. Pointcut(切入点) – 所谓切入点是指我们要对哪些Joinpoint进行拦截的定义
  3. Advice(通知/增强) – 所谓通知是指拦截到Joinpoint之后所要做的事情就是通知.通知分为前置通知,后置通知,异常通知,最终通知,环绕通知(切面要完成的功能)
  4. Introduction(引介) – 引介是一种特殊的通知在不修改类代码的前提下, Introduction可以在运行期为类动态地添加一些方法或Field
  5. Target(目标对象) – 代理的目标对象
  6. Weaving(织入) – 是指把增强应用到目标对象来创建新的代理对象的过程
  7. Proxy(代理) – 一个类被AOP织入增强后,就产生一个结果代理类
  8. Aspect(切面) – 是切入点和通知的结合,以后咱们自己来编写和配置的

以上内容摘自Spring AOP文档!

源码示例

1.自定义注解:

package com.javapub.demo.annotation.springbootannotation.annotation;

/**
 * @Version: 1.0
 * @Description: #自定义日志注解。
 * <p>
 * ①:什么时候使用该注解,我们定义为运行时;
 * ②:注解用于什么地方,我们定义为作用于方法上;
 * ③:注解是否将包含在 JavaDoc 中;
 * ④:注解名为 Log;
 * ⑤:定义一个属性,默认为空字符串;
 */

import java.lang.annotation.*;

@Target(ElementType.METHOD) //注解用于什么地方,我们定义为作用于方法上;
@Retention(RetentionPolicy.RUNTIME) //什么时候使用该注解,我们定义为运行时;
@Documented //注解是否将包含在 JavaDoc 中;
public @interface Log {//注解名为Log

    String value() default ""; //定义一个属性,默认为空字符串;
}

2.切面编程:

package com.javapub.demo.annotation.springbootannotation.aop;

/**
 * @Version: 1.0
 * @Description: 注释式日志切面
 */

import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;
import java.util.Arrays;

@Slf4j
@Aspect //@Aspect:声明该类为一个注解类
@Component
public class LogAspect {

    /**
     * @Pointcut:定义一个切点,后面跟随一个表达式,表达式可以定义为切某个注解,也可以切某个 package 下的方法;
     * <p>
     * 此处的切点是注解的方式,也可以用包名的方式达到相同的效果
     * '@Pointcut("execution(* com.javapub.demo.annotation.springbootannotation.*.*(..))")'
     */
    @Pointcut("@annotation(com.javapub.demo.annotation.springbootannotation.annotation.Log)")
    public void logPointCut() {
    }

    /**
     * @param joinPoint
     * @return
     * @throws Throwable
     * @Around 环绕,可以在切入点前后织入代码,并且可以自由的控制何时执行切点;
     * @Description: 这里其实应该使用 try{}catch(){}finally{} 做容错,为了代码简洁易懂就不加了
     */
    @Around("logPointCut()")
    public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
        long beginTime = System.currentTimeMillis();
        // 执行方法
        Object result = joinPoint.proceed();
        // 执行时长(毫秒)
        long time = System.currentTimeMillis() - beginTime;
        //异步保存日志
        saveLog(joinPoint, time);
        return result;
    }

    void saveLog(ProceedingJoinPoint joinPoint, long time) {
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        Method method = signature.getMethod();
        String methodName = signature.getName();
        // 请求的方法名
        String className = joinPoint.getTarget().getClass().getName();
        System.out.println("**************************");
        System.out.println(method);
        log.info("------------------------接口日志-----------------------" + "\n"
                + "类名称:" + className + "\n"
                + "方法名:" + methodName + "\n"
                + "执行时间:" + time + "毫秒");
        log.info("接口参数" + "\n" + Arrays.toString(joinPoint.getArgs()));
    }

    /**
     * 在切点之前,织入相关代码;
     *
     * @param joinPoint
     */
    @Before("logPointCut()")
    public void doBeforeAdvice(JoinPoint joinPoint) {
        log.info("进入方法前执行.....");
    }

    /**
     * 在切点返回内容后,织入相关代码,一般用于对返回值做些加工处理的场景;
     *
     * @param ret
     */
    @AfterReturning(returning = "ret", pointcut = "logPointCut()")
    public void doAfterReturning(Object ret) {
        log.info("方法的返回值 : {}", ret);
    }

    /**
     * 用来处理当织入的代码抛出异常后的逻辑处理;
     */
    @AfterThrowing("logPointCut()")
    public void throwss(JoinPoint jp) {
        log.info("方法异常时执行.....");
    }


    /**
     * 后置最终通知,final增强,不管是抛出异常或者正常退出都会执行
     */
    @After("logPointCut()")
    public void after(JoinPoint jp) {
        log.info("方法最后执行.....");
    }
}

Controller代码:

package com.javapub.demo.annotation.springbootannotation.controller;

import com.javapub.demo.annotation.springbootannotation.annotation.Log;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/order")
public class OrderController {

    /**    
     * @param id
     * @return
     */
    @Log
    @RequestMapping("/order-info")
    public String OrderInfo(@RequestParam String id) {
        return "this is OrderController.OrderInfo id=" + id;
    }
}

3

评论区