当前位置: 首页 > news >正文

【Spring学习】为什么Spring中的IOC(控制反转)能够降低耦合性(解耦)?

为什么Spring中的IOC能够降低耦合性?

  • 前言
  • 1.传统方式
  • 2.使用接口
  • 3.工厂方法
  • 4.反射改造工厂类
  • 5.IOC
  • 总结
  • 参考

前言

本文目标:本文旨在讲解为什么IOC能够降低耦合性。

情景:假设你是一个爱摸鱼的程序员,现在需要测试一个游戏,该游戏有很多动物(假设1万种,每种动物的叫声都不相同),现在需要测试动物的叫声是否能正常发出,老板让你3天时间完成任务,你如何在3天内完成任务并尽可能留出摸鱼时间。

1.传统方式

首先我们对可爱的Dog进行测试,我们打开了Dog.java文件,有如下代码:

public class Dog(){public void bark(){System.out.println("Bark");}
}

接下来进行测试:

public class TestGame {public static void main(String[] args) {Dog animal  = new Dog();animal.bark();//do something....}
}

然后,我们打开Cat.java,代码如下:

public class Cat(){public void meow(){System.out.println("Meow");}
}

我们将 Dog 替换为 Cat,但 Cat 没有 bark 方法,传统方式下可能的修改如下:

public class TestGame {public static void main(String[] args) {Cat animal  = new Cat();animal.meow();//do something....}
}

在我们的假设中有1万种动物,且每种动物的叫声都不相同,那我们需要逐一查看1万种动物的叫声实现方法,即要修改1万次的测试代码,假设修改代码和测试时间为1分钟,假设每天工作八小时(那是不可能的,这是理想状态下),那就是10000/60/8=18天,不仅不能摸鱼还得加班。那我一天干24小时,那就是10000/60/24=6天,这只有3天时间,这也没办法完成呀!

这时候我们发现了如下问题:
硬编码依赖:TestGame代码对特定的实现类(如 Dog 或 Cat)有直接依赖,不具有灵活性。
高耦合度:每次修改都需要手动调整代码(不同动物有不同的发声),增加了系统的维护成本。
不易扩展:每当新增动物(如 Bird)时,必须修改大量代码,难以应对系统复杂度的增加。

2.使用接口

为了避免传统方式中的硬编码依赖、低耦合度以及不易扩展的问题,我们引入面向接口编程。
爱摸鱼的程序员通过定义一个Animal接口,使得每个动物类只需要实现该接口,并提供自己的叫声实现方法。这样就不再需要在TestGame类中直接依赖具体的动物类,而是依赖接口。

首先,定义接口:

public interface Animal {void makeSound();
}

实现接口的具体动物类:

public class Dog implements Animal {@Overridepublic void makeSound() {System.out.println("Bark");}
}public class Cat implements Animal {@Overridepublic void makeSound() {System.out.println("Meow");}
}

修改测试类:

public class TestGame {public static void main(String[] args) {Animal animal = new Dog();  // 可以替换为其他动物类,如Catanimal.makeSound();  // 通过接口调用,不依赖具体类// do something....}
}

来对比下先后方法的不同:

1.打开一个动物的java文件,查看叫声方法。
2.复制类名和叫声方法。
3.修改类名和叫声方法。

修改后:
不用打开java文件查看叫声方法了,我们有接口。
1.打开一个动物的java文件,查看叫声方法。
2.复制类名和叫声方法
3.修改类名和叫声方法
方法变更如下:

1.复制类名
2.修改类名

通过上面的修改,我们来计算看看节约了多少时间,因为节约了打开文件查看叫声方法的步骤,我们假设减少了50%的时间,那18/2=9天,也就是说每天八小时我们还需要9天才能完成,要是一天24小时那就是6/2=3天。这啥也不干就是干活才能刚刚完成任务,这可不行我们还得继续改。

优势
灵活性提高:通过接口Animal,TestGame类不再依赖于具体的动物类,而是依赖于一个抽象的接口。我们只需更换Dog为Cat等其他动物类,而不必修改TestGame类。
易扩展性:当新增动物类时,只需要实现Animal接口,并提供自己的makeSound()实现即可,不需要修改TestGame类的代码,系统更容易扩展。

缺点
1.需要创建接口和具体的实现类,稍微增加了代码的复杂度。
2.在类的数量非常多(如1万种动物)的情况下,仍然需要创建大量的具体类。

3.工厂方法

有new的地方需要具体类,这就有硬编码,有硬编码的地方就需要我们一个个修改代码文件,如果没有new那就没有硬编码,没有硬编码那我们就可以不用手动修改,那如何取消硬编码不用new但是可以创建对象。
爱摸鱼的程序员尽管爱摸鱼,但基本功还是蛮扎实嘛,突然想起来了设计模式,设计模式中好像有个工厂模式嘛,那不是能创建对象嘛,而且不需要指定要创建的具体类。
马上百度:工厂模式,一搜就是暴击,菜鸟教程,虽然我菜但是不要这么直白嘛!
菜鸟教程|工厂模式

工厂模式
工厂模式(Factory Pattern)是 Java 中最常用的设计模式之一,它提供了一种创建对象的方式,使得创建对象的过程与使用对象的过程分离。工厂模式提供了一种创建对象的方式,而无需指定要创建的具体类。通过使用工厂模式,可以将对象的创建逻辑封装在一个工厂类中,而不是在客户端代码中直接实例化对象,这样可以提高代码的可维护性和可扩展性。

看了一眼,我会了let me show you the code:

创建工厂类:

public class AnimalFactory {public static Animal createAnimal(String animalType) {if (animalType.equals("Dog")) {return new Dog();} else if (animalType.equals("Cat")) {return new Cat();}// 可以继续添加更多动物类return null;}
}

那接下来如何简化呢,减少重复步骤呢?唉,重复,重复就是循环,那就来for循环呀!

public class TestGame {public static void main(String[] args) {// 定义动物类型的数组String[] animalTypes = {"Dog", "Cat", "Bird"};// 使用for循环遍历每种动物类型for (String animalType : animalTypes) {// 使用工厂方法创建动物对象Animal animal = AnimalFactory.createAnimal(animalType);// 检查工厂是否成功创建了动物对象if (animal != null) {System.out.println("Testing " + animalType + ":");animal.makeSound();  // 调用动物的发声方法System.out.println();  // 输出一个空行分隔每个测试结果} else {System.out.println("Unknown animal type: " + animalType);}}}
}

这一我们上述的步骤就改变了:
1.复制类名
2.修改类名
步骤变为:

1.复制类名,加入列表
2.修改工厂类

通过上面的修改,我们减少了一个步骤,但是增加了一些动作,导致时间没有什么太多的变化。但是工厂类加入相应的判断可以通过程序遍历一万种动物的java文件,来自动化生成。那假如我们建立好了工厂类和整个列表,那测试起来就很快了。
但是我们现在假设只能获得整个列表,而不能自动化修改工厂类,仍然需要手动修改。那实际上我们只是把TestGame上的硬编码转移到了工厂类,所以本次修改并未节约时间

优点
简化代码:通过工厂方法和循环,我们可以用很少的代码测试多个动物类型,避免重复编写类似的测试代码。
易于扩展:如果需要新增动物类型,只需要在 AnimalFactory 中添加相应的条件判断,并确保每个动物类实现 Animal 接口,无需修改测试代码。
自动化测试:通过自动化地创建动物对象并调用其方法,我们可以快速进行批量测试,适应动物种类数量增加的需求。

缺点
1.需要创建工厂类,增加了代码复杂度。
2.如果动物种类非常多,工厂类的代码可能会变得庞大,管理起来比较麻烦。如果工厂类过于庞大,违反单一职责原则(SRP)(包含1万条判断的工厂类,几万行的代码,这是一个多么恐怖的类)。

4.反射改造工厂类

上面说了可以通过程序遍历一万种动物的java文件,来自动化生成工厂类,但是这种方法不够优雅。(程序员的品味还是很高滴,优雅是我们永恒滴追求)
为了提高品味,更优雅的使用,我们使用反射进一步简化代码的创建过程,尤其是在类的数量非常庞大的情况下。反射能够在运行时动态地加载类并创建对象,而不需要预先在代码中硬编码类名。

在反射改造版本中,我们将不再依赖硬编码的类名,而是通过类名字符串来动态加载和实例化动物对象。通过反射,我们可以通过类的全名(包括包路径)来加载和实例化对象,从而使得扩展和测试变得更加灵活。

public class AnimalFactory {// 通过反射动态创建动物对象public static Animal createAnimal(String animalType) {try {// 根据动物类型字符串构建类名(假设类都在同一包下)String className = "com.example.animals." + animalType;// 获取对应的Class对象Class<?> clazz = Class.forName(className);// 创建该类的实例return (Animal) clazz.getDeclaredConstructor().newInstance();} catch (Exception e) {e.printStackTrace();}return null;}
}
public class TestGame {public static void main(String[] args) {// 定义动物类型的数组String[] animalTypes = {"Dog", "Cat", "Bird"};// 使用for循环遍历每种动物类型for (String animalType : animalTypes) {// 使用工厂方法创建动物对象(通过反射)Animal animal = AnimalFactory.createAnimal(animalType);// 检查工厂是否成功创建了动物对象if (animal != null) {System.out.println("Testing " + animalType + ":");animal.makeSound();  // 调用动物的发声方法System.out.println();  // 输出一个空行分隔每个测试结果} else {System.out.println("Unknown animal type: " + animalType);}}}
}

反射改造的优势
灵活性:通过反射,类名是动态指定的,不需要在代码中硬编码类名。这使得当动物种类增多时,不需要修改代码,只需要确保类的全名和接口实现一致即可。
扩展性:如果新增动物类,只需要将新类添加到对应的包下,无需修改 TestGame 或 AnimalFactory 类,只需将类名添加到动物类型数组中即可。
减少硬编码:避免了每个类都需要在工厂类中手动添加判断条件,简化了代码。
极高的灵活性:可以动态地加载类和创建对象,不需要在编译时就知道具体的实现类。
本次修改的步骤改造为:

1.复制类名,加入列表
2.修改工厂类

所以步骤变为:

1.复制类名,加入列表

现在我们还有一处硬编码,就是需要在TestGame加入列表,每次修改都需要修改代码。聪明的小伙伴应该已经想到办法了,那就是使用配置文件将该处的代码设置从外部读取。
先来一个配置读取类:

import java.io.FileInputStream;
import java.io.IOException;
import java.util.Properties;public class ConfigReader {// 读取配置文件的方法public static String[] getAnimalTypes() {Properties properties = new Properties();try (FileInputStream inputStream = new FileInputStream("config.properties")) {properties.load(inputStream);String animalTypes = properties.getProperty("animal.types");return animalTypes.split(",");} catch (IOException e) {e.printStackTrace();return new String[0]; // 返回空数组以防出错}}
}

修改TestGame:

public class TestGame {public static void main(String[] args) {// 从配置文件读取动物类型String[] animalTypes = ConfigReader.getAnimalTypes();// 使用for循环遍历每种动物类型for (String animalType : animalTypes) {// 使用工厂方法创建动物对象(通过反射)Animal animal = AnimalFactory.createAnimal(animalType);// 检查工厂是否成功创建了动物对象if (animal != null) {System.out.println("Testing " + animalType + ":");animal.makeSound();  // 调用动物的发声方法System.out.println();  // 输出一个空行分隔每个测试结果} else {System.out.println("Unknown animal type: " + animalType);}}}
}

本次修改的步骤改造为:

1.复制类名,加入列表

所以步骤变为:

1.复制类名,加入到配置文件

到此为止我们已经完全消灭了硬编码,也就是不需要修改任何代码,只需要修改配置文件,即可完成测试任务。

通过上面的修改,我们来计算看看节约了多少时间,因为不需要再修改任何的代码,我们只要把配置文件完成就可以自动化测试了。此时假设整理配置文件1小时,测试2小时。总时间是1+2=3小时。成功从18天->9天->3小时。我们本次共赢得摸鱼时间为 8*3-3=21小时。好了原神启动!

5.IOC

好吧,经过几轮奋战,我们已经通过接口、工厂模式、反射,甚至配置文件等一系列努力,逐步消灭了代码中的硬编码,简化了我们的开发过程,成功为我们的摸鱼时间争取到了宝贵的小时数。可是!这个世界上永远有更强大的魔法,而这个魔法就是——控制反转(IOC)!
上面的工厂+反射+配置文件只能针对特定的类进行,假如我们要推广到任意类那就得使用IOC了。
1. 什么是 IOC?
IOC,听起来好像一个神秘的魔法词汇,实际上它的含义是:把“控制”反转给了外部容器或框架。说得更直白一点,就是你的代码再也不用自己创建和管理对象了!这就像你不需要自己做饭了,外卖小哥会按时送到,你只要坐等美味。你只需要专心写业务逻辑,其他的交给它!

2. 如何使用 IOC?
我们要干的就是通过 IOC 容器让我们所有的动物对象都交给容器管理,容器负责从我们不关心的角落里提取出这些对象并把它们放到我们需要的地方。是不是听起来很神奇?

步骤:
1.配置容器:告诉它我们有哪些类需要它管理。
2.自动注入:让容器通过魔法把需要的对象送到我们手上。
3.提取对象:从容器里取对象,直接用。

3. 使用 Spring 框架实现 IOC

首先,我们告诉 Spring 容器哪些类是要交给它管理的。这里的“告诉”就是通过注解或者 XML 配置文件来完成。

import org.springframework.stereotype.Component;public interface Animal {void makeSound();
}@Component  // 我告诉Spring,Dog是我托管的对象
public class Dog implements Animal {@Overridepublic void makeSound() {System.out.println("Bark");}
}@Component  // 我告诉Spring,Cat也是我托管的对象
public class Cat implements Animal {@Overridepublic void makeSound() {System.out.println("Meow");}
}

你看,是不是有点意思?我们通过 @Component 告诉 Spring 哪些类是它需要管理的,这样 Spring 就能在幕后默默为我们工作。
3.2 配置 Spring 容器
然后,我们告诉 Spring 去哪里找这些类。可以使用注解,也可以使用 XML 文件。如果你喜欢用注解,那就直接 @ComponentScan;如果你喜欢老派一点的方式,XML 也能给你安排得明明白白。

import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;@Configuration
@ComponentScan(basePackages = "com.example.animals") // 让Spring去这个包里找类
public class AppConfig {
}

3.3 使用 IOC 获取对象
这时,Spring 就会在后台准备好对象,并且等着你去提取了。你再也不需要自己手动实例化 Dog 或 Cat,直接从 Spring 容器里取出来就行了。

import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;public class TestGame {public static void main(String[] args) {// 创建Spring容器ApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);// 从容器中获取动物对象Animal dog = context.getBean(Dog.class);dog.makeSound();  // 调用方法Animal cat = context.getBean(Cat.class);cat.makeSound();  // 调用方法}
}

看!是不是很轻松?你只需要和容器打个招呼,剩下的事情交给它去做。你甚至可以在容器里大呼一声:“嘿,给我一个 Dog 和 Cat,马上来!”容器就像个万能的外卖小哥,立马送到。

3.4 IOC 的优势
解耦合:你不再需要直接实例化 Dog 或 Cat 类,它们的创建由容器来完成。你就像个指挥官,坐在指挥室里,其他所有的事交给你的“魔法师”容器。

灵活性:需要换动物了?直接告诉容器换一个别的。它会默默地帮你换好,换完了你还不用看一眼代码!是不是有点神奇?

自动化依赖管理:容器会根据配置自动把依赖注入给你。你只需要在代码里声明,容器会悄无声息地把合适的对象交到你手里,免去自己创建对象的麻烦。

高扩展性:当你想增加新动物时,只需要让它实现 Animal 接口,并把它交给容器管理,容器就会自动照顾好它。完全不用担心手动修改 TestGame 类了,完全不需要!你增加一只动物,TestGame 基本不需要动弹,容器会自己照顾好所有细节。

总结

第四层就是IOC在做的事情
IOC = 工厂模式+反射+配置文件读取

工厂模式提高内聚性(创建对象的事情就由专门的类负责),反射+配置文件就是IOC生产bean的法宝。

所有的JavaBean都被IOC的工厂管理,你需要的时候就和工厂说一声我需要XXX就可以了,这样工厂就会返回给你一个正确的对象.在不需要的时候修改工厂的生产列表(配置文件),工厂就停止生产该JavaBean了。

我们一步一步的降低了代码的耦合性,提高了内聚性。

参考

原创经典-为什么Spring中的IOC(控制反转)能够降低耦合性(解耦)?

相关文章:

【Spring学习】为什么Spring中的IOC(控制反转)能够降低耦合性(解耦)?

为什么Spring中的IOC能够降低耦合性&#xff1f; 前言1.传统方式2.使用接口3.工厂方法4.反射改造工厂类5.IOC总结参考 前言 本文目标&#xff1a;本文旨在讲解为什么IOC能够降低耦合性。 情景&#xff1a;假设你是一个爱摸鱼的程序员&#xff0c;现在需要测试一个游戏&#x…...

机场安全项目|基于改进 YOLOv8 的机场飞鸟实时目标检测方法

目录 论文信息 背景 摘要 YOLOv8模型结构 模型改进 FFC3 模块 CSPPF 模块 数据集增强策略 实验结果 消融实验 对比实验 结论 论文信息 《科学技术与工程》2024年第24卷第32期刊载了中国民用航空飞行学院空中交通管理学院孔建国, 张向伟, 赵志伟, 梁海军的论文——…...

卸载干净 IDEA(图文讲解)

目录 1、卸载 IDEA 程序 2、注册表清理 3、残留清理 1、卸载 IDEA 程序 点击屏幕左下角 Windows 图标 -> 设置-控制面板->intellij idea 勾选第一栏 Delete IntelliJ IDEA 2022.2 caches and local history&#xff0c;表示同时删除 IDEA 本地缓存以及历史。 Delete I…...

云端微光,AI启航:低代码开发的智造未来

文章目录 前言一、引言&#xff1a;技术浪潮中的个人视角初次体验腾讯云开发 Copilot1.1 低代码的时代机遇1.1.1 为什么低代码如此重要&#xff1f; 1.2 AI 的引入&#xff1a;革新的力量1.1.2 Copilot 的亮点 1.3 初学者的视角1.3.1 Copilot 带来的改变 二、体验记录&#xff…...

工程师了解的Lua语言

1、关于lua语言 lua语言是用于嵌入式领域当中的一门脚本语言&#xff0c;其实在大学期间&#xff0c;我也没有接触过这门语言&#xff0c;但是在未来的发展之路当中&#xff0c;需要用到这门语言&#xff0c;所以在我的知识库当中添加这门语言知识是必要而且重要的&#xff0c;…...

著名的软件都用什么语言编写?

你是否曾经好奇&#xff0c;身边那些耳熟能详的软件&#xff0c;它们究竟是用什么语言编写的&#xff1f;从操作系统到浏览器、从数据库到编程工具&#xff0c;每一款软件背后都承载着开发者们的智慧与技术选型。那么&#xff0c;究竟哪些编程语言成就了这些世界级的软件呢&…...

设计模式 结构型 代理模式(Proxy Pattern)与 常见技术框架应用 解析

代理模式&#xff08;Proxy Pattern&#xff09;是一种常见的设计模式&#xff0c;在软件开发中有着广泛的应用。其核心思想是通过创建一个代理类来控制对另一个对象的访问&#xff0c;从而实现对目标对象功能的扩展、保护或其他附加操作。 一、核心思想 代理模式的核心思想在…...

Linux环境(CentOs7) 安装 Node环境

Linux环境&#xff08;CentOs7&#xff09; 安装 Node环境 使用NodeSource安装Node.js 1、清除缓存&#xff08;可选但推荐&#xff09; sudo yum clean all2、添加NodeSource仓库&#xff0c;根据你想要安装的Node.js版本&#xff0c;选择相应的NodeSource安装脚本。例如&am…...

Tailwind CSS 实战:现代登录注册页面开发

在前端开发中&#xff0c;登录注册页面是最常见的需求之一。一个设计精美、交互友好的登录注册页面不仅能提升用户体验&#xff0c;还能增加产品的专业度。本文将详细介绍如何使用 Tailwind CSS 开发一个现代化的登录注册页面。 设计思路 在开始编码之前&#xff0c;我们先明…...

Unity2022接入Google广告与支付SDK、导出工程到Android Studio使用JDK17进行打包完整流程与过程中的相关错误及处理经验总结

注&#xff1a;因为本人也是第一次接入广告与支付SDK相关的操作&#xff0c;网上也查了很多教程&#xff0c;很多也都是只言片语或者缺少一些关键步骤的说明&#xff0c;导致本人也是花了很多时间与精力踩了很多的坑才搞定&#xff0c;发出来也是希望能帮助到其他人在遇到相似问…...

反向传播算法的偏置更新步骤

偏置的更新步骤 假设我们有一个三层神经网络&#xff08;输入层、隐藏层和输出层&#xff09;&#xff0c;并且每层的激活函数为 sigmoid 函数。我们需要更新隐藏层和输出层的偏置。以下是详细的步骤&#xff1a; 1. 计算误差项&#xff08;Error Term&#xff09; 输出层的…...

条款47:请使用 traits classes 表现类型信息(Use traits classes for information about types)

条款47&#xff1a;请使用 traits classes 表现类型信息 1.1 提出问题 想一想&#xff0c;下面的功能如何实现&#xff1f;&#xff08;可以查看std::advance源码&#xff09; template<typename IterT, typename DistT> void advance(IterT& iter, DistT d); /…...

yolov5和yolov8的区别

1. yolov5有建议框&#xff0c;yolov8没有建议框 2. yolov5标签中有自信度&#xff0c;而yolov8没有自信度。因为自信度是建议框和真实框的交集 3. yolov5有三个损失函数&#xff0c;回归问题&#xff1a;预测框和建议框的损失(中心点宽高偏移量的损失)&#xff1a;CIOUFocal…...

Redis 实现分布式锁

文章目录 引言一、Redis的两种原子操作1.1 Redis 的原子性1.2 单命令1.3 Lua 脚本1.4 对比单命令与 Lua 脚本 二、Redis 实现分布式锁2.1 分布式锁的概念与需求2.1.1 什么是分布式锁&#xff1f;2.1.2 分布式锁的常见应用场景 2.2 基于 Redis 的分布式锁实现2.2.1 锁的获取与释…...

django StreamingHttpResponse fetchEventSource实现前后端流试返回数据并接收数据的完整详细过程

django后端环境介绍&#xff1a; Python 3.10.14 pip install django-cors-headers4.4.0 Django5.0.6 django-cors-headers4.4.0 djangorestframework3.15.2 -i https://mirrors.tuna.tsinghua.edu.cn/pypi/web/simple 总环境如下&#xff1a; Package Version -…...

SpringSpringBoot常用注解总结

目录 1. SpringBootApplication 2. Spring Bean 相关 2.1. Autowired 2.2. Component,Repository,Service, Controller 2.3. RestController 2.4. Scope 2.5. Configuration 3. 处理常见的 HTTP 请求类型 3.1. GET 请求 3.2. POST 请求 3.3. PUT 请求 3.4. DELETE 请…...

24.小R的随机播放顺序<字节青训营-中等题>

1.题目 问题描述 小R有一个特殊的随机播放规则。他首先播放歌单中的第一首歌&#xff0c;播放后将其从歌单中移除。如果歌单中还有歌曲&#xff0c;则会将当前第一首歌移到最后一首。这个过程会一直重复&#xff0c;直到歌单中没有任何歌曲。 例如&#xff0c;给定歌单 [5, …...

【QT】增删改查 XML 文件的类

使用单例类模板实现的对XML文件的节点、属性、文本进行增删改查,可以直接用! 直接POST代码,比较简单好用。 针对以下格式的xml文件比较适用 每个节点的名称都不一样,节点包含了各种属性。 <?xml version="1.0" encoding="UTF-8"?> <config…...

Linux-掉电保护方案

参考链接 https://blog.csdn.net/pwl999/article/details/109411919硬件设计 设备树 驱动程序 #include <linux/module.h> #include <linux/init.h> #include <linux/interrupt.h> #include <linux/gpio.h>int irq;//中断服务函数 irqreturn_t tes…...

php获取字符串中的汉字

在PHP中&#xff0c;可以使用正则表达式来提取字符串中的汉字。汉字通常位于Unicode范围\u4e00-\u9fa5之内&#xff0c;因此可以使用preg_match_all函数配合适当的正则表达式来实现。 以下是一个PHP代码示例&#xff0c;它会从给定的字符串中提取出所有的汉字&#xff1a; fu…...

java: JDK isn‘t specified for module ‘product-service‘问题解决

目录 问题 解决方法 1.打开File->Project Structure... 2.将Project SDK修改为17 Oracle OpenJDK 17.0.12&#xff0c;并Apply&#xff0c;OK 问题 添加module后报错&#xff1a;java: JDK isnt specified for module product-service 查看pom.xml文件也添加了对应的JDK…...

使用工厂+策略模式实现去除繁琐的if else

使用工厂策略模式实现去除繁琐的if else 在中间有一个mapstruct的bug&#xff0c;即在修改实体类中的类型时&#xff0c;或者修改属性名字&#xff0c;mapstruct都无法进行转换&#xff0c;会报错&#xff0c;此时需要maven cleanmaven compile即可 前言 在这次的开发中&#…...

Dubbo3入门项目搭建

开发环境&#xff1a;jdk8、dubbo3.2.9、nacos2.3.0、springboot2.7.17、dubbo-admin0.6.0。 Dubbo 是一个高性能的 Java RPC&#xff08;远程调用&#xff09;框架&#xff0c;最初由阿里巴巴开发并开源&#xff0c;主要用于构建 SOA 架构下的分布式应用系统( soa简单理解就是…...

形象地理解UE4中的数据结构 TLinkedListBase

大家都熟知链表&#xff0c;但不一定能快速看懂UE4中的数据结构。 TLinkedListBase表示“链接”中的一个结点&#xff0c;有三个成员&#xff1a; 一、ElementType Element; 表示具体的业务&#xff0c;例如int链条中的一个整数。 二、NextLink 表示 “下一个Node”&#…...

Python自然语言处理利器:SnowNLP模块深度解析、安装指南与实战案例

Python自然语言处理之SnowNLP模块介绍、安装与常见操作案例 一、SnowNLP模块介绍 SnowNLP是一个专为中文文本设计的Python库&#xff0c;它基于自然语言处理技术&#xff0c;提供了多种功能&#xff0c;包括分词、词性标注、情感分析、文本转换&#xff08;简繁转换&#xff…...

Llama系列关键知识总结

系列文章目录 第一章&#xff1a;LoRA微调系列笔记 第二章&#xff1a;Llama系列关键知识总结 第三章&#xff1a;LLaVA模型讲解与总结 文章目录 系列文章目录Llama: Open and Efficient Foundation Language Models关键要点LLaMa模型架构&#xff1a;Llama2分组查询注意力 (G…...

【开源】创建自动签到系统—QD框架

1. 介绍 QD是一个 基于 HAR 编辑器和 Tornado 服务端的 HTTP 定时任务自动执行 Web 框架。 主要通过抓包获取到HAR来制作任务模板&#xff0c;从而实现异步响应和发起HTTP请求 2. 需要环境 2.1 硬件需求 CPU&#xff1a;至少1核 内存&#xff1a;推荐 ≥ 1G 硬盘&#xff1a;推…...

​​​​​​​CDP集群安全指南系列文章导读

[一]大数据安全综述 1-认证 身份验证是任何计算环境的基本安全要求。简单来说&#xff0c;用户和服务必须在使用系统功能并获得授权之前&#xff0c;向系统证明其身份&#xff08;进行身份验证&#xff09;。身份验证与授权紧密配合&#xff0c;共同保护系统资源。大多数 CDH …...

MT8788安卓核心板_MTK8788核心板参数_联发科模块定制开发

MT8788安卓核心板是一款尺寸为52.5mm x 38.5mm x 2.95mm的高集成度电路板&#xff0c;专为各种智能设备应用而设计。该板卡整合了处理器、图形处理单元(GPU)、LPDDR3内存、eMMC存储及电源管理模块&#xff0c;具备出色的性能与低功耗特性。 这款核心板搭载了联发科的MT8788处理…...

【微软,模型规模】模型参数规模泄露:理解大型语言模型的参数量级

模型参数规模泄露&#xff1a;理解大型语言模型的参数量级 关键词&#xff1a; #大型语言模型 Large Language Model #参数规模 Parameter Scale #GPT-4o #GPT-4o-mini #Claude 3.5 Sonnet 具体实例与推演 近日&#xff0c;微软在一篇医学相关论文中意外泄露了OpenAI及Claud…...