【学习笔记】手写一个简单的 Spring IOC

news2024/10/3 23:22:34

目录

一、什么是 Spring IOC?

二、IOC 的作用

1. IOC 怎么知道要创建哪些对象呢?

2. 创建出来的对象放在哪儿?

3. 创建出来的对象如果有属性,如何给属性赋值?

三、实现步骤

1. 创建自定义注解

2. 创建 IOC 容器

1. 创建 Bean 定义类

2. 创建 IOC 容器

3. 扫描包

4. 使用自定义注解

5. 在 Servlet 中初始化 IOC 对象

6. 测试

3. 实例化 Bean 

测试

4. 设置属性

1. 创建一个Controller

2. 属性赋值

3. 测试

四、使用 Bean

1. 创建获取 Bean 的方法

2. 使用 Bean 

3. 测试

五、优化 IOC 容器


一、什么是 Spring IOC?

Spring 的核心之一是 IOC,全称 Inversion Of Control ,控制反转,用来统一管理 Bean

二、IOC 的作用

IOC 的作用就是帮程序员创建 Bean 对象

1. IOC 怎么知道要创建哪些对象呢?

xml 配置文件中指定要创建哪些对象,或者使用注解的方式

如果使用注解的方式,需要自定义注解,自定义注解说白了就是告诉 JVM 这个 Bean 不同于其他普通的 Bean ,它有其他的用途,标记一下。

@Component、@Controller、@Service 可以用于创建 Bean 的注解

2. 创建出来的对象放在哪儿?

先记录需要被创建的 Bean 对象的信息,存到 HashMap 中,然后根据 HashMap 中存储的Bean 的信息创建对象,存到IOC容器中

3. 创建出来的对象如果有属性,如何给属性赋值?

使用注解 @Autowired

三、实现步骤

1. 创建自定义注解

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Controller {
    String value() default "";
}
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Autowired {
    String value() default "";
}

2. 创建 IOC 容器

扫描包,判断类上是否有注解,如果有,存储需要被创建的对象的id,也就是全类名。

定义一个类用于存储这些信息,这个类就是 BeanDefinition  叫做 Bean 定义类,Bean 定义类创建对象后称为 Bean 定义对象,这个对象存储了 Bean 的信息

然后把 Bean 定义对象存放到 HashMap 中,这个 Map 叫做 Bean 定义Map

1. 创建 Bean 定义类

package com.shao.IOC;

public class BeanDefinition {

    private String id;
    private String className;


    public BeanDefinition() {
    }

    public BeanDefinition(String id, String className) {
        this.id = id;
        this.className = className;
    }

    /**
     * 获取
     *
     * @return id
     */
    public String getId() {
        return id;
    }

    /**
     * 设置
     *
     * @param id
     */
    public void setId(String id) {
        this.id = id;
    }

    /**
     * 获取
     *
     * @return className
     */
    public String getClassName() {
        return className;
    }

    /**
     * 设置
     *
     * @param className
     */
    public void setClassName(String className) {
        this.className = className;
    }

    public String toString() {
        return "BeanDefinition{id = " + id + ", className = " + className + "}";
    }
}

2. 创建 IOC 容器

3. 扫描包

扫描包需要先知道路径是什么,从哪开始

这里扫描的是编译后的 class 文件,并不是类文件,因为程序运行后使用的是编译后的文件

那如何从这里开始呢?

使用类加载器获取路径

package com.shao.IOC;

import com.shao.Annotation.Controller;
import com.shao.Annotation.Service;

import java.io.File;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.util.HashMap;

/**
 * ApplicationContext 类. 当作 IOC 容器
 *
 * @author shao.
 */
public class ApplicationContext {
    /**
     * Bean 定义Map   存储Bean 定义对象
     */
    private HashMap<String, BeanDefinition> beanDefinitionMap = new HashMap<>();

    /**
     * Bean 实例Map 存储Bean 实例对象
     */
    private HashMap<String, Object> BeanMap = new HashMap<>();


    public ApplicationContext(String basePackage) throws UnsupportedEncodingException {
        scanPackage(basePackage);
    }

    /**
     * 1. 扫描包,扫描需要被创建的类,创建 Bean 定义对象,并放入 Bean 定义Map中
     */
    public void scanPackage(String basePackage) throws UnsupportedEncodingException {
//        String basePackage = "com.shao";

        // 获取类加载器
        ClassLoader classLoader = this.getClass().getClassLoader();
        // 获取包路径
        String path = classLoader.getResource(basePackage.replace(".", "/")).getPath();
        // 路径解码,如果路径有空格或者中文,会出现 16 进制的字符
        String packagePath = URLDecoder.decode(path, "UTF-8");

        // 创建文件对象
        File fileDir = new File(packagePath);

        // 获取包下的文件列表
        File[] files = fileDir.listFiles();

        for (File file : files) {
            if (file.isFile()) {
                // 判断是否是class文件
                if (file.getName().endsWith(".class")) {

                    // 包路径 + 文件名 构成全类名
                    String className = basePackage + "." + file.getName().replace(".class", "");

                    try {
                        // 动态加载了名为 className 的类,获取该类的 Class 对象
                        Class<?> aClass = Class.forName(className);

                        // 判断该类是否有 @Service 或 @Controller 注解
                        if (aClass.isAnnotationPresent(Service.class) ||
                                aClass.isAnnotationPresent(Controller.class)) {

                            System.out.println("需要创建:" + className);

                            // 将该类的类名首字母小写作为 id
                            String id = aClass.getSimpleName().substring(0, 1).toLowerCase() + aClass.getSimpleName().substring(1);
                            System.out.println("id:" + id);

                            // 创建 Bean 定义对象,并放入 Bean 定义Map中
                            beanDefinitionMap.put(id, new BeanDefinition(id, className));
                        }

                    } catch (ClassNotFoundException e) {
                        e.printStackTrace();
                    }

                }

            } else if (file.isDirectory()) {
                // 递归扫描子文件夹
                String subPackagePath = basePackage + "." + file.getName();
                scanPackage(subPackagePath);
            }
        }

    }
}

4. 使用自定义注解

5. 在 Servlet 中初始化 IOC 对象

这里为了方便测试,所以先在 Servlet 中初始化 IOC 

6. 测试

3. 实例化 Bean 

遍历 Bean 定义Map ,取出 Bean 定义对象,根据对象的信息使用反射技术创建 Bean 对象,这个时候这个 Bean 也叫裸Bean,然后存入 Bean Map 中。这一步在 Bean 的生命周期中属于 Bean 的实例化

package com.shao.IOC;

import com.shao.Annotation.Controller;
import com.shao.Annotation.Service;

import java.io.File;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Set;

/**
 * ApplicationContext 类. 当作 IOC 容器
 *
 * @author shao.
 */
public class ApplicationContext {
    /**
     * Bean 定义Map   存储Bean 定义对象
     */
    private HashMap<String, BeanDefinition> beanDefinitionMap = new HashMap<>();

    /**
     * Bean 实例Map 存储Bean 实例对象
     */
    private HashMap<String, Object> BeanMap = new HashMap<>();

    public ApplicationContext(String basePackage) throws UnsupportedEncodingException {
        scanPackage(basePackage);
        createBean();
    }

    /**
     * 1. 扫描包,扫描需要被创建的类,创建 Bean 定义对象,并放入 Bean 定义Map中
     */
    public void scanPackage(String basePackage) throws UnsupportedEncodingException {

        // 获取类加载器
        ClassLoader classLoader = this.getClass().getClassLoader();
        // 获取包路径
        String path = classLoader.getResource(basePackage.replace(".", "/")).getPath();
        // 路径解码,如果路径有空格或者中文,会出现 16 进制的字符
        String packagePath = URLDecoder.decode(path, "UTF-8");

        // 创建文件对象
        File fileDir = new File(packagePath);

        // 获取包下的文件列表
        File[] files = fileDir.listFiles();

        for (File file : files) {
            if (file.isFile()) {
                // 判断是否是class文件
                if (file.getName().endsWith(".class")) {

                    // 包路径 + 文件名 构成全类名
                    String className = basePackage + "." + file.getName().replace(".class", "");

                    try {
                        // 动态加载了名为 className 的类,获取该类的 Class 对象
                        Class<?> aClass = Class.forName(className);

                        // 判断该类是否有 @Service 或 @Controller 注解
                        if (aClass.isAnnotationPresent(Service.class) ||
                                aClass.isAnnotationPresent(Controller.class)) {

                            System.out.println("需要创建:" + className);

                            // 将该类的类名首字母小写作为 id
                            String id = aClass.getSimpleName().substring(0, 1).toLowerCase() + aClass.getSimpleName().substring(1);
                            System.out.println("id:" + id);

                            // 创建 Bean 定义对象,并放入 Bean 定义Map中
                            beanDefinitionMap.put(id, new BeanDefinition(id, className));
                        }

                    } catch (ClassNotFoundException e) {
                        e.printStackTrace();
                    }

                }

            } else if (file.isDirectory()) {
                // 递归扫描子文件夹
                String subPackagePath = basePackage + "." + file.getName();
                scanPackage(subPackagePath);
            }
        }

    }

    /**
     * 2. 创建 Bean 实例,并放入 Bean Map 中
     */
    public void createBean() {

        // 获取 Bean 定义 Map 的 所有 id
        Set<String> ids = beanDefinitionMap.keySet();
        Iterator<String> it = ids.iterator();
        while (it.hasNext()) {
            String id = it.next();

            // 获取 Bean 定义对象
            BeanDefinition beanDefinition = beanDefinitionMap.get(id);

            // 获取 Bean 定义对象中的 Bean 的全类名
            String className = beanDefinition.getClassName();

            try {
                // 动态加载类,并创建 Bean 实例,这时候的 Bean 是裸Bean
                Object bean = Class.forName(className).newInstance();

                // 将 Bean 实例放到 Bean Map 中
                BeanMap.put(id, bean);

            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        System.out.println("实例化 Bean 完成");
        System.out.println(BeanMap);
    }
}

测试

4. 设置属性

给Bean 对象做初始化,也就是依赖注入。Bean 的生命周期中属于 Bean 的初始化

这里是使用类作为属性进行依赖注入,不是接口,后续需要优化

1. 创建一个Controller

使用 @Autowired 注解,这里为了方便测试,重写一下 toString 方法

2. 属性赋值
package com.shao.IOC;

import com.shao.Annotation.Autowired;
import com.shao.Annotation.Controller;
import com.shao.Annotation.Service;

import java.io.File;
import java.io.UnsupportedEncodingException;
import java.lang.reflect.Field;
import java.net.URLDecoder;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Set;

/**
 * ApplicationContext 类. 当作 IOC 容器
 *
 * @author shao.
 */
public class ApplicationContext {
    /**
     * Bean 定义Map   存储Bean 定义对象
     */
    private HashMap<String, BeanDefinition> beanDefinitionMap = new HashMap<>();

    /**
     * Bean 实例Map 存储Bean 实例对象
     */
    private HashMap<String, Object> BeanMap = new HashMap<>();

    public ApplicationContext(String basePackage) throws UnsupportedEncodingException {
        scanPackage(basePackage);
        createBean();
        injectBean();
    }

    /**
     * 1. 扫描包,扫描需要被创建的类,创建 Bean 定义对象,并放入 Bean 定义Map中
     */
    public void scanPackage(String basePackage) throws UnsupportedEncodingException {

        // 获取类加载器
        ClassLoader classLoader = this.getClass().getClassLoader();
        // 获取包路径
        String path = classLoader.getResource(basePackage.replace(".", "/")).getPath();
        // 路径解码,如果路径有空格或者中文,会出现 16 进制的字符
        String packagePath = URLDecoder.decode(path, "UTF-8");

        // 创建文件对象
        File fileDir = new File(packagePath);

        // 获取包下的文件列表
        File[] files = fileDir.listFiles();

        for (File file : files) {
            if (file.isFile()) {
                // 判断是否是class文件
                if (file.getName().endsWith(".class")) {

                    // 包路径 + 文件名 构成全类名
                    String className = basePackage + "." + file.getName().replace(".class", "");

                    try {
                        // 动态加载了名为 className 的类,获取该类的 Class 对象
                        Class<?> aClass = Class.forName(className);

                        // 判断该类是否有 @Service 或 @Controller 注解
                        if (aClass.isAnnotationPresent(Service.class) ||
                                aClass.isAnnotationPresent(Controller.class)) {

                            System.out.println("需要创建:" + className);

                            // 将该类的类名首字母小写作为 id
                            String id = aClass.getSimpleName().substring(0, 1).toLowerCase() + aClass.getSimpleName().substring(1);
                            System.out.println("id:" + id);

                            // 创建 Bean 定义对象,并放入 Bean 定义Map中
                            beanDefinitionMap.put(id, new BeanDefinition(id, className));
                        }

                    } catch (ClassNotFoundException e) {
                        e.printStackTrace();
                    }

                }

            } else if (file.isDirectory()) {
                // 递归扫描子文件夹
                String subPackagePath = basePackage + "." + file.getName();
                scanPackage(subPackagePath);
            }
        }

    }

    /**
     * 2. 创建 Bean 实例,并放入 Bean Map 中
     */
    public void createBean() {

        // 获取 Bean 定义 Map 的 所有 id
        Set<String> ids = beanDefinitionMap.keySet();
        Iterator<String> it = ids.iterator();
        while (it.hasNext()) {
            String id = it.next();

            // 获取 Bean 定义对象
            BeanDefinition beanDefinition = beanDefinitionMap.get(id);

            // 获取 Bean 定义对象中的 Bean 的全类名
            String className = beanDefinition.getClassName();

            try {
                // 动态加载类,并创建 Bean 实例,这时候的 Bean 是裸Bean
                Object bean = Class.forName(className).newInstance();

                // 将 Bean 实例放到 Bean Map 中
                BeanMap.put(id, bean);

            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        System.out.println("实例化 Bean 完成");
        System.out.println(BeanMap);
    }

    /**
     * 3. 给 Bean 设置属性,Autowired 赋值
     */
    public void injectBean() {

        // 获取 Bean Map 的 所有 id
        Iterator<String> it = BeanMap.keySet().iterator();
        while (it.hasNext()) {

            String id = it.next();

            // 获取 Bean 实例
            Object bean = BeanMap.get(id);

            // 获取 Bean 的 Class 对象
            Class<?> aClass = bean.getClass();

            // 获取这个类的所有属性,不包括父类的属性
            Field[] declaredFields = aClass.getDeclaredFields();

            for (Field field : declaredFields) {

                // 判断该属性是否有 @Autowired 注解
                if (field.isAnnotationPresent(Autowired.class)) {
                    // 设置属性可访问
                    field.setAccessible(true);

                    try {
                        // 给属性赋值
                        field.set(bean, BeanMap.get(field.getName()));
                    } catch (IllegalAccessException e) {
                        e.printStackTrace();
                    }
                }
            }

        }
        System.out.println("属性赋值完成");
        System.out.println(BeanMap);
    }
}

3. 测试

四、使用 Bean

1. 创建获取 Bean 的方法

要使用 Bean ,需要从IOC 容器中获取 Bean,所以还需要在 IOC 容器中提供获取 Bean 的接口

package com.shao.IOC;

import com.shao.Annotation.Autowired;
import com.shao.Annotation.Controller;
import com.shao.Annotation.Service;

import java.io.File;
import java.io.UnsupportedEncodingException;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.net.URLDecoder;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Set;

/**
 * ApplicationContext 类. 当作 IOC 容器
 *
 * @author shao.
 */
public class ApplicationContext {
    /**
     * Bean 定义Map   存储Bean 定义对象
     */
    private HashMap<String, BeanDefinition> beanDefinitionMap = new HashMap<>();

    /**
     * Bean 实例Map 存储Bean 实例对象
     */
    private HashMap<String, Object> BeanMap = new HashMap<>();

    public ApplicationContext(String basePackage) throws UnsupportedEncodingException {
        scanPackage(basePackage);
        createBean();
        injectBean();
    }

    /**
     * 1. 扫描包,扫描需要被创建的类,创建 Bean 定义对象,并放入 Bean 定义Map中
     */
    public void scanPackage(String basePackage) throws UnsupportedEncodingException {

        // 获取类加载器
        ClassLoader classLoader = this.getClass().getClassLoader();
        // 获取包路径
        String path = classLoader.getResource(basePackage.replace(".", "/")).getPath();
        // 路径解码,如果路径有空格或者中文,会出现 16 进制的字符
        String packagePath = URLDecoder.decode(path, "UTF-8");

        // 创建文件对象
        File fileDir = new File(packagePath);

        // 获取包下的文件列表
        File[] files = fileDir.listFiles();

        for (File file : files) {
            if (file.isFile()) {
                // 判断是否是class文件
                if (file.getName().endsWith(".class")) {

                    // 包路径 + 文件名 构成全类名
                    String className = basePackage + "." + file.getName().replace(".class", "");

                    try {
                        // 动态加载了名为 className 的类,获取该类的 Class 对象
                        Class<?> aClass = Class.forName(className);

                        // 判断该类是否有 @Service 或 @Controller 注解
                        if (aClass.isAnnotationPresent(Service.class) ||
                                aClass.isAnnotationPresent(Controller.class)) {

                            System.out.println("需要创建:" + className);

                            // 将该类的类名首字母小写作为 id
                            String id = aClass.getSimpleName().substring(0, 1).toLowerCase() + aClass.getSimpleName().substring(1);
                            System.out.println("id:" + id);

                            // 创建 Bean 定义对象,并放入 Bean 定义Map中
                            beanDefinitionMap.put(id, new BeanDefinition(id, className));
                        }

                    } catch (ClassNotFoundException e) {
                        e.printStackTrace();
                    }

                }

            } else if (file.isDirectory()) {
                // 递归扫描子文件夹
                String subPackagePath = basePackage + "." + file.getName();
                scanPackage(subPackagePath);
            }
        }

    }

    /**
     * 2. 创建 Bean 实例,并放入 Bean Map 中
     */
    public void createBean() {

        // 获取 Bean 定义 Map 的 所有 id
        Set<String> ids = beanDefinitionMap.keySet();
        Iterator<String> it = ids.iterator();
        while (it.hasNext()) {
            String id = it.next();

            // 获取 Bean 定义对象
            BeanDefinition beanDefinition = beanDefinitionMap.get(id);

            // 获取 Bean 定义对象中的 Bean 的全类名
            String className = beanDefinition.getClassName();

            try {
                // 动态加载类,并创建 Bean 实例,这时候的 Bean 是裸Bean
                Object bean = Class.forName(className).newInstance();

                // 将 Bean 实例放到 Bean Map 中
                BeanMap.put(id, bean);

            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        System.out.println("实例化 Bean 完成");
        System.out.println(BeanMap);
    }

    /**
     * 3. 给 Bean 设置属性,Autowired 赋值
     */
    public void injectBean() {

        // 获取 Bean Map 的 所有 id
        Iterator<String> it = BeanMap.keySet().iterator();
        while (it.hasNext()) {

            String id = it.next();

            // 获取 Bean 实例
            Object bean = BeanMap.get(id);

            // 获取 Bean 的 Class 对象
            Class<?> aClass = bean.getClass();

            // 获取这个类的所有属性,不包括父类的属性
            Field[] declaredFields = aClass.getDeclaredFields();

            for (Field field : declaredFields) {

                // 判断该属性是否有 @Autowired 注解
                if (field.isAnnotationPresent(Autowired.class)) {
                    // 设置属性可访问
                    field.setAccessible(true);

                    try {
                        // 给属性赋值
                        field.set(bean, BeanMap.get(field.getName()));
                    } catch (IllegalAccessException e) {
                        e.printStackTrace();
                    }
                }
            }

        }
        System.out.println("属性赋值完成");
        System.out.println(BeanMap);
    }

    /**
     * 对外提供获取 Bean 的接口
     */
    public Object GetBean(String id) {
        return BeanMap.get(id);
    }

    public <T> T GetBean(Class<T> clazz) {
        // 获取类名,首字母小写
        String id = clazz.getSimpleName().substring(0, 1).toLowerCase() + clazz.getSimpleName().substring(1);
        // 获取 Bean 实例
        Object value = BeanMap.get(id);

        // 判断 value 是否为 clazz 类型
        if (clazz.isInstance(value)) {
            // 安全的转换类型
            return clazz.cast(value);
        } else {
            return null;
        }
    }
}

2. 使用 Bean 

这里为了方便测试,在Servlet 中获取 Bean,然后调用 Bean 实例的方法

3. 测试

五、优化 IOC 容器

现在需要注入依赖的属性是类,不是接口,需要改成给接口赋值其 实现类的对象

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

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

相关文章

IO模型介绍

一、理解IO 网络通信的本质就是进程间通信&#xff0c;进程间通信本质就是IO TCP中的IO接口&#xff1a;read / write / send / recv&#xff0c;本质都是&#xff1a;等 拷贝 所以IO的本质就是&#xff1a;等 拷贝 那么如何高效的IO&#xff1f; 减少“等”在单位时间的…

在VS code 中部署C#和avalonia开发环境

要在 Mac 的 VS Code 中配置 C# 和 Avalonia 的开发环境&#xff0c;您可以按照以下步骤进行&#xff1a; 1. 安装 .NET SDK 下载 .NET SDK&#xff1a; 访问 .NET 下载页面。选择适用于 macOS 的最新稳定版本的 .NET SDK&#xff0c;并下载安装程序。安装 .NET SDK&#xff1…

PADS自动导出Gerber文件 —— 6层板

在出GERBER文件之前要给PCB文件铺完铜并且检查连接性和安全间距无错误。进入CAM中之后点自动定义。如果电气层不需要用到2D线&#xff0c;保险起见在电气层中把2D线和文本去掉&#xff08;在DRC检查时2D线不会报错&#xff0c;文本是会报错的&#xff09;&#xff0c;因为有些时…

【JAVA开源】基于Vue和SpringBoot的校园资料分享平台

本文项目编号 T 059 &#xff0c;文末自助获取源码 \color{red}{T059&#xff0c;文末自助获取源码} T059&#xff0c;文末自助获取源码 目录 一、系统介绍二、演示录屏三、启动教程四、功能截图五、文案资料5.1 选题背景5.2 国内外研究现状5.3 可行性分析 六、核心代码6.1 查…

(功能测试)熟悉web项目及环境 测试流程

1.环境&#xff1f;有没有考虑过什么是环境&#xff1f; web网站为什么能打开&#xff1f; &#xff08;是因为他的服务器已经在运行了&#xff0c;网站服务器相关环境已部署及运行&#xff09; 所以什么是环境&#xff1f; 环境&#xff1a;就是项目运行所需要的软件及硬件组合…

php与python建站的区别有哪些

php与Python建站的区别&#xff1a; 1、语言层面Python的特性比php好&#xff0c;更加规范。 2、Python的性能比php高。 3、有只需要启动服务的时候执行一次的代码&#xff0c;在php里每个请求都会被执行一次&#xff0c;Python不需要。虽然php可以通过缓存缩短这方面的差距…

CVPR论文《DETRs Beat YOLOs on Real-time Object Detection》读后思维导图

下面欣赏一下论文中的图和表&#xff1a; 1、与YOLOs的性能对比图 2、不同置信度阈值下的框数 3、IoU阈值和置信度阈值对准确性和NMS执行时间的影响 4、混合编码器不同变体 5、模型概述。将骨干网后三个阶段的特征输入到编码器中。高效混合编码器通过基于注意力的尺度内特征交…

Linux网络基础:HTTPS 网络传输协议

HTTPS HTTPS 网络传输协议加密常见的加密方式&#xff08;对称/非对称加密&#xff09;数据摘要、数字签名HTTPS 加密过程探索的方案只使用对称加密&#xff08;效率低下、不安全&#xff09;只使用非对称加密&#xff08;不靠谱、不安全&#xff09;双方都使用非对称加密&…

js中的深拷贝与浅拷贝 手写深拷贝代码

1 什么是深拷贝和浅拷贝&#xff1f; 深拷贝和浅拷贝都是复制对象时常用的两种方式&#xff0c;区别在于对于嵌套对象的处理&#xff0c;浅拷贝只复制属性的第一层属性&#xff0c;双方修改嵌套对象将会互相影响。深拷贝会递归复制每一层的属性&#xff0c;修改任意一方互不影响…

YOLO11项目实战1:道路缺陷检测系统设计【Python源码+数据集+运行演示】

一、项目背景 随着城市化进程的加速和交通网络的不断扩展&#xff0c;道路维护成为城市管理中的一个重要环节。道路缺陷&#xff08;如裂缝、坑洞、路面破损等&#xff09;不仅影响行车安全&#xff0c;还会增加车辆的磨损和维修成本。传统的道路缺陷检测方法主要依赖人工巡检…

[云服务器17] 搭建PMail个性邮箱!我的邮箱我做主

哈喽大家好啊&#xff01; 我们先来看一个邮箱: 123456example163.com你发现了吗&#xff1f;后面有163的域名&#xff01; 这个就标志了邮箱服务提供商的名字&#xff0c;像常见的Outlook 163等。 那么作为一个追求自由主义的人&#xff0c;今天&#xff0c;我们就要使用开…

exe4j安装使用教程

A-XVK258563F-1p4lv7mg7sav A-XVK209982F-1y0i3h4ywx2h1 A-XVK267351F-dpurrhnyarva A-XVK204432F-1kkoilo1jy2h3r A-XVK246130F-1l7msieqiwqnq A-XVK249554F-pllh351kcke50

出口企业财务管理,六款热门产品测评与推荐

本文介绍了ZohoBooks、QuickBooks、Xero等6款外贸管理软件&#xff0c;各有特点如全球化管理、移动应用、自动对账等&#xff0c;适合不同出口企业需求。选择时应考虑企业规模、业务复杂度和预算&#xff0c;建议先试用再购买。 一、Zoho Books Zoho Books是一款适合外贸企业跨…

【C++】迭代器失效问题解析

✨ Blog’s 主页: 白乐天_ξ( ✿&#xff1e;◡❛) &#x1f308; 个人Motto&#xff1a;他强任他强&#xff0c;清风拂山冈&#xff01; &#x1f525; 所属专栏&#xff1a;C深入学习笔记 &#x1f4ab; 欢迎来到我的学习笔记&#xff01; 一、迭代器失效的概念 迭代器的作用…

每日OJ题_牛客_游游的水果大礼包_枚举_C++_Java

目录 牛客_游游的水果大礼包 题目解析 C代码 Java代码 牛客_游游的水果大礼包 游游的水果大礼包 (nowcoder.com) 描述&#xff1a; 游游有n个苹果&#xff0c;m个桃子。她可以把2个苹果和1个桃子组成价值a元的一号水果大礼包&#xff0c;也可以把1个苹果和2个桃子…

GO网络编程(二):客户端与服务端通信【重要】

本节是新知识&#xff0c;偏应用&#xff0c;需要反复练习才能掌握。 目录 1.C/S通信示意图2.服务端通信3.客户端通信4.通信测试5.进阶练习&#xff1a;客户端之间通信 1.C/S通信示意图 客户端与服务端通信的模式也称作C/S模式&#xff0c;流程图如下 其中P是协程调度器。可…

【Qt】控件概述——按钮类控件(2)

控件概述&#xff08;2&#xff09; 1. PushButton2. RadioButton——单选按钮2.1 使用2.2 区分信号 clicked&#xff0c;clicked(bool)&#xff0c;pressed&#xff0c;released&#xff0c;toggled(bool)2.3 QButtonGroup分组 3. CheckBox——复选按钮 1. PushButton QPushB…

写不出论文?分享7款写论文的ai免费工具网站

在当今学术研究和写作领域&#xff0c;撰写高质量的论文是一项挑战性的任务。幸运的是&#xff0c;随着人工智能技术的发展&#xff0c;AI论文写作工具逐渐成为帮助学者和学生提高写作效率的重要工具。这些工具不仅能够提高写作速度&#xff0c;还能通过智能校对和优化&#xf…

【大数据】Doris 数据库与表操作语法实战详解

目录 一、前言 二、数据库基本操作 2.1 修改账户密码 2.2 创建新用户 2.3 创建数据库与账户授权 2.3.1 数据库创建补充说明 2.3.2 数据库账户赋权 三、数据表基本操作 3.1 Doris 数据表介绍与使用 3.1.1 建表结构说明 3.1.2 建表语法与操作 3.1.3 建表示例 - 单分区…

Android KMP 快速入门2 - Koin依赖注入

这里写目录标题 代码仓库KMP 框架基本框架actual&expectKoin 依赖注入管理 代码仓库 本小节代码已经上传到gitee&#xff0c;请自行查看&#xff1a; 点击访问仓库 KMP 框架 基本框架 源码集合描述存放内容示例androidMain针对 Android 平台的代码使用 Android SDK、Andr…