使用SpringBoot AOP记录操作日志和异常日志

使用SpringBoot AOP记录操作日志和异常日志

平时我们在做项目时经常需要对一些重要功能操作记录日志,方便以后跟踪是谁在操作此功能;我们在操作某些功

能时也有可能会发生异常,但是每次发生异常要定位原因我们都要到服务器去查询日志才能找到,而且也不能对发

生的异常进行统计,从而改进我们的项目,要是能做个功能专门来记录操作日志和异常日志那就好了。

当然我们肯定有方法来做这件事情,而且也不会很难,我们可以在需要的方法中增加记录日志的代码,和在每个方

法中增加记录异常的代码,最终把记录的日志存到数据库中。听起来好像很容易,但是我们做起来会发现,做这项

工作很繁琐,而且都是在做一些重复性工作,还增加大量冗余代码,这种方式记录日志肯定是不可行的。

我们以前学过Spring 三大特性,IOC(控制反转),DI(依赖注入),AOP(面向切面),那其中AOP的主要功能

就是将日志记录,性能统计,安全控制,事务处理,异常处理等代码从业务逻辑代码中划分出来。今天我们就来用

springBoot Aop 来做日志记录,好了,废话说了一大堆还是上货吧。

1、创建日志记录表、异常日志表,表结构如下

操作日志表OperationLog

CREATE TABLE `OperationLog` (
  `oper_id` varchar(64) DEFAULT NULL COMMENT '主键id',
  `oper_modul` varchar(64) DEFAULT NULL COMMENT '功能模块',
  `oper_type` varchar(64) DEFAULT NULL COMMENT '操作类型',
  `oper_desc` varchar(500) DEFAULT NULL COMMENT '操作描述',
  `oper_requ_param` text COMMENT '请求参数',
  `oper_resp_param` text COMMENT '返回参数',
  `oper_user_id` varchar(64) DEFAULT NULL COMMENT '操作员ID',
  `oper_user_name` varchar(64) DEFAULT NULL COMMENT '操作员名称',
  `oper_method` varchar(255) DEFAULT NULL COMMENT '操作方法',
  `oper_uri` varchar(255) DEFAULT NULL COMMENT '请求URI',
  `oper_ip` varchar(64) DEFAULT NULL COMMENT '请求ID',
  `oper_create_time` datetime DEFAULT NULL COMMENT '操作时间',
  `oper_ver` varchar(64) DEFAULT NULL COMMENT '操作版本号',
  PRIMARY KEY (`oper_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

在这里插入图片描述

异常日志表ExceptionLog

CREATE TABLE `ExceptionLog` (
  `exp_id` varchar(64) NOT NULL DEFAULT "" COMMENT '主键id',
  `exp_requ_param` text COMMENT '请求参数',
  `exp_name` varchar(255) COMMENT '异常名称',
  `exp_message` text COMMENT '异常信息',
  `oper_user_id` varchar(64) DEFAULT NULL COMMENT '操作员ID',
  `oper_user_name` varchar(64) DEFAULT NULL COMMENT '操作员名称',
  `oper_method` varchar(255) DEFAULT NULL COMMENT '操作方法',
  `oper_uri` varchar(255) DEFAULT NULL COMMENT '请求URI',
  `oper_ip` varchar(64) DEFAULT NULL COMMENT '请求ID',
  `oper_create_time` datetime DEFAULT NULL COMMENT '操作时间',
  `oper_ver` varchar(64) DEFAULT NULL COMMENT '操作版本号',
  PRIMARY KEY (`exp_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

在这里插入图片描述

2、添加Maven依赖

<?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>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.5.6</version>
        <relativePath/>
    </parent>

    <groupId>com.aop.log</groupId>
    <artifactId>spring-boot-aop-log</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>spring-boot-aop-log</name>
    <description>spring-boot-aop-log</description>

    <properties>
        <java.version>1.8</java.version>
    </properties>

    <dependencies>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-aop</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>

        <!-- mybatis-plus依赖 -->
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
            <version>3.3.1.tmp</version>
        </dependency>

        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>1.2.58</version>
        </dependency>

        <!-- mysql依赖 -->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>8.0.22</version>
        </dependency>

    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>

3、创建实体类

ExceptionLog实体

package com.aop.log.entity;

import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.util.Date;

/**
 * @author zhangshixing
 * @date 2021年11月05日 10:04
 */
@Data
@NoArgsConstructor
@AllArgsConstructor
@TableName("ExceptionLog")
public class ExceptionLog {

    // 主键id
    @TableId(value = "exp_id", type = IdType.ASSIGN_UUID)
    private String expId;
    // 请求参数
    private String expRequParam;
    // 异常名称
    private String expName;
    // 异常信息
    private String expMessage;
    // 操作员ID
    private String operUserId;
    // 操作员名称
    private String operUserName;
    // 操作方法
    private String operMethod;
    // 请求URI
    private String operUri;
    // 请求ID
    private String operIp;
    // 操作时间
    private Date operCreateTime;
    // 操作版本号
    private String operVer;
}

OperationLog实体

package com.aop.log.entity;

import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.io.Serializable;
import java.util.Date;

/**
 * @author zhangshixing
 * @date 2021年11月05日 9:52
 */

@Data
@NoArgsConstructor
@AllArgsConstructor
@TableName("OperationLog")
public class OperationLog implements Serializable {

    // 主键id
    @TableId(value = "oper_id", type = IdType.ASSIGN_UUID)
    private String operId;
    // 功能模块
    private String operModul;
    // 操作类型
    private String operType;
    // 操作描述
    private String operDesc;
    // 请求参数
    private String operRequParam;
    // 返回参数
    private String operRespParam;
    // 操作员ID
    private String operUserId;
    // 操作员名称
    private String operUserName;
    // 操作方法
    private String operMethod;
    // 请求URI
    private String operUri;
    // 请求ID
    private String operIp;
    // 操作时间
    private Date operCreateTime;
    // 操作版本号
    private String operVer;
}

OrderInfo订单实体类

package com.aop.log.entity;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

/**
 * @author zhangshixing
 * @date 2021年11月05日 10:33
 */
@Data
@NoArgsConstructor
@AllArgsConstructor
public class OrderInfo {

    /**
     * 这里只是为了测试所以只添加了两个参数
     */
    // 订单编号
    private String orderid;
    // 订单金额
    private int price;

}

RespBean公共返回对象

package com.aop.log.entity;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

/**
 * @author zhangshixing
 * @date 2021年11月05日 10:31
 */
@Data
@NoArgsConstructor
@AllArgsConstructor
public class RespBean {

    private long code;
    private String message;
    private Object obj;

    public static RespBean success(String message) {

        return new RespBean(200, message, null);
    }

    public static RespBean success(String message, Object obj) {

        return new RespBean(200, message, obj);
    }

    public static RespBean error(String message) {

        return new RespBean(500, message, null);
    }

    public static RespBean error(String message, Object obj) {

        return new RespBean(500, message, obj);
    }
}

4、创建Mapper

OperationLogMapper

package com.aop.log.mapper;

import com.aop.log.entity.OperationLog;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.apache.ibatis.annotations.Mapper;

@Mapper
public interface OperationLogMapper extends BaseMapper<OperationLog> {

}

ExceptionLogMapper

package com.aop.log.mapper;

import com.aop.log.entity.ExceptionLog;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.apache.ibatis.annotations.Mapper;

@Mapper
public interface ExceptionLogMapper extends BaseMapper<ExceptionLog> {

}

5、创建service

OperationLogService

package com.aop.log.service;

import com.aop.log.entity.OperationLog;
import com.baomidou.mybatisplus.extension.service.IService;

public interface OperationLogService extends IService<OperationLog> {
}

ExceptionLogService

package com.aop.log.service;

import com.aop.log.entity.ExceptionLog;
import com.baomidou.mybatisplus.extension.service.IService;

public interface ExceptionLogService extends IService<ExceptionLog> {

}

OperationLogServiceImpl

package com.aop.log.service.impl;

import com.aop.log.entity.OperationLog;
import com.aop.log.mapper.OperationLogMapper;
import com.aop.log.service.OperationLogService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.stereotype.Service;

/**
 * @author zhangshixing
 * @date 2021年11月05日 10:10
 */
@Service
public class OperationLogServiceImpl extends ServiceImpl<OperationLogMapper, OperationLog>
        implements OperationLogService {
}

ExceptionLogServiceImpl

package com.aop.log.service.impl;

import com.aop.log.entity.ExceptionLog;
import com.aop.log.mapper.ExceptionLogMapper;
import com.aop.log.service.ExceptionLogService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.stereotype.Service;

/**
 * @author zhangshixing
 * @date 2021年11月05日 10:10
 */
@Service
public class ExceptionLogServiceImpl extends ServiceImpl<ExceptionLogMapper, ExceptionLog>
        implements ExceptionLogService {
}

6、创建操作日志注解类OperLog

package com.aop.log.annotation;

import java.lang.annotation.*;

/**
 * @author zhangshixing
 * @date 2021年11月05日 9:43
 * 自定义操作日志注解
 */
//注解放置的目标位置,METHOD是可注解在方法级别上
@Target(ElementType.METHOD)
//注解在哪个阶段执行
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface OperLog {
    // 操作模块
    String operModul() default "";

    // 操作类型
    String operType() default "";

    // 操作说明
    String operDesc() default "";
}
package com.aop.log.annotation;

public class OprLogConst {

    public static final String ADD = "add";
    public static final String INSERET = "insert";
    public static final String UPDATE = "update";
    public static final String DELETE = "delete";
}

7、创建切面类记录操作日志

package com.aop.log.aop;

import com.alibaba.fastjson.JSON;
import com.aop.log.annotation.OperLog;
import com.aop.log.entity.ExceptionLog;
import com.aop.log.entity.OperationLog;
import com.aop.log.service.ExceptionLogService;
import com.aop.log.service.OperationLogService;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;

import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;

/**
 * @author zhangshixing
 * @date 2021年11月05日 9:46
 * 切面处理类,操作日志异常日志记录处理
 */
@Aspect
@Component
public class OperLogAspect {

    /**
     * 操作版本号
     * 项目启动时从命令行传入,例如:java -jar xxx.war --version=201902
     */
    @Value("${version}")
    private String operVer;

    @Autowired
    private OperationLogService operationLogService;

    @Autowired
    private ExceptionLogService exceptionLogService;

    /**
     * 设置操作日志切入点 记录操作日志 在注解的位置切入代码
     */
    @Pointcut("@annotation(com.aop.log.annotation.OperLog)")
    public void operLogPoinCut() {
    }

    /**
     * 设置操作异常切入点记录异常日志 扫描所有controller包下操作
     */
    @Pointcut("execution(* com.aop.log.controller..*.*(..))")
    public void operExceptionLogPoinCut() {
    }

    /**
     * 正常返回通知,拦截用户操作日志,连接点正常执行完成后执行, 如果连接点抛出异常,则不会执行
     *
     * @param joinPoint 切入点
     * @param keys      返回结果
     */
    @AfterReturning(value = "operLogPoinCut()", returning = "keys")
    public void saveOperLog(JoinPoint joinPoint, Object keys) {
        // 获取RequestAttributes
        RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
        // 从获取RequestAttributes中获取HttpServletRequest的信息
        HttpServletRequest request = (HttpServletRequest) requestAttributes
                .resolveReference(RequestAttributes.REFERENCE_REQUEST);
        // 创建OperationLog对象
        OperationLog operlog = new OperationLog();
        try {
            // 主键ID
            String uuid = UUID.randomUUID().toString();
            System.out.println("saveOperLog:" + uuid);
            operlog.setOperId(uuid);
            // 从切面织入点处通过反射机制获取织入点处的方法
            MethodSignature signature = (MethodSignature) joinPoint.getSignature();
            // 获取切入点所在的方法
            Method method = signature.getMethod();
            // 获取操作
            OperLog opLog = method.getAnnotation(OperLog.class);
            if (opLog != null) {
                String operModul = opLog.operModul();
                String operType = opLog.operType();
                String operDesc = opLog.operDesc();
                // 操作模块
                operlog.setOperModul(operModul);
                // 操作类型
                operlog.setOperType(operType);
                // 操作描述
                operlog.setOperDesc(operDesc);
            }
            // 获取请求的类名
            String className = joinPoint.getTarget().getClass().getName();
            // 获取请求的方法名
            String methodName = method.getName();
            methodName = className + "." + methodName;
            // 请求方法
            operlog.setOperMethod(methodName);
            // 请求的参数
            Map<String, String> rtnMap = converMap(request.getParameterMap());
            // 将参数所在的数组转换成json
            String params = JSON.toJSONString(rtnMap);
            // 请求参数
            operlog.setOperRequParam(params);
            // 返回结果
            operlog.setOperRespParam(JSON.toJSONString(keys));
            // 请求用户ID
            // operlog.setOperUserId(UserShiroUtil.getCurrentUserLoginName());
            // 这里写一个固定的用户ID
            operlog.setOperUserId("100293784");
            // 请求用户名称
            // operlog.setOperUserName(UserShiroUtil.getCurrentUserName());
            // // 这里写一个固定的用户名
            operlog.setOperUserName("yiyiyi");
            // 请求IP
            // operlog.setOperIp(IPUtil.getRemortIP(request));
            // 这里写一个固定的IP
            operlog.setOperIp("127.0.0.1");
            // 请求URI
            operlog.setOperUri(request.getRequestURI());
            // 创建时间
            operlog.setOperCreateTime(new Date());
            // 操作版本
            operlog.setOperVer(operVer);
            // 将数据插入到数据库
            operationLogService.save(operlog);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 异常返回通知,用于拦截异常日志信息 连接点抛出异常后执行
     *
     * @param joinPoint 切入点
     * @param e         异常信息
     */
    @AfterThrowing(pointcut = "operExceptionLogPoinCut()", throwing = "e")
    public void saveExceptionLog(JoinPoint joinPoint, Throwable e) {
        // 获取RequestAttributes
        RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
        // 从获取RequestAttributes中获取HttpServletRequest的信息
        HttpServletRequest request = (HttpServletRequest) requestAttributes
                .resolveReference(RequestAttributes.REFERENCE_REQUEST);
        ExceptionLog excepLog = new ExceptionLog();
        try {
            // 从切面织入点处通过反射机制获取织入点处的方法
            MethodSignature signature = (MethodSignature) joinPoint.getSignature();
            // 获取切入点所在的方法
            Method method = signature.getMethod();
            String uuid = UUID.randomUUID().toString();
            System.out.println("saveExceptionLog:" + uuid);
            excepLog.setExpId(uuid);
            // 获取请求的类名
            String className = joinPoint.getTarget().getClass().getName();
            // 获取请求的方法名
            String methodName = method.getName();
            methodName = className + "." + methodName;
            // 请求的参数
            Map<String, String> rtnMap = converMap(request.getParameterMap());
            // 将参数所在的数组转换成json
            String params = JSON.toJSONString(rtnMap);
            // 请求参数
            excepLog.setExpRequParam(params);
            // 请求方法名
            excepLog.setOperMethod(methodName);
            // 异常名称
            excepLog.setExpName(e.getClass().getName());
            // 异常信息
            excepLog.setExpMessage(stackTraceToString(e.getClass().getName(), e.getMessage(), e.getStackTrace()));
            // 操作员ID
            // excepLog.setOperUserName(UserShiroUtil.getCurrentUserName());
            excepLog.setOperUserId("100293784");
            // 操作员名称
            excepLog.setOperUserName("yiyiyi");
            // 操作URI
            excepLog.setOperUri(request.getRequestURI());
            // 操作员IP
            // excepLog.setOperIp(IPUtil.getRemortIP(request));
            excepLog.setOperIp("127.0.0.1");
            // 操作版本号
            excepLog.setOperVer(operVer);
            // 发生异常时间
            excepLog.setOperCreateTime(new Date());
            exceptionLogService.save(excepLog);
        } catch (Exception e2) {
            e2.printStackTrace();
        }

    }

    /**
     * 转换request 请求参数
     *
     * @param paramMap request获取的参数数组
     */
    public Map<String, String> converMap(Map<String, String[]> paramMap) {
        Map<String, String> rtnMap = new HashMap<String, String>();
        for (String key : paramMap.keySet()) {
            rtnMap.put(key, paramMap.get(key)[0]);
        }
        return rtnMap;
    }

    /**
     * 转换异常信息为字符串
     *
     * @param exceptionName    异常名称
     * @param exceptionMessage 异常信息
     * @param elements         堆栈信息
     */
    public String stackTraceToString(String exceptionName, String exceptionMessage, StackTraceElement[] elements) {
        StringBuffer strbuff = new StringBuffer();
        for (StackTraceElement stet : elements) {
            strbuff.append(stet + "\n");
        }
        String message = exceptionName + ":" + exceptionMessage + "\n\t" + strbuff.toString();
        return message;
    }
}

8、在Controller层方法添加@OperLog注解

package com.aop.log.controller;

import com.aop.log.annotation.OperLog;
import com.aop.log.annotation.OprLogConst;
import com.aop.log.entity.OrderInfo;
import com.aop.log.entity.RespBean;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

/**
 * @author zhangshixing
 * @date 2021年11月05日 10:25
 */
@Controller
public class OrderController {
    /**
     * 新增订单信息
     */
    @RequestMapping(value = "addOrderInfo")
    @ResponseBody
    @OperLog(operModul = "销售管理-订单新增", operType = OprLogConst.ADD, operDesc = "订单新增功能")
    public RespBean addOrderInfo(OrderInfo orderInfo) {
        if (orderInfo.getPrice() < 0) {
            System.out.println(1 / 0);
            return RespBean.error("提交失败!!!");
        } else if (orderInfo.getPrice() < 30) {
            return RespBean.success("提交成功!!!");
        } else {
            return RespBean.error("提交失败!!!");
        }
    }
}

9、启动类和配置文件

version = 2.1.1
server.port = 9000
spring.datasource.driver-class-name = com.mysql.cj.jdbc.Driver
spring.datasource.url = jdbc:mysql://localhost:3306/log?useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai
spring.datasource.username = root
spring.datasource.password = root
package com.aop.log;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class SpringBootAopLogApplication {

	public static void main(String[] args) {
		SpringApplication.run(SpringBootAopLogApplication.class, args);
	}

}

10、操作日志、异常日志查询功能

10.1 记录操作日志

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

10.2 记录异常日志

在这里插入图片描述

在这里插入图片描述

11、execution 表达式

学习Spring中的aop组装切面时遇到的execution表达式,下面是execution表达式的详解。

切入点表达式:

整个表达式可以分为五个部分:

1、execution(): 表达式主体。

2、第一个*号:方法返回类型, *号表示所有的类型。

3、包名:表示需要拦截的包名。

4、第二个*号:表示类名,*号表示所有的类。

5、*(..):最后这个星号表示方法名,*号表示所有的方法,后面( )里面表示方法的参数,两个句点表示任

何参数。

表达式语法:

execution(* 包名.*.*(..))

规范写法:execution(* com.baizhi.service.UserServiceImpl.*(..))

这个表达式是重点 ,是最通用的,表示执行接口下的所有接口方法。

execution表达式举例:

书写接口实现方法:UserServiceImpl

方法类型:

add(); query(); add(String name); add(User user); add(String name,Integer age);

execution(* service.UserServiceImpl.add(..))        //执行add()方法
execution(* service.UserServiceImpl.add(String))    //执行add(String name)方法
execution(* service.UserServiceImpl.add(com.baizhi.entity.User))   //执行add(User user)方法
execution(* service.UserServiceImpl.add(String , Integer))    //执行add(String name,Interger age)方法

execution表达式的一般用法:

execution(* service.UserServiceImpl.*(java.util.List))
返回值:任意
包:com.baizhi.service
类:UserServiceImpl
方法:任意
参数:必须是List集合
execution(* service.UserServiceImpl.add*(..))  重点
返回值:任意
包:com.baizhi.service
类:UserServiceImpl
方法:以add关键字开头的方法
参数:任意
execution(* service.UserServiceImpl.*.*(..))  重点
返回值:任意
包:com.baizhi.service
类:当前包下的所有类
方法:所有类中的所有方法
参数:任意
execution(* service..*.*(..))  重点
返回值:任意
包:service包以及它下面所有子包
类:所有包中的所有类
方法:所有类中的所有方法
参数:任意
execution(* *(..))   重点,不建议这样写,栈溢出

注意:要尽可能精准的切入。

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

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

相关文章

Stable Diffusion 系列教程 - 5 ControlNet

ControlNet和LORA的定位都是对大模型做微调的额外网络。作为入门SD的最后一块拼图是必须要去了解和开发的。为什么ControlNet的影响力如此的大&#xff1f;在它之前&#xff0c;基于扩散模型的AIGC是非常难以控制的&#xff0c;扩散整张图像的过程充满了随机性。这种随机性并不…

FFmpeg学习笔记--Centos8安装FFmpeg

1--安装指令 sudo yum install epel-releasesudo yum localinstall --nogpgcheck https://download1.rpmfusion.org/free/el/rpmfusion-free-release-8.noarch.rpmsudo yum install ffmpeg ffmpeg-develffmpeg -version 2--版本信息

【Transformer】深入理解Transformer模型2——深入认识理解(下)

前言 Transformer模型出自论文&#xff1a;《Attention is All You Need》 2017年 近年来&#xff0c;在自然语言处理领域和图像处理领域&#xff0c;Transformer模型都受到了极为广泛的关注&#xff0c;很多模型中都用到了Transformer或者是Transformer模型的变体&#xff0…

python可视化界面自动生成,python如何做可视化界面

大家好&#xff0c;小编来为大家解答以下问题&#xff0c;python gui可视化操作界面制作&#xff0c;python做出的炫酷的可视化&#xff0c;现在让我们一起来看看吧&#xff01; 目录 前言 一.环境配置 插件&#xff1a; 1.python 2.Chinese 3.Open In Default Browser 安装pyt…

zabbix添加监控主机(agent)并告警

一、添加监控主机 总体来说&#xff0c;在被监控主机上安装部署zabbix-agent&#xff0c;并修改配置文件&#xff08;zabbix_agentd.conf&#xff09;的参数。然后在zabbix 服务端zabbix-get检查是否可以监控。如果可以了&#xff0c;就可以在web页面添加了&#xff0c;要监控…

据报道,微软的下一代 Surface 笔记本电脑将是其首款真正的“人工智能 PC”

明年&#xff0c;微软计划推出 Surface Laptop 6和 Surface Pro 10&#xff0c;这两款设备将提供 Arm 和 Intel 两种处理器选项。不愿意透露姓名的不透露姓名人士透露&#xff0c;这些新设备将引入先进的人工智能功能&#xff0c;包括配备下一代神经处理单元 (NPU)。据悉&#…

vue-mixin(混入---入门到离职系列)

定义 官方定义(官网定义:组合选项 | Vue.js (vuejs.org)) mixins 选项接受一个 mixin 对象数组。这些 mixin 对象可以像普通的实例对象一样包含实例选项&#xff0c;它们将使用一定的选项合并逻辑与最终的选项进行合并。举例来说&#xff0c;如果你的 mixin 包含了一个 create…

【Vue】computed详解

✨ 专栏介绍 在当今Web开发领域中&#xff0c;构建交互性强、可复用且易于维护的用户界面是至关重要的。而Vue.js作为一款现代化且流行的JavaScript框架&#xff0c;正是为了满足这些需求而诞生。它采用了MVVM架构模式&#xff0c;并通过数据驱动和组件化的方式&#xff0c;使…

jQuery日历签到插件下载

jQuery日历签到插件下载-遇见你与你分享

听GPT 讲Rust源代码--src/tools(39)

File: rust/src/tools/rustfmt/src/config/config_type.rs 在Rust代码中&#xff0c;rust/src/tools/rustfmt/src/config/config_type.rs文件的作用是定义了与配置相关的数据结构和函数。 Config struct&#xff08;配置结构体&#xff09;&#xff1a;该结构体用于存储rustfmt…

WEB渗透—PHP反序列化(十一)

Web渗透—PHP反序列化 课程学习分享&#xff08;课程非本人制作&#xff0c;仅提供学习分享&#xff09; 靶场下载地址&#xff1a;GitHub - mcc0624/php_ser_Class: php反序列化靶场课程&#xff0c;基于课程制作的靶场 课程地址&#xff1a;PHP反序列化漏洞学习_哔哩…

FTP的基本介绍

FTP ftp的介绍&#xff1a; ftp是一个可以提供共享文件的服务器&#xff0c;他可以通过iis.msc也就是windows 的服务器管理器来打开&#xff0c;或者通过cmd命令行打开 如何使用iis.msc打开ftp&#xff0c;如何使用cmd打开ftp &#xff0c;如何匿名登录ftp&#xff0c;ftp和…

MySQL入门教程-触发器

9.触发器 什么是触发器 触发器(trigger)&#xff1a;监视某种情况&#xff0c;并进行某种操作&#xff0c;它的执行并不是程序调用&#xff0c;也不是手工启动&#xff0c;而是由事件来触发&#xff0c;例如&#xff1a;对一张表进行操作&#xff08;插入&#xff0c;更新&…

关于表格太大了jupyter无法单次处理的问题

记录下自己的心路历程…耗时耗精力 我用的数据库单个表格就很大&#xff0c;一个表格有30多G&#xff0c;jupyter无法处理这么大的表格&#xff0c;会直接把电脑的进程全部结束掉&#xff0c;结束掉要是能运行成功倒也行啊&#xff0c;然鹅…给我报错说处理不了&#xff0c;罢工…

Rust之构建命令行程序(二):读取文件

开发环境 Windows 10Rust 1.74.1 VS Code 1.85.1 项目工程 这次创建了新的工程minigrep. 读取文件 现在&#xff0c;我们将添加读取file_path参数中指定的文件的功能。首先&#xff0c;我们需要一个样本文件来测试它:我们将使用一个包含少量文本的文件&#xff0c;多行包含一…

技能分析:这就是人们写在简历上的内容

您希望您的技能部分听起来像其他人一样吗&#xff1f;另一方面&#xff0c;您是否想遗漏一项顶级技能&#xff0c;因为许多其他简历也列出了它&#xff1f;在脱颖而出和涵盖雇主寻求的所有技能之间找到平衡可能是一个挑战。 优秀的简历技能部分会考虑到每个雇主所寻求的特质。…

08.哲说建造者模式(Builder Pattern)

“The odds that we’re in ‘base reality’ is one in billions.” —— Elon Musk 这段话出自马斯克在2016年的一次演讲&#xff0c;“人类活在真实世界的几率&#xff0c;可能不到十亿分之一”。此言一出&#xff0c;可谓一石激起千层浪。有人嘲讽马斯克是“语不惊人死不休…

GBASE南大通用-GBase 8s数据库日志模式及切换

一、 GBase 8s数据库共有以下 4 种日志模式&#xff1a;无日志模式、缓冲日志模式、无缓冲日志模式、ANSI 模式。详细介绍如下&#xff1a; 1、无日志模式&#xff08;Non logging&#xff09;&#xff1a; 采用无日志模式时&#xff0c;所有 DML 操作都不会被记录到日志中&…

LVS负载均衡配置虚拟引起微服务注册混乱

线上小程序突然报错&#xff0c;查看网关日志&#xff0c;访问下游微服务A时大量报错&#xff1a; 1&#xff09;检查微服务是否未注册。登录eureka页面&#xff0c;发现三个节点均正常注册 三个微服务节点地址分别为&#xff1a;13.9.1.91:8080&#xff0c;13.9.1.92:8080和1…

ssm504基于web的经典电影推荐网站的设计与实现论文

摘 要 传统办法管理信息首先需要花费的时间比较多&#xff0c;其次数据出错率比较高&#xff0c;而且对错误的数据进行更改也比较困难&#xff0c;最后&#xff0c;检索数据费事费力。因此&#xff0c;在计算机上安装经典电影推荐网站软件来发挥其高效地信息处理的作用&#x…