10000字讲解IoC 思想以及五大注解

文章目录

  • IoC 思想
    • 通过案例讲解 IoC
    • 1.传统的开发方式
  • SpringIoC 和 DI
  • 五大注解
    • @Controller
    • @Service
    • @Component
    • @Repository
    • @Configuration
  • 为什么要有这么多的类注解
  • 类注解之间的关系
  • 方法注解 @Bean
  • 重命名 bean
  • 扫描路径

IoC 思想

什么是 Spring 呢?

我们经常听到的都是说 Spring 是一个框架,让我们的开发变得更加简单了,但是这个概念相对来说,太抽象化了,具体点来讲:Spring是包含了很多工具方法的IoC容器

什么是容器?

容器是用来容纳某种物品的装置。

在生活中,水杯就是一个容器,它用容纳水,垃圾桶也是一个容器,它用来装垃圾。

在我们学习过的数据结C构中,像 List,Map,Statck等,也都是一个容器,用来存储数据。还有 Tomcat,它就是一个 Web 容器,在上面可以部署很多的Web服务。

什么是 IoC?

IoC:inversion of Control(控制反转),所以,Spring 就是一个“控制反转”的容器,而这个“控制反转”也就是控制权反转

举一个生活中的例子:

比如自动驾驶,在不使用自动驾驶时,踩油门,踩刹车,以及转动方向盘都是由人来操作的,在使用了自动驾驶之后,这些执行这些动作的权力就交给了系统来控制。

Spring 中的控制权反转说的更具体一点就是**“获取依赖对象的过程被反转了”**,也就是说,当我们需要某个对象时,传统的开发模式是需要手动的去new对象,而现在不需要再进行创建对象了,把创建对象的任务交给了 IoC容器,使用时只需要注入依赖就可以了,这个容器就称为 IoC容器。

通过案例讲解 IoC

现在有一个需求:造一辆车

1.传统的开发方式

思路:我们想要造一辆车,那么车肯定得需要 轮子,地盘,车身这三个重要得部分,所以,就需要先造出轮子,然后根据轮子设计出地盘,再根据地盘设计出车身,所以它们三个就产生了一个依赖的关系:汽车依赖车身,车身依赖地盘,地盘依赖轮子,所以,代码如下:

public class CarExample {
    public static void main(String[] args) {
        //创建一个汽车
        Car car = new Car();
        //让汽车跑起来
        car.run();
    }

    static class Car {
        //在创建汽车之前,需要先创建车车身
        private CarBody carBody;

        public Car() {
            this.carBody = new CarBody();
            //车身初始化完成
            System.out.println("Car init....");
        }
        public void run() {
            System.out.println("car start");
        }

        static class CarBody {
            //在创建车身之前,需要依赖地盘
            private Bottom bottom;
            public CarBody() {
                this.bottom = new Bottom();
                System.out.println("CarBody init....");
            }
        }

        static class Bottom {
            //在创建地盘之前,需要先创建出轮子
            private Tire tire;
            public Bottom() {
                this.tire = new Tire();
                System.out.println("Bottom init....");
            }

        }

        static class Tire {
            //定义车轮的尺寸
            private int size;
            public Tire() {
                this.size = 17;
                System.out.println("tire init...");
            }
        }
    }
}

问题分析

现在增加了新的需求:为了能让客户多样的选择,所以在生产汽车时,就指定了不同轮胎大小和颜色的车,所以,就需要对代码进行修改,将轮胎的大小size 作为参数传进去,需要多大的尺寸就传多大的尺寸,代码修改如下:

在这里插入图片描述

因为这里的类都是依赖的关系,所以这时候上面的代码也都会出现问题,都需要进行修改,修改如下:

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

整体代码如下:

public class CarExample {
    public static void main(String[] args) {
        //创建一个汽车
        Car car = new Car(17);
        //让汽车跑起来
        car.run();
    }

    static class Car {
        //在创建汽车之前,需要先创建车车身
        private CarBody carBody;

        public Car(int size) {
            this.carBody = new CarBody(size);
            //车身初始化完成
            System.out.println("Car init....");
        }
        public void run() {
            System.out.println("car start");
        }

        static class CarBody {
            //在创建车身之前,需要依赖地盘
            private Bottom bottom;
            public CarBody(int size) {
                this.bottom = new Bottom(size);
                System.out.println("CarBody init....");
            }
        }

        static class Bottom {
            //在创建地盘之前,需要先创建出轮子
            private Tire tire;
            public Bottom(int size) {
                this.tire = new Tire(size);
                System.out.println("Bottom init....");
            }

        }

        static class Tire {
            //定义车轮的尺寸
            private int size;
            public Tire(int size) {
                this.size = size;
                System.out.println("tire init...");
            }
        }
    }

从上面的改动可以看出,只要修改底层的代码,就会影响到调用链间的所有代码,代码的耦合性非常高,只要一处修改,那么处处都得修改。

解决方案:

上面的方案是,先创造出了轮子,然后根据轮子创造出了底盘,但是,只要轮子发生了变化,底盘紧接也要发生变化,同理,车身是根据底盘进行创造了,只要底盘发生了变化,车身就得发生变化,而车身一旦发生变化,整个车的设计也就得发生变化,所以这样得方式就导致一动牵扯全身,所以,就可以使用下面这种方案进行替换:

比如,在造一辆完整的车时,如果从车身到轮胎都需要厂家自己造,那么当客户的要求改变时,我们就得再自己重新造,与其这样,不如将这些配件外包出去,比如,将轮胎外包出去,当客户有了新的要求时,我们只需要将要求给外包商即可,然后我们直接就可以拿来使用

实现方案:

我们可以不在每个类中创建下级类,因为,如果自己创建下级类,那么随着下级类的改变,当前的类也会发生改变,针对于这种情况,此时,就可以将原来自己创建的下级类改为传递的方式(也就是注入的方式),就像上面所举的例子,我们不自己造轮胎,而是从外包商哪里直接拿来用,也就是,直接先将下级类创建好,然后进行传递即可,这样,即使下级类发生变化,也不会影响到上级类,也就达到了解耦合的效果;

所以,代码就可以这样修改:

public class CarExample {
    public static void main(String[] args) {
        Tire tire = new Tire(17);
        Bottom bottom = new Bottom(tire);
        CarBody carBody = new CarBody(bottom);
        Car car = new Car(carBody);
        car.run();
    }

    static class Car {
        //在创建汽车之前,需要先创建车车身
        private CarBody carBody;

        public Car(CarBody carBody) {
            this.carBody = carBody;
            //车身初始化完成
            System.out.println("Car init....");
        }
        public void run() {
            System.out.println("car start");
        }

    }
    static class CarBody {
        //在创建车身之前,需要依赖地盘
        private Bottom bottom;
        public CarBody(Bottom bottom) {
            this.bottom = bottom;
            System.out.println("CarBody init....");
        }
    }

    static class Bottom {
        //在创建地盘之前,需要先创建出轮子
        private Tire tire;
        public Bottom(Tire tire) {
            this.tire = tire;
            System.out.println("Bottom init....");
        }
    }

    static class Tire {
        //定义车轮的尺寸
        private int size;
        public Tire(int size) {
            this.size = size;
            System.out.println("tire init...");
        }
    }
}

在这里插入图片描述

这两种方式进行比较就可以看出,在传统方式中,如果想要创建 CarBody,就需要通过 Car创建,所以,创建 CarBody的控制权是在 Car 中的,同时,Car 也依赖于 CarBody,如果不创建 CarBody,Car就无法创建成功,同理,依次往下,而改进之后控制权发生了反转,不再是使用方对象创建并控制依赖对象了(比如车身是使用方,而需要使用底盘),而是把依赖对象注入到当前对象中,依赖对象不再由当前类控制了,这样,即使依赖对象发生改变,当前类也不会收到任何影响。这就是控制权反转,也就是 IoC 的实现思想;

IoC 容器

这一部分的代码就是 Ioc 容器所做的工作:我们所需要的资源不需要我们自己管理,而是由IoC容器帮我们创建并且管理

在这里插入图片描述

从上⾯也可以看出来,IoC容器具备以下优点:

资源不由使用资源的双方管理,而是由不使用资源的第三方管理,这样可以带来很多的好处:

1.资源集中管理,IoC容器会帮我们管理一些资源,我们需要使用时,只需要从IoC容器中取就可以了,实现了资源的可配置和易管理

2.我们在创建实例的时候不需要了解其中的细节,降低了使⽤资源双⽅的依赖程度,也就是耦合度

以上内容讲解了IoC的思想,下面,来将讲解一下 SpringIoC 和 DI 如何使用。

SpringIoC 和 DI

上面讲了,Spring 是一个 IoC 容器,主要用来管理对象

既然是容器,那么它就具备两个基础的操作:

  • 存对象
  • 取对象

在Spring容器中,被管理的对象称为“Bean(注意,这个Bean和@Bean含义是不一样的,这个Bean指的是对象)”,Spring 负责对象的创建及销毁,我们的程序只需要告诉 Spring 哪些对象需要存,以及如何从 Spring 中取对象。下面先讲解一下如何告诉 Spring 哪些对象需要进行管理。

五大注解

如果想要将某个类交给 IoC 容器进行管理,就需要在该类上面添加注解,Spring 给我们提供了五大类注解和一个方法注解

类注解:@Controller、@Component、@Service、@Repository、@Configuration

方法注解:@Bean

下面先讲解一下这五个类注解的使用:

@Controller

1.使用@Controller注解,告诉Spring来管理这个对象

package com.example.springioc;

import org.springframework.stereotype.Controller;

@Controller //告诉Spring来管理这个对象
public class ControllerDemo {
    public void sayHi() {
        System.out.println("hello Controller");
    }
}

2.从 Spring 中取对象,观察这个对象有没有被Spring创建了

@SpringBootApplication
public class SpringIoCApplication {

    public static void main(String[] args) {
        //获取Spring上下文
       ApplicationContext context = SpringApplication.run(SpringIoCApplication.class, args);
        //从Spring上下文中获取对象
        ControllerDemo bean = context.getBean(ControllerDemo.class);
        //调用对象中的方法,检查是否已经获取到了对象
        bean.sayHi();

  

从结果中可以看到,我们可以调用对象里的方法,表示Spring已经帮我们管理了这个对象。

在这里插入图片描述

获取bean对象的其他方式

在代码中,我们获取bean对象时,传递的参数是根据类型获取 bean 对象,除了这个,ApplicationContext 还提供了其他的获取bean对象的方式,而这些获取bean对象的功能都是由父类 BeanFactory 提供的,如下图:
在这里插入图片描述

public interface BeanFactory {
    //以上省略......
 
	//1.根据bean名称获取bean
    Object getBean(String var1) throws BeansException;
    
	//2.根据bean名称以及类型获取bean
    <T> T getBean(String var1, Class<T> var2) throws BeansException;

    // 3.按bean名称和构造函数参数动态创建bean,只适⽤于具有原型(prototype)作⽤域的bean
    Object getBean(String var1, Object... var2) throws BeansException;
	
    //4.根据bean类型获取bean
    <T> T getBean(Class<T> var1) throws BeansException;

    // 5.按bean类型和构造函数参数动态创建bean,只适⽤于具有原型(prototype)作⽤域的bean
    <T> T getBean(Class<T> var1, Object... var2) throws BeansException;
    //以下省略.......

上述的四种方式中,1、2、4 最常用,获取到的bean是一样的,掌握这三种即可。

其中1、2方式提到了使用 bean名称 获取bean,那么,bean名称又是什么???

SpringIoC 管理对象时,会给对象分配一个名字,就好像学校给每个学生都会分配一个学号,根据学号就可以找到某个学生,所以,Spring也是如此,根据bean名称,就可以找到bean。

bean 名称的规定:

1.bean 名称以小写字母开头,使用小驼峰的形式

例如:

类名:StudentController,bean名称:studentController

类名:classController,bean名称:classController

类名:schoolcontroller,bean名称:schoolController

2.当有多个字符并且字符的第一个和第二个字母都是大写,那么将保留原始大小写

例如:

类名:STudentController,bean名称:STudentController

类名:CLasscontroller,bean名称:CLasscontroller

根据这三种方式,进行代码演示:

@SpringBootApplication
public class SpringIoCApplication {

    public static void main(String[] args) {
        //获取Spring上下文对象
        ApplicationContext context = SpringApplication.run(SpringIoCApplication.class, args);
        //1.根据类名获取bean
        ControllerDemo bean1 = context.getBean(ControllerDemo.class);
        //2.根据bean名称+类名,获取bean
        ControllerDemo bean2 = context.getBean("controllerDemo", ControllerDemo.class);
        //3.根据bean名称获取bean
        ControllerDemo bean3 = (ControllerDemo)context.getBean("controllerDemo");
       System.out.println(bean1);
        System.out.println(bean2);
        System.out.println(bean3);

    }
}

在这里插入图片描述

获取bean对象,是⽗类BeanFactory提供的功能。 ApplicationContext VS BeanFactory(常⻅⾯试题)

  1. 继承关系和功能⽅⾯来说:Spring容器有两个顶级的接⼝:BeanFactory和ApplicationContext。其中BeanFactory提供了基础的访问容器的能⼒,⽽ApplicationContext属于BeanFactory的⼦类,它除了继承了BeanFactory的所有功能之外,它还拥有独特的特性,还添加了对国际化⽀持、资源访问⽀持、以及事件传播等⽅⾯的⽀持.
  2. 性能⽅⾯来说:ApplicationContext是⼀次性加载并初始化所有的Bean对象,⽽BeanFactory是需要那个才去加载那个,因此更加轻量.(空间换时间)

@Service

使用 @Service 注解告诉Spring来管理某个对象

@Service

public class ServiceDemo {
    public void sayHi() {
        System.out.println("hello Service");
    }
}
@SpringBootApplication
public class SpringIoCApplication {

    public static void main(String[] args) {
        //获取Spring上下文对象
        ApplicationContext context = SpringApplication.run(SpringIoCApplication.class, args);
        ServiceDemo bean = context.getBean(ServiceDemo.class);
        bean.sayHi();
    }

在这里插入图片描述

@Component

使用 @Component 注解告诉Spring来管理某个对象

@Component
public class ComponentDemo {
    public void sayHi() {
        System.out.println("hello Component");
    }
}
@SpringBootApplication
public class SpringIoCApplication {

    public static void main(String[] args) {
        //获取Spring上下文对象
        ApplicationContext context = SpringApplication.run(SpringIoCApplication.class, args);
        ComponentDemo bean = context.getBean(ComponentDemo.class);
        bean.sayHi();
    }
}

在这里插入图片描述

@Repository

使用 @Repository注解告诉Spring来管理某个对象

@Repository
public class RepositoryDemo {
    public void sayHi() {
        System.out.println("hello Repository");
    }
}
@SpringBootApplication
public class SpringIoCApplication {

    public static void main(String[] args) {
        //获取Spring上下文对象
        ApplicationContext context = SpringApplication.run(SpringIoCApplication.class, args);
        RepositoryDemo bean = context.getBean(RepositoryDemo.class);
        bean.sayHi();
    }
}

在这里插入图片描述

@Configuration

使用 @Configuration注解告诉Spring来管理某个对象

@Configuration
public class ConfigurationDemo {
    public void sayHi() {
        System.out.println("hello Configuration");
    }
}
@SpringBootApplication
public class SpringIoCApplication {

    public static void main(String[] args) {
        //获取Spring上下文对象
        ApplicationContext context = SpringApplication.run(SpringIoCApplication.class, args);
        ConfigurationDemo bean = context.getBean(ConfigurationDemo.class);
        bean.sayHi();
    }
}

在这里插入图片描述

为什么要有这么多的类注解

首先呢,分成这么多的注解是为了和分层进行呼应,当成程序员看到注解时,就能直接明白这个类的用途。

  • @Controller:控制层,接收请求,对请求进行处理,进行响应

  • @Service:业务层,处理具体的业务逻辑

  • @Repository:数据访问层,负责数据访问操作

  • @Configuration:配置层,处理项目中的配置信息

    这样标识的作用和我们现实中的车牌号是差不多的,我们的车牌号就是唯一的,标识一个车辆,但是,为什么不一样呢?

    比如,河南的车牌号就是:豫X:xxxxx,北京的车牌号就是:京X:xxxxx,而且同一个省的不同市也是不一样的,这样做的好处就是处理可以节约号码,同时也可以根据车牌号就可以直观的明白车辆的归属地

    程序的应用分层,调用如下:

    在这里插入图片描述

类注解之间的关系

在这里插入图片描述

从源码中,可以看到,@Controller、@Repository、@Service、@Configuration 都被@Component注解修饰,也可以理解成这四个注解都是 @Component 注解的“子类”,@Component 是一个元注解,也就是可以注解其它类注解的注解,而@Controller、@Repository、@Service、@Configuration 都是@Component的衍生注解。

方法注解 @Bean

上述的类注解都是需要加在某个类上,但是存在两个问题:

  1. 引入外部包的类时,就无法添加类注解

  2. 一个类,创建多个不同的对象时,无法添加类注解

使用@Bean方法注解,就可以解决以上问题,方法如下:

@Bean注解要搭配类注解使用,才能正常的将对象交给Spring进行管理,单独的使用@Bean注解是会报错的。这里搭配哪个类注解都可以。

1.创建一个User类,假设User是一个外部类


public class User {
    private String name;
    private Integer age;
}

2.利用方法注解,将User交给Spring进行管理

@Component
public class BeanDemo {
    @Bean
    public User user() {
        User user = new User();
        user.setName("张三");
        user.setAge(19);
        return user;
    }
}

3.获取到bean对象并打印

@SpringBootApplication
public class SpringIoCApplication {

    public static void main(String[] args) {
        //获取Spring上下文对象
        ApplicationContext context = SpringApplication.run(SpringIoCApplication.class, args);
        User bean = context.getBean(User.class);
        bean.toString();
    }
}

在这里插入图片描述

这样就可以使用方法注解将外部类交给Spring进行管理

对于同一个类,如何定义多个对象呢?

方法如下:

@Component
public class BeanDemo {
    @Bean
    public User user1() {
        User user = new User();
        user.setName("张三");
        user.setAge(19);
        return user;
    }
    @Bean
    public User user2() {
        User user = new User();
        user.setName("李四");
        user.setAge(30);
        return user;
    }
}
@SpringBootApplication
public class SpringIoCApplication {

    public static void main(String[] args) {
        //获取Spring上下文对象
        ApplicationContext context = SpringApplication.run(SpringIoCApplication.class, args);
        //@Bean注解的bean,bean名称就是方法名称
        User user1 = (User)context.getBean("user1");
        User user2 = (User)context.getBean("user2");
        System.out.println(user1);
        System.out.println(user2);
    }
}

在这里插入图片描述

注意:使用方法注解@Bean进行注解时,bean的名称时方法的名称

重命名 bean

可以通过设置@Bean中的name属性,对bean进行重命名,代码如下:

使用u1代表user1,使用u2代表user2

@Component
public class BeanDemo {
    @Bean(name = {"u1","user1"})
    public User user1() {
        User user = new User();
        user.setName("张三");
        user.setAge(19);
        return user;
    }
	
    //这里的name=也可以省略
    @Bean({"u2","user2"})
    public User user2() {
        User user = new User();
        user.setName("李四");
        user.setAge(30);
        return user;
    }
}

此时就可以通过u1获取到user1,通过u2获取到user2

@SpringBootApplication
public class SpringIoCApplication {

    public static void main(String[] args) {
        //获取Spring上下文对象
        ApplicationContext context = SpringApplication.run(SpringIoCApplication.class, args);
        //@Bean注解的bean,bean名称就是方法名称
        User user1 = (User)context.getBean("u1");
        User user2 = (User)context.getBean("u2");
        System.out.println(user1);
        System.out.println(user2);
    }
}

在这里插入图片描述

扫描路径

上面讲解了五大注解和方法注解,那么,使用这些注解声明的bean一定会生效吗?换句话说,使用了这些注解就一定能够保证声明的类能够交给SpringIoC管理吗?

这是不一定的,因为还需要注意的一个问题就是:扫描路径。

扫描路径是指在项目启动时,Spring 会扫描启动类所在的包,没有启动类的包不会被Spring扫描,也就不会交给Spring进行管理。

启动类:被@SpringBootApplication修饰的类称为启动类。

如下图演示:

启动类 SpringIoCApplication 在controller包中,尝试从SpringIoC中获取ServiceDemo,查看ServiceDemo是否交给了Spring来管理。

结果发生了报错

在这里插入图片描述

在这里插入图片描述

下面再尝试获取ControllerDemo,就可以获取成功。

在这里插入图片描述

所以,得出结论:在使用五大注解时,要想生效,必须得在扫描路径下。

能让Spring扫描这些注解的原因是,@SpringBootApplication 注解被 @ComponentScan 这个注解所注解了,这个注解就是来配置扫描路径的。如下图:

配置了新的扫描路径后,在扫描时,就把ServiceDemo交给了Spring来管理,所以,就可以从Spring中获取到bean。

在这里插入图片描述

@ComponentScan 这个注解可以不用显示的配置,因为他已经包含在了启动类的声明注解中,扫描范围就是启动类所在的包及其子包

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Android 13 aosp 默认关闭SELinux

通过adb修改 adb root adb shell setenforce 0 // 开SELinux&#xff0c;设置成模式permissive adb shell setenforce 1 // 关SELinux&#xff0c;设置成模式enforce adb shell getenforce // 获取当前SELinux状态源码修改 Android_source/system/core/init/selinu…

JS-导入导出

export和export default是ES6中导出模块中变量的语法 导入导出变量 //导出方法&#xff08;js文件中&#xff09; export const 变量名值//导入方法 对应导入的变量&#xff0c;一定要加花括号 import {变量名} from js文件路径 导入导出函数 //导出方法&#xff08;js文件中…

2024.1IDEA 到2026年

链接&#xff1a;https://pan.baidu.com/s/1hjJEV5A5k1Z9JbPyBXywSw?pwd9g4i 提取码&#xff1a;9g4i解压之后,按照 操作说明.txt 操作; IntelliJ IDEA 2024.1 (Ultimate Edition) Build #IU-241.14494.240, built on March 28, 2024 Licensed to gurgles tumbles You have…

福汇美股开户教程

福汇作为全球知名的外汇交易平台&#xff0c;也提供美股交易服务。在福汇交易美股&#xff0c;首先需要开立一个福汇账户。本教程将详细介绍福汇美股开户流程。 第一步&#xff1a;访问福汇官网并填写开户表格 访问福汇美股入口点击页面顶部的“开户”按钮。选择您的国籍&…

JetsonNano —— Windows下对Nano板卡烧录刷机(官方教程)

介绍 NVIDIA Jetson Nano™ 开发者套件是一款面向创客、学习者和开发人员的小型 AI 计算机。按照这个简短的指南&#xff0c;你就可以开始构建实用的 AI 应用程序、酷炫的 AI 机器人等了。 烧录刷机 1、下载 Jetson Nano开发者套件SD卡映像&#xff0c;并记下它在计算机上的保存…

初探MFC程序混合使用QT

一、背景 随着操作系统国产化替代的趋势越发明显&#xff0c;软件支持国际化、跨平台&#xff0c;已然是必须做的一件事情。原有的软件UI层用的是MFC&#xff0c;将其换成QT&#xff0c;想必是一种较好的方案。对于大型软件&#xff0c;特别是已发布&#xff0c;但还处于不断迭…

vue 开发环境的搭建

一、整个流程&#xff1a; 安装nodejs >> 安装vue >> 安装vue-cli >> 初始化 webpack(生成代码) >> 安装依赖 >> 运行vue程序 二、详细安装流程&#xff1a; 1.安装nodejs 下载&#xff1a;https://nodejs.org/dist/v12.18.3/node-v12.18.3-x…

《米小圈上学记》|快乐读书,从身边的人身边的事开始!

时间&#xff0c;抓住了就是黄金&#xff0c;虚度了就是流水;书&#xff0c;看了就是学问&#xff0c;没看就是废纸:抱负&#xff0c;努力了才叫幻想&#xff0c;放弃了那只是妄想。读书&#xff0c;不一定能转变命运&#xff0c;但肯定能让我们安静&#xff0c;安静本身就是一…

【触摸案例-手势解锁案例-连线到按钮 Objective-C语言】

一、接下来,我们接着来说这个,连线的问题啊, 1.连线的问题啊,也就是说,我现在点击一个按钮, 在移动到下一个按钮的时候,在两个按钮中间,在两个按钮都亮起来的时候呢,我们肯定是让它去画一条线的,那么, 1)首先,如果我现在从第一个按钮,连到第二个按钮,那么,这条…

二叉树的实现(详解,数据结构)

目录 一&#xff0c;二叉树需要实现的功能 二&#xff0c;下面是各功能详解 0.思想&#xff1a; 1.创建二叉树结点&#xff1a; 2.通过前序遍历的数组"ABD##E#H##CF##G##"构建二叉树 3.二叉树销毁&#xff1a; 4.前序遍历&#xff1a; 5.中序遍历&#xff1a;…

小红书「打工人」生存现状实录

近年来&#xff0c;“打工人”一词频繁出现在内容场景中。调研小红书数据显示&#xff0c;近一年“打工人、职场”相关笔记声量增长440%&#xff0c;预估互动总量增长128%&#xff0c;热度高涨。 职场年轻人正披着“打工人”的外壳&#xff0c;不断自嘲中寻求身份认同。基于此&…

各种流量包特征

[CVE-2013-1966] Apache Struts2 远程命令执行漏洞 要执行的命令在exec里面&#xff0c;而且回显数据包里面有明显执行结果回显 [CVE-2017-8046] Spring Data Rest 远程命令执行漏洞 回显不明显&#xff0c;考试提供的解码工具不能解密&#xff0c; [CVE-2017-12149] JBOSS…

java多线程编码应用1——java多线程CompletableFuture使用技巧

在实际项目开发过程中&#xff0c;大部分程序的执行顺序都是按照代码编写的先后顺序&#xff0c;依次从上往下挨个执行的&#xff0c;但是对于统计或者批量操作数据时&#xff0c;是否有更好的方案呢&#xff1f;这时候就可以考虑使用多线程编程&#xff0c;异步并行执行多个任…

Flink checkpoint 源码分析- Checkpoint snapshot 处理流程

背景 在上一篇博客中我们分析了代码中barrier的是如何流动改的。Flink checkpoint 源码分析- Checkpoint barrier 传递源码分析-CSDN博客 最后跟踪到了代码org.apache.flink.streaming.runtime.io.checkpointing.CheckpointedInputGate#handleEvent 现在我们接着跟踪相应代…

投资线上黄金是否属于外汇交易?探究黄金与外汇市场的关系

在金融市场中&#xff0c;线上黄金投资和外汇交易都是备受关注的领域。许多人可能会混淆这两者&#xff0c;认为投资线上黄金也是一种外汇交易。但实际上&#xff0c;尽管线上黄金和外汇交易有一些相似之处&#xff0c;但它们在本质上是不同的投资领域。本文将探讨投资线上黄金…

前端 Android App 上架详细流程 (Android App)

1、准备上架所需要的材料 先在需要上架的官方网站注册账号。提前把手机号&#xff0c;名字&#xff0c;身份证等等材料准备好&#xff0c;完成开发者实名认证&#xff1b;软著是必要的&#xff0c;提前准备好&#xff0c;软著申请时间比较长大概需要1-2周时间才能下来&#xf…

流畅的python-学习笔记_序列修改+散列+切片

vector第一版 reprlib.repr用于选取有限长度较长变量 vector第二版切片 注意切片还有indices属性&#xff0c;它可以入参一个序列长度&#xff0c;根据此序列长度&#xff0c;转化不规矩的start stop stride&#xff0c; vector第三版动态存取属性 obj.attra时&#xff0c;先…

构建 imx6ull sd 卡启动

1. 硬件环境 imx6ull 256MB tf 卡 512 MB 的 ddr&#xff1b; ubuntu 20.04&#xff1b; 芯片默认的启动方式是通过 LCD_DATA0 ~ LCD_DATA23&#xff1b;上下拉方式来确认的&#xff1b; 需要注意的上下拉是 BOOT_CFG1[7] BOOT_CFG1[6] BOOT_CFG1[5] 启动选择 和 BOOT_CF…

leetcode-矩阵最长递增路径-102

题目要求 思路 1.通过双循环去把每一个结点作为起始点进行统计&#xff0c;将返回的路径长度存放在res中&#xff0c;取最大的res的长度。 2.递归中需要的几个值&#xff0c;x和y当前结点的坐标&#xff0c;pre用于存储上一个结点的元素值&#xff0c;因为要求是路径上的元素是…

3D 交互展示该怎么做?

在博维数孪&#xff08;Bowell&#xff09;平台制作3D交互展示的流程相对简单&#xff0c;主要分为以下几个步骤&#xff1a; 1、准备3D模型&#xff1a;首先&#xff0c;你需要有一个3D模型。如果你有3D建模的经验&#xff0c;可以使用3ds Max或Blender等软件自行创建。如果没…