设计模式——工厂方法模式(创建型)

article/2025/6/22 9:43:54

摘要

工厂方法模式是一种创建型设计模式,通过定义创建对象的接口,让子类决定实例化哪个类。它包含抽象产品、具体产品、抽象工厂和具体工厂等角色。该模式使类的实例化延迟到子类,具有良好的扩展性和灵活性,适用于多种场景,如Spring框架中的应用。文中还探讨了结合配置中心动态切换消息类型、Spring Boot自动配置与SPI加载工厂等实战示例,以及运行时动态添加SPI实现类的热插拔技术。

1. 工厂方法设计模式定义

工厂方法模式 是一种 创建型设计模式,它通过 定义一个创建对象的接口,让子类决定实例化哪一个类。工厂方法模式使一个类的实例化延迟到其子类。

定义一个用于创建对象的接口,但由子类决定要实例化的类是哪一个。工厂方法让类的实例化推迟到子类中进行。

角色

说明

Product

抽象产品类,定义产品的公共接口

ConcreteProduct

具体产品类,实现了抽象产品接口

Creator

抽象工厂类,声明工厂方法 createProduct(),返回 Product 类型对象

ConcreteCreator

具体工厂类,重写 createProduct()方法,返回具体的产品对象

2. 工厂方法设计模式结构

2.1. 工厂方法类图

  • Product:抽象产品
  • ConcreteProduct:具体产品
  • Factory:抽象工厂
  • ConcreteFactory:具体工厂

2.2. 工厂方法时序图

3. 工厂方法设计模式实现方式

工厂方法模式的核心是:将对象的创建延迟到子类中去实现,从而达到 “对扩展开放,对修改关闭” 的目的。

3.1. 🧱 实现步骤

  1. 定义产品接口(Product)
  2. 创建多个具体产品类(ConcreteProduct)
  3. 定义工厂抽象类(Creator)
  4. 创建具体工厂类(ConcreteCreator)实现产品的创建
  5. 客户端调用:使用工厂创建产品

3.2. ✅ 示例代码实现

3.2.1. 1️⃣ 抽象产品接口

public interface Product {void use();
}

3.2.2. 2️⃣ 具体产品类

public class ConcreteProductA implements Product {@Overridepublic void use() {System.out.println("使用产品 A");}
}public class ConcreteProductB implements Product {@Overridepublic void use() {System.out.println("使用产品 B");}
}

3.2.3. 3️⃣ 抽象工厂类

public abstract class Creator {public abstract Product createProduct();
}

3.2.4. 4️⃣ 具体工厂类

public class ConcreteCreatorA extends Creator {@Overridepublic Product createProduct() {return new ConcreteProductA();}
}public class ConcreteCreatorB extends Creator {@Overridepublic Product createProduct() {return new ConcreteProductB();}
}

3.2.5. 5️⃣ 客户端代码

public class Client {public static void main(String[] args) {Creator creatorA = new ConcreteCreatorA();Product productA = creatorA.createProduct();productA.use();Creator creatorB = new ConcreteCreatorB();Product productB = creatorB.createProduct();productB.use();}
}

3.2.6. 📌 可扩展性体现

如果后续要新增 ConcreteProductC,只需:

  • 新增一个 ConcreteProductC
  • 新增一个 ConcreteCreatorC 类,实现 createProduct() 返回 ProductC
  • 不修改原有工厂代码,符合开闭原则(OCP)

3.3. ✳️ 工厂方法总结:

比较点

简单工厂

工厂方法

工厂类

一个类处理所有产品创建逻辑

每个产品对应一个工厂类

新增产品时

修改工厂逻辑(违反OCP)

新增产品类和工厂类(符合OCP)

灵活性

较差

更强

类的数量

4. 工厂方法设计模式适合场景

4.1. ✅ 适合使用工厂方法模式的场景

场景

描述

需要创建的对象具有复杂构建逻辑

创建过程复杂或需要依赖其他对象时,用工厂方法将其封装。

系统中有多个产品族,且产品种类经常扩展

每新增一个产品,不希望改动原来的工厂类。符合开闭原则(OCP)

希望客户端代码与具体产品解耦

客户端只依赖产品接口,不依赖具体实现类,降低耦合。

框架级别开发,预留扩展点给业务方

比如 Spring 中的 BeanFactory、MyBatis 的 TypeHandlerFactory。

对象生命周期由工厂统一管理

方便缓存、单例控制等。

例子:

  • Spring 框架中使用 FactoryBean 实现 bean 的创建解耦。
  • JDBC 的 ConnectionStatement 创建由 ConnectionFactory 封装。
  • 不同支付渠道(微信、支付宝、银联)通过工厂生成对应 PayHandler

4.2. ❌ 不适合使用工厂方法模式的场景

场景

原因

产品种类非常少或固定,不需要频繁扩展

工厂类和产品类较少,使用工厂方法反而会增加类数量和结构复杂度。

系统结构简单,对象创建逻辑很简单

直接 new

就可以,无需额外的设计模式来封装。

项目初期阶段,功能未稳定,频繁重构

工厂方法类结构多,频繁修改成本高。

对性能要求极高的场景

反射或额外的工厂逻辑可能有性能损耗,需权衡使用。

例子:

  • 简单数据封装对象(DTO/VO),使用 new 更直观清晰。
  • 稳定的、不会扩展的枚举或常量类,不需要通过工厂方法。

总结:当你面对不断变化的对象创建需求,并希望将变化隔离时,用工厂方法模式;如果结构简单、创建逻辑轻量,直接 new 反而更高效。

5. 工厂方法设计模式实战示例

下面是一个基于 工厂方法模式 的 Spring 项目示例,适合的业务场景是:多渠道消息发送系统,如支持短信(SMS)、邮件(Email)、推送(Push)等多种消息发送方式,且将来可能还会扩展更多类型。消息发送系统:不同类型消息的发送逻辑不同,易于扩展,且客户端无需关心具体实现。

5.1. Spring 工厂方法模式示例

5.1.1. 定义统一接口(产品接口)

public interface MessageSender {void send(String message);
}

5.1.2. 实现具体的产品类(不同的消息发送实现)

@Component
public class EmailMessageSender implements MessageSender {@Overridepublic void send(String message) {System.out.println("发送邮件: " + message);}
}
@Component
public class SmsMessageSender implements MessageSender {@Overridepublic void send(String message) {System.out.println("发送短信: " + message);}
}

5.1.3. 定义抽象工厂类(可选)

public interface MessageSenderFactory {MessageSender createSender();
}

5.1.4. 实现具体工厂类(根据不同产品创建)

@Component("emailFactory")
public class EmailSenderFactory implements MessageSenderFactory {@Autowiredprivate EmailMessageSender sender;@Overridepublic MessageSender createSender() {return sender;}
}
@Component("smsFactory")
public class SmsSenderFactory implements MessageSenderFactory {@Autowiredprivate SmsMessageSender sender;@Overridepublic MessageSender createSender() {return sender;}
}

5.1.5. 客户端代码(注入工厂调用)

@Service
public class NotificationService {// 也可以通过配置动态切换@Resource(name = "emailFactory") private MessageSenderFactory senderFactory;public void notify(String msg) {MessageSender sender = senderFactory.createSender();sender.send(msg);}
}

5.2. ✅ 优点体现(为何使用工厂方法)

优点

说明

解耦

NotificationService只依赖 MessageSenderFactory不关心实现细节

易于扩展

新增 WeChatMessageSender、对应工厂类,无需改动现有代码

支持 Spring 管理生命周期

工厂和产品都可作为 Spring Bean 管理

单一职责

每个工厂负责一个类型消息的创建,职责清晰

5.3. ✅ 可选升级(使用配置中心 + @Value)

你还可以用配置中心动态切换默认的消息类型:

@Value("${message.type}")
private String type; // "email" / "sms"

通过一个总的 MessageSenderFactoryRegistry 统一管理不同的工厂,按类型取出。

6. 工厂方法设计模式思考

6.1. 配置中心动态切换消息类型

6.1.1. 总体结构图

application.yml└─ message.type=emailMessageSender 接口├─ EmailMessageSender└─ SmsMessageSenderMessageSenderFactory 接口├─ EmailSenderFactory└─ SmsSenderFactoryMessageSenderFactoryRegistry(注册所有工厂)
NotificationService(注入 @Value 配置,从 Registry 获取 Sender)

6.1.2. ✅ application.yml 配置

message:type: email

6.1.3. ✅ @Value 注入默认类型

在客户端(如 NotificationService)中使用:

@Value("${message.type}")
private String type; // 注入配置中心的值

6.1.4. ✅ Factory Registry 实现

@Component
public class MessageSenderFactoryRegistry {private final Map<String, MessageSenderFactory> factoryMap = new HashMap<>();@Autowiredpublic MessageSenderFactoryRegistry(List<MessageSenderFactory> factories) {for (MessageSenderFactory factory : factories) {factoryMap.put(factory.getType(), factory); // getType 方法用于标识工厂}}public MessageSenderFactory getFactory(String type) {MessageSenderFactory factory = factoryMap.get(type);if (factory == null) {throw new IllegalArgumentException("不支持的消息类型: " + type);}return factory;}
}

6.1.5. ✅ 各具体工厂类

@Component
public class EmailSenderFactory implements MessageSenderFactory {@Autowiredprivate EmailMessageSender sender;@Overridepublic MessageSender createSender() {return sender;}@Overridepublic String getType() {return "email";}
}
@Component
public class SmsSenderFactory implements MessageSenderFactory {@Autowiredprivate SmsMessageSender sender;@Overridepublic MessageSender createSender() {return sender;}@Overridepublic String getType() {return "sms";}
}

6.1.6. ✅ 客户端使用 Registry + 配置中心

@Service
public class NotificationService {// 配置中心动态切换消息类型@Value("${message.type}")private String type;@Autowiredprivate MessageSenderFactoryRegistry factoryRegistry;public void notify(String msg) {MessageSender sender = factoryRegistry.getFactory(type).createSender();sender.send(msg);}
}

6.1.7. ✅ 动态配置总结

说明

@Value

注入配置中心的策略标识

MessageSenderFactoryRegistry

将所有具体工厂注册进来,用于动态选择

getType() 方法

各个工厂自报家门,便于注册

使用场景

可灵活切换策略(消息类型),并支持后续热扩展

6.2. Spring Boot 自动配置 + SPI 加载工厂

以下是基于 Spring Boot 自动配置 + SPI + 工厂注册机制 的完整示例,用于实现插件式、可扩展的策略工厂体系,常用于消息发送、支付方式、风控策略等系统中。

支持在多个 jar 插件中以 SPI 方式注册 MessageSender 实现,并通过 Spring Boot 自动装配到一个注册中心中,客户端使用配置中心动态切换使用哪个策略。

6.2.1. ✅ 项目结构概览

src
├── META-INF
│   └── services
│       └── com.example.spi.MessageSender
├── com.example.spi
│   └── MessageSender.java
├── com.example.impl
│   ├── EmailMessageSender.java
│   └── SmsMessageSender.java
├── com.example.factory
│   ├── MessageSenderFactory.java
│   └── MessageSenderFactoryRegistry.java
├── com.example.config
│   └── MessageSenderAutoConfiguration.java
└── com.example.client└── NotificationService.java

6.2.2. ✳️ 定义 SPI 接口

package com.example.spi;public interface MessageSender {void send(String message);// 用于标识类型,比如 "email"、"sms"String type(); 
}

6.2.3. ✳️ 实现两个 SPI 类

package com.example.impl;import com.example.spi.MessageSender;public class EmailMessageSender implements MessageSender {public void send(String message) {System.out.println("发送邮件:" + message);}public String type() {return "email";}
}
package com.example.impl;import com.example.spi.MessageSender;public class SmsMessageSender implements MessageSender {public void send(String message) {System.out.println("发送短信:" + message);}public String type() {return "sms";}
}

6.2.4. ✳️ 创建 SPI 配置文件

resources/META-INF/services/ 目录下创建:

com.example.spi.MessageSender

内容为:

com.example.impl.EmailMessageSender
com.example.impl.SmsMessageSender

6.2.5. ✳️ 创建 Spring 注册工厂类

package com.example.factory;import com.example.spi.MessageSender;
import org.springframework.stereotype.Component;import java.util.*;@Component
public class MessageSenderFactoryRegistry {private final Map<String, MessageSender> senderMap = new HashMap<>();public MessageSenderFactoryRegistry() {// 初始化加载spi接口实现类ServiceLoader<MessageSender> loader = ServiceLoader.load(MessageSender.class);for (MessageSender sender : loader) {senderMap.put(sender.type(), sender);}}public MessageSender getSender(String type) {MessageSender sender = senderMap.get(type);if (sender == null) {throw new IllegalArgumentException("不支持的消息类型: " + type);}return sender;}public Set<String> supportedTypes() {return senderMap.keySet();}
}

6.2.6. ✳️ 自动配置类(可选)

如果你将 SPI 插件打包成独立 jar,可以增加 spring.factories / spring.factories 文件,实现自动注册:

resources/META-INF/spring.factories(Spring Boot 2)或 spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports(Spring Boot 3):

# spring.factories 示例
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.example.config.MessageSenderAutoConfiguration
package com.example.config;import com.example.factory.MessageSenderFactoryRegistry;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;@Configuration
public class MessageSenderAutoConfiguration {@Beanpublic MessageSenderFactoryRegistry messageSenderFactoryRegistry() {return new MessageSenderFactoryRegistry();}
}

6.2.7. ✳️ 使用配置动态切换策略

package com.example.client;import com.example.factory.MessageSenderFactoryRegistry;
import com.example.spi.MessageSender;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;@Service
public class NotificationService {@Value("${message.type}")private String messageType;private final MessageSenderFactoryRegistry registry;public NotificationService(MessageSenderFactoryRegistry registry) {this.registry = registry;}public void notify(String content) {MessageSender sender = registry.getSender(messageType);sender.send(content);}
}

6.2.8. ✳️ application.yml 示例

message:type: email

6.2.9. ✅ 与普通简单工厂对比总结

方式

普通简单工厂

SPI + 自动装配

实现方式

手动注册、代码维护类型 -> 实例映射

自动发现、解耦模块

扩展性

差,改动原有类

高,插件式加载新类型

热插拔

不支持

支持 jar 插件添加

Spring 兼容

需自己管理 Bean

可与自动配置整合

6.2.10. ✅ 适用场景

  • 插件式服务(支付、消息、风控、日志);
  • 支持 jar 级别扩展;
  • 想在不改主工程代码的情况下支持“新策略”;
  • 想结合 Spring 自动装配实现热加载、策略切换。

6.3. ServiceLoader.load(...)是不是启动加载?

6.3.1. 是不是 Spring 启动时加载?

ServiceLoader.load(...) 不是 Spring 启动时自动加载的 —— 它是懒加载(Lazy Load)机制。

  • ServiceLoader 是 Java 原生的 SPI 加载器。
  • 它的加载时机完全取决于你调用 ServiceLoader.load(...) 的代码什么时候执行。
  • Spring 启动过程中,不会自动调用 ServiceLoader 来加载服务,除非你显式地调用了它(例如在某个 Bean 初始化时)。

6.3.2. 会不会动态加载 SPI 接口实现类?

会在第一次使用时动态加载,但不是运行时热加载。

  • ServiceLoader 内部使用懒加载机制:在第一次调用 iterator()for 循环时才加载并实例化实现类。
  • 但是这个“动态加载”是指JVM 当前 ClassLoader 中已存在的实现类
  • 不是热加载,不支持运行时新增 jar 或类不重启就生效。

6.3.3. 加载机制原理简要

ServiceLoader.load(Xxx.class) 会从:

classpath:/META-INF/services/your.interface.FullyQualifiedName

去读取该文件,文件内容为接口实现类的全限定类名,每一行一个。

然后使用当前线程的 ClassLoader 反射实例化实现类。

举个例子:

// 手动触发加载(不是 Spring 自动做的)
ServiceLoader<PayChannel> loader = ServiceLoader.load(PayChannel.class);
for (PayChannel channel : loader) {channel.pay();
}
  • 只有执行 for (PayChannel channel : loader) 时,才真正实例化每个实现类。
  • Spring 本身并不扫描 /META-INF/services 目录。

6.3.4. 如果你想支持运行时动态添加 SPI 实现类(热插拔):

  • 需要自定义 ClassLoader 加载外部 Jar。
  • 手动调用 ServiceLoader.reload()
  • Spring 本身不提供这套机制,但可以封装插件框架支持(如 Apache Dubbo、Spring Boot Plugin、OSGi)。

6.4. 运行时动态添加 SPI 实现类(热插拔)示例

如果你希望 在运行时动态添加 SPI 实现类(即热插拔插件机制),Java 原生的 ServiceLoader默认不支持运行时添加实现类,但你可以通过自定义 ClassLoader + SPI 机制 + 热加载逻辑 实现。

要实现 动态 SPI 实现类加载(比如加载一个新 jar 的 SPI 实现),需要以下关键步骤:

6.4.1. 🧩 准备 SPI 接口

例如:

public interface MessageSender {String type();void send(String message);
}

6.4.2. 📁 插件 jar 的 META-INF/services/com.example.MessageSender 中声明实现类

com.example.impl.EmailSender

6.4.3. 🛠️ 构建插件目录结构

plugins/└── message-email.jar

6.4.4. 🧠 自定义 URLClassLoader 加载 jar

public class PluginClassLoader extends URLClassLoader {public PluginClassLoader(URL[] urls, ClassLoader parent) {super(urls, parent);}public static PluginClassLoader loadFrom(String jarPath) throws MalformedURLException {File jarFile = new File(jarPath);return new PluginClassLoader(new URL[]{jarFile.toURI().toURL()}, Thread.currentThread().getContextClassLoader());}
}

6.4.5. 🧪 使用 ServiceLoader + 自定义 ClassLoader 加载插件

public class PluginManager {private final Map<String, MessageSender> senderMap = new ConcurrentHashMap<>();public void loadPlugin(String jarPath) throws Exception {// 自定义 URLClassLoader 加载 jarPluginClassLoader loader = PluginClassLoader.loadFrom(jarPath);ServiceLoader<MessageSender> serviceLoader = ServiceLoader.load(MessageSender.class, loader);for (MessageSender sender : serviceLoader) {senderMap.put(sender.type(), sender);}}public MessageSender getSender(String type) {return senderMap.get(type);}
}

6.4.6. 🔄 动态加载新 jar 插件

PluginManager manager = new PluginManager();
manager.loadPlugin("plugins/message-email.jar");MessageSender sender = manager.getSender("email");
sender.send("Hello Dynamic Plugin!");

6.5. ✅ 支持动态热插拔的关键点

步骤

说明

ClassLoader

使用 URLClassLoader 加载外部 jar,和主应用隔离

ServiceLoader

指定加载器读取 META-INF/services实现

插件隔离

保持插件 jar 中依赖与主程序不冲突

实例缓存

senderMap做好类型-实现缓存映射

SPI 定义

每个插件都要放置标准的 META-INF/services/<接口名>声明文件

博文参考

  • 2. 工厂方法模式(Factory Method Pattern) — Graphic Design Patterns
  • 工厂方法设计模式
  • 创建型 - 简单工厂(Simple Factory) | Java 全栈知识体系

http://www.hkcw.cn/article/hOohRGwHui.shtml

相关文章

软件性能之CPU

性能是个宏大而驳杂话题&#xff0c;从代码&#xff0c;到网络&#xff0c;到实施&#xff0c;方方面面都会涉及到性能问题&#xff0c;网上对性能讲解的文章多如牛毛&#xff0c;从原理到方法再到工具都有详细的介绍&#xff0c;本文虽不能免俗&#xff0c;但期望能从另外一个…

腾讯云推出云开发AI Toolkit,国内首个面向智能编程的后端服务

5月28日&#xff0c;腾讯云开发 CloudBase 宣布推出 AI Toolkit&#xff08;CloudBase AI Toolkit&#xff09;&#xff0c;这是国内首个面向智能编程的后端服务&#xff0c;适配 Cursor 等主流 AI 编程工具。 云开发 AI Toolkit旨在解决 AI 辅助编程的“最后一公里”问题&…

当前用户的Git本地配置情况:git config --local --list

通过config命令可以查询当前用户的本地配置情况。这些配置项定义了 Git 在当前仓库中的行为&#xff0c;包括文件权限处理、符号链接处理以及大小写敏感性等。 git config --local --list core.repositoryformatversion0 指定 Git 仓库的格式版本。版本 0 是最初的格式。 cor…

修改 vscode 左侧导航栏的文字大小 (更新版)

1. 起因&#xff0c; 目的: 问题&#xff1a; vscode 左侧的文字太小了&#xff01;&#xff01;&#xff01;我最火的一篇文章&#xff0c;写的就是这个问题。 看来这个问题&#xff0c;是很广泛的一个痛点。我最近更新了 vscode&#xff0c; 这个问题又出现了。再来搞一下。…

Python训练第四十天

DAY 40 训练和测试的规范写法 知识点回顾&#xff1a; 彩色和灰度图片测试和训练的规范写法&#xff1a;封装在函数中展平操作&#xff1a;除第一个维度batchsize外全部展平dropout操作&#xff1a;训练阶段随机丢弃神经元&#xff0c;测试阶段eval模式关闭dropout 昨天我们介绍…

Fine Pruned Tiled Light Lists(精细删减的分块光照列表)

概括 在这篇文章&#xff0c; 我将介绍一种Tiled Light 变体&#xff0c;主要针对AMD Graphics Core Next&#xff08;GCN&#xff09;架构进行优化&#xff0c;我们的方法应用于游戏 古墓丽影:崛起 中&#xff0c;特别是我们在通过光列表生成和阴影贴图渲染之间交错进行异步计…

《信号与系统》第 5 章 离散时间傅里叶变换

5.0 引言 第4章研究了连续时间傅里叶变换&#xff0c;并研究了这种变换的许多特性&#xff0c;这些特性使傅里叶分析方法在分析和理解连续时间信号与系统的性质时具有很大的价值。这一章将介绍并研究离散时间傅里叶变换&#xff0c;这样就完整地建立了傅里叶分析方法。 在第3…

5.2 初识Spark Streaming

在本节实战中&#xff0c;我们初步探索了Spark Streaming&#xff0c;它是Spark的流式数据处理子框架&#xff0c;具备高吞吐量、可伸缩性和强容错能力。我们了解了Spark Streaming的基本概念和运行原理&#xff0c;并通过两个案例演示了如何利用Spark Streaming实现词频统计。…

Kafka消息中间件

window中的安装 ①、下载并解压kafka压缩包&#xff0c;进入config目录下修改zookeeper.properties配置文件 因为kafka内置了zookeeper&#xff0c;所以不需安装zookeeper。设置zookeeper数据存储位置&#xff0c;如果该路径不存在&#xff0c;则自动创建 dataDir E:/kafka…

4.2.4 Spark SQL 数据写入模式

在本节实战中&#xff0c;我们详细探讨了Spark SQL中数据写入的四种模式&#xff1a;ErrorIfExists、Append、Overwrite和Ignore。通过具体案例&#xff0c;我们演示了如何使用mode()方法结合SaveMode枚举类来控制数据写入行为。我们首先读取了一个JSON文件生成DataFrame&#…

day23-计算机网络-1

1. 网络简介 1.1. 网络介质 网线&#xff1a;cat5,cat5e 六类网线&#xff0c;七类网线&#xff0c;芭蕾网线光纤&#xff1a;wifi&#xff1a;无线路由器&#xff0c;ap5G 1.2. 常见网线类型 1.2.1. 双绞线&#xff08;Twisted Pair Cable&#xff09;【最常用】 按性能主…

Ubuntu下编译mininim游戏全攻略

目录 一、安装mininim 软件所依赖的库&#xff08;重点是allegro游戏引擎库&#xff09;二、编译mininim 软件三、将mininim打包给另一个Ubuntu系统使用四、安卓手机运行mininim 一、安装mininim 软件所依赖的库&#xff08;重点是allegro游戏引擎库&#xff09; 1. 用apt-get…

org.junit.runners.model.InvalidTestClassError:此类问题的解决

不知道大家是否遇见过以上这种情况&#xff0c;我也是今天被这个错误搞得很烦&#xff0c;后来通过网上查找资料终于找到了问题所在————就是简单的Test注解的错误使用 Test注解的注意情况 &#xff1a;1 权限必须是public 2 不能有参数 3 返回值类型是void 4 本类的其他的…

2025年渗透测试面试题总结-匿名[校招]渗透测试(打击黑灰产)(题目+回答)

安全领域各种资源&#xff0c;学习文档&#xff0c;以及工具分享、前沿信息分享、POC、EXP分享。不定期分享各种好玩的项目及好用的工具&#xff0c;欢迎关注。 目录 匿名[校招]渗透测试(打击黑灰产) 2. 实习时达成的目标 3. 文件包含漏洞 4. Redis未授权访问利用 5. 钓鱼…

【Hot 100】55. 跳跃游戏

目录 引言跳跃游戏我的解题 &#x1f64b;‍♂️ 作者&#xff1a;海码007&#x1f4dc; 专栏&#xff1a;算法专栏&#x1f4a5; 标题&#xff1a;【Hot 100】55. 跳跃游戏❣️ 寄语&#xff1a;书到用时方恨少&#xff0c;事非经过不知难&#xff01; 引言 跳跃游戏 &#x…

Go 语言的 GC 垃圾回收

序言 垃圾回收&#xff08;Garbage Collection&#xff0c;简称 GC&#xff09;机制 是一种自动内存管理技术&#xff0c;主要用于在程序运行时自动识别并释放不再使用的内存空间&#xff0c;防止内存泄漏和不必要的资源浪费。这篇文章让我们来看一下 Go 语言的垃圾回收机制是如…

qwen 2.5 并行计算机制:依靠 PyTorch 和 Transformers 库的分布式能力

qwen 2.5 并行计算机制:依靠 PyTorch 和 Transformers 库的分布式能力 完整可运行代码: import torch import torch.nn.functional as F from transformers

如何评估CAN总线信号质量

CAN总线网络的性能在很大程度上取决于其信号质量。信号质量差可能导致通信错误&#xff0c;进而引发系统故障、效率降低甚至安全隐患。因此&#xff0c;评估和确保CAN总线信号质量是维护系统健康和可靠性的关键。 在CAN总线网络中&#xff0c;数据通过双绞线上的差分信号传输。…

第三方软件评测机构如何助力软件品质提升及企业发展?

第三方软件评测机构与软件开发者及使用者无直接关联&#xff0c;它们提供全方位的检测和公正的评价服务。这样的评测可以展现客观的成效&#xff0c;对提升软件的品质具有显著影响&#xff0c;且在软件产业中发挥着至关重要的角色。 评测的客观性 独立第三方机构与软件开发者…

Linux之MySQL安装篇

1.确保Yum环境是否能正常使用 使用yum环境进行软件的安装 yum -y install mysql-server mysql2.确保软件包已正常完成安装 3.设置防火墙和selinux配置 ## 关闭防火墙 systemctl stop firewalld## 修该selinux配置 vim /etc/selinux/config 将seliuxenforcing修改为sel…