记录SpringBoot使用AOP编程

背景

公司需要新增一个配置,用于控制文件存储到本地还是MinIO服务器,使用AOP动态代理中的环绕通知控制MINIO上传逻辑。

代码

  1. 自定义注解,用于接收切点方法参数信息
/**
 * AOP执行自定义存储的方法
 *
 * @author Lyr
 * @projectName ecgnetwork
 * @date 2023/03/15 16:07
 */
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface StorageAopMethod {
    /**
     * 指定storage中的执行方法
     */
    String storageMethod();

    /**
     * 桶名
     */
    int bucketName();

    /**
     * 全限定文件路径参数在方法参数中的位置下标
     * 参数类型必须为String
     */
    int pathIndex();

    /**
     * 参数文件位置
     */
    int paramFilePosition() default -1;
}
  1. 定义切点
    /**
     * 文件下载
     *
     * @param bucketName 桶名称
     * @param objectName 路径
     * @return java.io.InputStream
     * @author Lyr
     * @date 2023/3/16 17:57
     */
    @StorageAopMethod(storageMethod = "download", bucketName = 0, pathIndex = 1)
    public InputStream download(String bucketName, String objectName) {
        MinioClient minioClient = getMinIoClient(bucketName);
        if (minioClient == null) {
            return null;
        }
        try {
            minioClient.statObject(bucketName, objectName);
        } catch (Exception e) {
            log.error("objectName no exist {}", e.getMessage(), e);
            return null;
        }
        try {
            return minioClient.getObject(bucketName, objectName);

        } catch (Exception e) {
            log.error("objectName download error {}", e.getMessage(), e);
            return null;
        }
    }
  1. 定义切面类,环绕通知执行本地上传逻辑
import com.alibaba.fastjson.JSON;
import com.medwings.ecgnetwork.annotation.StorageAopMethod;
import com.medwings.storage.request.UploadParameter;
import com.medwings.storage.server.UploadFileService;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;
import java.net.*;
import java.util.Enumeration;

/**
 * MinIO切面类 处理
 *
 * @author Lyr
 * @projectName ecgnetwork
 * @date 2023/03/15 11:40
 */
@Slf4j
@Aspect
@Component
public class MinIoToStorageAspect {

    @Value("${minio.enable}")
    private boolean enableMinIo;

    @Value("${minio.storage-disk}")
    private String disk;

    @Value("${server.port}")
    private String port;

    /**
     * 定义切点:
     * 在MinioConfig类中将所有被@StorageAopMethod注解过的方法为切点
     *
     * @param
     * @return void
     * @author Lyr
     * @date 2023/3/15 16:35
     */
    @Pointcut("within(com.medwings.ecgnetwork.config.MinioConfig) && @annotation(com.medwings.ecgnetwork.annotation.StorageAopMethod)")
    public void storageAspectLog() {
        log.info("执行的MinIO的AOP逻辑");
    }


    /**
     * AOP环绕处理
     *
     * @param joinPoint joinPoint
     * @return java.lang.Object
     * @author Lyr
     * @date 2023/3/15 14:59
     */
    @Around("storageAspectLog()")
    public Object storageAspect(ProceedingJoinPoint joinPoint) throws Throwable {
        if (enableMinIo) {
            Object proceed = joinPoint.proceed();
            log.info("MinIO处理完成:{}", proceed);
            return proceed;
        }
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        Method method = signature.getMethod();
        //获取代理对象实例
        UploadFileService uploadFileService = UploadFileService.getUploadFileService();
        //通过注解获取执行storage方法名称、相关参数信息
        StorageAopMethod storageAopMethod = method.getAnnotation(StorageAopMethod.class);
        //获取执行storage方法名称
        String storageMethodName = storageAopMethod.storageMethod();
        log.info("AOP处理MinIO相关逻辑:{}", storageMethodName);
        //获取代理类class对象
        Class<UploadFileService> uploadFileServiceClass = UploadFileService.class;
        //获取被代理方法实参
        Object[] args = joinPoint.getArgs();
        //上传逻辑单独处理
        Object results;
        if ("upload".equals(storageMethodName)) {
            UploadParameter uploadParameter = this.storageUpload(storageAopMethod, args);
            Method storageMethod = uploadFileServiceClass.getMethod(storageMethodName, UploadParameter.class);
            results = storageMethod.invoke(uploadFileService, uploadParameter);
            String url = this.getServerUrl();
            results = url + results;
        } else {
            String bucketName = args[storageAopMethod.bucketName()].toString();
            String path = args[storageAopMethod.pathIndex()].toString();
            Method storageMethod = uploadFileServiceClass.getMethod(storageMethodName, String.class);
            results = storageMethod.invoke(uploadFileService, this.pathSplice(path, bucketName));
        }
        log.info("AOP处理MinIO相关逻辑完成,返回值:{}", JSON.toJSONString(results));
        return results;
    }

    /**
     * 处理上传参数处理
     *
     * @param storageAopMethod storage服务中的上传方法
     * @param args             minIo入参
     * @return com.medwings.storage.request.UploadParameter
     * @author Lyr
     * @date 2023/3/17 17:25
     */
    private UploadParameter storageUpload(StorageAopMethod storageAopMethod, Object[] args) {
        UploadParameter uploadParameter = new UploadParameter();
        String bucketName = args[storageAopMethod.bucketName()].toString();
        String path = args[storageAopMethod.pathIndex()].toString();
        String s = this.pathSplice(path, bucketName);
        uploadParameter.setFilePath(s);
        int i = storageAopMethod.paramFilePosition();
        uploadParameter.setContent(args[i]);
        return uploadParameter;
    }

    /**
     * 桶名与参数路径拼接
     *
     * @param path       路径
     * @param bucketName 桶名称
     * @return java.lang.String
     * @author Lyr
     * @date 2023/3/21 16:02
     */
    private String pathSplice(String path, String bucketName) {
        return disk + bucketName + "/" + path;
    }

    /**
     * 获取当前 Java 服务的地址和端口号
     *
     * @return 地址和端口号,格式为 "http://<hostname>:<port>"
     * @throws UnknownHostException 如果无法解析本机的主机名时抛出此异常
     */
    private String getServerUrl() throws SocketException, UnknownHostException {
        String localAddr = null;
        Enumeration<NetworkInterface> ifaces = NetworkInterface.getNetworkInterfaces();
        while (ifaces.hasMoreElements()) {
            NetworkInterface iface = ifaces.nextElement();
            Enumeration<InetAddress> addrs = iface.getInetAddresses();
            while (addrs.hasMoreElements()) {
                InetAddress addr = addrs.nextElement();
                if (!addr.isLoopbackAddress() && addr instanceof Inet4Address) {
                    localAddr = addr.getHostAddress();
                    break;
                }
            }
            if (localAddr != null) {
                break;
            }
        }
        if (localAddr == null) {
            localAddr = InetAddress.getLocalHost().getHostAddress();
        }

        String format = String.format("http://%s:%s", localAddr, port);
        return format + "/api/minio/getFileInputStream?id=";
    }

总结:

  1. 当方法执行到MinIO上传逻辑时,进入切面方法
  2. 在环绕逻辑里根据配置判断是执行原方法还是继续执行切面方法
  3. 原方法上传到MinIO环绕逻辑继续执行则将文件信息上传到本地
  • 7
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值