Spring AOP实现操作日志记录示例

1. 准备工作

项目环境:jdk8+springboot2.6.13+mysql8

1.1 MySQL表

/*
 Navicat Premium Data Transfer

 Source Server         : localhost
 Source Server Type    : MySQL
 Source Server Version : 50730
 Source Host           : 127.0.0.1:3306
 Source Schema         : test

 Target Server Type    : MySQL
 Target Server Version : 50730
 File Encoding         : 65001

 Date: 04/07/2024 17:50:35
*/

SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;

-- ----------------------------
-- Table structure for operation_log
-- ----------------------------
DROP TABLE IF EXISTS `operation_log`;
CREATE TABLE `operation_log`  (
  `id` bigint(20) NOT NULL AUTO_INCREMENT,
  `module_name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
  `operation_name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
  `method_type` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
  `url` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
  `args` text CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL,
  `ip_addr` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
  `create_time` datetime NOT NULL,
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 6 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = Dynamic;

-- ----------------------------
-- Table structure for tb_seller
-- ----------------------------
DROP TABLE IF EXISTS `tb_seller`;
CREATE TABLE `tb_seller`  (
  `sellerid` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
  `name` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
  `nickname` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
  `password` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
  `status` int(11) NOT NULL,
  `address` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
  `createtime` datetime NOT NULL,
  PRIMARY KEY (`sellerid`) USING BTREE,
  UNIQUE INDEX `name`(`name`, `status`, `address`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = Dynamic;



SET FOREIGN_KEY_CHECKS = 1;

1.2 项目目录结构

新建一个springboot项目,项目目录结构如下:
在这里插入图片描述

1.3 maven依赖

pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>com.spring</groupId>
    <artifactId>demo</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>spring-demo</name>
    <description>spring-demo</description>
    <properties>
        <java.version>1.8</java.version>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <spring-boot.version>2.6.13</spring-boot.version>
    </properties>
    <dependencies>
        <!-- web -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <!-- lombok -->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <!-- test -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>

        <!-- MySQL JDBC -->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>8.0.13</version>
        </dependency>
        <!-- mybatis-plus -->
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
            <version>3.5.4</version>
        </dependency>

        <!-- hutool -->
        <dependency>
            <groupId>cn.hutool</groupId>
            <artifactId>hutool-all</artifactId>
            <version>5.6.6</version>
        </dependency>
        <!-- spring-aop -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-aop</artifactId>
        </dependency>

    </dependencies>
    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-dependencies</artifactId>
                <version>${spring-boot.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>
</project>

1.4 项目配置文件

配置文件application.yml

spring:
  datasource:
    username: root
    password: xxxx
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=utf-8&serverTimezone=GMT%2B8&useSSL=false

  mvc:
    pathmatch:
      matching-strategy: ant_path_matcher # 路径匹配


mybatis-plus:
  configuration:
    #配置该类sql只会在控制台打印,不会输出到日志文件
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl

1.5 domain层

TbSeller

package com.spring.demo.domain.po;

import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableName;
import com.baomidou.mybatisplus.annotation.IdType;
import java.time.LocalDateTime;
import com.baomidou.mybatisplus.annotation.TableId;
import java.io.Serializable;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;
import lombok.experimental.Accessors;

/**
 * <p>
 * 
 * </p>
 *
 * @author Sakura
 * @since 2024-07-03
 */
@Data
@EqualsAndHashCode(callSuper = false)
@Accessors(chain = true)
@TableName("tb_seller")
@ToString
public class TbSeller implements Serializable {

    private static final long serialVersionUID = 1L;

    @TableId(value = "sellerid", type = IdType.AUTO)
    private String sellerid;

    private String name;

    private String nickname;

    private String password;

    private Integer status;

    private String address;

    private LocalDateTime createtime;

}

OperationLog

package com.spring.demo.domain.po;

import com.baomidou.mybatisplus.annotation.TableName;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import java.time.LocalDateTime;
import java.io.Serializable;

import lombok.Builder;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;
import lombok.experimental.Accessors;

/**
 * <p>
 * 操作日志实体类,用于记录每个请求的详细信息。
 * </p>
 *
 * @author Sakura
 * @since 2024-07-03
 */
@Data
@EqualsAndHashCode(callSuper = false)
@Accessors(chain = true)
@Builder
@TableName("operation_log")
@ToString
public class OperationLog implements Serializable {

    private static final long serialVersionUID = 1L;

    /**
     * 日志记录的主键ID,自动递增。
     */
    @TableId(value = "id", type = IdType.AUTO)
    private Long id;

    /**
     * 模块名称,表示在哪个模块执行了操作。
     */
    private String moduleName;

    /**
     * 操作名称,表示执行了什么操作。
     */
    private String operationName;

    /**
     * 请求方法类型(GET, POST, PUT, DELETE等)。
     */
    private String methodType;

    /**
     * 请求的URL地址。
     */
    private String url;

    /**
     * 请求的参数,记录请求时传递的参数信息。
     */
    private String args;

    /**
     * 请求的IP地址,记录请求的来源IP。
     */
    private String ipAddr;

    /**
     * 创建时间,记录请求的时间。
     */
    private LocalDateTime createTime;
}

1.6 controller层

TbSellerController

/**
 * <p>
 * 前端控制器
 * </p>
 *
 * @author Sakura
 * @since 2024-07-03
 */
@RestController
@RequestMapping("/tb-seller")
@RequiredArgsConstructor
public class TbSellerController {

    private final ITbSellerService tbSellerService;

    /**
     * 获取供应商信息
     */
    @GetMapping("/getById/{id}")
    @MyLog(moduleName = "供应商模块", operationName = "获取供应商信息")
    public TbSeller getTbSellerById(@PathVariable String id) {
        return tbSellerService.getTbSellerById(id);
    }
}

1.7 service层

ITbSellerService

package com.spring.demo.service;

import com.spring.demo.domain.po.TbSeller;
import com.baomidou.mybatisplus.extension.service.IService;

/**
 * <p>
 *  服务类
 * </p>
 *
 * @author Sakura
 * @since 2024-07-03
 */
public interface ITbSellerService extends IService<TbSeller> {
    TbSeller getTbSellerById(String id);
}

TbSellerServiceImpl

package com.spring.demo.service.impl;

import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.spring.demo.domain.po.OperationLog;
import com.spring.demo.domain.po.TbSeller;
import com.spring.demo.mapper.TbSellerMapper;
import com.spring.demo.service.IOperationLogService;
import com.spring.demo.service.ITbSellerService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;

import java.util.List;

/**
 * <p>
 * 服务实现类
 * </p>
 *
 * @author Sakura
 * @since 2024-07-03
 */
@Service
@Slf4j
@RequiredArgsConstructor
public class TbSellerServiceImpl extends ServiceImpl<TbSellerMapper, TbSeller> implements ITbSellerService {

    private final TbSellerMapper tbSellerMapper;


    @Override
    public TbSeller getTbSellerById(String id) {
        return this.lambdaQuery().eq(TbSeller::getSellerid,id).one();
    }
}

IOperationLogService

package com.spring.demo.service;

import com.spring.demo.domain.po.OperationLog;
import com.baomidou.mybatisplus.extension.service.IService;

/**
 * <p>
 *  服务类
 * </p>
 *
 * @author Sakura
 * @since 2024-07-03
 */
public interface IOperationLogService extends IService<OperationLog> {

}

OperationLogServiceImpl

package com.spring.demo.service.impl;

import com.spring.demo.domain.po.OperationLog;
import com.spring.demo.mapper.OperationLogMapper;
import com.spring.demo.service.IOperationLogService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.spring.demo.service.ITbSellerService;
import lombok.RequiredArgsConstructor;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;

/**
 * <p>
 *  服务实现类
 * </p>
 *
 * @author Sakura
 * @since 2024-07-03
 */
@Service
@RequiredArgsConstructor
public class OperationLogServiceImpl extends ServiceImpl<OperationLogMapper, OperationLog> implements IOperationLogService {

}

1.8 mapper层

TbSellerMapper

import com.spring.demo.domain.po.TbSeller;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;

/**
 * <p>
 *  Mapper 接口
 * </p>
 *
 * @author Sakura
 * @since 2024-07-03
 */
public interface TbSellerMapper extends BaseMapper<TbSeller> {
    /**
     * 根据sellerid查询
     */
    TbSeller getTbSellerById(String sellerid);
}

OperationLogMapper

package com.spring.demo.mapper;

import com.spring.demo.domain.po.OperationLog;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;

/**
 * <p>
 *  Mapper 接口
 * </p>
 *
 * @author Sakura
 * @since 2024-07-03
 */
public interface OperationLogMapper extends BaseMapper<OperationLog> {

}

1.9 util包

package com.spring.demo.util;

import javax.servlet.http.HttpServletRequest;

/**
 * @author:
 * @date 2024-07-03 17:23
 */
public class IpUtil {
    /**
     * 获取ip地址
     */
    public static String getIpAddr(HttpServletRequest request) {
        String ip = request.getHeader("x-forwarded-for");
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("Proxy-Client-IP");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("WL-Proxy-Client-IP");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getRemoteAddr();
        }

        return "0:0:0:0:0:0:0:1".equals(ip) ? "127.0.0.1" : ip;
    }
}

1.10 启动类

package com.spring.demo;

import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.EnableAspectJAutoProxy;

@SpringBootApplication
@MapperScan(basePackages = "com.spring.demo.mapper")
public class SpringDemoApplication {

    public static void main(String[] args) {
        ConfigurableApplicationContext run = SpringApplication.run(SpringDemoApplication.class, args);
        System.out.println();
    }

}

2. AOP配置

2.1 开启AOP代理

在启动类上添加注解@EnableAspectJAutoProxy开启AOP代理。

2.2 定义日志注解

在aop包下新建注解MyLog

package com.spring.demo.aop;

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

/**
 * 自定义注解:实现登录日志记录
 */
@Retention(RetentionPolicy.RUNTIME) // 运行时生效
@Target(ElementType.METHOD) // 作用在方法上
public @interface MyLog {

    /**
     * 模块名称
     */
    String moduleName() default "";

    /**
     * 操作名称
     */
    String operationName() default "";
    

}

2.3 定义切面类

在aop包下定义切面类

package com.spring.demo.aop;

import com.spring.demo.domain.po.OperationLog;
import com.spring.demo.service.IOperationLogService;
import com.spring.demo.util.IpUtil;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;
import java.time.LocalDateTime;
import java.util.Arrays;
import java.util.stream.Collectors;

/**
 * 日志切面类
 *
 * @author: 
 * @date 2024-07-03 16:30
 */
@Slf4j
@Component
@Aspect
@RequiredArgsConstructor
public class MyAspect {

    private final IOperationLogService operationLogService;

    @AfterReturning("@annotation(com.spring.demo.aop.MyLog)")
    public void afterReturningMethod(JoinPoint joinPoint) {

        // 获取被增强类和方法的信息
        Signature signature = joinPoint.getSignature();
        MethodSignature methodSignature = (MethodSignature) signature;
        // 获取被增强的方法对象
        Method method = methodSignature.getMethod();
        String moduleName = "";   // 模块名称
        String operationName = "";    // 操作名称
        // 从方法中解析注解
        MyLog myLog = null;
        if (method != null) {
            myLog = method.getAnnotation(MyLog.class);
            moduleName = myLog.moduleName();
            operationName = myLog.operationName();
        }
        // 通过工具类获取Request对象
        RequestAttributes reqa = RequestContextHolder.getRequestAttributes();
        ServletRequestAttributes sra = (ServletRequestAttributes) reqa;
        HttpServletRequest request = sra.getRequest();
        // 请求方式
        String methodType = request.getMethod();
        // 请求地址
        String url = request.getRequestURI().toString();
        // 请求参数
        String args = Arrays.stream(joinPoint.getArgs()).map(Object::toString).collect(Collectors.joining(","));
        // 登录IP
        String ipAddr = IpUtil.getIpAddr(request);
        // 请求时间
        LocalDateTime createTime = LocalDateTime.now();
        // 打印日志
        log.info("moduleName:{}, operationName:{}, methodType:{}, url:{}, args:{}, ipAddr:{}, createTime:{}",
                moduleName, operationName, methodType, url, args, ipAddr, createTime);
        // 保存日志
        OperationLog operationLog = OperationLog.builder()
                .moduleName(moduleName)
                .operationName(operationName)
                .methodType(methodType)
                .url(url)
                .args(args)
                .ipAddr(ipAddr)
                .createTime(createTime)
                .build();
        operationLogService.save(operationLog);
    }
    
}

2.4 日志注解使用

修改TbSellerController,在方法上添加MyLog注解

    /**
     * 获取供应商信息
     */
    @GetMapping("/getById/{id}")
    @MyLog(moduleName = "供应商模块", operationName = "获取供应商信息")
    public TbSeller getTbSellerById(@PathVariable String id) {
        return tbSellerService.getTbSellerById(id);
    }

3.测试

启动springboot项目,访问路径:http://localhost:8080/tb-seller/getById/baidu
控制台输出如下:

JDBC Connection [HikariProxyConnection@130007367 wrapping com.mysql.cj.jdbc.ConnectionImpl@116246bc] will not be managed by Spring
==>  Preparing: SELECT s.sellerid AS sellerid, s.name AS name, s.nickname AS nickname, s.password AS password, s.status AS status, s.address AS address, s.createtime AS createtime, s.uid AS uid FROM tb_seller s where s.sellerid = ?;
==> Parameters: baidu(String)
<==    Columns: sellerid, name, nickname, password, status, address, createtime, uid
<==        Row: baidu, 百度科技有限公司, 百度小店, e10adc3949ba59abbe56e057f20f883e, 1, 北京市, 2088-01-01 12:00:00, 1
<==      Total: 1
Closing non transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@118dd810]
2024-07-04 18:25:54.559  INFO 251072 --- [nio-8080-exec-1] com.spring.demo.aop.MyAspect             : moduleName:供应商模块, operationName:获取供应商信息, methodType:GET, url:/tb-seller/getById/baidu, args:baidu, ipAddr:127.0.0.1, createTime:2024-07-04T18:25:54.559
Creating a new SqlSession
SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@1416af2f] was not registered for synchronization because synchronization is not active
JDBC Connection [HikariProxyConnection@136495746 wrapping com.mysql.cj.jdbc.ConnectionImpl@116246bc] will not be managed by Spring
==>  Preparing: INSERT INTO operation_log ( module_name, operation_name, method_type, url, args, ip_addr, create_time ) VALUES ( ?, ?, ?, ?, ?, ?, ? )
==> Parameters: 供应商模块(String), 获取供应商信息(String), GET(String), /tb-seller/getById/baidu(String), baidu(String), 127.0.0.1(String), 2024-07-04T18:25:54.559(LocalDateTime)
<==    Updates: 1
Closing non transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@1416af2f]

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

通过IDEA生成webapp及web.xml配置文件

1、选择File->Project Structure 2、选择Modules-> + -> Web 有的springboot工程选择是war工程,这个web可能已经存在了。 如果不存在,就手动创建,创建后,需要修改pom.xml中的配置 <packaging>war</packaging> 3、创建webapp根目录 这步重点就是创建…

ARM架构以及程序运行解析

文章目录 1. ARM架构 2. ARM处理器程序运行的过程 3. 示例 3. 基于ARM架构的STM32单片机 1. 运行模式 2. 寄存器组 3. STM32的基本结构 4. STM32的运行模式 4. 寄存器组详解 1. 未备份寄存器 2. 备份寄存器 3. 程序计数器 4. 程序状态寄存器 5. CPSR和SPSR寄存器…

运维Tips | Ubuntu 24.04 安装配置 xrdp 远程桌面服务

[ 知识是人生的灯塔&#xff0c;只有不断学习&#xff0c;才能照亮前行的道路 ] Ubuntu 24.04 Desktop 安装配置 xrdp 远程桌面服务 描述&#xff1a;Xrdp是一个微软远程桌面协议&#xff08;RDP&#xff09;的开源实现&#xff0c;它允许我们通过图形界面控制远程系统。这里使…

Banana Pi BPI-M5 Pro 低调 SBC 采用 Rockchip RK3576 八核 Cortex-A72/A53 AIoT SoC

Banana Pi BPI-M5 Pro&#xff0c;也称为 Armsom Sige5&#xff0c;是一款面向 AIoT 市场的低调单板计算机 (SBC)&#xff0c;由 Rockchip RK3576 八核 Cortex-A72/A53 SoC 驱动&#xff0c;提供Rockchip RK3588和RK3399 SoC 之间的中档产品。 该主板默认配备 16GB LPDDR4X 和…

学习笔记——动态路由——OSPF(特殊区域)

十、OSPF特殊区域 1、技术背景 早期路由器靠CPU计算转发&#xff0c;由于硬件技术限制问题&#xff0c;因此资源不是特别充足&#xff0c;因此是要节省资源使用&#xff0c;规划是非常必要的。 OSPF路由器需要同时维护域内路由、域间路由、外部路由信息数据库。当网络规模不…

NAT:地址转换技术

为什么会引入NAT&#xff1f; NAT&#xff08;网络地址转换&#xff09;的引入主要是为了解决两个问题 IPv4地址短缺&#xff1a;互联网快速发展&#xff0c;可用的公网IP地址越来越少。网络安全&#xff1a;需要一种方法来保护内部网络不被直接暴露在互联网上。 IPv4 &…

人脸检测(Python)

目录 环境&#xff1a; 初始化摄像头&#xff1a; 初始化FaceDetector对象&#xff1a; 获取摄像头帧&#xff1a; 获取数据&#xff1a; 绘制数据&#xff1a; 显示图像&#xff1a; 完整代码&#xff1a; 环境&#xff1a; cvzone库&#xff1a;cvzone是一个基于…

RAG实践:ES混合搜索BM25+kNN(cosine)

1 缘起 最近在研究与应用混合搜索&#xff0c; 存储介质为ES&#xff0c;ES作为大佬牌数据库&#xff0c; 非常友好地支持关键词检索和向量检索&#xff0c; 当然&#xff0c;支持混合检索&#xff08;关键词检索向量检索&#xff09;&#xff0c; 是提升LLM响应质量RAG(Retri…

spring boot(学习笔记第十二课)

spring boot(学习笔记第十二课) Spring Security内存认证&#xff0c;自定义认证表单 学习内容&#xff1a; Spring Security内存认证自定义认证表单 1. Spring Security内存认证 首先开始最简单的模式&#xff0c;内存认证。 加入spring security的依赖。<dependency>…

【TB作品】51单片机 Proteus仿真 MAX7219点阵驱动数码管驱动

1、8乘8点阵模块&#xff08;爱心&#xff09; 数码管测试程序与仿真 实验报告: MAX7219 数码管驱动测试 一、实验目的 通过对 MAX7219 芯片的编程与控制&#xff0c;了解如何使用单片机驱动数码管显示数字&#xff0c;并掌握 SPI 通信协议的基本应用。 二、实验器材 51…

触发器编程-创建(CREATE TRIGGER)、删除(DROP TRIGGER)

一、定义 1、触发器&#xff08;Trigger&#xff09;是用户对某一表中的数据做插入、更新和删除操作时被处罚执行的一段程序&#xff0c;通常我们使用触发器来检查用户对表的操作是否合乎整个应用系统的需求&#xff0c;是否合乎商业规则以维持表内数据的完整性和正确性 2、一…

SPL-404:如何彻底改变Solana上的NFT与DeFi

在不断发展的数字资产领域中&#xff0c;非同质化Token&#xff08;NFT&#xff09;已成为一股革命性力量&#xff0c;彻底改变了我们对数字所有权的看法和互动方式。从艺术和收藏品到游戏和虚拟房地产&#xff0c;NFT吸引了创作者、投资者和爱好者的想象力。 本指南将带您进入…

力扣-双指针1

何为双指针 双指针指向同一数组&#xff0c;然后配合着进行搜索等活动。 滑动窗口的时候很好使用。 167.两数之和Ⅱ-输入有序数组 167. 两数之和 II - 输入有序数组 题目 给你一个下标从 1 开始的整数数组 numbers &#xff0c;该数组已按 非递减顺序排列 &#xff0c;请你从…

[Flink]三、Flink1.13

11. Table API 和 SQL 如图 11-1 所示&#xff0c;在 Flink 提供的多层级 API 中&#xff0c;核心是 DataStream API &#xff0c;这是我们开发流 处理应用的基本途径&#xff1b;底层则是所谓的处理函数&#xff08; process function &#xff09;&#xff0c;可以访…

Android 简单快速实现 下弧形刻度尺(滑动事件)

效果图&#xff1a; 直接上代码&#xff1a; package com.my.view;import android.content.Context; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Pai…

iptables实现端口转发ssh

iptables实现端口转发 实现使用防火墙9898端口访问内网front主机的22端口&#xff08;ssh连接&#xff09; 1. 防火墙配置(lb01) # 配置iptables # 这条命令的作用是将所有目的地为192.168.100.155且目标端口为19898的TCP数据包的目标IP地址改为10.0.0.148&#xff0c;并将目标…

基于Java+SpringMvc+Vue技术的在线学习交流平台的设计与实现---60页论文参考

博主介绍&#xff1a;硕士研究生&#xff0c;专注于Java技术领域开发与管理&#xff0c;以及毕业项目实战✌ 从事基于java BS架构、CS架构、c/c 编程工作近16年&#xff0c;拥有近12年的管理工作经验&#xff0c;拥有较丰富的技术架构思想、较扎实的技术功底和资深的项目管理经…

【PB案例学习笔记】-29制作一个调用帮助文档的小功能

写在前面 这是PB案例学习笔记系列文章的第29篇&#xff0c;该系列文章适合具有一定PB基础的读者。 通过一个个由浅入深的编程实战案例学习&#xff0c;提高编程技巧&#xff0c;以保证小伙伴们能应付公司的各种开发需求。 文章中设计到的源码&#xff0c;小凡都上传到了gite…

【Spring Boot】关系映射开发(三):多对多映射

《JPA 从入门到精通》系列包含以下文章&#xff1a; Java 持久层 API&#xff1a;JPA认识 JPA 的接口JPA 的查询方式基于 JPA 开发的文章管理系统&#xff08;CRUD&#xff09;关系映射开发&#xff08;一&#xff09;&#xff1a;一对一映射关系映射开发&#xff08;二&#…

Java_网络编程

网络通信的关键三要素 IP、端口号、协议 IP地址 IP地址&#xff08;Internet Protocol&#xff09;&#xff1a;全程“互联网协议地址”&#xff0c;是分配给上网设备的唯一标志。 IP地址有两种形式&#xff1a;IPv4、IPv6 InetAddress 代表IP地址 InetAddress 的常用方法…