【SpringBoot苍穹外卖】debugDay03.5

news2025/2/13 7:04:04

1、AOP面向切面编程

1. @Target(ElementType.METHOD)

  • 作用:指定自定义注解可以应用的目标范围。

  • 参数ElementType 是一个枚举类,定义了注解可以应用的目标类型。

    • ElementType.METHOD 表示该注解只能用于方法上。

    • 其他常见的 ElementType 值:

      • TYPE:类、接口、枚举等。

      • FIELD:字段(包括枚举常量)。

      • PARAMETER:方法参数。

      • CONSTRUCTOR:构造函数。

      • ANNOTATION_TYPE:注解类型。

      • PACKAGE:包。

      • 等等。

示例

@Target(ElementType.METHOD)
public @interface MyAnnotation {
    String value() default "";
}
  • 上面的 @MyAnnotation 注解只能用于方法上,如果尝试将其用于类或字段上,编译器会报错。


2. @Retention(RetentionPolicy.RUNTIME)

  • 作用:指定自定义注解的生命周期,即注解在什么阶段有效。

  • 参数RetentionPolicy 是一个枚举类,定义了注解的保留策略。

    • RetentionPolicy.RUNTIME:注解在运行时保留,可以通过反射机制读取。

    • RetentionPolicy.CLASS:注解在编译时保留,但运行时不可见(默认行为)。

    • RetentionPolicy.SOURCE:注解仅在源码阶段保留,编译后会被丢弃。

示例

@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotation {
    String value() default "";
}
  • 上面的 @MyAnnotation 注解在运行时仍然有效,可以通过反射获取注解信息。


结合使用

通常,@Target 和 @Retention 会一起使用,以明确注解的作用范围和生命周期。

示例

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

@Target(ElementType.METHOD) // 该注解只能用于方法上
@Retention(RetentionPolicy.RUNTIME) // 该注解在运行时有效
public @interface MyAnnotation {
    String value() default "";
}

使用场景

public class MyClass {
    @MyAnnotation(value = "This is a method")
    public void myMethod() {
        System.out.println("Hello, World!");
    }
}

通过反射获取注解信息

import java.lang.reflect.Method;

public class Main {
    public static void main(String[] args) throws Exception {
        Method method = MyClass.class.getMethod("myMethod");
        MyAnnotation annotation = method.getAnnotation(MyAnnotation.class);
        if (annotation != null) {
            System.out.println("Annotation value: " + annotation.value());
        }
    }
}

输出

Annotation value: This is a method

总结

  • @Target(ElementType.METHOD):指定注解只能用于方法上。

  • @Retention(RetentionPolicy.RUNTIME):指定注解在运行时仍然有效,可以通过反射读取。

  • 这两个注解通常一起使用,用于定义自定义注解的行为和适用范围。

3、反射(Reflection)

 是 Java 提供的一种强大的机制,允许程序在运行时动态地获取类的信息(如类名、方法、字段、构造函数等),并操作这些信息(如调用方法、访问字段、创建对象等)。反射的核心思想是“在运行时检查和修改程序的行为”。

反射的主要用途包括:

  1. 动态加载类。

  2. 动态创建对象。

  3. 动态调用方法。

  4. 动态访问和修改字段。

  5. 获取注解信息。


反射的核心类

Java 反射的核心类和接口位于 java.lang.reflect 包中,主要包括:

  • Class<T>:表示一个类或接口,是反射的入口。

  • Method:表示类的方法。

  • Field:表示类的字段(成员变量)。

  • Constructor<T>:表示类的构造函数。

  • Annotation:表示注解。


反射的基本用法

1. 获取 Class 对象

Class 对象是反射的起点,可以通过以下方式获取:

  • Class.forName("全限定类名"):通过类的全限定名获取。

  • 对象.getClass():通过对象的实例获取。

  • 类名.class:通过类字面量获取。

示例

Class<?> clazz = Class.forName("com.example.MyClass");
2. 创建对象

通过 Class 对象可以动态创建类的实例。

示例

Class<?> clazz = Class.forName("com.example.MyClass");
Object obj = clazz.getDeclaredConstructor().newInstance();
3. 调用方法

通过 Method 对象可以动态调用类的方法。

示例

Class<?> clazz = Class.forName("com.example.MyClass");
Object obj = clazz.getDeclaredConstructor().newInstance();

// 获取方法
Method method = clazz.getMethod("myMethod", String.class);

// 调用方法
method.invoke(obj, "Hello, Reflection!");
4. 访问字段

通过 Field 对象可以动态访问和修改类的字段。

示例

Class<?> clazz = Class.forName("com.example.MyClass");
Object obj = clazz.getDeclaredConstructor().newInstance();

// 获取字段
Field field = clazz.getDeclaredField("myField");

// 设置字段可访问(如果是私有字段)
field.setAccessible(true);

// 修改字段值
field.set(obj, "New Value");

// 获取字段值
Object value = field.get(obj);
System.out.println(value);
5. 获取注解信息

通过反射可以获取类、方法、字段等元素上的注解信息。

示例

Class<?> clazz = Class.forName("com.example.MyClass");

// 获取类上的注解
MyAnnotation classAnnotation = clazz.getAnnotation(MyAnnotation.class);
if (classAnnotation != null) {
    System.out.println("Class Annotation Value: " + classAnnotation.value());
}

// 获取方法上的注解
Method method = clazz.getMethod("myMethod");
MyAnnotation methodAnnotation = method.getAnnotation(MyAnnotation.class);
if (methodAnnotation != null) {
    System.out.println("Method Annotation Value: " + methodAnnotation.value());
}

反射的优缺点

优点
  1. 灵活性:可以在运行时动态加载类、调用方法、访问字段,适合编写通用框架或工具。

  2. 扩展性:通过反射可以实现插件化架构,动态加载第三方类。

  3. 注解处理:反射是处理注解的基础,许多框架(如 Spring、MyBatis)都依赖反射来解析注解。

缺点
  1. 性能开销:反射操作比直接调用方法或访问字段要慢,因为涉及动态解析。

  2. 安全性问题:反射可以绕过访问控制(如访问私有字段或方法),可能导致安全问题。

  3. 代码可读性差:反射代码通常比较复杂,难以理解和维护。


反射的实际应用场景

  1. 框架开发:许多框架(如 Spring、Hibernate、MyBatis)都使用反射来实现依赖注入、动态代理、注解解析等功能。

  2. 单元测试:测试框架(如 JUnit)使用反射来调用测试方法。

  3. 动态加载类:在插件化架构中,通过反射动态加载第三方类。

  4. 序列化和反序列化:通过反射访问对象的字段,实现对象的序列化和反序列化。


示例代码

以下是一个完整的反射示例,展示了如何通过反射创建对象、调用方法和访问字段:

import java.lang.reflect.Field;
import java.lang.reflect.Method;

public class ReflectionExample {
    public static void main(String[] args) throws Exception {
        // 获取 Class 对象
        Class<?> clazz = Class.forName("com.example.MyClass");

        // 创建对象
        Object obj = clazz.getDeclaredConstructor().newInstance();

        // 调用方法
        Method method = clazz.getMethod("myMethod", String.class);
        method.invoke(obj, "Hello, Reflection!");

        // 访问字段
        Field field = clazz.getDeclaredField("myField");
        field.setAccessible(true); // 设置私有字段可访问
        field.set(obj, "New Value");
        System.out.println("Field Value: " + field.get(obj));
    }
}

class MyClass {
    private String myField = "Default Value";

    public void myMethod(String message) {
        System.out.println("Method Invoked: " + message);
    }
}

输出

Method Invoked: Hello, Reflection!
Field Value: New Value

总结

反射是 Java 中一种强大的机制,允许程序在运行时动态操作类和对象。虽然反射提供了极大的灵活性,但也带来了性能开销和安全性问题,因此应谨慎使用。在实际开发中,反射常用于框架开发、注解处理、动态加载类等场景。

4、idea快捷键ctrl + alt + b

快速转到接口实现方法。

5、实战

1 步骤一

自定义注解 AutoFill

进入到sky-server模块,创建com.sky.annotation包。

package com.sky.annotation;

import com.sky.enumeration.OperationType;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 * 自定义注解,用于标识某个方法需要进行功能字段自动填充处理
 */
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface AutoFill {
    //数据库操作类型:UPDATE INSERT
    OperationType value();
}

其中OperationType已在sky-common模块中定义

package com.sky.enumeration;

/**
 * 数据库操作类型
 */
public enum OperationType {

    /**
     * 更新操作
     */
    UPDATE,

    /**
     * 插入操作
     */
    INSERT
}
2 步骤二

自定义切面 AutoFillAspect

在sky-server模块,创建com.sky.aspect包。

package com.sky.aspect;

/**
 * 自定义切面,实现公共字段自动填充处理逻辑
 */
@Aspect
@Component
@Slf4j
public class AutoFillAspect {

    /**
     * 切入点
     */
    @Pointcut("execution(* com.sky.mapper.*.*(..)) && @annotation(com.sky.annotation.AutoFill)")
    public void autoFillPointCut(){}

    /**
     * 前置通知,在通知中进行公共字段的赋值
     */
    @Before("autoFillPointCut()")
    public void autoFill(JoinPoint joinPoint){
        /重要
        //可先进行调试,是否能进入该方法 提前在mapper方法添加AutoFill注解
        log.info("开始进行公共字段自动填充...");

    }
}

完善自定义切面 AutoFillAspect 的 autoFill 方法

package com.sky.aspect;

import com.sky.annotation.AutoFill;
import com.sky.constant.AutoFillConstant;
import com.sky.context.BaseContext;
import com.sky.enumeration.OperationType;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;
import java.lang.reflect.Method;
import java.time.LocalDateTime;

/**
 * 自定义切面,实现公共字段自动填充处理逻辑
 */
@Aspect
@Component
@Slf4j
public class AutoFillAspect {

    /**
     * 切入点
     */
    @Pointcut("execution(* com.sky.mapper.*.*(..)) && @annotation(com.sky.annotation.AutoFill)")
    public void autoFillPointCut(){}

    /**
     * 前置通知,在通知中进行公共字段的赋值
     */
    @Before("autoFillPointCut()")
    public void autoFill(JoinPoint joinPoint){
        log.info("开始进行公共字段自动填充...");

        //获取到当前被拦截的方法上的数据库操作类型
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();//方法签名对象
        AutoFill autoFill = signature.getMethod().getAnnotation(AutoFill.class);//获得方法上的注解对象
        OperationType operationType = autoFill.value();//获得数据库操作类型

        //获取到当前被拦截的方法的参数--实体对象
        Object[] args = joinPoint.getArgs();
        if(args == null || args.length == 0){
            return;
        }

        Object entity = args[0];

        //准备赋值的数据
        LocalDateTime now = LocalDateTime.now();
        Long currentId = BaseContext.getCurrentId();

        //根据当前不同的操作类型,为对应的属性通过反射来赋值
        if(operationType == OperationType.INSERT){
            //为4个公共字段赋值
            try {
                Method setCreateTime = entity.getClass().getDeclaredMethod(AutoFillConstant.SET_CREATE_TIME, LocalDateTime.class);
                Method setCreateUser = entity.getClass().getDeclaredMethod(AutoFillConstant.SET_CREATE_USER, Long.class);
                Method setUpdateTime = entity.getClass().getDeclaredMethod(AutoFillConstant.SET_UPDATE_TIME, LocalDateTime.class);
                Method setUpdateUser = entity.getClass().getDeclaredMethod(AutoFillConstant.SET_UPDATE_USER, Long.class);

                //通过反射为对象属性赋值
                setCreateTime.invoke(entity,now);
                setCreateUser.invoke(entity,currentId);
                setUpdateTime.invoke(entity,now);
                setUpdateUser.invoke(entity,currentId);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }else if(operationType == OperationType.UPDATE){
            //为2个公共字段赋值
            try {
                Method setUpdateTime = entity.getClass().getDeclaredMethod(AutoFillConstant.SET_UPDATE_TIME, LocalDateTime.class);
                Method setUpdateUser = entity.getClass().getDeclaredMethod(AutoFillConstant.SET_UPDATE_USER, Long.class);

                //通过反射为对象属性赋值
                setUpdateTime.invoke(entity,now);
                setUpdateUser.invoke(entity,currentId);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
}
3 步骤三

在Mapper接口的方法上加入 AutoFill 注解

CategoryMapper为例,分别在新增和修改方法添加@AutoFill()注解,也需要EmployeeMapper做相同操作

package com.sky.mapper;

@Mapper
public interface CategoryMapper {
    /**
     * 插入数据
     * @param category
     */
    @Insert("insert into category(type, name, sort, status, create_time, update_time, create_user, update_user)" +
            " VALUES" +
            " (#{type}, #{name}, #{sort}, #{status}, #{createTime}, #{updateTime}, #{createUser}, #{updateUser})")
    @AutoFill(value = OperationType.INSERT)
    void insert(Category category);
    /**
     * 根据id修改分类
     * @param category
     */
    @AutoFill(value = OperationType.UPDATE)
    void update(Category category);

}

同时,将业务层为公共字段赋值的代码注释掉。

1). 将员工管理的新增和编辑方法中的公共字段赋值的代码注释。

2). 将菜品分类管理的新增和修改方法中的公共字段赋值的代码注释。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2297227.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

flink实时集成利器 - apache seatunnel - 核心架构详解

SeaTunnel&#xff08;原名 Waterdrop&#xff09;是一个分布式、高性能、易扩展的数据集成平台&#xff0c;专注于大数据领域的数据同步、数据迁移和数据转换。它支持多种数据源和数据目标&#xff0c;并可以与 Apache Flink、Spark 等计算引擎集成。以下是 SeaTunnel 的核心架…

视频理解新篇章:Mamba模型的探索与应用

人工智能咨询培训老师叶梓 转载标明出处 想要掌握如何将大模型的力量发挥到极致吗&#xff1f;叶老师带您深入了解 Llama Factory —— 一款革命性的大模型微调工具&#xff08;限时免费&#xff09;。 1小时实战课程&#xff0c;您将学习到如何轻松上手并有效利用 Llama Facto…

分形几何表明数学一直存在有首、末的无穷序列

分形几何表明数学一直存在有首、末的无穷序列。一有穷长直线段S可变为锯齿状图形G而由无穷多无穷短直线段连接而成。G和S一样有左、右两个端点。

Python接口自动化测试—接口数据依赖

一般在做自动化测试时&#xff0c;经常会对一整套业务流程进行一组接口上的测试&#xff0c;这时候接口之间经常会有数据依赖&#xff0c;那又该如何继续呢&#xff1f; 那么有如下思路&#xff1a; 抽取之前接口的返回值存储到全局变量字典中。初始化接口请求时&#xff0c;…

C++ 实践扩展(Qt Creator 联动 Visual Studio 2022)

​ 这里我们将在 VS 上实现 QT 编程&#xff0c;实现如下&#xff1a; 一、Vs 2022 配置&#xff08;若已安装&#xff0c;可直接跳过&#xff09; 点击链接&#xff1a;​​​​​Visual Studio 2022 我们先去 Vs 官网下载&#xff0c;如下&#xff1a; 等待程序安装完成之…

Java中性能瓶颈的定位与调优方法

Java中性能瓶颈的定位与调优方法 Java作为一种高效、跨平台的编程语言&#xff0c;广泛应用于企业级应用、服务器端开发、分布式系统等领域。然而&#xff0c;在面对大量并发、高负载的生产环境时&#xff0c;Java应用的性能瓶颈问题往往会暴露出来。如何定位并优化这些性能瓶…

openbmc sdbusplus接口使用(持续更新...)

1.说明 本节介绍如何使用sdbusplus&#xff0c;用来对应不同的场景。 可以参考之前的文章: https://blog.csdn.net/wit_yuan/article/details/145192471 建议阅读本篇文章一定要仔细阅读sd-bus specification 2.说明 2.1 简单server服务注册 本节参考: https://gitee.com…

2.12寒假作业

web&#xff1a;[HDCTF 2023]Welcome To HDCTF 2023 可以直接玩出来 但是这边还是看一下怎么解吧&#xff0c;看一下js代码&#xff0c;在js.game里面找到一个类似brainfuck加密的字符串 解密可以得到答案&#xff0c;但是后面我又去了解了一下let函数let命令、let命令 let命…

GitHub项目推荐--适合练手的13个C++开源项目

1 C 那些事 这是一个适合初学者从入门到进阶的仓库&#xff0c;解决了面试者与学习者想要深入 C及如何入坑 C的问题。 除此之外&#xff0c;本仓库拓展了更加深入的源码分析&#xff0c;多线程并发等的知识&#xff0c;是一个比较全面的 C 学习从入门到进阶提升的仓库。 项目…

【2025 Nature】AI 生成材料算法 MatterGen 文章要点

文章目录 1. MatterGen 框架2. 评价基础模型生成能力的指标3. MatterGen 基础生成能力表现4. MatterGen 定向生成能力表现i. 指定晶体化学式ii. 指定标量性质1. 每个性质微调一次。2. 两个性质联合微调 5. 实验合成6. 模型细节 这篇文档简单介绍 MatterGen 论文亮点。 标题&…

时间序列分析(三)——白噪声检验

此前篇章&#xff1a; 时间序列分析&#xff08;一&#xff09;——基础概念篇 时间序列分析&#xff08;二&#xff09;——平稳性检验 一、相关知识点 白噪声的定义&#xff1a;白噪声序列是一种在统计学和信号处理中常见的随机过程&#xff0c;由一系列相互独立、具有相同…

[前端] axios网络请求二次封装

一、场景描述 为什么要对axios网络请求进行二次封装? 解决代码的复用&#xff0c;提高可维护性。 —这个有两个方案&#xff1a;一个是二次封装一个是实例化。&#xff08;设置一些公共的参数&#xff0c;然后进行请求&#xff09; 为什么可以解决代码的复用&#xff1a; 这是…

【学术投稿-2025年计算机视觉研究进展与应用国际学术会议 (ACVRA 2025)】CSS样式解析:行内、内部与外部样式的区别与优先级分析

简介 2025年计算机视觉研究进展与应用&#xff08;ACVRA 2025&#xff09;将于2025年2月28-3月2日在中国广州召开&#xff0c;会议将汇聚世界各地的顶尖学者、研究人员和行业专家&#xff0c;聚焦计算机视觉领域的最新研究动态与应用成就。本次会议将探讨前沿技术&#xff0c;…

麒麟信安系统隔核后iperf网络测试影响说明

1、背景介绍 采用麒麟信安系统&#xff0c;在飞腾平台&#xff08;X86平台类似&#xff09;上进行了系统核隔离&#xff0c;修改了grub.cfg配置文件中的启动项增加isolcpus2-63 操作&#xff0c;隔核后发现40G网络iperf测试存在影响。 测试命令 taskset -c 16-23 iperf -s -…

WPF进阶 | WPF 资源管理与本地化:多语言支持与资源复用

WPF进阶 | WPF 资源管理与本地化&#xff1a;多语言支持与资源复用 前言一、WPF 资源管理基础1.1 什么是 WPF 资源1.2 资源的定义与存储位置1.3 资源的引用方式 二、资源字典的深入应用2.1 创建资源字典2.2 在应用程序中合并资源字典2.3 资源字典的层级结构与合并顺序 三、WPF …

数据结构与算法-动态规划-区间dp(石子合并,环形石子合并,凸多边形的划分,加分二叉树,棋盘分割)

概念 区间动态规划&#xff08;Interval Dynamic Programming&#xff09;是动态规划的一个分支&#xff0c;它在处理一些与区间相关的最优解问题上非常有效。以下从基本概念、解题步骤、经典例题、优缺点等方面为你详细介绍&#xff1a; 基本概念&#xff1a;区间 DP 的核心…

32单片机学习记录4之串口通信

32单片机学习记录4之串口通信 前置 STM32的GPIO口有通用模式&#xff0c;复用模式&#xff0c;模拟模式三种&#xff0c;加上输入输出就是有6中对应的模式。 我学习了通用模式&#xff0c;会使用GPIO口使用一些简单外设&#xff0c;如LED&#xff0c;独立按键&#xff0c;红外…

开源、免费项目管理工具比较:2025最新整理30款

好用的开源、免费版项目管理系统有&#xff1a;1.Redmine&#xff1b;2. Taiga&#xff1b;3. OpenProject&#xff1b; 4.ProjectLibre&#xff1b; 5.GanttProject&#xff1b; 6.Tuleap&#xff1b; 7.Trac&#xff1b;8. Phabricator&#xff1b; 9.Notion&#xff1b; 10.…

Android10 音频参数导出合并

A10 设备录音时底噪过大&#xff0c;让音频同事校准了下&#xff0c;然后把校准好的参数需要导出来&#xff0c;集成到项目中&#xff0c;然后出包&#xff0c;导出方式在此记录 设备安装debug系统版本调试好后&#xff0c; adb root adb remount adb shell 进入设备目录 导…

在 Azure 上部署 DeepSeek 并集成 Open WebUI

DeepSeek 是杭州深度求索人工智能基础技术研究有限公司发布的开源大模型&#xff0c;最近是持续火爆&#xff0c;使得官方服务经常不可用。网上各种本地部署和私有部署的文章已经很多&#xff0c;这里我们提供一个全部基于 Azure 的私有部署方案。 使用 Azure AI Foundry 部署…