SpringBoot+Aop+注解方式 实现多数据源动态切换

news2024/12/27 1:41:21

整体思路:

  1. 引入基本依赖SpringBoot+Aop+MySql+MyBatis+lombok
  2. 在配置文件中配置多个数据源
  3. 创建数据源配置类用于读取配置
  4. 编写用于标识切换数据源的注解
  5. 创建数据源切换工具类DataSourceContextHolder
  6. 编写切面类用于在注解生效处切换数据源
  7. 编写配置类,加载数据源
  8. 创建动态数据源类,并继承AbstractRoutingDataSource,指定使用哪个数据源(关键)

项目demo gitee地址:多数据源动态切换demo

1.引入依赖

<dependency>
     <groupId>org.springframework.boot</groupId>
     <artifactId>spring-boot-starter-web</artifactId>
     <version>2.7.10</version>
 </dependency>
 <dependency>
     <groupId>org.springframework.boot</groupId>
     <artifactId>spring-boot-starter-aop</artifactId>
     <version>2.7.10</version>
 </dependency>
 <dependency>
     <groupId>mysql</groupId>
     <artifactId>mysql-connector-java</artifactId>
     <version>8.0.21</version>
 </dependency>
 <dependency>
     <groupId>org.mybatis.spring.boot</groupId>
     <artifactId>mybatis-spring-boot-starter</artifactId>
     <version>2.1.3</version>
 </dependency>
 <dependency>
     <groupId>org.projectlombok</groupId>
     <artifactId>lombok</artifactId>
     <version>1.18.22</version>
 </dependency>
 <dependency>
     <groupId>com.alibaba</groupId>
     <artifactId>druid</artifactId>
     <version>1.2.20</version>
 </dependency>

2.在配置文件中配置多个数据源

这里配置了上海,深圳,北京3个数据源,需要自己创建这3个库multi-sh,multi-sz,multi-bj

#默认数据源
datasource.default=sh
#上海库
spring.datasource.sh.url=jdbc:mysql://localhost:3306/multi-sh?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=UTC
spring.datasource.sh.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.sh.username=root
spring.datasource.sh.password=123

#深圳库
spring.datasource.sz.url=jdbc:mysql://localhost:3306/multi-sz?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=UTC
spring.datasource.sz.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.sz.username=root
spring.datasource.sz.password=123

#北京库
spring.datasource.bj.url=jdbc:mysql://localhost:3306/multi-bj?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=UTC
spring.datasource.bj.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.bj.username=root
spring.datasource.bj.password=123

3.创建数据源配置类用于读取配置

spring获取统一前缀配置需要可以看我之前的文章:SpringBoot项目获取统一前缀配置以及获取非确定名称配置

package com.gooluke.datasource;

import lombok.Getter;
import lombok.Setter;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;

import java.util.Map;

/**
 * @author gooluke
 */
@Setter
@Getter
@Component
@ConfigurationProperties(prefix = "spring")
public class MultiDataSourceProperties {

    /**
     * 这里的datasource是因为配置是spring.datasource.xx.xx,要配置成datasource,这样才会把配置自动映射进来
     * 分别映射到url、driverClassName、username、password
     */
    private Map<String, DataSourceConfig> datasource;

    @Setter
    @Getter
    public static class DataSourceConfig {
        private String url;
        private String driverClassName;
        private String username;
        private String password;
    }
}

4.编写用于标识切换数据源的注解

package com.gooluke.common.annotation;

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

/**
 * @author gooluke
 */
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface FixedDataSource {

    String value();

    /**
     * 是否需要还原回之前的数据源(拓展)
     */
    boolean needRecover() default false;

}

5.创建数据源切换工具类DataSourceContextHolder

package com.gooluke.datasource;

import com.gooluke.config.DataSourceConfig;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * @author gooluke
 * 将数据源信息存放至ThreadLocal
 */
public class DatasourceContextHolder {
    private static final Logger log = LoggerFactory.getLogger(DatasourceContextHolder.class);

    private static final ThreadLocal<String> DATASOURCE_THREAD_LOCAL = new ThreadLocal<>();

    public static void setDatasource(String datasource) {
        if (datasource != null && DataSourceConfig.dataSources.get(datasource) == null) {
            String errorMsg = String.format("数据源[%s]未配置", datasource);
            log.error(errorMsg);
            throw new RuntimeException(errorMsg);
        }
        DATASOURCE_THREAD_LOCAL.set(datasource);
    }

    public static String getDatasource() {
        return DATASOURCE_THREAD_LOCAL.get();
    }

    public static void clearDatasource() {
        DATASOURCE_THREAD_LOCAL.remove();
    }
}

6.编写切面类用于在注解生效处切换数据源

package com.gooluke.aspect;

import com.gooluke.common.annotation.FixedDataSource;
import com.gooluke.datasource.DatasourceContextHolder;
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.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

/**
 * @author gooluke
 * 切换数据源切面类
 * 这个已不再使用,使用com.gooluke.aop.DataSourceAnnotationAdvisor替代
 */
@Aspect
@Component
public class DataSourceAspect {

    private static final Logger log = LoggerFactory.getLogger(DataSourceAspect.class);

    /**
     * 注解加在方法上
     */
    @Pointcut("@annotation(com.gooluke.common.annotation.FixedDataSource)")
    private void methodPointCut() {}

    /**
     * 注解加在方法上
     */
    @Pointcut("@within(com.gooluke.common.annotation.FixedDataSource)")
    public void classPointcut() {}

    @Around(value = "methodPointCut() || classPointcut()")
    public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
        //记录当前数据源,和准备切换的数据源
        String oldDatasource = DatasourceContextHolder.getDatasource();
        MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
        java.lang.reflect.Method method = methodSignature.getMethod();

        FixedDataSource annotation = method.getAnnotation(FixedDataSource.class);
        //方法上获取注解为空,再从类上获取
        if (annotation == null) {
            annotation = method.getDeclaringClass().getAnnotation(FixedDataSource.class);
        }
        String newDatasource = annotation.value();
        //切换数据源,并执行操作
        DatasourceContextHolder.setDatasource(newDatasource);
        try {
            return joinPoint.proceed();
        } finally {
            //是否切换回初始数据源
            if (annotation.needRecover()) {
                DatasourceContextHolder.setDatasource(oldDatasource);
            }
        }
    }
}

7.编写配置类,加载数据源

这个配置类,主要就是将我们配置的多数据源解析然后统一管理,dynamicDataSource.setTargetDataSources(targetDataSources); 以及设置默认数据源。

package com.gooluke.config;

import com.alibaba.druid.pool.DruidDataSource;
import com.gooluke.aop.DataSourceAnnotationAdvisor;
import com.gooluke.aop.DataSourceAnnotationInterceptor;
import com.gooluke.datasource.DynamicDataSource;
import com.gooluke.datasource.MultiDataSourceProperties;
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;

import javax.sql.DataSource;
import java.util.HashMap;
import java.util.Map;

/**
 * @author gooluke
 */

@Configuration
@Slf4j
public class DataSourceConfig {

    public static final Map<String, String> dataSources = new HashMap<>();

    @Autowired
    MultiDataSourceProperties dataSourceProperties;

    @Value("${datasource.default:}")
    private String defaultDataSourceName;

    @Bean
    @Primary
    public DynamicDataSource dynamicDataSource() {
        DynamicDataSource dynamicDataSource = new DynamicDataSource();
        //存放所有数据源
        Map<Object, Object> targetDataSources = new HashMap<>();
        Map<String, MultiDataSourceProperties.DataSourceConfig> datasourceMap = dataSourceProperties.getDatasource();
        if (datasourceMap.entrySet().size() > 1 && (defaultDataSourceName == null || defaultDataSourceName.isEmpty())) {
            throw new RuntimeException("存在多个数据源,未配置默认数据源:datasource.default");
        }
        datasourceMap.forEach((datasourceName, config) -> {
            DataSource dataSource = createDataSource(config);
            targetDataSources.put(datasourceName, dataSource);
            dataSources.put(datasourceName, datasourceName);
            log.info("已初始化数据库:{}", datasourceName);
            if (datasourceMap.size() == 1 || (defaultDataSourceName != null && !defaultDataSourceName.isEmpty() && defaultDataSourceName.equals(datasourceName))) {
                //这里设置默认数据源
                dynamicDataSource.setDefaultTargetDataSource(dataSource);
                log.info("已设置默认数据源: {}", datasourceName);
            }
        });
        //这里把数据源统一管理
        dynamicDataSource.setTargetDataSources(targetDataSources);
        return dynamicDataSource;
    }

    private DataSource createDataSource(MultiDataSourceProperties.DataSourceConfig dataSourceConfig) {
        DruidDataSource dataSource = new DruidDataSource();
        dataSource.setUrl(dataSourceConfig.getUrl());
        dataSource.setDriverClassName(dataSourceConfig.getDriverClassName());
        dataSource.setUsername(dataSourceConfig.getUsername());
        dataSource.setPassword(dataSourceConfig.getPassword());
        dataSource.setValidationQuery("SELECT 1");
        dataSource.setTestWhileIdle(true);
        dataSource.setTestOnBorrow(false);
        dataSource.setTestOnReturn(false);
        dataSource.setPoolPreparedStatements(true);
        dataSource.setMaxPoolPreparedStatementPerConnectionSize(20);
        return dataSource;
    }

    @Bean
    @Primary
    public SqlSessionFactory sqlSessionFactory(DynamicDataSource dynamicDataSource) throws Exception {
        SqlSessionFactoryBean sessionFactory = new SqlSessionFactoryBean();
        sessionFactory.setDataSource(dynamicDataSource);
        PathMatchingResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
        org.springframework.core.io.Resource[] resources = resolver.getResources("classpath:/mapper/*.xml");
        //org.springframework.core.io.Resource config = resolver.getResource("classpath:mybatis-config.xml");
        sessionFactory.setMapperLocations(resources);
        //sessionFactory.setConfigLocation(config);
        return sessionFactory.getObject();
    }

    @Bean
    @Primary
    public DataSourceTransactionManager transactionManager(DynamicDataSource dynamicDataSource) {
        return new DataSourceTransactionManager(dynamicDataSource);
    }

}

8.创建动态数据源类,并继承AbstractRoutingDataSource,指定使用哪个数据源(关键)

这里可以理解为就是一个口子,让我们自己指定数据源,如果你返回的是null,则会指定我们配置类中设置的默认数据源:dynamicDataSource.setDefaultTargetDataSource(dataSource);

package com.gooluke.datasource;

import lombok.extern.slf4j.Slf4j;
import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;

/**
 * @author gooluke
 * 动态数据源
 */
@Slf4j
public class DynamicDataSource extends AbstractRoutingDataSource {

    /**
     * 这里返回ThreadLocal中的数据源
     */
    @Override
    protected Object determineCurrentLookupKey() {
        return DatasourceContextHolder.getDatasource();
    }

}

9.请求完成后,记得清空ThreadLocal,否则会造成内存泄漏

编写一个拦截器,在请求完成后,remove

package com.gooluke.interceptor;

import com.gooluke.datasource.DatasourceContextHolder;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

/**
 * @author gooluke
 */
@Component
@Slf4j
public class DataSourceInterceptor implements HandlerInterceptor {

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        DatasourceContextHolder.clearDatasource();
        log.info("请求处理完成,清除数据源");
    }
}

10.代码演示

将注解加在实现类方法上,或者加在mapper/dao接口上(一般加在这里,因为dao接口一般都是操作同一个库,这里指定了,其它别的方法直接调用即可)

10.1 service层:

package com.gooluke.service.impl;

import com.gooluke.dao.UserInfoDao;
import com.gooluke.dao.UserInfoDao2;
import com.gooluke.entity.TUserInfo;
import com.gooluke.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.List;

/**
 * @author gooluke
 */
@Service
public class UserServiceImpl implements UserService {

    @Autowired
    private UserInfoDao userInfoDao;

    @Autowired
    private UserInfoDao2 userInfoDao2;

    /**
     * 在这里没有设置数据源,dao层设置了数据源,可以自动切换
     */
    @Override
    public List<TUserInfo> selectList() {
        //先查深圳库,再查上海库
        List<TUserInfo> tUserInfos = userInfoDao.selectUserList(new TUserInfo());
        tUserInfos.forEach(System.out::println);
        List<TUserInfo> tUserInfos2 = userInfoDao2.selectUserList(new TUserInfo());
        tUserInfos2.forEach(System.out::println);
        tUserInfos.addAll(tUserInfos2);
        return tUserInfos;
    }

}

10.2 dao层:

dao1指定深圳库:

package com.gooluke.dao;

import com.gooluke.common.annotation.FixedDataSource;
import com.gooluke.common.constants.DataSourceName;
import com.gooluke.entity.TUserInfo;
import org.apache.ibatis.annotations.Mapper;

import java.util.List;

/**
 * @author gooluke
 */
@Mapper
@FixedDataSource(DataSourceName.SHENZHEN)
public interface UserInfoDao {

    List<TUserInfo> selectUserList(TUserInfo userInfo);

}

dao2指定上海库:

package com.gooluke.dao;

import com.gooluke.common.annotation.FixedDataSource;
import com.gooluke.common.constants.DataSourceName;
import com.gooluke.entity.TUserInfo;
import org.apache.ibatis.annotations.Mapper;

import java.util.List;

/**
 * @author gooluke
 */
@Mapper
@FixedDataSource(DataSourceName.SHANGHAI)
public interface UserInfoDao2 {

    List<TUserInfo> selectUserList(TUserInfo userInfo);

}

10.3 观察结果

切库成功,分别查询了不同库的数据,并在最后清空了ThreadLocal中的数据

11.动态数据源(开源)dynamic-datasource-spring-boot-starter

上面这种Aop的实现方式在注解加在service接口的方法上其实是不生效的,当然也不建议加在service接口上,通常是加在实现类类上或者方法上。而Mapper/Dao接口的实现类是通过mybatis动态代理生成的,注解加在Mapper/Dao接口上是能生效的,我没有找到为啥他的实现类可以的文章。而我们也可以通过别的方式,把注解加在接口上的场景通过Aop拦截,只是不建议。下面是开源组件-动态数据源

'com.baomidou:dynamic-datasource-spring-boot-starter:3.3.2'

的Aop方案,有兴趣的可以去看一下他的源码,我的工程里也是用的这种方案,需要在配置类中声明@bean

11.1 创建一个DataSourceAnnotationAdvisor去继承AbstractPointcutAdvisor类,并实现BeanFactoryAware接口

11.2 重写getPointcut()、getAdvice()、setBeanFactory()方法

11.3 配置声明@Bean

    @Bean
    public DataSourceAnnotationAdvisor dataSourceAnnotationAdvisor() {
        DataSourceAnnotationInterceptor dataSourceAnnotationInterceptor = new DataSourceAnnotationInterceptor();
        return new DataSourceAnnotationAdvisor(dataSourceAnnotationInterceptor);
    }

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

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

相关文章

在线文档搜索服务测试报告

目录 1. 项目背景: 2. 项目功能: 3. 测试计划: 1. 项目背景: 1.1 在线搜索服务的前端主要一下几个功能, 分别是进入搜索引擎界面(有提示输入关键词信息); 进行输入关键词的界面, 以及显示有关关键词的文档url, 点击跳转至目标文档的界面; 1.2 该在线搜索服务的文档可以实现用…

2024/9/22周报

文章目录 摘要Abstract可能的数据结构数据集结构 数据处理步骤数据集示例人工智能模型应用关键评估目标评价指标分类应用实例最终目标多目标优化的基本概念1. Pareto最优解&#xff08;Pareto Optimality&#xff09;2. 目标权重法&#xff08;Weighted Sum Method&#xff09;…

Python3 爬虫教程 - Web 网页基础

Web网页基础 1&#xff0c;网页的组成HTMLcssJavaScript2&#xff0c;网页的结构 3&#xff0c;节点树及节点间的关系4&#xff0c;选择器开头代表选择 id&#xff0c;其后紧跟 id 的名称。如&#xff1a;div 节点的 id 为 container&#xff0c;那么就可以表示为 #container 1…

Vue3(二)计算属性Computed,监视属性watch,watchEffect,标签的ref属性,propos属性,生命周期,自定义hook

文章目录 一 、计算属性1. 简写2. 完整写法 二、监视watch1. 监视【ref】定义的【基本类型】数据2. 监视【ref】定义的【对象类型】数据3. 监视【reactive】定义的【对象类型】数据4. 监视【ref】或【reactive】定义的【对象类型】数据中的某个属性5. 监视多个数据总结 三、wat…

matlab之数据处理:滑动平均滤波算法与五点三次平滑算法

关注微♥公众号&#xff1a;“电击小子程高兴的MATLAB小屋”获取专属优惠 一.滑动平均滤波算法 算数平均滤波需要多次采样后才能得出一个有效值&#xff0c;如果被检测量变化较快&#xff0c;多次采样后才输出一次有效值&#xff0c;表现就是系统反应迟钝。将当前采样值与之前…

LLM大模型开源微调宝典:LLaMA Factory,汇聚100+国内外实战教程

LLaMA Factory是一个高效、易用、可扩展的开源全栈大模型微调框架&#xff0c;并得到Hugging Face、Avalon Labs、美团等多家国内外企业的关注或落地应用。 本次分享将从大模型高效训练的角度详细剖析LLaMA Factory的构建动机与组成模块&#xff0c;包括上百种大模型的全栈微调…

19_Python中的上下文管理器

Python中的上下文管理器 在Python中&#xff0c;上下文管理器&#xff08;Context Manager&#xff09;是一种用于资源管理的技术&#xff0c;它可以确保资源在使用后被正确释放&#xff0c;例如文件、网络连接或锁。 上下文管理器&#xff08;Context Manager&#xff09;是…

每日一练:二叉树的直径

543. 二叉树的直径 - 力扣&#xff08;LeetCode&#xff09; 一、题目要求 给你一棵二叉树的根节点&#xff0c;返回该树的 直径 。 二叉树的 直径 是指树中任意两个节点之间最长路径的 长度 。这条路径可能经过也可能不经过根节点 root 。 两节点之间路径的 长度 由它们之…

SpringBoot实现OAuth客户端

背景 5 月份的时候&#xff0c;我实践并整理了一篇博客&#xff1a;SpringBoot搭建OAuth2&#xff0c;该博客完成之后&#xff0c;本以为能对OAuth2的认证机制更加清晰&#xff0c;但我却觉得自己更“迷惘”了。 抛开我在项目中积累的浅薄经验不谈&#xff0c;单从在网…

9月16日笔记

访问控制列表 访问控制列表(ACL)是访问控制项(Access Control Entry , ACE)的列表。安全对象的安全描述可以通过两种访问控制列表DACL和SACL进行。 DACL DACL 是由一条条的访问控制项(ACE)条目构成的&#xff0c;每条ACE定义了哪些用户或组对该对象拥有怎样的访问权限。DACL…

进阶SpringBoot之集合 Redis

&#xff08;在跑 Redis 测试之前&#xff0c;需要先安装 Redis&#xff0c;并开启 Redis 服务&#xff09; Spring Boot 项目添加依赖 NoSQL -> Spring Data Redis pom.xml 文件如下 <dependencies><dependency><groupId>org.springframework.boot<…

实时数据的处理一致性

实时数据一致性的定义以及面临的挑战‍‍‍‍‍ 数据一致性通常指的是数据在整个系统或多个系统中保持准确、可靠和同步的状态。在实时数据处理中&#xff0c;一致性包括但不限于数据的准确性、完整性、时效性和顺序性。 下图是典型的实时/流式数据处理的流程&#xff1a; 1、…

佰朔资本:沪港通,深港通,港股通的区别与联系?

沪港通&#xff0c;深港通&#xff0c;港股通的差异与联系&#xff1a; 1、沪港通是“沪港股票市场生意互联互通机制”的简称&#xff0c;包含“沪股通”和“沪港通下的港股通”。 &#xff08;1&#xff09;“沪股通”是指投资者托付香港经纪商&#xff0c;经由联交地址上海…

kettle从入门到精通 第八十六课 ETL之kettle kettle调用https接口忽略SSL校验

1、在使用kettle调用接口的时候不可避免要调用http或者https接口&#xff0c;调用http接口kettle可以正常工作&#xff0c;但是遇到https接口的时候kettle就会提示证书有误&#xff0c;无法正常调用接口&#xff0c;今天咱们一起通过自研插件的方式来解决这个问题。自研插件需要…

启明云端WT32C3-S2模组,乐鑫ESP32-C3芯片开发应用,设备联网通信方案

随着科技的飞速发展&#xff0c;我们正步入一个全新的时代——物联网时代。在这个时代&#xff0c;每一个设备都不再是孤立的个体&#xff0c;而是通过无线网络相互连接、相互沟通的智能节点。 想象一下&#xff0c;当你走进家门&#xff0c;灯光自动亮起&#xff0c;空调调整…

认识NDK

什么是NDK&#xff08;Native Development Kit&#xff09; The Android NDK is a toolset that lets you implement parts of your app in native code, using languages such as C and C. &emdp; Android NDK 是一个工具集&#xff0c;可让您使用 C 和 C 等语言以原生代…

CANdela/Diva系列8--如何生成0x27服务解锁的DLL

本系列的上一篇文章中&#xff0c;我们介绍了如何在CDD文件中去根据客户需求来配置诊断服务&#xff0c;其实每个诊断服务的配置方式都是大同小异&#xff0c;但是0x27服务略有不同&#xff0c;为了能够让CDD文件根据ECU返回的种子去自动计算出密钥&#xff0c;需要添加一个解锁…

流域碳中和技术

随着全球气候变化的加剧&#xff0c;碳中和已成为实现可持续发展的重要目标之一。碳中和不仅仅是能源和工业领域的调整&#xff0c;它涉及整个生态系统的转型与再生。在这一过程中&#xff0c;流域的生态系统作为水、土、生物多样性等自然资源的集成体&#xff0c;扮演着至关重…

解密.baxia勒索病毒:.baxia勒索病毒的攻击手法及防护建议

导言 在当前网络安全形势日益严峻的背景下&#xff0c;勒索软件的威胁正不断升级&#xff0c;其中.baxia勒索病毒尤为突出。作为一种新型恶意软件&#xff0c;.baxia病毒通过加密用户的文件并要求支付赎金来获取解密密钥&#xff0c;对个人和企业的安全构成了严重威胁。随着其…

【LLM多模态】视频理解模型Cogvlm-video和MVBench评测基准

note Cogvlm-video模型通过视频抽帧&#xff08;24帧&#xff0c;每帧大小为224 x 224&#xff09;后经过ViT进行图像编码&#xff08;ViT中添加了2x2的卷积核更好的压缩视觉信息&#xff09;&#xff0c;使用adapter模块更好的将视觉特征和文本特征对齐&#xff0c;得到的图像…