手写Spring源码(简化版)

news2024/12/23 4:24:56

导航: 

【Java笔记+踩坑汇总】Java基础+进阶+JavaWeb+SSM+SpringBoot+瑞吉外卖+SpringCloud+黑马旅游+谷粒商城+学成在线+MySQL高级篇+设计模式+牛客面试题

参考视频:

周瑜大都督手写模拟Spring_哔哩哔哩

源码:

https://gitee.com/vincewm/spring-master

目录

一、准备工作

1.0 项目目录

1.1 Spring包下的类

1.1.1 容器类

1.1.2 @ComponentScan注解

1.1.3 @Component注解

1.1.4 @Autowired注解

1.1.5 @Scope注解

1.1.6 Bean的声明类:BeanDefinition

1.1.7 Bean名回调接口:BeanNameAware

1.1.8 初始化接口:InitializingBean

1.1.9 Bean后置处理器接口:BeanPostProcessor 

1.2 用户业务相关类  

1.2.1 测试类创建容器

1.2.2 配置类AppConfig

1.2.3 UserService

1.2.4 OrderService,用于依赖注入 

1.2.5 Bean后置处理器接口实现类:MyBeanPostProcessor

二、完善容器类:VinceApplicationContext

2.0 创建Bean流程

2.1 代码实现

2.1.1 成员变量 

2.1.2 构造方法,参数是配置类

2.1.3 createBean()方法 

3.1.4 getBean()方法


一、准备工作

1.0 项目目录

1.1 Spring包下的类

com.vince.spring包下的类:

1.1.1 容器类

 对比一下创建Spring容器的方式,我们需要模拟的是AnnotationConfigApplicationContext类。

  • ClassPathXmlApplicationContext:是需要传入一个xml配置文件,告诉Spring需要按照指定的配置创建一个Spring容器ApplicationContext,创建完之后就可以通过getBean从容器中拿到相应的bean对象
  • AnnotationConfigApplicationContext :也是一个容器,只是传入的是以Java配置类的形式传入。可以在类上加上@ComponentScan定义Spring等下需要扫描的路径。还可以通过@Bean的方法(等于bean标签),通过这种注解的方式,给容器加入一个Bean
public class Test {
	public static void main(String[] args) {
		ClassPathXmlApplicationContext classPathXmlApplicationContext = new ClassPathXmlApplicationContext("spring.xml");
		classPathXmlApplicationContext.getBean("user");

		AnnotationConfigApplicationContext annotationConfigApplicationContext = new AnnotationConfigApplicationContext(AppConfig.class);
		
		SpringCodeApplicationContext applicationContext = new SpringCodeApplicationContext(AppConfig.class);
	}
}

参考Spring框架可知,这个容器类需要有构造方法、传入参数、getBean()方法

package com.vince.spring; 

//模拟AnnotationConfigApplicationContext
public class VinceApplicationContext{

    private Class configClass;
//构造方法,参数是配置类的字节码文件
	public SpringCodeApplicationContext(Class configClass) {
        //1.赋值配置类成员变量
    	this.configClass = configClass;

		// 2.解析配置类
        // 通过反射,判断配置类字节码有没有注解@ComponentScan
		// 如果有,就根据@ComponentScan传入的扫描路径,扫描那个包下的Bean

        // 3.实例化bean

    }
//容器里拥有getBean()方法,获取Bean
    public Object getBean(String beanName){
		return null;
	}

1.1.2 @ComponentScan注解

package com.vince.spring; 

/**
 * 用于扫描指定包路径的Bean
 */
@Retention(RetentionPolicy.RUNTIME)//注解生命周期,不仅被保存到class文件中,jvm加载class文件之后,仍然存在
//@Target用来表示注解作用范围,超过这个作用范围,编译的时候就会报错。
@Target(ElementType.TYPE)//注解范围是:接口、类、枚举、注解
public @interface ComponentScan {

    String value() default "";

}

1.1.3 @Component注解

package com.vince.spring; 

/**
 * 注解为Bean
 */
@Retention(RetentionPolicy.RUNTIME)//注解生命周期,不仅被保存到class文件中,jvm加载class文件之后,仍然存在
@Target(ElementType.TYPE)//注解范围是:接口、类、枚举、注解
public @interface Component {

    String value() default "";

}

1.1.4 @Autowired注解

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface Autowired {

}

1.1.5 @Scope注解

@Retention(RetentionPolicy.RUNTIME)//注解生命周期,不仅被保存到class文件中,jvm加载class文件之后,仍然存在
@Target(ElementType.TYPE)//注解范围是:接口、类、枚举、注解
public @interface Scope {

    String value() default "";

}

1.1.6 Bean的声明类:BeanDefinition

BeanDefinition 定义 Bean 的配置元信息,包含:

  • Bean 的类名
  • 设置父 bean 名称、是否为 primary、
  • Bean 行为配置信息,作用域、自动绑定模式、生命周期回调、延迟加载、初始方法、销毁方法等
  • Bean 之间的依赖设置,dependencies
  • 构造参数、属性设置  
//Bean的声明类,用来描述Bean;里面定义的Bean的类型(Class格式)和作用域
public class BeanDefinition {

    private Class type;
    private String scope;

    @Override
    public String toString() {
        return "BeanDefinition{" +
                "type=" + type +
                ", scope='" + scope + '\'' +
                '}';
    }

    public Class getType() {
        return type;
    }

    public void setType(Class type) {
        this.type = type;
    }

    public String getScope() {
        return scope;
    }

    public void setScope(String scope) {
        this.scope = scope;
    }
}

1.1.7 Bean名回调接口:BeanNameAware

//回调接口,主要setter方法给Bean的beanName变量赋值。Aware译为明白的,意识到的。
public interface BeanNameAware {

    public void setBeanName(String beanName);
}

1.1.8 初始化接口:InitializingBean

//初始化Bean的接口,有个方法,
public interface InitializingBean {

    //在属性填充后执行
    public void afterPropertiesSet();

}

1.1.9 Bean后置处理器接口:BeanPostProcessor 

本接口的两个方法在初始化之前和之后执行,可以通过实现类使用JDK的Proxy 

//Bean后置处理器接口
public interface BeanPostProcessor {

    //在Bean 的初始化方法(如 @PostConstruct 注解的方法)被调用之前被自动调用
    public Object postProcessBeforeInitialization(String beanName,Object bean);

    //在 Bean 的初始化方法被调用之后被自动调用
    public Object postProcessAfterInitialization(String beanName,Object bean);

}

1.2 用户业务相关类  

com.vince.service包下的类: 

1.2.1 测试类创建容器

package com.vince.spring; 

public class Test {
    public static void main(String[] args){
//模拟AnnotationConfigApplicationContext容器,构造参数传入配置类
        VinceApplicationContext applicationContext = new VinceApplicationContext(AppConfig.class);
        UserInterface userService = (UserInterface)applicationContext.getBean("userService");
        userService.test();
    }
}

  

1.2.2 配置类AppConfig

package com.vince.spring; 

// com/springCode/service/UserService.java
@ComponentScan("com.springCode.service")
public class AppConfig {
}

1.2.3 UserService

package com.vince.service; 

public interface UserInterface {

    public void test();

}
@Component
//@Scope("prototype") //多例
public class UserService implements BeanNameAware , InitializingBean ,UserInterface{

    @Autowired
    private OrderService orderService;

    private String beanName;

    @Override
    public void test(){
        System.out.println(orderService);
    }

    @Override
    public void setBeanName(String beanName) {
        this.beanName = beanName;
    }

    @Override
    public void afterPropertiesSet() {
        System.out.println("初始化方法");
    }
}

1.2.4 OrderService,用于依赖注入 

@Component
//@Scope("prototype") //多例
public class OrderService {
    public void submitOrder(){
        System.out.println("下单。。。");
    }

}

1.2.5 Bean后置处理器接口实现类:MyBeanPostProcessor

//Bean后置处理器实现类,两个方法在初始化之前和之后执行,可以通过实现类使用JDK的Proxy.newProxyInstance();基于反射实现aop。
@Component
public class MyBeanPostProcessor implements BeanPostProcessor {

    @Override
    public Object postProcessBeforeInitialization(String beanName, Object bean) {
        if ("userService".equals(beanName)) {
            System.out.println("BeanPostProcessor实现类的postProcessBeforeInitialization()方法");
        }
        return bean;
    }

    @Override
    public Object postProcessAfterInitialization(String beanName, Object bean) {
        if ("userService".equals(beanName)) {
            //代理对象,通过JDK的Proxy.newProxyInstance();基于反射实现aop。
            // 第一个参数类加载器,第二个参数接口的class对象,第三个参数重写invoke()的InvocationHandler实现类
            Object proxyInstance = Proxy.newProxyInstance(MyBeanPostProcessor.class.getClassLoader(),
                    bean.getClass().getInterfaces(), new InvocationHandler() {
                @Override
                public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                    System.out.println("切面逻辑,BeanPostProcessor实现类的postProcessAfterInitialization()方法");
                    return method.invoke(bean,args);
                }
            });
            return proxyInstance;
        }
        return bean;
    }
}

二、完善容器类:VinceApplicationContext

2.0 创建Bean流程

注解容器类构造方法创建Bean:

  • 获取Bean扫描路径:获取配置类的Class对象,基于反射获取@ComponentScan注解里设置的扫描路径;
  • 遍历扫描Bean:遍历扫描路径里每个类的字节码文件,用类加载器加载成class对象,基于反射判断@Component等注解,有的话这个类就是Bean类。
    • BeanPostProcessor实例化后存列表:如果Bean类是BeanPostProcessor接口的实现类,那么就将这个Bean类实例化并存进成员变量List<BeanPostProcessor>里;
    • 创建并赋值BeanDefinition对象:创建BeanDefinition对象并赋值,type变量赋值Bean类的class对象,scope变量赋值作用域(通过反射获取@Scope注解值)。BeanDefinition存储Bean的对象、作用域等元信息。
    • beanDefinitionMap添加键值对:把Bean名和它对应的BeanDefinition对象,以键值对形式添加到容器类成员变量beanDefinitionMap里。beanDefinitionMap线程安全,是ConcurrentHashMap类型。
  • 遍历beanDefinitionMap创建Bean:遍历beanDefinitionMap的键,根据作用域判断createBean()方法创建Bean,还是放进单例池(一个ConcurrentHashMap类型的成员变量)。createBean()方法:
    • 实例化:基于反射,根据Bean类的Class对象创建实例,如果是单例就存入单例池。实例化这个方法在getBean()时候也会用到,所以抽取成私有方法,这个方法获取Bean先判断作用域,单例的话就优先根据Bean名从单例池取,单例池没有就调用createBean()方法,根据Bean名和对应的BeanDefinition创建Object类型的Bean对象。
    • 属性填充:基于反射,根据Bean类的Class对象获取它所有属性,将注解了@Autowired等的属性开启私有成员访问限制,通过getBean(名或类)方法给该属性填充它的Bean对象。
    • 处理Aware回调:如果Bean实例实现了BeanNameAware接口(通过instanceof判断),调用Bean重写的setBeanName()方法,给Bean实例的beanName变量赋值。
    • 执行所有BeanPostProcessor的初始化前方法:遍历BeanPostProcessor列表,执行每个BeanPostProcessor对象里的postProcessBeforeInitialization()方法。本方法可以通过JDK的Proxy.newProxyInstance()实现动态代理返回目标对象的代理对象。
    • 初始化:如果Bean实例实现了InitializingBean接口(通过instanceof判断),调用Bean重写的afterPropertiesSet()方法,处理初始化逻辑。afterPropertiesSet译为“在属性填充之后”
    • 执行所有BeanPostProcessor的初始化后方法:遍历BeanPostProcessor列表,执行每个BeanPostProcessor对象里的postProcessAfterInitialization()方法。本方法可以通过JDK的Proxy.newProxyInstance()实现动态代理返回目标对象的代理对象。

2.1 代码实现

spring包下的VinceApplicationContext类  

2.1.1 成员变量 

    private Class configClass;//配置类的Class对象

    //Bean名和它对应的BeanDefinition键值对;
    private ConcurrentHashMap<String,BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>();

    //单例池;map存各Bean名所对应的Bean实例
    private ConcurrentHashMap<String,Object> singletonObjects = new ConcurrentHashMap<>();
    //存放扫描包路径下的所有BeanPostProcessor类
    private ArrayList<BeanPostProcessor> beanPostProcessorList = new ArrayList<>();

2.1.2 构造方法,参数是配置类

//构造方法,参数是配置类的Class对象
    public VinceApplicationContext(Class configClass) {
        //1.赋值配置类成员变量
        this.configClass = configClass;
        // 2.通过反射,判断配置类Class对象有没有注解@ComponentScan;如果有,就解析ComponentScan对象
        // --->BeanDefinition -->beanDefinitionMap
        if (configClass.isAnnotationPresent(ComponentScan.class)) {
            //解析ComponentScan对象
            ComponentScan componentScanAnnotation = (ComponentScan) configClass.getAnnotation(ComponentScan.class);
//            获取ComponentScan对象的value,也就是用户传入的扫描路径。例如@ComponentScan("com.vince.service"),扫描这个包下的Bean
            String path = componentScanAnnotation.value();// com.vince.service
            // 将包名中的点号替换为斜杠
            path = path.replace(".","/");//com/vince/service
            // 获取容器类的类加载器
            ClassLoader classLoader = VinceApplicationContext.class.getClassLoader();
            // 类加载器获取扫描包的绝对路径
            URL resource = classLoader.getResource(path);//file:/D:/xxx/com/vince/service
            // 把URL绝对路径对象封装成File对象
            File file = new File(resource.getFile());
            // 如果File对象是目录,则遍历判断扫描目录下的文件是不是bean;
            if (file.isDirectory()) {
                File[] files = file.listFiles();
                //遍历“扫描路径”下所有文件中,找到Bean,创建对应BeanDefinition对象,并存到map里;
                // 判断Bean方式:基于反射,判断该字节码文件对应的类有没有@Component注解
                for (File f: files) {
                    String fileName = f.getAbsolutePath();
                    if (fileName.endsWith(".class")) {
                        //将文件名转为类名;D:\xxx\com\vince\service\AppConfig.class ----> AppConfig
                        //这里其实不应该写死成“com”,只是方便起见,其实应该用更复杂的逻辑截取全名为类名
                        String className = fileName.substring(fileName.indexOf("com"), fileName.indexOf(".class"));
                        className = className.replace("\\",".");
                        try {
                            //根据类的全限定名加载类
                            Class<?> clazz = classLoader.loadClass(className);
                            //如果该类有@Component注解
                            if (clazz.isAnnotationPresent(Component.class)) {
                                System.out.println("容器类构造方法里,扫描@ComponentScan('xx'),发现xx路径下这个类是Bean:"+clazz.getName());
                                //a.如果@Component("类名")有设置类名,Bean名就是设置的这个类名
                                Component component = clazz.getAnnotation(Component.class);
                                String beanName = component.value();

                                //判断这个Bean类是不是BeanPostProcessor接口的实现类,如果是的话就通过反射创建实例,并放进处理器列表里。
                                //注意不能用instanceof,因为instanceof是实例与类的关系比较,isAssignableFrom是类和接口的关系比较
                                if (BeanPostProcessor.class.isAssignableFrom(clazz)) {
                                    BeanPostProcessor instance = (BeanPostProcessor) clazz.newInstance();
                                    beanPostProcessorList.add(instance);
                                    System.out.println("这个Bean是BeanPostProcessor接口的实现类,需要加入list里,以供所有Bean初始化之前和之后增强");
                                }

                                //b.如果@Component没有设置类名,就将首字母小写后的Bean类名设为Bean名
                                if ("".equals(beanName)) {
                                    beanName = Introspector.decapitalize(clazz.getSimpleName());//工具类将字符串校验后首字母小写
                                }

                                // 创建一个BeanDefinition对象,用来描述Bean,里面赋值这个Bean的类型和作用域
                                BeanDefinition beanDefinition = new BeanDefinition();
                                //反射获取@Scope注解指定的作用域,赋值给BeanDefinition对象的scope变量
                                if (clazz.isAnnotationPresent(Scope.class)) {
                                    Scope scopeAnnotation = clazz.getAnnotation(Scope.class);
                                    beanDefinition.setScope(scopeAnnotation.value());
                                }else{
                                    beanDefinition.setScope("singleton");
                                }
                                //把这个Bean的Class对象赋值给BeanDefinition对象的type变量
                                beanDefinition.setType(clazz);
                                //把所有Bean名和它对应的BeanDefinition对象映射关系,存到map里统一管理
                                beanDefinitionMap.put(beanName,beanDefinition);
                                System.out.println("Bean名:"+beanName+",BeanDefinition对象:"+beanDefinition);
                            }
                        } catch (ClassNotFoundException | InstantiationException | IllegalAccessException e) {
                            e.printStackTrace();
                        }
                    }
                }
            }
        }

        // 3.实例化bean,如果作用域是单例,则从单例池中取;取不到就创建新的Bean对象,并存入单例池
        for (String beanName:beanDefinitionMap.keySet()) {
            BeanDefinition beanDefinition = beanDefinitionMap.get(beanName);
            if ("singleton".equals(beanDefinition.getScope())) {
                //实例化Bean
                Object bean = createBean(beanName,beanDefinition);
                singletonObjects.put(beanName,bean);
            }
        }
    }

2.1.3 createBean()方法 

    //根据Bean名和BeanDefinition对象实例化Bean的方法;私有,只供本类内部调用
    //BeanDefinition对象的成员变量type就是Bean的类型(Class格式)
    private Object createBean(String beanName,BeanDefinition beanDefinition){
//        获取Bean的Class格式的类型
        Class clazz = beanDefinition.getType();
        try {
            //1.基于反射创建Bean的实例;
            Object instance = clazz.getConstructor().newInstance();
            //2.依赖注入
            //遍历Bean类Class对象的所有属性;
            for (Field f : clazz.getDeclaredFields()) {
                //找出有@Autowired注解的属性,给这些属性进行填充
                if (f.isAnnotationPresent(Autowired.class)) {
                    //破除 private 修饰符访问限制;这样就可以访问注入的这个依赖里面的私有成员
                    f.setAccessible(true);
                    //给该属性填充属性名对应的Bean对象。
                    // 该属性通过getBean能获得Bean,因为上面构造方法里已经扫描了所有的Bean,并创建了BeanDefinition对象加入到“声明map”里了。
                    //void set(Object obj, Object value)
                    //obj 表示要设置属性值的对象;value 表示要为该成员变量设置的新值。
                    f.set(instance,getBean(f.getName()));
                }
            }

            // 3.Aware回调。如果Bean实例实现了BeanNameAware接口,就调用重写的setter方法,给Bean实例的beanName变量赋值
            if (instance instanceof BeanNameAware) {
                ((BeanNameAware) instance).setBeanName(beanName);
            }

            // 遍历后置处理器列表,执行后置处理器的before方法,在Bean 的初始化方法(如 @PostConstruct 注解的方法)被调用之前被自动调用
            for (BeanPostProcessor beanPostProcessor : beanPostProcessorList) {
                instance = beanPostProcessor.postProcessBeforeInitialization(beanName,instance);
            }

            // 初始化
            if (instance instanceof InitializingBean) {
                ((InitializingBean) instance).afterPropertiesSet();
            }

            //Bean后置处理器接口的after方法,在 Bean 的初始化方法被调用之后被自动调用
            for (BeanPostProcessor beanPostProcessor : beanPostProcessorList) {
                instance = beanPostProcessor.postProcessAfterInitialization(beanName,instance);
            }
            return instance;
        } catch (InstantiationException e) {            //异常处理
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        }
        return null;
    }

3.1.4 getBean()方法

//容器类的getBean()方法,根据作用域获取Bean
    public Object getBean(String beanName){
        //从map里获取BeanDefinition对象
        BeanDefinition beanDefinition = beanDefinitionMap.get(beanName);
        if (beanDefinition == null) {
            //如果BeanDefinition对象为空,说明这个bean没有创建成功,抛异常;
            throw new NullPointerException();
        }else{
            //如果BeanDefinition对象不为空,则判断作用域后获取Bean
            String scope = beanDefinition.getScope();
            if ("singleton".equals(scope)) {
                //如果是单例则从单例池根据Bean名取Bean;
                Object bean= singletonObjects.get(beanName);
                if (bean == null) {
                    //如果单例池查到的是null,则新创建Bean,再给单例池赋值
                    bean = createBean(beanName, beanDefinition);
                    singletonObjects.put(beanName,bean);
                }
                //返回Bean
                return bean;
            }else{
                // 多例,每次直接创建新的
                return createBean(beanName, beanDefinition);
            }
        }
    }

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

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

相关文章

windows自动保存git密码

Windows平台在使用Git的时候经常会出现反复输入密码&#xff0c;生成密钥对的时候如果设置了密码&#xff0c;那么每次使用时都会要求输入密码&#xff0c;那可以通过下面的方式解决。 1. 配置ssh自动启动 管理员启动终端 Set-Service ssh-agent -StartupType Auto # 设置为…

C语言实现分数求和

代码&#xff1a; // 计算1/1 - 1/2 1/3 - 1/4 ...1/99 - 1/100的值 // 计算1/1 - 1/2 1/3 - 1/4 ...1/99 - 1/100的值 int main() {int i 0;double sum 0.0;int flag 1;for (i 1; i < 100; i) {sum sum flag*(1.0 / i);flag -flag;}printf("%lf\n", su…

Caffeine 本地高速缓存工具类

目录 Caffeine工具类方式 SpringBoot 整合 Caffeine 缓存 &#xff08;SpringCache模式&#xff09; 驱逐策略 开发使用 Caffeine是一种高性能的缓存库&#xff0c;是基于Java 8的最佳&#xff08;最优&#xff09;缓存框架&#xff0c;性能各方面优于guava。 Caffeine工具…

脉蜂:Django + Flutter 开发的进销存管理系统【已开源】

项目说明 小规模零售&#xff08;包括电商&#xff09;跟大规模零售企业的差别在哪里呢&#xff1f; 以我当前的认知来看&#xff0c;小规模零售跟大规模零售企业的差别更多的是在供应链管理、进销存管控上面产生的。如果有一个工具&#xff0c;能够帮他们减少这方面的差异&…

我们拆了一款Tof+AI避障的扫地机,小米铁蛋铁大机器人同款

追觅W10 Pro是2022年初推出的新品&#xff0c;相较前一代W10&#xff0c;两者间最大的区别是将LDS避障升级为了TofAI避障&#xff0c;扫地机本体前脸像给W10开了“大眼特效”的传感器和摄像头就是机械避障升级的最佳佐证。 在外观上扫地机还是延续了以往的设计&#xff0c;顶部…

ReactRouterDom-v5v6用法与异同

本文作者系360奇舞团前端开发工程师 简介&#xff1a; React Router Dom是React.js中用于实现路由功能的常用库。在React应用中&#xff0c;路由可以帮助我们管理页面之间的导航和状态&#xff0c;并实现动态加载组件。本文将深入探讨React Router Dom的两个主要版本&#xff1…

​一文学会iOS画中画浮窗

本文字数&#xff1a;11934字 预计阅读时间&#xff1a;40分钟 背景 之前有看到有人用画中画实现时分秒的计时&#xff0c;顺手收藏了&#xff0c;一直没来及看。最近使用《每日英语听力》&#xff0c;突然发现它用画中画实现了听力语句的显示&#xff0c;顿时来了兴趣&#xf…

超线程技术

超线程&#xff08;HT, Hyper-Threading&#xff09;是英特尔研发的一种技术&#xff0c;于2002年发布。超线程技术原先只应用于Xeon 处理器中&#xff0c;当时称为“Super-Threading”。之后陆续应用在Pentium 4 HT中。早期代号为Jackson。 [1] 通过此技术&#xff0c;英特尔实…

Golang每日一练(leetDay0077) 存在重复元素、天际线问题

目录 217. 存在重复元素 Contains Duplicate &#x1f31f; 218. 天际线问题 The Skyline Problem &#x1f31f;&#x1f31f;&#x1f31f; &#x1f31f; 每日一练刷题专栏 &#x1f31f; Rust每日一练 专栏 Golang每日一练 专栏 Python每日一练 专栏 C/C每日一练 …

PyTorch-torchvision

1. 以Torchvision中的CIFAR10为例 dataset.py Ps&#xff1a;如果是从其他地方下载的gz文件&#xff0c;直接建立一个dataset文件夹然后将gz文件放进去&#xff0c;再运行。 import torchvisiontrain_set torchvision.datasets.CIFAR10(root"./dataset", trainTr…

SpringCloud Alibaba Nacos

SpringCloud Alibaba Nacos Nacos 基础 1 官网 1.1 https://github.com/alibaba/Nacos 2 Nacos 是什么&#xff1f; 2.1 一句话: Nacos 就是注册中心[替代Eureka]配置中心[替代Config] 2.2 Nacos&#xff1a;Dynamic Naming and Configuration Service 2.3 Nacos&#xff…

数据结构---二叉树(C语言)

二叉树 1. 二叉树1.1 二叉树的遍历1.1.1二叉树前中后序遍历&#xff08;递归版&#xff09;1.1.2 层序遍历 1.2 二叉树的其他相关接口1.2.1 求二叉树的结点数量1.2.2 求叶子结点个数1.2.3 求树高1.2.4 求第k层结点个数1.2.5 查找二叉树值为k的结点 1. 二叉树 空树非空&#xff…

跨境DNS解析失败问题解决

新钛云服已累计为您分享748篇技术干货 问题 公司使⽤的是阿⾥云基础设施&#xff0c;DNS解析使⽤的是境外的Akamai作为域名的DNS解析服务商。 当前有部分应⽤需要被第三⽅应⽤调⽤&#xff0c;同时也有主动调⽤第三⽅应⽤的需求。最近突发了很多调⽤失败问题。 应⽤调⽤失败&am…

YB时代,正寻找新的数据支点

每个人可能都想过这样一些问题&#xff1a;我们的地球&#xff0c;能够承载多少人口&#xff1f;地球上的石油和煤炭能用多久&#xff1f;碳排放的极限在哪里&#xff1f; 但你有没有想过&#xff0c;我们的地球&#xff0c;能够承载多少数据&#xff1f; 根据《数据存储2030白…

手把手教你打造自己的 AI 虚拟主播

零、写在前面一、 准备环境二、配置软件环境2.1 OBS 安装2.2. VTube Studio 安装 三、开启 B 站直播四、开启 AI 主播五、直播最终效果 零、写在前面 AI 直播在近年来得到了快速的发展和普及&#xff0c;它已经成为了直播平台的重要组成部分。目前&#xff0c;许多知名的直播平…

「读书感悟系列」生命的礼物 · 关于爱、死亡及存在的意义

作者 | gongyouliu 编辑 | gongyouliu 最近花了不到一周时间看完了著名的团体心理治疗大师欧文D.亚隆和他的妻子玛丽莲亚隆合著的这本『生命的礼物 关于爱、死亡及存在的意义』。这本书非常特别&#xff0c;这是玛丽莲在87岁得癌症后跟丈夫一起合作的&#xff0c;每人写一章&a…

URLConnection(二)

文章目录 1. 缓存2. Java的Web缓存 1. 缓存 Web浏览器会缓存页面和图片&#xff0c;将资源缓存在本地&#xff0c;每次需要时会从缓存中重新加载&#xff0c;而不是每次都请求远程服务器。一些HTTP首部&#xff08;包括Expires和Cache-Control&#xff09;可以控制首部。默认情…

OJ练习第116题——二进制矩阵中的最短路径(BFS)

二进制矩阵中的最短路径 力扣链接&#xff1a;1091. 二进制矩阵中的最短路径 题目描述 给你一个 n x n 的二进制矩阵 grid 中&#xff0c;返回矩阵中最短 畅通路径 的长度。如果不存在这样的路径&#xff0c;返回 -1 。 二进制矩阵中的 畅通路径 是一条从 左上角 单元格&am…

记一次Redis消息订阅序列化和反序列化的错误

1、使用的SpringBoot&#xff1b; 2、Redis的Config配置了JSON序列化&#xff0c;覆盖JDK序列化&#xff0c;便于中文查看&#xff0c;配置文件使用ConditionalOnProperty断言&#xff1b; 3、Nacos动态配置&#xff1b; 解决思路&#xff1a; 1、查看Redis中存入的数据乱码&am…