Mybatis学习笔记4 用javassist动态实现DAO接口基于接口的CRUD

news2024/11/18 9:40:25

Mybatis学习笔记3 在Web中应用Mybatis_biubiubiu0706的博客-CSDN博客

上篇最后在DAO实现类中,代码固定,没有业务逻辑,这篇笔记中对该实现类进行封装,就是说,以后不用写DAO实现类了

我们不难发现,这个dao实现类中的⽅法代码很固定,基本上就是⼀⾏代码,通过SqlSession对象调⽤
insert、delete、update、select等⽅法,这个类中的⽅法没有任何业务逻辑,既然是这样, 这个类我们
能不能动态的⽣成 ,以后可以不写这个类吗?答案:可以。
Javassist是⼀个开源的分析、编辑和创建Java字节码的类库。是由东京⼯业⼤学的数学和计算机科学
系的 Shigeru Chiba (千叶 滋)所创建的。它已加⼊了开放源代码JBoss 应⽤服务器项⽬,通过使⽤
Javassist对字节码操作为JBoss实现动态"AOP"框架。
新建moudle测试javassist
引入依赖
<?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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>org.example</groupId>
    <artifactId>javassist</artifactId>
    <version>1.0-SNAPSHOT</version>

    <properties>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
    </properties>

    <dependencies>
        <!--javassist依赖-->
        <dependency>
            <groupId>org.javassist</groupId>
            <artifactId>javassist</artifactId>
            <version>3.29.1-GA</version>
        </dependency>
        <!--junit依赖-->
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.13.2</version>
            <scope>test</scope>
        </dependency>
    </dependencies>
</project>

下面是几个javassist基本使用的demo

import javassist.*;

import java.io.File;
import java.lang.reflect.Method;
import java.net.URL;
import java.net.URLClassLoader;

/**
 * @author hrui
 * @date 2023/9/17 18:05
 */
public class Test {


    @org.junit.Test
    public void test03() throws Exception {
        // 获取类池,这个类池就是用来生成class的
        ClassPool pool = ClassPool.getDefault();
        // 制造类(需要告诉javassist,类名是啥)
        CtClass ctClass = pool.makeClass("com.example.javassist.Test");//在内存中

        //制造方法
        String methodCode="public void insert(){System.out.println(\"Hello World\");}";
        CtMethod ctMethod=CtMethod.make(methodCode,ctClass);

        // 给类添加⽅法
        ctClass.addMethod(ctMethod);
        //写入磁盘
        ctClass.writeFile("src/main/java/");


        //还可以这样   下面是可以加载到的,加载不到原因可能是我当时没有放到测试目录
//      Class<?> aClass = Class.forName("com.example.javassist.Test");//这样加载不到类
        //用自定义类加载器
        ClassLoader customClassLoader = new URLClassLoader(new URL[]{new File("src/main/java/").toURI().toURL()});
        Class<?> aClass = customClassLoader.loadClass("com.example.javassist.Test");
        Object instance = aClass.newInstance();
        // 调用生成的方法
        Method method = aClass.getDeclaredMethod("insert");
        method.invoke(instance);

    }
    @org.junit.Test
    public void test02() throws Exception {
        // 获取类池,这个类池就是用来生成class的
        ClassPool pool = ClassPool.getDefault();
        // 制造类(需要告诉javassist,类名是啥)
        CtClass ctClass = pool.makeClass("com.example.javassist.Test");//在内存中
        //制造方法
        String methodCode="public void insert(){System.out.println(123);}";
        CtMethod ctMethod=CtMethod.make(methodCode,ctClass);

        // 给类添加⽅法
        ctClass.addMethod(ctMethod);

        // 用反射调⽤⽅法  这样是内存中直接调用
        Class<?> aClass = ctClass.toClass();
        Object o = aClass.newInstance();
        Method method = aClass.getDeclaredMethod("insert");
        method.invoke(o);
    }


    @org.junit.Test
    public void test01() throws Exception {
        // 获取类池,这个类池就是用来生成class的
        ClassPool pool = ClassPool.getDefault();
        // 制造类(需要告诉javassist,类名是啥)
        CtClass ctClass = pool.makeClass("com.example.javassist.Test");//在内存中
        // 制造⽅法
        // 1.返回值类型 2.⽅法名 3.形式参数列表 4.所属类
        CtMethod ctMethod = new CtMethod(CtClass.voidType, "insert", new
                CtClass[]{}, ctClass);
        // 设置⽅法的修饰符列表
        ctMethod.setModifiers(Modifier.PUBLIC);
        // 设置⽅法体
        ctMethod.setBody("{System.out.println(\"hello world\");}");

        // 给类添加⽅法
        ctClass.addMethod(ctMethod);

        // 用反射调⽤⽅法  这样是内存中直接调用
        Class<?> aClass = ctClass.toClass();
        Object o = aClass.newInstance();
        Method method = aClass.getDeclaredMethod("insert");
        method.invoke(o);
    }
}

因本机安装的是JDK8,没有报错,将高版本报错情况记录下来,以便以后用高版本JDK遇到类似问题,方便解决

解决办法

两个参数
--add-opens java.base/java.lang=ALL-UNNAMED
--add-opens java.base/sun.net.util=ALL-UNNAMED
如果第一次用可能不能设置参数,如下点击设置
高版本JDK遇到如上问题,记得仔细配置,可能第一次配置完还是有错,记得仔细检查,重新配置一次

设想一个问题  既然这样的话,能不能用javassist动态设计一个类,然后实现DAO接口呢

@org.junit.Test
    public void testGenerateImpl() throws Exception {
        //获取类池
        ClassPool pool = ClassPool.getDefault();
        //制造类
        CtClass ctClassImpl = pool.makeClass("com.example.javassist.TestImpl");//在内存中
        //制造接口
        CtClass ctClassInterface = pool.makeInterface("com.example.javassist.Test1");//在内存中
        //类去实现接口  又可以说是类去添加接口
        ctClassImpl.addInterface(ctClassInterface);//TestImpl implements Test1
        //去实现接口中的方法 这个相对比较复杂,这里我们假设接口里就一个方法

        //先制造方法
        CtMethod ctMethod=CtMethod.make("public void delete(){System.out.println(\"删除成功!\");}",ctClassImpl);
        //将方法添加到类中
        ctClassImpl.addMethod(ctMethod);

        //在内存中生成类(这一步JVM已经将类加载到内存中)
        Class<?> aClass = ctClassImpl.toClass();
        Test1 test=(Test1)aClass.newInstance();
        test.delete();

    }

上面方法示例可见,我们是有办法动态生成接口实现类的

上面方法演示过于简单

比如说

下面使用javassist动态生成实现类并实现接口中所有方法

 @org.junit.Test
    public void testGenerateImpl2(){
        //获取类池
        ClassPool pool = ClassPool.getDefault();
        //制造类
        CtClass ctClassImpl = pool.makeClass("com.example.javassist.TestImpl2");//在内存中
        //制造接口
        CtClass ctClassInterface = pool.makeInterface("com.example.javassist.Test1");//在内存中
        //类去实现接口  又可以说是类去添加接口
        ctClassImpl.addInterface(ctClassInterface);//TestImpl2 implements Test1
        //实现接口中所有方法
        //先获取接口中的所有方法
        Method[] declaredMethods = Test1.class.getDeclaredMethods();
        //System.out.println(declaredMethods.length);
        //System.out.println(Arrays.toString(declaredMethods));
        Arrays.stream(declaredMethods).forEach(method -> {
            //method是接口中的抽象方法,我们需要把抽象方法实现了
            try {
                //methodCode public void delete(){}
                //methodCode public int update(String actno,double balance)
                StringBuilder methodCode=new StringBuilder();
                methodCode.append("public ");
                methodCode.append(method.getReturnType().getName()+" ");//返回值类型
                methodCode.append(method.getName());//追加方法名
                methodCode.append("(");

                Class<?>[] parameterTypes = method.getParameterTypes();//参数有可能1个也可能有多个
                for(int i=0;i<parameterTypes.length;i++){
                    Class<?> parameterType = parameterTypes[i];//第一个参数的类型
                    methodCode.append(parameterType.getName());//参数类型
                    methodCode.append(" ");
                    methodCode.append("arg"+i);//参数名

                    if(i!=(parameterTypes.length-1)) {
                        methodCode.append(",");//如果不是最后一个参数
                    }
                }

                methodCode.append("){System.out.println(\"Hello World\");}");
                System.out.println(methodCode);

                //CtMethod ctMethod=CtMethod.make(methodCode.toString(),ctClassImpl);
                //将方法添加到类中
                //ctClassImpl.addMethod(ctMethod);
            } catch (Exception e) {
                e.printStackTrace();
            }
        });
    }

程序写到这部  先执行一下

public int update(java.lang.String arg0,double arg1){System.out.println("Hello World");}
public void delete(){System.out.println("Hello World");}
public int insert(java.lang.String arg0){System.out.println("Hello World");}
public java.lang.String selectByActno(java.lang.String arg0){System.out.println("Hello World");}

接下来就是关于返回值的问题

  @org.junit.Test
    public void testGenerateImpl2() throws IllegalAccessException, InstantiationException, CannotCompileException {
        //获取类池
        ClassPool pool = ClassPool.getDefault();
        //制造类
        CtClass ctClassImpl = pool.makeClass("com.example.javassist.TestImpl2");//在内存中
        //制造接口
        CtClass ctClassInterface = pool.makeInterface("com.example.javassist.Test1");//在内存中
        //类去实现接口  又可以说是类去添加接口
        ctClassImpl.addInterface(ctClassInterface);//TestImpl2 implements Test1
        //实现接口中所有方法
        //先获取接口中的所有方法
        Method[] declaredMethods = Test1.class.getDeclaredMethods();
        //System.out.println(declaredMethods.length);
        //System.out.println(Arrays.toString(declaredMethods));
        Arrays.stream(declaredMethods).forEach(method -> {
            //method是接口中的抽象方法,我们需要把抽象方法实现了
            try {
                //methodCode public void delete(){}
                //methodCode public int update(String actno,double balance)
                StringBuilder methodCode=new StringBuilder();
                methodCode.append("public ");
                methodCode.append(method.getReturnType().getName()+" ");//返回值类型
                methodCode.append(method.getName());//追加方法名
                methodCode.append("(");

                Class<?>[] parameterTypes = method.getParameterTypes();//参数有可能1个也可能有多个
                for(int i=0;i<parameterTypes.length;i++){
                    Class<?> parameterType = parameterTypes[i];//第一个参数的类型
                    methodCode.append(parameterType.getName());//参数类型
                    methodCode.append(" ");
                    methodCode.append("arg"+i);//参数名

                    if(i!=(parameterTypes.length-1)) {
                        methodCode.append(",");//如果不是最后一个参数
                    }
                }

                methodCode.append("){System.out.println(\"Hello World\");");
                //动态添加renturn语句
                String simpleName = method.getReturnType().getSimpleName();//比如  int void  String
                if("void".equals(simpleName)){
                    //如果是void啥都不写
                }else if("int".equals(simpleName)){
                    methodCode.append("return 1;");
                }else if("String".equals(simpleName)){
                    methodCode.append("return \"1\";");
                }
                //System.out.println(simpleName);
                methodCode.append("}");
                System.out.println(methodCode);

                CtMethod ctMethod=CtMethod.make(methodCode.toString(),ctClassImpl);
                //将方法添加到类中
                ctClassImpl.addMethod(ctMethod);
            } catch (Exception e) {
                e.printStackTrace();
            }
        });
        //在内存中生成类(这一步JVM已经将类加载到内存中)
        Class<?> aClass = ctClassImpl.toClass();
        Test1 test=(Test1)aClass.newInstance();
        test.delete();
        int count = test.insert("sad");
        test.update("sad",1.1);
        

    }

上面方式虽然实现的比较low,主要为说明Mybatis底层javassist的使用

下面对

Mybatis学习笔记3 在Web中应用Mybatis_biubiubiu0706的博客-CSDN博客

中的项目进行修改,就是说AccountDaoImpl不写了

mybatis-03中只引入了mybatis依赖,但是可以直接使用ClassPool,而这个类是javassist的,原因是mybatis对javassist进行了封装

下面这段代码就是大概性的介绍Mybatis内部的一种封装    

package com.example.utils;

import org.apache.ibatis.javassist.ClassPool;
import org.apache.ibatis.javassist.CtClass;
import org.apache.ibatis.javassist.CtMethod;
import org.apache.ibatis.mapping.SqlCommandType;
import org.apache.ibatis.session.SqlSession;

import java.lang.reflect.Method;
import java.util.Arrays;

/**
 * 工具类:可以动态的生成DAO的实现类。(或者说可以动态生成DAO的代理类)
 * 注意注意注意注意注意!!!!!!:
 *      凡是使用GenerateDaoProxy的,SQLMapper.xml映射文件中namespace必须是dao接口的全名,id必须是dao接口中的方法名。
 */
public class GenerateDaoProxy { // GenerateDaoProxy是mybatis框架的开发者写的。

    /**
     * 生成dao接口实现类,并且将实现类的对象创建出来并返回。
     * @param daoInterface dao接口
     * @return dao接口实现类的实例化对象。
     */
    public static Object generate(SqlSession sqlSession, Class daoInterface){
        // 类池
        ClassPool pool = ClassPool.getDefault();
        // 制造类(com.powernode.bank.dao.AccountDao --> com.powernode.bank.dao.AccountDaoProxy)
        CtClass ctClass = pool.makeClass(daoInterface.getName() + "Proxy"); // 实际本质上就是在内存中动态生成一个代理类。
        // 制造接口
        CtClass ctInterface = pool.makeInterface(daoInterface.getName());
        // 实现接口
        ctClass.addInterface(ctInterface);
        // 实现接口中所有的方法
        Method[] methods = daoInterface.getDeclaredMethods();
        Arrays.stream(methods).forEach(method -> {
            // method是接口中的抽象方法
            // 将method这个抽象方法进行实现
            try {
                // Account selectByActno(String actno);
                // public Account selectByActno(String actno){ 代码; }
                StringBuilder methodCode = new StringBuilder();
                methodCode.append("public ");
                methodCode.append(method.getReturnType().getName());
                methodCode.append(" ");
                methodCode.append(method.getName());
                methodCode.append("(");
                // 需要方法的形式参数列表
                Class<?>[] parameterTypes = method.getParameterTypes();
                for (int i = 0; i < parameterTypes.length; i++) {
                    Class<?> parameterType = parameterTypes[i];
                    methodCode.append(parameterType.getName());
                    methodCode.append(" ");
                    methodCode.append("arg" + i);
                    if(i != parameterTypes.length - 1){
                        methodCode.append(",");
                    }
                }
                methodCode.append(")");
                methodCode.append("{");
                // 需要方法体当中的代码
                methodCode.append("org.apache.ibatis.session.SqlSession sqlSession = com.example.utils.SqlSessionUtil.openSession();");
                // 需要知道是什么类型的sql语句
                // sql语句的id是框架使用者提供的,具有多变性。对于我框架的开发人员来说。我不知道。
                // 既然我框架开发者不知道sqlId,怎么办呢?mybatis框架的开发者于是就出台了一个规定:凡是使用GenerateDaoProxy机制的。
                // sqlId都不能随便写。namespace必须是dao接口的全限定名称。id必须是dao接口中方法名。
                String sqlId = daoInterface.getName() + "." + method.getName();
                SqlCommandType sqlCommandType = sqlSession.getConfiguration().getMappedStatement(sqlId).getSqlCommandType();
                if (sqlCommandType == SqlCommandType.INSERT) {

                }
                if (sqlCommandType == SqlCommandType.DELETE) {

                }
                if (sqlCommandType == SqlCommandType.UPDATE) {
                    methodCode.append("return sqlSession.update(\""+sqlId+"\", arg0);");
                }
                if (sqlCommandType == SqlCommandType.SELECT) {
                    String returnType = method.getReturnType().getName();
                    methodCode.append("return ("+returnType+")sqlSession.selectOne(\""+sqlId+"\", arg0);");
                }

                methodCode.append("}");
                CtMethod ctMethod = CtMethod.make(methodCode.toString(), ctClass);
                ctClass.addMethod(ctMethod);
            } catch (Exception e) {
                e.printStackTrace();
            }

        });

        // 创建对象
        Object obj = null;
        try {
            Class<?> clazz = ctClass.toClass();
            obj = clazz.newInstance();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return obj;
    }

}

那么业务层就可以这么写

上面这步,出了点错,写完时候还没查出来

其实这个封装Mybatis已经做好了

这样,面向接口的CRUD就产生了,以后无需再写持久层的实现类

完整的SqlSessionUtil类

package com.example.utils;

import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;

import java.io.IOException;

/**
 * @author hrui
 * @date 2023/9/8 14:55
 */
public class SqlSessionUtil {

    //工具类的构造方法一般都是私有化
    //方法都是静态的
    //为了防止new对象,构造方法私有化
    private SqlSessionUtil(){

    }
    private static SqlSessionFactory sqlSessionFactory;

    //类加载时候执行
    //SqlSessionUtil工具类在被加载的时候,解析mybatis-config1.xml.创建sqlSessionFactory对象
    static{
        try {
            //SqlSessionFactoryBuilder sqlSessionFactoryBuilder=new SqlSessionFactoryBuilder();
            //下面这么写的原因是SqlSessionFactoryBuilder就是为了创建sqlSessionFactory而来的,使用完后,就不需要,都不需要创建个局部变量
            //一个sqlSessionFactory对应一个数据库
            sqlSessionFactory= new SqlSessionFactoryBuilder().build(Resources.getResourceAsStream("mybatis-config1.xml"));
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    //全局的 服务器级别的,一个服务器当中定义一个即可
    private static ThreadLocal<SqlSession> local=new ThreadLocal<>();

    //获取会话对象 返回会话对象
    public static SqlSession openSession(){
        SqlSession sqlSession=local.get();
        if(sqlSession==null){
            sqlSession = sqlSessionFactory.openSession();
            local.set(sqlSession);
        }

        return sqlSession;
    }

    //提供一个关闭的方法
    public static void close(SqlSession sqlSession){
        if(sqlSession!=null){
            //因为核心配置文件中配置POOLED  这里关闭是交还给连接池
            sqlSession.close();
            //注意移除SqlSession对象和当前线程的绑定关系
            //因为Tomcat服务器支持线程池 比如说t1线程用完了,close交还给连接池了,这个sqlSession属于不可用的状态,你没有remove出去 如果t2线程拿到了,那么这个sqlSession不可用
            local.remove();
        }
    }
}

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

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

相关文章

【面试经典150 | 双指针】三数之和

文章目录 写在前面Tag题目来源题目解读解题思路方法一&#xff1a;暴力枚举方法二&#xff1a;双指针 写在最后 写在前面 本专栏专注于分析与讲解【面试经典150】算法&#xff0c;两到三天更新一篇文章&#xff0c;欢迎催更…… 专栏内容以分析题目为主&#xff0c;并附带一些对…

AIGC|从革新内容创作到社会共识建立,迎接全新技术维度

在人工智能的巨浪之下&#xff0c;我们身临一场前所未有的文化演变&#xff0c;一股革命性的力量正在重新定义我们的创造性边界。这股力量不是人类的智慧&#xff0c;而是人工智能生成内容&#xff08;AIGC&#xff09;技术&#xff0c;它正以前所未有的速度和广度改变着我们的…

上PICO,沉浸式观看亚运直播,参与跨国界游戏竞技

备受瞩目的杭州第19届亚运会&#xff0c;将于9月23日正式开幕。据悉&#xff0c;这也是有史以来项目最多的一届亚运会&#xff0c;除部分传统奥运项目外&#xff0c;还包含武术、藤球、板球、克柔术、柔术等亚洲特色项目&#xff0c;以及霹雳舞、电子竞技等深受年轻人喜爱的新兴…

数字赋能 融链发展 ——2023工博会数字化赋能专精特新“小巨人”企业高质量发展论坛顺利举行

编者按&#xff1a;2023年政府工作报告提出“加快传统产业和中小企业数字化转型”要求&#xff0c;按照《“十四五”促进中小企业发展规划》《关于开展中小企业数字化转型城市试点工作的通知》等文件的部署&#xff0c;通过开展城市试点&#xff0c;支持地方政府综合施策&#…

AI视频剪辑:批量智剪技巧大揭秘

对于许多内容创作者来说&#xff0c;视频剪辑是一项必不可少的技能。然而&#xff0c;传统的视频剪辑方法需要耗费大量的时间和精力。如今&#xff0c;有一种全新的剪辑方式正在改变这一现状&#xff0c;那就是批量AI智剪。这种智能化的剪辑方式能够让你在短时间内轻松剪辑大量…

pg数据表同步到hive表数据压缩总结

1、背景 pg库存放了大量的历史数据&#xff0c;pg的存储方式比较耗磁盘空间&#xff0c;pg的备份方式&#xff0c;通过pgdump导出后&#xff0c;进行gzip压缩&#xff0c;压缩比大概1/10&#xff0c;随着数据的积累磁盘空间告警。为了解决pg的压力&#xff0c;尝试采用hive数据…

如何在没有第三方.NET库源码的情况,调试第三库代码?

大家好&#xff0c;我是沙漠尽头的狼。 本方首发于Dotnet9&#xff0c;介绍使用dnSpy调试第三方.NET库源码&#xff0c;行文目录&#xff1a; 安装dnSpy编写示例程序调试示例程序调试.NET库原生方法总结 1. 安装dnSpy dnSpy是一款功能强大的.NET程序反编译工具&#xff0c;…

Qt创建线程(使用moveToThread方法创建子线程)

1.moveTothread方法: &#xff08;1&#xff09;要使用moveToThread方法必须继承与QObject类 &#xff08;2&#xff09;创建任务对象时不能指定父对象 例子&#xff1a; MyWork* work new MyWork(this); // error MyWork* work new MyWork; // ok &#xff08;3&#…

常用的深度学习自动标注软件

0. 简介 自动标注软件是一个非常节省人力资源的操作&#xff0c;而随着深度学习的发展&#xff0c;这些自动化标定软件也越来越多。本文章将会着重介绍其中比较经典的自动标注软件 1. AutoLabelImg AutoLabelImg 除了labelimg的初始功能外&#xff0c;额外包含十多种辅助标注…

vue3——pixi初学,编写一个简单的小游戏,复制粘贴可用学习

pixi官网 小游戏效果 两个文件夹 一个index.html 一个data.js //data.js import { reactive } from "vue"; import { Sprite, utils, Rectangle, Application, Text, Graphics } from "pixi.js";//首先 先创建一个舞台 export const app new Applicat…

基于开源模型的实时人脸识别系统(九):软件说明

续 人脸识别_CodingInCV的博客-CSDN博客 文章目录 前言简介模型选择的要求总体流程图人脸检测人脸跟踪人脸质量人脸关键点人脸识别代码结构人脸识别的逻辑高阶设置 前言 前面的文章我们介绍了整个系统里的关键步骤&#xff0c;基于这些步骤我们就可以搭建出属于自己的人脸识别…

Java 并发编程面试题——Lock 与 AbstractQueuedSynchronizer (AQS)

目录 1.Lock1.1.Lock 是什么&#xff1f;1.2.Lock 接口提供了哪些 synchronized 关键字不具备的主要特性&#xff1f;1.3.✨Lock 与 synchronized 有什么区别&#xff1f;1.4.Lock 接口中有哪些方法&#xff1f;1.5.哪些类实现了 Lock 接口&#xff1f; 2.AbstractQueuedSynchr…

使用YOLOv5-C3模块识别图像天气 - P8

&#x1f368; 本文为&#x1f517;365天深度学习训练营 中的学习记录博客&#x1f356; 原作者&#xff1a;K同学啊 | 接辅导、项目定制&#x1f680; 文章来源&#xff1a;K同学的学习圈子 目录 环境步骤环境设置引用包全局设备对象 数据准备数据集信息收集图像预处理读取数据…

【Vue】模块基本语法

&#x1f389;&#x1f389;欢迎来到我的CSDN主页&#xff01;&#x1f389;&#x1f389; &#x1f3c5;我是Java方文山&#xff0c;一个在CSDN分享笔记的博主。&#x1f4da;&#x1f4da; &#x1f31f;在这里&#xff0c;我要推荐给大家我的专栏《Vue快速入门》。&#x1f…

什么是WhatsApp群发,WhatsApp协议,WhatsApp云控

那么WhatsApp群控云控可以做什么呢&#xff1f; 1、获客 自动化引流&#xff0c;强大的可控性&#xff0c;产品快速拓客 2、导流 一键式傻瓜化自动加好友&#xff0c;群发&#xff0c;朋友圈营销 3、群控 一键式拉群好友&#xff0c;建群&#xff0c;进群 …

精通git,没用过git cherry-pick?

前言 git cherry-pick是git中比较有用的命令&#xff0c;cherry是樱桃&#xff0c;cherry-pick就是挑樱桃&#xff0c;从一堆樱桃中挑选自己喜欢的樱桃&#xff0c;在git中就是多次commit中挑选一个或者几个commit出来&#xff0c;也可以理解为把特定的commit复制到一个新分支…

大模型应用发展的方向|代理 Agent 的兴起及其未来(上)

“ 介绍了人工智能代理的历史渊源与演进&#xff0c;接着探讨了大型语言模型&#xff08;LLMs&#xff09;的发展&#xff0c;以及它们在知识获取、指令理解、泛化、规划和推理等方面所展现出的强大潜力。在此基础上&#xff0c;提出了一个以大型语言模型为核心的智能代理概念框…

[论文笔记]P-tuning v2

引言 今天带来第五篇大模型微调论文笔记P-tuning v2: Prompt Tuning Can Be Comparable to Fine-tuning Across Scales and Tasks。 作者首先指出了prompt tuning的一些不足,比如在中等规模的模型上NLU任务表现不好,还不能处理困难的序列标记任务,缺乏统一应用的能力。 然…

【学习草稿】背包问题

一、01背包问题 图解详细解析 &#xff08;转载&#xff09; https://blog.csdn.net/qq_37767455/article/details/99086678 &#xff1a;Vi表示第 i 个物品的价值&#xff0c;Wi表示第 i 个物品的体积&#xff0c;定义V(i,j)&#xff1a;当前背包容量 j&#xff0c;前 i 个物…

Vue中的自定义指令详解

文章目录 自定义指令自定义指令-指令的值&#xff08;给自定义指令传参数&#xff09; 自定义指令 自定义指令&#xff1a;自己定义的指令&#xff0c;可以封装一些dom 操作&#xff0c;扩展额外功能&#xff08;自动聚焦&#xff0c;自动加载&#xff0c;懒加载等复杂的指令封…