IoC设计模式详解:控制反转的核心思想

前言:在软件开发中,设计模式是一种经过验证的、在特定场景下能有效解决问题的解决方案。控制反转(Inversion of Control,IoC) 作为一种设计模式,通过让程序的控制流和对象管理反转,从而使得代码的解耦性和可维护性大大提高。
学习Spring,其中一个核心——Spring IoC容器,则是这一模式在Spring框架中的具体实现。

一、IoC简介

IoC(控制反转)是一种设计模式(原则),核心思想是将对象的创建、初始化和依赖关系的管理从程序中反转出来,交由外部容器(例如 Spring)来负责。

也就是说,程序不再控制对象的创建和生命周期,而是通过外部容器来进行管理,这样可以实现更高的解耦和灵活性。

1d8ce4dd-a4ea-4e26-93ea-c3d26dda88c6

控制反转显然是一个抽象的概念,举一个鲜明的例子来说明:

在现实生活中,人们要用到一样东西的时候,第一反应就是去找到这件东西,比如想喝新鲜橙汁,在没有饮品店的日子里,最直观的做法就是:买果汁机、买橙子,然后准备开水。值得注意的是:这些都是你自己 “主动”创造的过程,也就是说一杯橙汁需要你自己创造。

然而到了今时今日,由于饮品店的盛行,当我们想喝橙汁时,第一想法就转换成了找到饮品店的联系方式,通过电话等渠道描述你的需要、地址、联系方式等,下订单等待,过一会儿就会有人送来橙汁了。

image

请注意你并没有“主动”去创造橙汁,橙汁是由饮品店创造的,而不是你,然而也完全达到了你的要求,甚至比你创造的要好上那么一些。

所以控制反转IoC(Inversion of Control)是说创建对象的控制权进行转移,以前创建对象的主动权和创建时机是由自己把控的,而现在这种权力转移到第三方,比如转移交给了IoC容器,它就是一个专门用来创建对象的工厂,你要什么对象,它就给你什么对象,有了 IoC容器,依赖关系就变了,原先的依赖关系就没了,它们都依赖IoC容器了,通过IoC容器来建立它们之间的关系。

二、发展

IoC(Inversion of Control,控制反转)设计原则的发展历程反映了软件工程领域中对解耦合、模块化和可测试性的不断追求。以下是IoC设计理念从萌芽到成熟的主要发展阶段:

1. 早期探索与概念形成
  • 20世纪80年代至90年代初:在面向对象编程(OOP)逐渐普及的背景下,开发者开始意识到直接在代码中创建和管理依赖会导致高度耦合的问题。此时,一些先驱者开始思考如何将这种控制权转移给外部组件或框架。
  • MVC模式:Model-View-Controller架构是最早体现IoC思想的设计模式之一,它通过分离关注点来实现一定程度上的解耦。
2. IoC概念的正式提出
  • 1996年:Michael Mattson在他的论文《Object-Oriented Frameworks: A Survey》中首次使用了“控制反转”这个术语,用来描述框架如何接管应用程序的主控流程。

  • 2004年:Martin Fowler在其文章《Inversion of Control Containers and the Dependency Injection pattern》中详细阐述了IoC的概念,并引入了“依赖注入”(Dependency Injection, DI)这一术语作为IoC的具体实现方式。这篇文章极大地推动了DI模式的流行。

    image

​ ”软件开发教父“—Martin Fowler

3. 框架与工具的支持
  • Spring框架的崛起:Spring于2004年发布,迅速成为Java企业级应用开发中最受欢迎的框架之一。Spring的核心特性之一就是其强大的IoC容器,它不仅实现了基本的DI功能,还提供了丰富的配置选项和生命周期管理机制。
  • 其他框架:除了Spring,还有许多其他框架也采用了IoC/DI模式,如PicoContainer、Guice(Google推出的轻量级Java依赖注入框架)、以及.NET平台下的Ninject等。这些框架进一步促进了IoC理念在不同编程语言和技术栈中的传播。
4. 扩展与深化
  • AOP集成:随着面向切面编程(Aspect-Oriented Programming, AOP)的发展,IoC容器开始支持AOP特性,允许开发者以声明式的方式定义横切关注点(如日志记录、事务管理),从而进一步减少了代码中的重复劳动。
  • 事件驱动架构:现代应用越来越多地采用事件驱动的方式进行组件间通信,而IoC容器提供的事件发布/订阅机制正好满足了这一需求,使得系统更加灵活且易于扩展。
  • 微服务架构:在微服务架构中,每个服务都是独立部署的单元,它们之间的交互通常通过API网关或消息队列完成。在这种情况下,IoC容器的作用变得更加重要,因为它可以帮助管理和协调各个服务之间的依赖关系。
5. 当前趋势与发展
  • 函数式编程与响应式编程:随着函数式编程语言(如Scala、Clojure)以及响应式编程模型(Reactive Programming)的兴起,IoC/DI模式也在不断演进,以适应新的编程范式。例如,在响应式流处理中,依赖注入可以用于简化异步操作和背压管理。
  • 云原生应用:在云环境中,服务发现、配置管理等功能变得至关重要。IoC容器可以通过集成服务注册中心(如Eureka、Consul)和分布式配置管理系统(如Spring Cloud Config),为云原生应用提供动态的依赖管理和配置更新能力。
  • Serverless架构:无服务器计算模式下,函数即服务(FaaS)平台自动管理资源分配和扩展。尽管在这种环境下不再需要传统的IoC容器,但类似的思想仍然适用于定义和管理函数之间的依赖。

三、特点

IOC(Inversion of Control,控制反转)设计模式 是一种通过将对象的创建和依赖关系管理交给外部容器来实现松耦合的设计模式。它通过依赖注入(DI)等方式解耦系统中的组件,从而提升系统的可扩展性、可维护性和测试性。然而,像任何设计模式一样,IOC模式也有其优缺点。

优点
  1. 降低耦合度

    • IOC通过将依赖关系交给容器管理,避免了类与类之间的紧耦合。类之间不需要知道对方的具体实现,减少了模块之间的依赖,提高了系统的灵活性和可扩展性。
  2. 提高代码的可维护性

    • 由于类不再直接管理其依赖关系,修改某个组件的实现不会直接影响到其他组件。这使得系统更容易维护,尤其是在大型系统中,修改或替换某个依赖时,不需要修改应用程序的其他部分。
  3. 增强代码的可测试性

    • IOC容器通过依赖注入的方式将依赖项外部化,便于进行单元测试。在测试过程中,可以轻松地替换实际的依赖项为mock对象或模拟实现,独立地测试每个模块。
  4. 提升系统的灵活性和可扩展性

    • IOC容器提供了更为灵活的依赖关系配置。通过配置文件或注解方式,开发者可以在不修改代码的情况下替换不同的实现或改变依赖关系,从而增强系统的扩展性。
  5. 集中式管理对象生命周期

    • IOC容器负责对象的创建、管理和销毁,开发人员无需关注对象的生命周期和依赖关系的维护,从而简化了代码结构。
  6. 符合“面向接口编程”原则

    • IOC设计模式鼓励通过接口来定义依赖,而不是依赖于具体的实现类。这不仅提升了系统的灵活性,也使得系统可以更容易地进行替换和扩展。
  7. 有助于实现设计模式和最佳实践

    • IOC设计模式通常与其他设计模式(如工厂模式、策略模式等)配合使用,使得系统的设计更加规范,符合常见的设计最佳实践。

缺点
  1. 学习曲线陡峭

    • IOC和依赖注入(DI)虽然能带来很大的灵活性,但需要开发者对IOC容器、依赖注入以及相关的配置方式有一定的理解和掌握。在一些小型项目或团队中,过度使用IOC可能会带来额外的学习成本和开发复杂度。
  2. 增加系统的复杂性

    • 在使用IOC时,系统的结构变得更加抽象,特别是在没有合理的设计规范时,可能导致过度依赖配置或容器,导致系统的复杂性增加。
    • 例如,多个服务的依赖关系可能通过不同的配置文件或注解来管理,如果管理不当,可能导致系统难以理解和调试。
  3. 性能开销

    • 由于IOC容器负责对象的管理和依赖注入,容器在初始化阶段会进行对象的创建和依赖解析,可能会引入额外的性能开销,尤其是在大型应用中,启动时会有一定的延迟。
    • 此外,反射机制(如果使用)也会影响性能,虽然这种影响通常是微不足道的,但在一些高性能要求的场景中可能需要谨慎考虑。
  4. 调试和跟踪困难

    • 由于IOC容器负责管理对象的生命周期和依赖关系,调试时可能不易追踪依赖关系和对象的创建过程。尤其是在大型应用中,依赖关系可能变得非常复杂,不容易找到对象之间的依赖路径和问题所在。
    • 此外,依赖注入的配置错误(如错过某个依赖)可能导致应用启动失败,排查这些错误有时会变得复杂。
  5. 过度使用可能导致设计问题

    • 虽然IOC可以有效解耦,但在某些场景中,过度使用IOC可能会导致设计不够清晰。比如,如果没有合理的接口和模块划分,依赖注入可能会导致大量的配置和接口,反而增加了系统的复杂度。
    • 如果不加以控制,过度的依赖注入和反转控制可能让代码结构变得难以理解和维护。
  6. 隐式依赖

    • 在一些复杂的IOC实现中,依赖注入可能是隐式的,这意味着开发者可能并不直接看到某个类所依赖的所有对象。在这种情况下,阅读和理解代码可能需要花费额外的时间,尤其是当依赖关系非常复杂时。
  7. 可能增加配置和管理工作

    • 使用IOC容器通常需要大量的配置(如XML文件或注解配置)来管理对象的创建和依赖注入。如果容器配置不当,可能导致配置文件变得非常庞大和复杂,影响代码的清晰度和维护性。

四、实现方式

常见的 IOC 实现方式: 依赖注入(Dependency Injection, DI):最常用的 IOC 实现方式,容器通过构造函数、属性或方法来注入对象的依赖。 服务定位(Service Locator):通过集中管理的方式,容器提供一个访问接口,让程序员通过此接口获取到需要的依赖。

IOC(Inversion of Control,控制反转) 是一种设计模式,它的核心思想是将控制对象创建和依赖关系管理的责任从类内部转移到外部容器中。实现IOC的方式有多种,常见的主要有以下几种:

1. 依赖注入(Dependency Injection, DI)

依赖注入是IOC实现的主要方式之一。它通过将类的依赖关系(即对象的引用)交给外部容器来管理,从而减少类之间的耦合。

依赖注入有三种方式

  • 构造器注入(Constructor Injection)
    依赖通过构造器传入。在对象创建时,IOC容器会通过构造函数将依赖注入到目标对象中。

    public class Service {
        private final Repository repository;
    
        // 依赖通过构造器注入
        public Service(Repository repository) {
            this.repository = repository;
        }
    
        public void execute() {
            repository.doSomething();
        }
    }
    
  • Setter注入(Setter Injection)
    依赖通过类的setter方法注入。IOC容器会通过反射调用setter方法为类提供所需的依赖。

    public class Service {
        private Repository repository;
    
        // 通过setter方法注入依赖
        public void setRepository(Repository repository) {
            this.repository = repository;
        }
    
        public void execute() {
            repository.doSomething();
        }
    }
    
  • 接口注入(Interface Injection)
    依赖通过接口注入。实现特定接口的类会提供一个方法,允许外部容器注入依赖。

    public interface RepositoryAware {
        void setRepository(Repository repository);
    }
    
    public class Service implements RepositoryAware {
        private Repository repository;
    
        @Override
        public void setRepository(Repository repository) {
            this.repository = repository;
        }
    
        public void execute() {
            repository.doSomething();
        }
    }
    

2. 依赖查找(Dependency Lookup)

依赖查找是IOC的另一种实现方式,其中对象本身没有直接接收依赖,而是通过查找容器来获取其依赖。依赖查找通常通过容器的API来获取需要的对象。

  • 查找容器:应用程序通过容器API获取依赖对象。
   public class Service {
       private ApplicationContext context;

       public Service(ApplicationContext context) {
           this.context = context;
       }
   
       public void execute() {
           // 依赖查找
           Repository repository = (Repository) context.getBean(Repository.class);
           repository.doSomething();
       }
   }

依赖查找虽然能实现IOC的目的,但它往往较为依赖容器,且不如依赖注入(DI)方式灵活,因为它涉及到容器的显式调用,违反了“松耦合”的原则。

3. Service Locator

Service Locator模式是一种经典的IOC实现方式。通过一个中央的服务定位器(Service Locator),对象可以在运行时获取依赖。Service Locator实际上是依赖查找的一个变体。

在这种方式中,类通过调用一个服务定位器来查找并获取它所需要的依赖对象。

   public class Service {
       public void execute() {
           Repository repository = ServiceLocator.getRepository();
           repository.doSomething();
       }
   }

缺点

  • Service Locator模式和依赖查找类似,容易使代码过于依赖容器,违反了松耦合的原则。
  • 难以进行单元测试,因为它隐藏了类的依赖关系。

4. 使用框架实现IOC

目前,大多数IOC的实现都是通过一些流行的框架来实现的,最典型的框架包括:

  • Spring Framework
    Spring是一个非常流行的Java框架,它通过依赖注入和AOP(面向切面编程)实现了强大的IOC容器。Spring可以通过XML配置、注解配置或者Java配置来实现IOC,自动管理对象的创建、生命周期和依赖注入。

    • XML配置方式

      <bean id="service" class="com.example.Service">
          <constructor-arg ref="repository"/>
      </bean>
      
      <bean id="repository" class="com.example.Repository"/>
      
    • 注解配置方式

      @Component
      public class Service {
          private final Repository repository;
      
          @Autowired
          public Service(Repository repository) {
              this.repository = repository;
          }
      }
      
      @Component
      public class Repository {
          public void doSomething() {
              // ...
          }
      }
      

    在Spring中,IOC容器会负责对象的实例化、依赖注入以及生命周期管理。

  • Guice (Google)
    Guice是Google推出的一个轻量级的DI框架,主要用于Java应用。它同样实现了IOC,使用注解和接口实现依赖注入,支持构造器注入、方法注入等多种方式。

    public class Service {
        private final Repository repository;
    
        @Inject
        public Service(Repository repository) {
            this.repository = repository;
        }
    }
    
  • Dagger
    Dagger是一个静态依赖注入框架,广泛用于Android应用开发中。它通过代码生成的方式来实现依赖注入,性能较高。

    @Component
    public interface ServiceComponent {
        Service getService();
    }
    
    @Module
    public class ServiceModule {
        @Provides
        public Repository provideRepository() {
            return new Repository();
        }
    }
    

5. 反射机制

在一些情况下,IOC容器使用反射机制动态创建对象并注入依赖。反射机制允许在运行时确定类的构造函数、字段和方法,从而实现依赖注入。

  • 反射实例化:在没有框架支持的情况下,可以手动使用反射创建对象并注入依赖。
   Constructor<?> constructor = Service.class.getConstructor(Repository.class);
   Service service = (Service) constructor.newInstance(repository);

注意:反射带来的性能开销较大,不推荐过度使用。

总结

IOC的实现方式多种多样,最常见的有依赖注入(DI)、依赖查找和Service Locator等方式。在现代开发中,使用框架(如Spring、Guice等)是最常见的方式,它们通过提供IOC容器来自动管理对象的创建、生命周期和依赖注入。依赖注入通常被认为是最优的IOC实现方式,因为它能够有效地解耦系统,提升系统的可维护性和测试性。

五、应用场景

IOC 主要用于 解耦 组件、提高灵活性可维护性,其典型应用场景包括:

1. 大型企业级应用开发

在大型应用中,各个模块和组件之间的依赖关系通常非常复杂。通过使用 IOC,可以将这些依赖关系交给外部容器(如 Spring 框架)来管理,从而降低系统的耦合度,使得系统更容易扩展和维护。

场景举例

  • 开发企业级管理系统(如 ERP 系统、CRM 系统等)时,可能涉及多个服务、数据访问层和业务逻辑层,通过 IOC 容器可以方便地注入依赖,进行灵活的模块化和配置。

2. 组件化和插件化的架构

在需要实现插件化或可扩展架构的应用中,IOC 可以帮助动态加载和管理不同的插件或模块。通过定义接口和注入不同的实现类,系统能够在运行时根据配置和需要灵活加载不同的组件。

场景举例

  • 在一个支持多种数据库的应用中,不同的数据库驱动实现可以作为插件模块,在启动时通过 IOC 容器注入和切换。
  • 在支付系统中,不同的支付方式(如支付宝、微信支付、银联等)可以作为插件模块,在运行时动态注入。

3. 跨平台开发

在跨平台开发中,不同平台的实现可能会有所不同。通过 IOC,可以将平台相关的实现与平台无关的业务逻辑解耦,使得相同的业务逻辑能够在多个平台上共享。

场景举例

  • 在 Android 和 iOS 平台上开发应用时,可以通过 IOC 将平台相关的代码(如网络请求、文件存储等)与平台无关的业务逻辑解耦,使得相同的业务逻辑代码在不同平台之间共享。

4. 单元测试与Mock

在进行单元测试时,使用 IOC 可以更方便地替换和模拟组件依赖,从而实现更高效的测试。通过 IOC 注入假对象(Mock 对象),可以让测试更加集中在某一单一组件的行为上,而不依赖于其实际的依赖。

场景举例

  • 进行单元测试时,可以用 Mock 对象替代数据库访问层、外部服务等,确保测试只关注业务逻辑本身。

5. Web 应用开发

在 Web 应用开发中,尤其是使用 MVC 架构的应用中,IOC 是非常重要的。通过 IOC 容器,Web 应用可以轻松地管理控制器、服务层和数据访问层之间的依赖关系。Spring MVC 就是通过 IOC 实现了控制器、服务和 DAO 的依赖注入,使得 Web 应用更加松耦合、易于维护。

场景举例

  • 在开发一个 Web 应用时,可以通过 IOC 容器注入控制器(Controller)和服务层(Service),将视图(View)与业务逻辑(Service)和数据层(Repository)解耦,从而简化了 Web 应用的开发和扩展。

6. 异步任务和消息队列

在处理异步任务、消息队列和事件驱动架构时,IOC 可以帮助简化事件处理的注册和触发。使用 IOC 容器管理任务和事件的监听器,可以使得事件和任务处理模块之间的依赖关系更加灵活。

场景举例

  • 在分布式系统中,使用消息队列(如 RabbitMQ、Kafka 等)来异步处理任务时,可以通过 IOC 注入消息处理类,使得消息处理逻辑与其他业务逻辑解耦,提高系统的可扩展性。

在本文中,我们深入探讨了IoC(控制反转)设计模式及其在现代软件开发中的应用。通过引入IoC,我们能够有效地降低模块间的耦合度,提高系统的灵活性和可维护性。希望这些知识对你有所帮助,并能在实际开发中灵活应用,打造更加高效和优雅的代码架构。

3ec794dacde34583aafafe15b1490012

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

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

相关文章

使用C#构建一个论文总结AI Agent

前言 我觉得将日常生活中一些简单重复的任务交给AI Agent&#xff0c;是学习构建AI Agent应用一个很不错的开始。本次分享我以日常生活中一个总结论文的简单任务出发进行说明&#xff0c;希望对大家了解AI Agent有所帮助。任务可以是多种多样的&#xff0c;真的帮助自己提升了…

[Qt] 常用控件 | QWidget | “表白程序2.0”

目录 一、控件概述 控件体系的发展阶段&#xff1a; 二、QWidget 核心属性 核心属性概览&#xff1a; 1、enabled 2、Geometry 实例 1: 控制按钮的位置 实例 2: 表白 程序 i、Window Frame 的影响 ii、API 设计理念 iii、Geometry 和 FrameGeometry 的区别 &#xf…

GAN对抗生成网络(二)——算法及Python实现

1 算法步骤 上一篇提到的GAN的最优化问题是&#xff0c;本文记录如何求解这一问题。 首先为了表示方便&#xff0c;记&#xff0c;这里让最大的可视作常量。 第一步&#xff0c;给定初始的&#xff0c;使用梯度上升找到 ,最大化。关于梯度下降&#xff0c;可以参考笔者另一篇…

JAVA(二)【未完】

数据类型与变量 数据类型&#xff1a;基本数据类型&#xff1a;整型&#xff1a;byte short int long 浮点型&#xff1a;float double char 布尔型&#xff1a;boolean 引用数据类型&#xff1a;数组 类 接口 枚举类型 long b 10l;System.out.println(b);System.out.printl…

C语言day5:shell脚本

一、练习题1 定义一个find函数&#xff0c;查找ubuntu和root的gid并使用变量接收结果 二、练习题2 定义一个数组&#xff0c;写一个函数完成对数组的冒泡排序 三、练习题3 使用break求1-100中的质数&#xff08;质数&#xff1a;只能被1和它本身整除&#xff0c;如&#xff1a;…

R语言6种将字符转成数字的方法,写在新年来临之际

咱们临床研究中&#xff0c;拿到数据后首先要对数据进行清洗&#xff0c;把数据变成咱们想要的格式&#xff0c;才能进行下一步分析&#xff0c;其中数据中的字符转成数字是个重要的内容&#xff0c;因为字符中常含有特殊符号&#xff0c;不利于分析&#xff0c;转成数字后才能…

C语言面的向对象编程(OOP)

如果使用过C、C#、Java语言&#xff0c;一定知道面向对象编程&#xff0c;这些语言对面向对象编程的支持是语言级别的。C语言在语言级别不支持面向对象&#xff0c;那可以实现面向对象吗&#xff1f;其实面向对象是一种思想&#xff0c;而不是一种语言&#xff0c;很多初学者很…

C++ 基础思维导图(一)

目录 1、C基础 IO流 namespace 引用、const inline、函数参数 重载 2、类和对象 类举例 3、 内存管理 new/delete 对象内存分布 内存泄漏 4、继承 继承权限 继承中的构造与析构 菱形继承 1、C基础 IO流 #include <iostream> #include <iomanip> //…

聊聊前端框架中的process.env,env的来源及优先级(next.js、vue-cli、vite)

在平时开发中&#xff0c;常常使用vue、react相关脚手架创建项目&#xff0c;在项目根目录可以创建.env、.env.[mode]&#xff08;mode为development、production、test)、.env.local等文件&#xff0c;然后在项目中就可以通过process.env来访问相关的环境变量了。 下面针对如下…

基于云架构Web端的工业MES系统:赋能制造业数字化变革

基于云架构Web端的工业MES系统:赋能制造业数字化变革 在当今数字化浪潮席卷全球的背景下,制造业作为国家经济发展的重要支柱产业,正面临着前所未有的机遇与挑战。市场需求的快速变化、客户个性化定制要求的日益提高以及全球竞争的愈发激烈,都促使制造企业必须寻求更加高效、智…

LeetCode算法题——螺旋矩阵ll

题目描述 给你一个正整数n&#xff0c;生成一个包含1到n2所有元素&#xff0c;且元素按顺时针顺序螺旋排列的n x n正方形矩阵matrix 。 示例 输入&#xff1a;n 3 输出&#xff1a;[[1,2,3],[8,9,4],[7,6,5]]题解 思路&#xff1a; 将整个过程分解为逐圈填充的过程&#xf…

MySQL 01 02 章——数据库概述与MySQL安装篇

一、数据库概述 &#xff08;1&#xff09;为什么要使用数据库 数据库可以实现持久化&#xff0c;什么是持久化&#xff1a;数据持久化意味着将内存中的数据保存到硬盘上加以“固化”持久化的主要作用是&#xff1a;将内存中的数据存储在关系型数据库中&#xff0c;当然也可以…

GPU 进阶笔记(四):NVIDIA GH200 芯片、服务器及集群组网

大家读完觉得有意义记得关注和点赞&#xff01;&#xff01;&#xff01; 1 传统原厂 GPU 服务器&#xff1a;Intel/AMD x86 CPU NVIDIA GPU2 新一代原厂 GPU 服务器&#xff1a;NVIDIA CPU NVIDIA GPU 2.1 CPU 芯片&#xff1a;Grace (ARM)2.2 GPU 芯片&#xff1a;Hopper/B…

vite6+vue3+ts+prettier+eslint9配置前端项目(后台管理系统、移动端H5项目通用配置)

很多小伙伴苦于无法搭建一个规范的前端项目&#xff0c;导致后续开发不规范&#xff0c;今天给大家带来一个基于Vite6TypeScriptVue3ESlint9Prettier的搭建教程。 目录 一、基础配置1、初始化项目2、代码质量风格的统一2.1、配置prettier2.2、配置eslint2.3、配置typescript 3、…

ESLint+Prettier的配置

ESLintPrettier的配置 安装插件 ​​​​​​ 在settings.json中写下配置 {// tab自动转换标签"emmet.triggerExpansionOnTab": true,"workbench.colorTheme": "Default Dark","editor.tabSize": 2,"editor.fontSize": …

Cyber Security 101-Web Hacking-JavaScript Essentials(JavaScript 基础)

任务1&#xff1a;介绍 JavaScript &#xff08;JS&#xff09; 是一种流行的脚本语言&#xff0c;它允许 Web 开发人员向包含 HTML 和 CSS&#xff08;样式&#xff09;的网站添加交互式功能。创建 HTML 元素后&#xff0c;您可以通过 JS 添加交互性&#xff0c;例如验证、on…

《机器学习》从入门到实战——逻辑回归

目录 一、简介 二、逻辑回归的原理 1、线性回归部分 2、逻辑函数&#xff08;Sigmoid函数&#xff09; 3、分类决策 4、转换为概率的形式使用似然函数求解 5、对数似然函数 ​编辑 6、转换为梯度下降任务 三、逻辑回归拓展知识 1、数据标准化 &#xff08;1&#xf…

JDK8源码分析Jdk动态代理底层原理

本文侧重分析JDK8中jdk动态代理的源码&#xff0c;若是想看JDK17源码分析可以看我的这一篇文章 JDK17源码分析Jdk动态代理底层原理-CSDN博客 两者之间有着略微的差别&#xff0c;JDK17在JDK8上改进了不少 目录 源码分析 过程 生成的代理类大致结构 本文侧重分析JDK8中jdk…

ZYNQ初识6(zynq_7010)clock时钟IP核

基于板子的PL端无时钟晶振&#xff0c;需要从PS端借用clock1&#xff08;50M&#xff09;晶振 接下去是自定义clock的IP核封装&#xff0c;为后续的simulation可以正常仿真波形&#xff0c;需要注意顶层文件的设置&#xff0c;需要将自定义的IP核对应的.v文件设置为顶层文件&a…

深度学习模型格式转换:pytorch2onnx(包含自定义操作符)

将PyTorch模型转换为ONNX&#xff08;Open Neural Network Exchange&#xff09;格式是实现模型跨平台部署和优化推理性能的一种常见方法。PyTorch 提供了多种方式来完成这一转换&#xff0c;以下是几种主要的方法&#xff1a; 一、静态模型转换 使用 torch.onnx.export() t…