JavaEE从入门到起飞 (三) ~AOP

news2024/11/24 22:31:42

 晚上好,愿这深深的夜色给你带来安宁,让温馨的夜晚抚平你一天的疲惫,美好的梦想在这个寂静的夜晚悄悄成长。

文章目录

  • 前言
  • 一、pandas是什么?
  • 二、使用步骤
    • 1.引入库
    • 2.读入数据
  • 总结


前言

了解面向切面编程(AOP)

在现代软件开发中,面向切面编程(Aspect-Oriented Programming,AOP)是一种重要的编程范式,它提供了一种有效的方式来解决横切关注点(cross-cutting concerns)的问题。本文将介绍AOP的概念、其主要组成部分以及如何在Java应用程序中应用AOP技术。

什么是面向切面编程(AOP)?

AOP是一种软件开发技术,它允许开发人员将横切关注点(如日志记录、事务管理、安全性、缓存等)从应用的核心逻辑中分离出来。这些横切关注点可能会散布在应用程序的多个模块中,传统的面向对象编程往往会导致这些关注点与核心业务逻辑混杂在一起,使得代码难以理解、维护困难。AOP的出现旨在通过模块化横切关注点的方式来提高代码的模块性、可维护性和可重用性。

举个例子理解AOP和代理思想

例如:理发店这个工作流程,这样来一个顾客,我们就需要这样的流程,十分繁琐,对于聪明的我们自然是不愿意这样干的

我们可以抽取一个公共的方法,来直接调用即可,但仍然还需要我们调用,不够懒。

因此我们可以引入切面的思想了,我们把匹配的流程,自动执行对应的操作。

主要原理:代码,通过代理对象伪装成真正的对象来为我们服务

剪发的时候代理对象,会给你找剪头发的托尼老师,如果你想染发,代理也可以给你找专门染发的托尼老师。这就是我们AOP的思想了。

将真实bean作为代理对象中的一个成员变量。


一、主要概念?

横向增强内容,是一种无侵入的对原始方法进行增强

三大条件需要声明:

  1. 要增强的方法(切入点表达式进行匹配)
  2. 增加方法
  3. 增强方法和切入点方法的执行顺序 (通知方式 四大通知类型和环绕通知)

在AOP中,有几个核心概念需要理解:

  • 连接点(Join Point): 连接点是程序执行过程中的一个特定点,例如方法调用或抛出异常。在AOP中,连接点是可以被增强(如添加日志、事务管理等)的程序执行点。

  • 切入点(Pointcut): 切入点定义了在程序中哪些连接点上应用通知(Advice)。它通过表达式或者模式匹配来描述要被增强的一组连接点。

  • 切面(Aspect): 切面是将通知(Advice)和切入点(Pointcut)结合起来的一个模块化单元。它描述了在何处以及何时应用通知来实现横切关注点的功能。

  • 通知(Advice): 通知是在切面的某个特定连接点上执行的动作。它定义了增强代码的类型和时机,如在方法调用之前、之后或者环绕方法执行。

  • 目标对象(Target Object): 目标对象是一个或多个切面所增强的原始对象。它包含业务逻辑,通常是不察觉被应用切面的存在。

  • 代理对象(Proxy Object): 代理对象是生成的对象,用作目标对象的替代品。它拦截对目标对象的方法调用,并允许AOP框架应用切面(如日志记录、安全性、事务管理等)。

1.1 Aspect(切面)

使用@Aspect和@Component的类

为什么切面还要声明成bean?

因为如果一个类没有被 Spring 管理,那么 Spring 将无法为它创建代理对象,也就无法实现 AOP 功能。

@Aspect的作用是什么?

因为,Spring容器启动后,会优先读取声明到@Aspect类并且读取所有切面配置的切入点,然后在初始化bean判断bean中的方法是否匹配切入点,匹配失败就创建对象到IOC,匹配成功,就创建原始对象的代理对象,然后会将代理对象注入为该类型的Bean,然后当你注入该Bean的时候实际注入的是代理对象的Bean,执行的就是代理对象与连接点映射的方法。

代理对象内容:根据切面中的增强方法和原始对象生成 。

1.2 切入点表达式

主要思想:匹配所有的切点,然后根据条件筛选出连接点,然后进行增强。

切入点表达式的标准格式:

execution([访问修饰符] [返回值类型] [包名.类名.]方法名(参数列表) [throws 异常名])
  • 访问修饰符:可选项,例如 publicprotected 等,可以省略。
  • 返回值类型:方法的返回类型,例如 voidint 等。
  • 包名.类名:类的完整路径,可以省略。
  • 方法名:目标方法的名称。
  • 参数列表:方法的参数列表。
  • throws 异常名:方法声明的异常,可选,指定方法可能抛出的异常。

 切入点表达式的通配符:

  • *:匹配任意数量的字符(单个独立的任意符号,可以单独使用或者作为前缀或后缀)。
  • ..:匹配任意数量的字符序列(多个连续的任意符号,常用于简化包名和参数的书写)。
  • +:写在类或接口的后面,专用于匹配子类类型。

@Pointcut

是在 Spring AOP 中用来定义切入点的注解。

在 Spring AOP 中,切入点(Pointcut)是一组匹配连接点(Join Point)的规则。连接点是在应用执行过程中能够插入切面的点,例如方法执行时、方法调用时等。而切入点则是为了从连接点中筛选出我们真正关心的一部分。

execution:有一定规律的(可以按照规律模糊匹配)

  • execution 表达式:这种表达式按照一定的规则和格式来匹配方法的执行。它允许根据方法的访问修饰符、返回值类型、包名、类名、方法名、参数列表以及可能抛出的异常来进行精确或模糊的匹配。

    @Pointcut("execution(* com.example.service.*.*(..))")
    private void serviceMethods() {}
    
    @Before("serviceMethods()")
    public void beforeServiceMethods(JoinPoint joinPoint) {
        // 在 serviceMethods() 匹配的方法执行前执行此通知
    }

@annotion:没有规律,指定注解被匹配

  • @annotation 注解表达式:这种表达式是一种特殊的切入点表达式,用于匹配被特定注解标注的方法。它不需要关注方法的签名、包名等,而是专门指定某个或某些特定的注解。

    @Pointcut("@annotation(org.springframework.transaction.annotation.Transactional)")
    private void transactionalMethods() {}
    
    @Around("transactionalMethods()")
    public Object aroundTransactionalMethods(ProceedingJoinPoint joinPoint) throws Throwable {
        // 在使用 @Transactional 注解的方法周围执行此通知
        return joinPoint.proceed();
    }

1.3 通知类型 

我认为通知类型,就是决定了原始方法的执行位置

  1. @Before: 前置通知

  2. @After: 后置通知

  3. @Around: 环绕通知

    1. 需要有参数来确定原始方法所在的位置,然后调用proceed()方法来调用原始方法。
    2. 需要把原始方法返回值扔出去,就是proceed()的返回值。注意,proceed()方法如果要修改参数的内容可以传参。
  4. @AfterReturning: 返回后通知

    1. 在方法返回后执行,不抛出异常的情况下。
  5. @AfterThrowing: 抛出异常后通知

对于环绕方式(Around)通过调用原始方法的实际,可以完成上述四者所有的功能。

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;

@Aspect
public class UserServiceAspect {

    @Before("execution(* com.example.service.UserService.*(..))")
    public void beforeAdvice() {
        System.out.println("前置通知: 在UserService方法执行前进行");
    }

    @After("execution(* com.example.service.UserService.*(..))")
    public void afterAdvice() {
        System.out.println("后置通知: 在UserService方法执行后进行(相当于finally块)");
    }

    @Around("execution(* com.example.service.UserService.*(..))")
    public Object aroundAdvice(ProceedingJoinPoint joinPoint) throws Throwable {
        System.out.println("环绕通知: UserService方法执行前");
        Object result = joinPoint.proceed(); // 执行方法
        System.out.println("环绕通知: UserService方法执行后");
        return result;
    }

    @AfterReturning(pointcut = "execution(* com.example.service.UserService.*(..))", returning = "result")
    public void afterReturningAdvice(Object result) {
        System.out.println("返回后通知: 在UserService方法返回后执行");
    }

    @AfterThrowing(pointcut = "execution(* com.example.service.UserService.*(..))", throwing = "ex")
    public void afterThrowingAdvice(Exception ex) {
        System.out.println("异常抛出通知: UserService方法中抛出的异常: " + ex.getMessage());
    }
}

二、日志案例

注意下述是使用SpringBoot的方式实现的Aop。

我们要实现的功能是,要记录controller中增 删 改方法的运行日志保存到日志表中

日志信息包含:操作人、操作时间、执行方法的全类名、执行方法名、方法作用、方法运行时参数、返回值、方法执行时长

1.导入初始环境

1.1 引入AOP的依赖

        <!--aop相关的依赖-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-aop</artifactId>
        </dependency>

1.2 初始化数据库和实体类

初始化数据库

-- 操作日志表
create table operate_log(
    id bigint unsigned primary key auto_increment comment 'ID',
    class_name varchar(100) comment '操作的类名',
    method_name varchar(100) comment '操作的方法名',
    method_desc varchar(100) comment '方法用途',
    method_params varchar(1000) comment '方法参数',
    return_value varchar(2000) comment '返回值',
    operate_user int unsigned comment '操作人ID',
    operate_time datetime comment '操作时间',
    cost_time bigint comment '方法执行耗时, 单位:ms'
) comment '操作日志表';

 创建实体类

package com.itheima.pojo;

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

import java.time.LocalDateTime;

@Data
@NoArgsConstructor
@AllArgsConstructor
public class OperateLog {
    private Integer id; //ID
    private String className; //操作类名
    private String methodName; //操作方法名
    private String methodDesc; //方法用途
    private String methodParams; //操作方法参数
    private String returnValue; //操作方法返回值
    private Integer operateUser; //操作人ID
    private LocalDateTime operateTime; //操作时间
    private Long costTime; //操作耗时
}

1.3 创建日志的持久层Mapper 

package com.itheima.mapper;

import com.itheima.pojo.OperateLog;
import org.apache.ibatis.annotations.Insert;
import org.apache.ibatis.annotations.Mapper;

@Mapper
public interface OperateLogMapper {

    //插入日志数据
    @Insert("insert into operate_log (operate_user, operate_time, class_name, method_name,method_desc, method_params, return_value, cost_time) " +
            "values (#{operateUser}, #{operateTime}, #{className}, #{methodName},#{methodDesc}, #{methodParams}, #{returnValue}, #{costTime});")
    public void insert(OperateLog log);
}

因为不是对Controller的所有方法进行增强,因此没有规律,所以需要注解的方法进行匹配切点。 

1.4 创建自定义注解

package com.csy.anno;

import java.lang.annotation.*;

/**
 * @author windStop
 * @version 1.0
 * @description 记录日志所有Controller的增删改的日志
 * @date 2024年08月08日19:42:23
 */
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface LogMyAnno {
    String value() default "";
}

2.核心逻辑

2.1 对要增强的方法添加自定义注解

package com.csy.controller;

import com.csy.anno.LogMyAnno;
import com.csy.entity.Dept;
import com.csy.service.DeptService;
import com.csy.vo.Result;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

import java.util.List;
import java.util.Map;

/**
 * @author windStop
 * @version 1.0
 * @description 关于部门管理的表现层
 * @date 2024年08月06日16:35:50
 */
@RestController
@RequestMapping("/depts")
public class DeptController {
    @Autowired
    private DeptService deptService;

    /**
     * 查询所有部门信息
     * 通过调用deptMapper的selectList方法,查询所有部门的信息
     * 该方法不需要任何查询条件,返回所有部门的列表
     *
     * @return 包含所有部门信息的List集合
     */
    @GetMapping
    public Result<List<Dept>> findAll() {
        return Result.success(deptService.findAll());
    }


    /**
     * 使用POST请求方式插入部门信息
     *
     * @param map 部门名称是json参数,string类型会把大括号和键值对一起转换,因此需要map
     * @return 包含操作结果的对象,成功时返回成功信息
     */
    @LogMyAnno("添加部门")
    @PostMapping
    public Result<Object> insertDept(@RequestBody Map<String,String> map) {
        deptService.insertDept(map.get("name"));
        return Result.success();
    }

    /**
     * 删除部门
     调用服务层的deleteDept方法来删除对应的部门
     * 不返回任何数据,使用Result对象表示操作结果
     * @param ids 部门的ID
     * @return Result<Object>对象,表示操作结果
     */
    @LogMyAnno("删除部门")
    @DeleteMapping("/{ids}")
    public Result<Object> deleteDept(@PathVariable List<Integer> ids) {
        deptService.deleteDept(ids);
        return Result.success();
    }

    /**
     * 更新部门信息
     * 通过PutMapping注解指定处理更新部门信息的HTTP Put请求
     * 该方法接收一个Dept对象,更新数据库中的部门信息,并返回操作结果
     *
     * @param dept 待更新的部门对象,包含部门的所有信息
     * @return Result<Object>类型的结果对象,包含操作是否成功的状态信息
     *         在本方法中,始终返回成功状态,不包含额外的数据
     * @see DeptService updateDept(Dept) 实际执行更新操作的服务方法
     */
    @LogMyAnno("更新员工")
    @PutMapping
    public Result<Object> updateDept(@RequestBody Dept dept) {
        deptService.updateDept(dept);
        return Result.success();
    }

    /**
     * 通过ID查找部门信息
     *
     * 此方法通过接收一个部门ID,调用部门服务(DeptService)的findById方法来查找特定的部门信息
     * 它使用了@GetMapping注解来处理HTTP GET请求,路径中的{id}是动态接收的参数,对应于数据库中的部门ID
     *
     * @param id 部门的唯一标识符,用于定位特定的部门
     * @return 返回一个Result对象,其中包含找到的部门信息如果找到,则Result的成功标志为true,数据为找到的部门;如果未找到,则成功标志为false,数据为空
     */
    @GetMapping("/{id}")
    public Result<Dept> findById(@PathVariable Integer id) {
        return Result.success(deptService.findById(id));
    }
}

2.2 编写切面类进行增强 

package com.csy.log;

import com.csy.anno.LogMyAnno;
import com.csy.dao.OperateLogMapper;
import com.csy.entity.OperateLog;
import com.csy.utils.ThreadLocalUtil;
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.Autowired;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;
import java.time.LocalDateTime;
import java.util.Arrays;
import java.util.Map;

/**
 * @author windStop
 * @version 1.0
 * @description 记录增删改查的日志
 * @date 2024年08月08日19:49:54
 */
@Component
@Aspect //切面类,会先扫描该注解在创建bean,会将切入点匹配的方法所对应的类创建代理对象。
                    // 原始对象不加入ioc管理,该类的代理对象完全替代它
public class Logger {
    @Autowired
    private OperateLogMapper operateLogMapper;


    //1.定义切入点表达式
    @Pointcut("@annotation(com.csy.anno.LogMyAnno)")
    public void pt(){};

    //2.增强方法
    @Around("pt()")
    public Object around(ProceedingJoinPoint pjp) throws RuntimeException {
        OperateLog log = new OperateLog();
        log.setOperateTime(LocalDateTime.now());//操作时间
        MethodSignature signature = (MethodSignature) pjp.getSignature();
        Method method = signature.getMethod();
//        log.setMethodParams(Arrays.toString(signature.getParameterNames()));操作变量名写错了
        log.setMethodParams(Arrays.toString(pjp.getArgs()));//操作方法参数
        log.setMethodName(method.getName());//操作方法名
        log.setMethodDesc(method.getAnnotation(LogMyAnno.class).value());//操作用途
        log.setClassName(pjp.getTarget().getClass().getName());//操作类名
        Map<String, Object> tokens = ThreadLocalUtil.get();
        log.setOperateUser((Integer) tokens.get("id"));//操作人ID
        long start = System.currentTimeMillis();
        Object proceed;
        //执行目标方法
        try {
            proceed = pjp.proceed();
            log.setReturnValue(proceed.toString());//操作返回值
            return proceed;
        }catch (Throwable t){
            throw new RuntimeException(t);
        }finally {
            long end = System.currentTimeMillis();
            log.setCostTime(end - start);//操作耗时(毫秒值)
            //保存日志
            operateLogMapper.insert(log);
        }
    }
}

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

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

相关文章

牛客网每日刷题之 HJ99.自守数(C++)

在不断学习的过程中也不能忘记了基础知识的巩固&#xff0c;在学习新的知识后要学会去举一反三&#xff0c;前不久我刚刚了解了一些关于 string 类的知识&#xff0c;对牛客网的 自守数 有了新的解题思路&#xff0c;让我们一起看看这道题吧 思路解析 a. 整数方法 1. 首先我们知…

C++(2):λ表达式,类函数重载

λ表达式 [&]捕获前面所有 [i] j闭包函数内访问不了&#xff0c;在函数内i只能用不能改 [&I,j] i在函数内可改j不行 未捕获可以充当函数指针使用 new,delete和malloc,free区别 new会调构造函数 malloc不会&#xff0c;delete会调析构函数free不会malloc调用需要强转…

从混沌到秩序:一本书教你掌握互联网内容审核与信息安全的密钥

随着互联网技术的迅猛发展&#xff0c;视频、图片、文字等多媒体内容以前所未有的速度在全球范围内传播与分享&#xff0c;极大地丰富了人们的信息获取渠道和娱乐生活方式。然而&#xff0c;这一繁荣景象背后&#xff0c;也隐藏着内容安全、版权侵犯、虚假信息传播、不良内容泛…

公司邮箱办理流程复杂吗?三步快速开通公司邮箱

对于许多企业而言, 开通公司邮箱是一项重要的基础建设工作。但是, 很多企业主可能会担心这一过程是否过于繁琐。实际上, 只需要注册、设置域名和邮箱, 以及开始使用这三个步骤。本文将为您介绍如何通过Zoho邮箱快速完成公司邮箱的开通。 Zoho邮箱因其卓越的服务质量和可靠性, …

数据传输为什么占用空间变大了

最近在做数据备份&#xff0c;很尴尬的是&#xff0c;不到3T的数据&#xff0c;用一个4T的硬盘拷贝中转到存储服务器上发现放不下。特别是以小文件为主的标注文件的文件夹&#xff0c;几十M几百M变成几十G或者100多G的空间占用。下面是几张图。 ​ ​ ​ 服务器之间拷贝占用空间…

Linux初启征程指南:攻克常见系统指令与权限初理解

有时候觉得&#xff0c;电脑就像一个高贵冷艳的妹纸。 400&#xff0c;是她冷冰冰地说&#xff1a;“我听不懂你在说什么”&#xff1b; 401&#xff0c;是她无情地转身&#xff1a;“我不认识你&#xff0c;别说那些奇怪的话”&#xff1b; 403&#xff0c;是她残酷的拒绝&…

Mysql-窗口函数二

文章目录 1. 前百分之N的问题 排名 row_number1.1 需求1.2 准备工作1.3 分析1.4 实现 2. 前百分之N的问题 ntile2.1 介绍2.2 语法2.2.1 示例2.2.2 结果示例2.2.3 注意事项 2.3 需求2.4 分析2.5 实现 3. 前百分之N的问题 百分比 PERCENT_RANK3.1 语法3.1.1 示例3.1.2 注意事项 3…

函数的练习

1.判断一个数是否为素数 代码如下&#xff1a; #include <stdio.h> int jum(int num) {int i 0;for (i 2;i < num;i){if (num % i 0){return 0;break;}}return 1; }void Jum1(int (*pf)(int)) {int num 0;printf("请输入操作数&#xff1a;");scanf_s…

声压、声强、响度之间的区别与联系

声压、声强、响度是声学中描述声音特性的重要概念&#xff0c;它们之间有一定的关系&#xff0c;但分别反映了不同的物理或感知特性。 1、声压&#xff08;Sound Pressure&#xff09; 声压是声波传播时在介质中引起的压力波动&#xff0c;通常以帕斯卡&#xff08;Pa&#x…

Windows电脑下载安装Kodcloud可道云结合内网穿透秒变私有网盘

文章目录 1.前言2. Kodcloud网站搭建2.1. Kodcloud下载和安装2.2 Kodcloud网页测试 3. cpolar内网穿透的安装和注册4. 本地网页发布4.1 Cpolar云端设置4.2 Cpolar本地设置 5. 公网访问测试6.结语 1.前言 本文主要为大家介绍一款国人自研的在线Web文件管理器可道云&#xff0c;…

苹果手机数据被抹除还能恢复吗?这两个方法强烈推荐

苹果手机数据被抹除还能恢复吗&#xff1f;我们在使用苹果手机时&#xff0c;有时由于误操作、系统故障或升级失败等原因&#xff0c;导致手机照片、备忘录、视频、联系人等数据被意外抹除。 面对这类情况&#xff0c;我们应该怎么办&#xff1f;下面牛小编给大家的分享2个方法…

CH07_数据绑定

第7章&#xff1a;数据绑定 本章目标 理解路由事件 掌握键盘输入事件 掌握鼠标输入事件 掌握多点触控输入事件 数据绑定概述 什么是数据绑定 ​ 将WPF中的至少一个带有依赖项属性的两个对象的两个属性进行绑定&#xff0c;使某一个依赖项属性可以更新和它绑定的属性的功…

奥尔特曼在X上发了颗“草莓” 网友疯狂猜测:这难道是GPT新模型?

ChatGPT开发商OpenAI的首席执行官山姆奥尔特曼&#xff08;Sam Altman&#xff09;在社交媒体X上发布了一张花园里自种草莓的照片后&#xff0c;引发了众多网友的热议&#xff0c;是否新的GPT模型即将上线&#xff1f; 周三&#xff08;8月7日&#xff09;&#xff0c;奥尔特曼…

海量数据处理商用短链接生成器平台 - 8

第十八章 短链服务-分库分表多维度查询解决方案《钻石玩法》 第1集 短链服务-短链URL跳转302跳转接口开发实战 简介&#xff1a; 短链URL 跳转302跳转接口开发实战 需求 接收一个短链码解析获取原始地址302进行跳转 编码实战 Controller Slf4j public class LinkApiControl…

搭建时空基底,建设“数字之城”

在这个日新月异的时代&#xff0c;数字技术正以前所未有的速度重塑我们的世界。今天&#xff0c;让我们一同深入探讨如何通过搭建时空基底&#xff0c;为“数字之城”的建设奠定坚实基础。 一、时空基底&#xff1a;数字之城的骨骼与脉络 所谓“时空基底”&#xff0c;是指结…

【vulnhub】DC-6靶机

靶机安装 下载地址&#xff1a;https://download.vulnhub.com/dc/DC-6.zip 运行靶机&#xff1a;VMware 信息收集 靶机扫描 nmap 192.168.93.0/24 端口扫描,根据80端口的信息&#xff0c;发现我们并不能直接访问靶机的web页面&#xff0c;和/wordy相关 nmap -A 192.168.9…

本地部署MySQL图形化管理工具phpMyAdmin结合内网穿透远程访问

文章目录 前言1. 安装MySQL2. 安装phpMyAdmin3. 修改User表4. 本地测试连接MySQL5. 安装cpolar内网穿透6. 配置MySQL公网访问地址7. 配置MySQL固定公网地址8. 配置phpMyAdmin公网地址9. 配置phpmyadmin固定公网地址 前言 本文主要介绍如何在群晖NAS安装MySQL与数据库管理软件p…

C++虚函数表、地址详解(x86/x64)

参考博文&#xff1a;c虚函数表、地址详解-CSDN博客 本文在上述博文的基础上&#xff0c;补充了x64下的验证代码。 一.什么是虚函数表&#xff0c;它有什么特点&#xff1f; 虚函数大家都知道是基本用于实现多态的&#xff0c;当父类指针指向子类对象的时候&#xff0c;如何确…

国自然即将放榜,还没消息是不是就凉了?

本周投稿推荐 SCI&EI • 医学与心理学&#xff0c;纯正刊&#xff08;基本不拒稿&#xff09; • 1区计算机水刊&#xff0c;3.5-4.0&#xff08;1个月录用&#xff09; • 2区-Top水刊&#xff0c;2.0-3.0&#xff08;沾边可录&#xff09; EI • 各领域沾边均可&am…

超分辨率重建——冠军队EDVR视频超分网络训练自己数据集与推理测试(详细图文教程)

&#x1f4aa; 专业从事且热爱图像处理&#xff0c;图像处理专栏更新如下&#x1f447;&#xff1a; &#x1f4dd;《图像去噪》 &#x1f4dd;《超分辨率重建》 &#x1f4dd;《语义分割》 &#x1f4dd;《风格迁移》 &#x1f4dd;《目标检测》 &#x1f4dd;《暗光增强》 &a…