当前位置:首页 > 技术 > 正文内容

Spring Boot中利用注解实现工厂模式

访客 技术 2026年11月3日 2

在软件设计中,工厂模式是一种常用的创建型模式,它能够根据传入的参数动态地创建不同类的实例。简单工厂模式(Simple Factory Pattern),有时也被称为静态工厂方法模式,虽然不属于GoF的23种设计模式,但因其简洁和实用而广受欢迎。其核心思想是由一个专门的工厂类负责根据客户端提供的标识来决定并实例化哪个具体的产品类。

简单工厂模式的优势在于,它将对象的创建逻辑集中管理,客户端只需知道如何请求对象,而无需关心对象的具体创建过程和实现细节。这在多种场景下都能发挥作用,例如:

  • 日志系统:根据用户选择将日志输出到文件、数据库或网络。
  • 产品配置:根据不同参数生成不同配置的产品。
  • 协议框架:根据指定的协议类型(如HTTP, FTP)创建相应的连接对象。

Spring框架,特别是Spring Boot,提供了强大的依赖注入(DI)和注解功能,可以极大地简化工厂模式的实现。通过结合Spring的Bean管理和注解,我们可以构建出更加灵活和易于维护的工厂。

简单工厂模式的实现

我们将以一个图形绘制的例子来演示。首先,定义一个通用的图形接口 Shape:

/**
 * 图形接口
 */
public interface Shape {
    void draw();
}

接着,创建几个实现该接口的具体图形类,并使用 @Service 注解将它们注册为Spring Bean:

@Service("rectangle") // 为Bean指定一个名称,方便后续按名称获取
public class Rectangle implements Shape {
    @Override
    public void draw() {
        System.out.println("绘制矩形");
    }
}
@Service("square")
public class Square implements Shape {
    @Override
    public void draw() {
        System.out.println("绘制正方形");
    }
}
@Service("circle")
public class Circle implements Shape {
    @Override
    public void draw() {
        System.out.println("绘制圆形");
    }
}

实现方式一:传统的静态工厂方法

这种方式不依赖Spring的Bean管理,通过一个静态方法根据传入的字符串参数创建并返回具体的Shape实例。这种方法虽然简单,但如果需要添加新的图形类型,就需要修改工厂类本身。

public class TraditionalShapeFactory {
    /**
     * 根据形状类型获取对应的Shape实例
     */
    public static Shape getShape(String shapeType) {
        if ("circle".equalsIgnoreCase(shapeType)) {
            return new Circle();
        } else if ("rectangle".equalsIgnoreCase(shapeType)) {
            return new Rectangle();
        } else if ("square".equalsIgnoreCase(shapeType)) {
            return new Square();
        }
        return null; // 或抛出异常
    }
}

实现方式二:基于Spring注解的工厂

这种方式充分利用了Spring的DI能力。我们创建一个Bean工厂类,使用 @Autowired 将所有 Shape 接口的实现类(Spring Bean)自动注入到一个Map中。Map的键通常是Bean的名称(在 @Service 注解中指定,或Spring自动生成的默认名称),值则是对应的实现类实例。

@Component
public class SpringShapeBeanFactory {

    // Spring 会将所有 Shape 接口的实现类注入到这个 Map 中
    // Map 的 key 是 Bean 的名称 (例如 "circle", "rectangle", "square")
    private final Map<String, Shape> shapeMap;

    @Autowired
    public SpringShapeBeanFactory(Map<String, Shape> shapeMap) {
        this.shapeMap = shapeMap;
    }

    public Shape getShape(String shapeType) {
        // 直接从Map中根据名称获取对应的Bean
        Shape shape = shapeMap.get(shapeType);
        if (shape == null) {
            System.err.println("未找到类型为 " + shapeType + " 的图形 Bean");
            // 可以选择返回null,或抛出异常
        }
        return shape;
    }
}

与第一种方式相比,基于Spring注解的实现更加简洁,避免了硬编码的 if-else 逻辑,并且更符合Spring的开发习惯。

客户端调用

现在,我们创建一个Controller来演示如何调用这两种工厂:

@RestController
public class ShapeController {

    // 注入 Spring 提供的 ShapeBeanFactory
    @Autowired
    private SpringShapeBeanFactory springShapeBeanFactory;

    @GetMapping("/drawShapes")
    public String drawShapes() {
        System.out.println("--- 使用传统静态工厂 ---");
        // 调用传统工厂
        Shape circleFromTraditional = TraditionalShapeFactory.getShape("circle");
        if (circleFromTraditional != null) {
            circleFromTraditional.draw();
        }
        Shape rectangleFromTraditional = TraditionalShapeFactory.getShape("rectangle");
        if (rectangleFromTraditional != null) {
            rectangleFromTraditional.draw();
        }

        System.out.println("\n--- 使用Spring注解工厂 ---");
        // 调用Spring工厂
        Shape circleFromSpring = springShapeBeanFactory.getShape("circle");
        if (circleFromSpring != null) {
            circleFromSpring.draw();
        }
        Shape squareFromSpring = springShapeBeanFactory.getShape("square");
        if (squareFromSpring != null) {
            squareFromSpring.draw();
        }

        return "图形绘制完成,请查看控制台输出";
    }
}

当客户端访问 /drawShapes 接口时,控制台会输出类似以下内容:

--- 使用传统静态工厂 ---
绘制圆形
绘制矩形

--- 使用Spring注解工厂 ---
绘制圆形
绘制正方形

优缺点与适用场景

优点

  • 封装性强:将对象的创建逻辑集中到工厂类中,客户端无需了解具体的实现类和创建过程,只需知道如何请求。
  • 易于扩展(Spring方式):通过Spring注解管理,添加新的图形实现类,只需将其注册为Bean,工厂类无需修改。
  • 提高代码可读性:隐藏了复杂的对象创建逻辑,使客户端代码更简洁。
  • 降低耦合:客户端依赖于工厂接口,而不是具体的实现类。

缺点

  • 违反开闭原则(传统方式):当需要添加新的产品类型时,必须修改工厂类。
  • 职责过重:传统工厂类可能集中了过多的创建逻辑,当产品种类繁多时,工厂类会变得臃肿。
  • Spring方式的限制:如果不使用Spring,则无法享受到其带来的便利。

适用场景

  • 当创建对象有复杂的逻辑时。
  • 当客户端只关心对象的功能,而不需要知道其具体实现细节时。
  • 当需要根据不同的条件返回不同的实例时。
  • 在Spring Boot中,当需要动态地根据某种标识选择并注入某个接口的实现类时,基于Spring注解的工厂方式是首选。

相关文章

Linux crontab 详解

1) crontab 是什么cron 是 Linux 的定时任务守护进程;crontab 是用来编辑/查看“按时间周期执行命令”的表(cron table)。常见两类:用户 crontab:每个用户一份(crontab -e 编辑)系统级 crontab / cron.d:可指定执行用户(/etc/crontab、/etc/cron.d/*)2) crontab 时间...

富文本里可以允许的 HTML 属性

一、所有标签默认允许的安全属性(极少)class        (可选)id           (通常建议禁用)title️ 注意:id 容易被滥用做锚点注入,很多系统直接禁用class 允许的话最好只允许固定前缀(如 editor-*)二、a 标签允许属性<a href="" t...

Mac 安装 Node.js 指南

方法一:通过官网安装包(最简单,适合初学者)如果你只是想快速安装并开始使用,这是最直接的方法。访问 Node.js 官网。页面会显示两个版本:LTS (Recommended For Most Users):长期支持版,最稳定。建议选这个。Current:最新特性版,包含最新功能但可能不够稳定。下载 .pkg 安装包并运行。按照安装向导点击“下一步”即可完成。方法二:使用 Homebrew 安装(...

Dom\HTML_NO_DEFAULT_NS 的副作用:自动加闭合标签

在使用Dom\HTMLDocument时,Dom\HTML_NO_DEFAULT_NS 将禁止在解析过程中设置元素的命名空间, 此设置是为了与DOMDocument向后兼容而存在的。当使用它时,已知的一个副作用就是:自动加闭合标签例如 </img> 为什么会这样?当你使用:Dom\HTML_NO_DEFAULT_NS文档会变成 无命名空间模式,此时内部更接近 XML...

Laravel 事件和监听器创建

在 Laravel 中,使用 Artisan 命令创建 Events(事件) 和 Listeners(监听器) 是非常高效的。你可以通过以下几种方式来实现:1. 手动创建单个 Event如果你只想创建一个事件类,可以使用 make:event 命令:Bashphp artisan make:event UserRegistered执行后,文件将生成在 app/Even...

自定义域名解析神器 dnsmasq

什么是 dnsmasq?dnsmasq 是一个轻量级、功能强大的网络服务工具,专为小型和中等规模网络设计。它是一个综合的网络基础设施解决方案[1]。dnsmasq 能做什么?功能说明应用场景DNS 转发与缓存将 DNS 查询转发到上游服务器(ISP、Google DNS 等),并在本地缓存结果加快 DNS 查询速度,减少外部 DNS 流量本地 DNS解析本地网络设备的主机名,无需编辑&n...

发表评论

访客

◎欢迎参与讨论,请在这里发表您的看法和观点。