新手篇工厂模式学习

0 阅读40分钟

一:为什么需要工厂类

核心:解耦对象的创建与使用,让代码更易扩展、更易维护

1.先来理解一下什么是代码耦合与解耦

(1)、核心概念
术语定义
耦合模块之间相互依赖的程度
解耦降低模块之间的依赖,让彼此更独立

简单来说:模块A内部使用了模块B,此时模块A就依赖了模块B,如果模块B内部的某个方法名字改变了,那么模块A如果调用了模块B的这个方法,那么模块A也要同步修改方法名,不然无法调用,这个就是耦合。按照博主来看,耦合限制了代码的可维护性扩展性

(2)耦合的讲解
1. 耦合对可维护性的影响:修改风险高、理解成本大

可维护性指的是修改代码的难易程度。耦合高时,维护工作会变得非常困难:

  • 修改的连锁反应:如果你想修改一个模块,必须连带检查和修改所有与之耦合的模块,比如依赖关系A->B->C,修改C要同步修改B,修改B又要同步修改A,因为一个模块的内部变化,可能会破坏依赖于它的其他模块。
  • 极高的理解成本:由于模块间依赖复杂,要修改一个地方,必须先理清背后一连串的关系,就像解开一团乱麻。这种心智负担会严重拖慢开发效率。
  • 难以安全测试:高耦合的代码很难单独测试。因为模块A依赖于模块B,要测试A就必须先把B搭建起来。如果B还依赖于C、D,测试环境会变得极其复杂,导致问题难以及时发现。
2. 耦合对可扩展性的影响:难以复用、难以替换

可扩展性指的是在不影响现有系统的情况下,添加新功能或修改旧功能的难易程度。耦合同样会在这方面造成阻碍:

  • 代码难以复用:如果你想复用某个模块,会发现它“拖家带口”。因为它与太多外部模块捆绑在一起,导致无法独立地应用到新项目中。
  • 功能难以替换:高耦合意味着具体实现被硬编码。例如,一个支付模块直接使用了 new AliPay(),而不是一个通用的 Pay 接口。当你想换成微信支付时,就必须修改这个模块的内部代码,重新new WeiXinPay(),如果还要更换支付方式就还要去支付模块硬编码的去重新new,无法做到简单地“即插即用”。
  • 开发互相阻塞:在团队协作中,如果模块高度耦合,任务就无法并行。例如,张三在写订单模块,李四在写物流模块,但两者代码写死了互相调用,那双方就必须等对方写完才能联调,造成时间浪费。
举个例子:高耦合

假设有一个OrderService(订单服务),需要记录日志。

高耦合的设计

class OrderService {
    // 直接依赖了具体的文件日志类
    private FileLogger logger = new FileLogger();

    public void createOrder() {
        // ... 业务逻辑
        logger.write("订单创建成功"); // 硬编码调用
    }
}

class FileLogger {
    public void write(String message) {
        // 将日志写入文件
    }
}
  • 问题依赖关系OrderService->FileLogger,OrderService 直接创建了 FileLogger。如果想改成数据库日志,就必须修改 OrderService 的代码,破坏了“开闭原则”。
(3)解耦的讲解

核心概念:解耦 = 降低模块之间的相互依赖,让彼此独立变化

高耦合的问题后果
一改A模块,B/C/D都要跟着改维护成本高
无法单独测试A模块必须搭全套环境
复用困难拆出来带着一堆依赖
协作冲突多人改同一块代码

解耦的实现方式

1. 面向接口编程(Interface-based Programming)

最根本的手段 模块 A 不直接调用模块 B 的具体类,而是调用一个接口(Interface)。 B 只需实现该接口,A 完全不知道 B 的内部细节。

// 1. 定义一个通知接口(抽象)
interface NotificationChannel {
    void send(String message);
}

// 2. 实现具体渠道
class SmsChannel implements NotificationChannel {
    @Override
    public void send(String message) {
        System.out.println("发送短信:" + message);
    }
}

class EmailChannel implements NotificationChannel {
    @Override
    public void send(String message) {
        System.out.println("发送邮件:" + message);
    }
}

// 3. 订单服务现在依赖接口,而不是具体类
public class OrderService {
    // 虽然这里是接口,但我们还需要处理具体传进来的对象
    private NotificationChannel channel;

    // 通过构造器把具体的实现传进来(这就是依赖注入的雏形)
    public OrderService(NotificationChannel channel) {
        this.channel = channel;
    }

    public void createOrder(String orderId) {
        System.out.println("订单创建成功:" + orderId);
        // 调用接口方法,不再关心具体是什么渠道
        channel.send("您的订单 " + orderId + " 已创建。");
    }
}

// 客户端调用
public class Main {
    public static void main(String[] args) {
        // 想用短信,就传入短信对象
        NotificationChannel channel = new SmsChannel();
        OrderService orderService = new OrderService(channel);
        orderService.createOrder("123456");

        // 想用邮件,只需换传入的对象,OrderService内部不用改
        // NotificationChannel channel2 = new EmailChannel();
        // OrderService orderService2 = new OrderService(channel2);
    }
}

image.png

需要注意的是:
public OrderService(NotificationChannel channel)接受的是接口类型的参数,只要是接口及接口的实现类,都可以接受
channel.send("您的订单 " + orderId + " 已创建。");这里调用接口的方法,本质是调用实现类重写的send方法

举个例子,Sms和Email都是NotificationChannel接口的实现类,且重写了send方法。我们OrderService都可以接受他们两个类,如果我们传入Sms,那么我们调用createOrder()方法,本质就是去调用Sms重写后的send方法

或许可能会问:为什么还要单独设置OrderService这个类,你main函数中 NotificationChannel channel = new SmsChannel();这里不还是硬编码了吗,直接new SmsChannel()了,以后SmsChannel名字变了,你不还要再这里同步改名字吗?

这确实是个好问题,博主以前也是这么想的,为什么还要设计OrderService类。但是后来也明白了,我们这里实现的是OrderService类解耦了,但是main函数还没解耦。

  • 现在我们的orderService接受的参数都是接口类,意味着他并不关心你到底传了哪个实现类
  • 以前的OrderService要接受Sms类的参数就要在内部定义变量 private Sms sms,你main函数传递Sms对象给OrderService类,OrderService的sms字段接受你的Sms对象,用sms变量的send方法发送短信,你有没有发现OrderService就和Sms类耦合在一起了。你想让OrderService实现发送短信功能,就必须在内部定义Sms类型的字段接受Sms对象。如果Sms类名字改成NewSms这个新名字,OrderService字段是不是也要一起改?如果你还要添加QQ类型的发送形式,你也想让OrderService可以新增QQ发送形式,是不是还要新增一个QQ字段?这不就再次耦合在一起了吗,OrderService想实现多种发送形式就必须要提前知道到底有哪些发送形式
  • 现在通过接口的接收形式,OrderService不关心你到底是Sms,QQ还是Weixin的发送方式,你只要是接口实现类,那我统统接受,然后调用接口的send方法(本质还是调用传入的接口实现重写后的send方法),实现你们各自的发送形式,即使你们修改名字了,对OrderService还是没有影响,因为你们不都还是接口实现类吗,这不就实现OrderService解耦了吗

然后就是解答为什么要设置OrderService,而不能在main函数中直接new的形式创建实现类,调用send方法

  • 这里本质只是拿OrderService举个例子,真是项目中功能太多太多了。就比如我们上面的订单发送方式就有好几种,我们不可能说这里需要WeiXin发送就单独用WeiXin类,那里要QQ就直接调用QQ,这样代码就耦合在一块了。而是把功能抽取成一个模块,比如发送模块就包含上面几种发送方式。我们需要发送时,只需要调用模块统一的方法就能实现方式,这样项目才好管理,而不是这里放一块,那里放一块。上面的OrderService就可以理解为发送模块,他就代替我们进行发送服务了
2.spring的@Autowired自动注入机制

Spring @Autowired:控制反转(IoC)

// Spring 方式 - 依赖"送"上门
@Service
public class OrderService {
    
    @Autowired
    private UserService userService;  // 只声明接口,不管谁实现
    
    @Autowired
    private EmailService emailService; // Spring 自动装配正确的bean
    
    public void createOrder() {
        userService.validateUser();  // 只管使用,不管创建
    }

只要在Autowired注解下注明我们要注入的类,容器就会自动去查找这个类并且注入进来,前提是一定要将该类注入Bean对象,放进容器才行,容器只能对加入到容器的类进行管理

解耦的核心机制

 依赖查找 → 依赖注入(控制反转)

传统方式:                Spring方式:
┌─────────────┐          ┌─────────────┐
│ OrderService │          │ OrderService │
│   ↓ new      │          │   ↑注入      │
│ UserService  │          │ UserService  │
└─────────────┘          └─────────────┘
    主动创建                  被动接收(IoC容器控制)

关键转变:对象不再控制依赖的创建,控制权交给 Spring 容器(IoC)。我们只需要申明要注入的对象,不用自己new,容器会帮我们创建并注入进来

这里需要讲解一下我们一般怎么用@Autowired注解进行解耦的,我们一般只声明要注入的接口类型

@Autowired
    private UserService userService;  // 只声明接口,不管谁实现,该接口的实现类就自动注入进来了

那么容器就会自动查找接口实现类并且注入,这样实现类的名字再怎么变,他都实现类该接口,我们都可以用声明接口的形式将该实现类注入进来,这就实现了解耦

或许还有人问:如果一个接口有多个实现类,那么我们只声明接口型,容器怎么区分哪些是我们要注入的实现类
下面是提供的四种解决方案
假设场景:

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

@Component  // 默认 bean 名称为 "smsSender"
public class SmsSender implements MessageSender { ... }

@Component  // 默认 bean 名称为 "emailSender"  
public class EmailSender implements MessageSender { ... }
方案1:@Primary(首选方案)
@Component
@Primary  // 标记为主要实现,默认注入这个
public class EmailSender implements MessageSender { ... }

// 使用处无需改动,默认注入 EmailSender
@Autowired
private MessageSender sender;  // 拿到 EmailSender

方案2:@Qualifier(精确指定,注入时指定实现类名称)
@Autowired
@Qualifier("smsSender")  // 指定 bean 名称(类名首字母小写)
private MessageSender smsSender;

@Autowired
@Qualifier("emailSender")
private MessageSender emailSender;

方案3:字段名/参数名匹配(Spring 4.3+)
@Autowired
private MessageSender smsSender;   // 字段名 = bean 名称,自动匹配

@Autowired  
private MessageSender emailSender; // 字段名 = bean 名称,自动匹配

原理:Spring 会先按类型找,找到多个时,再用字段名匹配 bean 名称。

方案4:注入集合/Map(全部获取)
// 获取所有实现类
@Autowired
private List<MessageSender> allSenders;  // [SmsSender, EmailSender]

// 或按名称映射
@Autowired
private Map<String, MessageSender> senderMap;  
// {"smsSender": SmsSender, "emailSender": EmailSender}

// 使用时动态选择
public void broadcast(String msg) {
    allSenders.forEach(s -> s.send(msg));  // 全渠道发送  
三、方案对比

表格

方案适用场景优点缺点
@Primary有默认实现,偶尔需要其他的代码简洁只能有一个默认
@Qualifier明确知道要哪个精确控制硬编码 bean 名
字段名匹配简单场景少写注解重构时易出错
集合注入策略模式、广播场景扩展性好需自行遍历选择

注意,这里不推荐注解注入实现类的形式

@Autowired
private UserServiceImpl userService;  // 具体实现类

注意:直接注入类会丧失解耦优势,但语法上是允许的。

2.再来理解一下为什么需要工厂类

一、传统 new 关键字的核心问题

直接用 new 创建对象,看似简单,但在复杂业务场景下会暴露以下痛点:

1. 耦合度极高

new 具体类() 会让调用方和具体的类强绑定。比如:

// 调用方直接new,和MySQLConnection强耦合
DatabaseConnection conn = new MySQLConnection("localhost", 3306, "user", "pass");

如果后续要替换成 OracleConnection,必须修改所有写了 new MySQLConnection() 的代码,同时需要修改数据库url等等,违反「开闭原则」(对扩展开放,对修改关闭)。

2. 对象创建逻辑复杂,代码冗余

如果对象创建需要:

  • 初始化参数(如数据库连接的 URL、用户名、密码)
  • 前置校验(如检查配置是否合法)
  • 资源准备(如加载驱动、建立连接池)
  • 异常处理(如连接失败重试)

这些逻辑会散落在所有 new 对象的地方,导致代码重复、维护困难。

3. 无法统一管理对象生命周期

调用方各自 new 对象,无法统一控制对象的创建、复用、销毁。比如需要实现「连接池复用连接」「单例对象」,直接 new 根本做不到。

4. 隐藏依赖,可读性差

调用方只需要「一个数据库连接」,但 new MySQLConnection() 要求调用方必须知道:

  • MySQLConnection 的构造参数
  • 依赖的驱动类
  • 初始化顺序这些细节本该被封装,却暴露给了调用方。

1.就是说调用方只想创建一个数据库连接,但是他却需要传递完整的创建数据库连接所需要的一系列对象,太过冗余了,如果我这里需要使用一个数据库连接,就需要new MySQLConnection("localhost", 3306, "user", "pass"),那里需要就在new一个,填写完整参数,是不是太麻烦了
2.如果我们有一个工厂类,我们将数据库连接的参数都设置给工厂,让他代替我们去创建数据库连接,我们哪里需要数据库连接,就在哪里调用工厂,由工厂统一创建,不再需要手动传递参数.同时如果我们想修改连接的数据库地址,只需要修改工厂参数,填入正确的数据库url,就行了.是不是就方便很多了
3.如果不好理解,我们就把工厂想象成一个方法,如下图的getMysqlConnection,我们在方法中定义了数据库连接的参数,这样我们哪里需要数据库连接就在哪里调用方法,需要修改数据库信息时在方法中修改,我们调用方只需要调用getMysqlConnection()方法即可,不用关注更多数据库细节,这就类似于工厂

private MySqlConnection getMysqlConnection()
{
    return new MySQLConnection("localhost", 3306, "user", "pass")
}
二、工厂类如何解决这些问题

工厂类的核心思想是:将对象的创建逻辑封装到专门的类(工厂类)中,调用方只需要告诉工厂「我要什么」,不需要知道「怎么创建」

1. 解耦:隔离调用方和具体实现

工厂类提供统一的接口,调用方只依赖接口 / 抽象类,不依赖具体实现类。

// 工厂类封装创建逻辑
public class ConnectionFactory {
    // 调用方只传类型,不需要知道具体类
    public static Connection createConnection(String dbType) {
        if ("mysql".equals(dbType)) {
            return new MySQLConnection();
        } else if ("oracle".equals(dbType)) {
            return new OracleConnection();
        }
        throw new IllegalArgumentException("不支持的数据库类型");
    }
}

// 调用方使用(只依赖工厂和接口,和具体类解耦)
Connection conn = ConnectionFactory.createConnection("mysql");

后续替换数据库类型,只需要修改工厂类,调用方代码完全不用动。

2. 封装复杂创建逻辑,消除冗余

把对象创建的复杂逻辑(参数、校验、资源)都放到工厂里,调用方只需一行代码:

public class ConnectionFactory {
    public static Connection createConnection(String dbType) {
        // 1. 加载配置(复杂逻辑封装)
        Properties prop = loadConfig("db.properties");
        String url = prop.getProperty(dbType + ".url");
        String user = prop.getProperty(dbType + ".user");
        String pwd = prop.getProperty(dbType + ".pwd");
        
        // 2. 校验参数
        if (url == null || user == null) {
            throw new RuntimeException("数据库配置缺失");
        }
        
        // 3. 创建对象
        if ("mysql".equals(dbType)) {
            return new MySQLConnection(url, user, pwd);
        } else if ("oracle".equals(dbType)) {
            return new OracleConnection(url, user, pwd);
        }
        throw new IllegalArgumentException("不支持的数据库类型");
    }
    
    // 私有方法:加载配置(调用方无需关心)
    private static Properties loadConfig(String path) {
        // 实现加载逻辑...
    }
}

// 调用方极简:只传类型,无需关心配置/校验/创建细节
Connection conn = ConnectionFactory.createConnection("mysql");
3. 统一管理对象生命周期

工厂类可以控制对象的创建规则(单例、池化、缓存),而调用方无感知:

// 工厂类实现连接池(复用对象,避免频繁new)
public class ConnectionFactory {
    // 连接池:缓存创建好的连接
    private static Map<String, Connection> pool = new HashMap<>();
    
    public static Connection createConnection(String dbType) {
        // 先查连接池,有则复用,无则创建
        if (pool.containsKey(dbType)) {
            Connection conn = pool.get(dbType);
            if (conn.isValid()) {
                return conn;
            }
        }
        
        // 创建新连接并放入池
        Connection newConn = createNewConnection(dbType);
        pool.put(dbType, newConn);
        return newConn;
    }
    
    // 私有方法:真正创建新连接
    private static Connection createNewConnection(String dbType) {
        // 实现创建逻辑...
    }
}
4. 隐藏依赖,提升可读性

调用方只需关注「获取对象」的目标,无需关注创建对象的依赖和细节,代码意图更清晰:

// 差:调用方要知道所有细节
Connection conn = new MySQLConnection("jdbc:mysql://localhost:3306/db", "root", "123456");

// 好:调用方只关注目标,细节由工厂封装
Connection conn = ConnectionFactory.createConnection("mysql");
三、工厂类的适用场景

不是所有场景都需要工厂类:

  • 简单对象(如 new String()new ArrayList()):直接 new 更简洁,无需工厂;
  • 复杂对象(创建需配置 / 校验 / 资源)、需要灵活替换实现、需要统一管理对象:工厂类价值极大。
四、工厂类的一些细节问题

问题:调用方不再直接关心数据库连接的逻辑了,但是调用方不就直接调用工厂了吗,这不就形成调用方与工厂的耦合了吗,同时如果我们要换数据库连接了不还是要修改工厂类吗

你提的这个问题非常关键,也是很多新手理解工厂模式时最容易产生的疑问 ——调用方确实和工厂类产生了耦合,但这是一种「更优的耦合」,且工厂模式本身也有进阶方案来解决「修改工厂逻辑」的问题

我们一步步拆解这个问题:

1.先明确:耦合不可消除,只能「优化耦合」

软件开发中,完全无耦合的代码是不存在的(否则代码无法协作)。我们的目标不是消灭耦合,而是降低耦合

  1. 让耦合发生在「低风险、易维护」的地方;
  2. 把「强耦合」(和具体实现类耦合)变成「弱耦合」(和抽象 / 工厂耦合);
  3. 把「多处耦合」收敛为「一处耦合」。

对比:传统 new vs 工厂类的耦合

维度传统 new 具体类工厂类
耦合对象调用方 ↔ 具体实现类调用方 ↔ 工厂类
耦合范围所有调用 new 的地方仅工厂类一处
变更成本改 N 处代码改 1 处工厂代码
对调用方的影响调用方需要感知实现细节调用方完全无感知

举个实际例子:

  • new MySQLConnection():100 个调用方都写了这句,换 Oracle 要改 100 处;
  • ConnectionFactory.createConnection("mysql"):换 Oracle 只需改工厂类里的逻辑,100 个调用方一行代码都不用动。

核心:工厂类把「分散的耦合」收敛成了「集中的耦合」,这是质的提升 —— 修改一处,影响全局,而不是修改百处,容易遗漏。

2.如何解决「修改工厂逻辑」的问题?

你担心的「改工厂逻辑」确实存在(比如简单工厂模式的缺点),但可以通过「工厂方法模式」「抽象工厂模式」进一步解耦,让工厂类也符合「开闭原则」。

a. 简单工厂的问题(你质疑的点)

我们之前写的简单工厂,核心问题是:新增数据库类型(如 PostgreSQL)时,必须修改工厂类的 if/else 逻辑,需要修改工厂类代码,违反开闭原则。

// 简单工厂:新增类型必须改这里
public static Connection createConnection(String dbType) {
    if ("mysql".equals(dbType)) {
        return new MySQLConnection();
    } else if ("oracle".equals(dbType)) { // 新增类型要加else if
        return new OracleConnection();
    }
    // ...
}
b. 工厂方法模式:让工厂也「可扩展」

思路:

  • 定义「抽象工厂接口」,每个具体实现类对应一个「具体工厂类」,每个工厂类只生产特定产品,如MySql工厂只生产Mysql连接,Redis工厂只生成Redis连接.如果需要增加工厂就必须实现抽象工厂接口
  • 新增类型时,只需要新增具体工厂类,无需修改原有代码

代码示例:

// 1. 连接接口(产品接口)
public interface Connection {
    void connect();
    // 新增:获取连接的基础信息(用于后续校验)
    String getUrl();
}

// 2. 具体产品:MySQLConnection(创建逻辑变复杂)
public class MySQLConnection implements Connection {
    private String url;
    private String username;
    private String password;

    // 构造方法:需要传入配置,且创建时要校验
    public MySQLConnection(String url, String username, String password) {
        if (url == null || !url.startsWith("jdbc:mysql://")) {
            throw new IllegalArgumentException("MySQL URL格式错误");
        }
        this.url = url;
        this.username = username;
        this.password = password;
    }

    @Override
    public void connect() {
        System.out.println("连接MySQL:" + url);
    }

    @Override
    public String getUrl() {
        return url;
    }
}

// 3. 抽象工厂接口(不变,但定义了「创建连接」的统一规范)
public interface ConnectionFactory {
    Connection createConnection(); // 工厂方法:封装所有创建逻辑
}

// 4. 具体工厂:MySQLConnectionFactory(工厂方法的核心作用体现)
public class MySQLConnectionFactory implements ConnectionFactory {
    // 工厂内部封装配置(调用方无需知道)
    private static final String MYSQL_URL = "jdbc:mysql://localhost:3306/test";
    private static final String MYSQL_USER = "root";
    private static final String MYSQL_PWD = "123456";

    @Override
    public Connection createConnection() {
        // 工厂方法封装「复杂创建逻辑」:
        // 1. 加载配置(这里简化为常量,实际可从配置文件读)
        // 2. 校验配置(比如检查密码是否为空)
        if (MYSQL_PWD == null || MYSQL_PWD.isEmpty()) {
            throw new RuntimeException("MySQL密码不能为空");
        }
        // 3. 创建具体产品(调用复杂的构造方法)
        MySQLConnection conn = new MySQLConnection(MYSQL_URL, MYSQL_USER, MYSQL_PWD);
        // 4. 额外逻辑:比如测试连接是否可用
        testConnection(conn);
        // 5. 返回产品
        return conn;
    }

    // 工厂内部的私有方法:测试连接(调用方无需关心)
    private void testConnection(Connection conn) {
        System.out.println("测试连接:" + conn.getUrl() + " → 可用");
    }
}

// 5. 调用方使用(核心:调用方完全不用关心创建逻辑)
public class Client {
    public static void main(String[] args) {
        // 调用方只依赖「抽象工厂接口」,且一行代码就能创建复杂对象
        ConnectionFactory factory = new MySQLConnectionFactory();
        Connection conn = factory.createConnection();
        conn.connect();

        // 换Oracle:只需替换工厂,调用逻辑完全不变
        // factory = new OracleConnectionFactory();
        // conn = factory.createConnection();
        // conn.connect();
    }
}

image.png
工厂方法到底「有用」在哪里?

从上面的示例能清晰看到,createConnection() 这个工厂方法的核心价值体现在 3 个层面:

  1. 封装「对象创建的复杂逻辑」,隔离调用方
  • 调用方视角:只需要 factory.createConnection() 一行代码,就能拿到可用的 Connection 对象,完全不用知道:

    • MySQL 连接需要什么 URL 格式;
    • 密码不能为空的校验规则;
    • 创建后还要测试连接是否可用;
    • 配置存在哪里(常量 / 配置文件 / 数据库)。
  • 工厂方法视角:把所有「创建对象的脏活累活」都包揽了,调用方只需「结果」,不用管「过程」。

  1. 定义「创建产品的统一规范」,解耦实现
  • 抽象工厂接口 ConnectionFactory 定义了 createConnection() 方法,这是所有具体工厂必须遵守的「创建规范」—— 不管是 MySQL、Oracle 还是 PostgreSQL 工厂,都必须实现这个方法,返回 Connection 类型。
  • 调用方只依赖 ConnectionFactory 接口,不依赖任何具体工厂 / 具体产品:哪怕后续新增 PostgreSQLConnectionFactory,调用方的代码也不用改(只需替换工厂实例)。
  1. 隔离「产品创建的变更风险」

如果后续 MySQL 的创建逻辑变了(比如 URL 格式改了、需要加超时时间、密码要加密),只需要修改 MySQLConnectionFactorycreateConnection() 方法,调用方和其他工厂(如 Oracle)完全不受影响。

核心:
1.我们可以进一步拆分工厂类,定义一个抽象工厂,让抽象工厂生产特定工厂,特定工厂只需生产一种产品即可,比如MysqlConnectionFactory只生产MysqlConnection。
2.我们又定义了一个Connection接口,提供了connect接口方法,它就像一个契约,不管是Redis,MySql数据库还是别的,都需要实现该接口,统一格式,重写connect方法,这样即使不同数据库的连接机制不同,我们只需要调用connect方法即可获得连接,MySqlConnection就实现了该接口
3.抽象工厂提供一个基础接口方法createConnection(),所有工厂类均要实现这个接口,具体建立连接的逻辑都要写在这个方法中,返回一个Connection对象,即已建立的连接。用户只需要调用该方法即可获取数据库连接,不用关心实现细节

c. 终极解耦:结合「配置 + 反射」(实战常用)

如果想让调用方连「具体工厂类」都不耦合,可以用配置文件 + 反射:

  1. 在配置文件(如 factory.properties)中指定要使用的工厂类:

    db.factory=com.example.MySQLConnectionFactory
    
  2. 工厂类通过反射动态加载:

    public class FactoryLoader {
        // 加载配置文件,反射创建工厂实例
        public static ConnectionFactory loadFactory() {
            Properties prop = new Properties();
            prop.load(FactoryLoader.class.getClassLoader().getResourceAsStream("factory.properties"));
            String factoryClass = prop.getProperty("db.factory");
            // 反射创建工厂
            return (ConnectionFactory) Class.forName(factoryClass).newInstance();
        }
    }
    
    // 调用方:完全不耦合任何具体类,只依赖接口
    public class Client {
        public static void main(String[] args) {
            ConnectionFactory factory = FactoryLoader.loadFactory();
            Connection conn = factory.createConnection();
            conn.connect();
            // 换数据库:只需改配置文件,代码一行不用动
        }
    }
    
3、核心认知:设计模式是「权衡」,不是「银弹」
  1. 工厂模式的目标不是「零修改」,而是「最小化修改」「最小化影响范围」;
  2. 简单工厂适合「实现类变化少」的场景(比如固定几种数据库),工厂方法适合「频繁新增实现类」的场景;
  3. 不要过度设计:如果你的业务中实现类几乎不会变,简单工厂甚至直接 new 都是合理的 —— 设计模式是解决问题的,不是炫技的。
总结
  1. 调用方与工厂耦合是「可接受的最优解」:将分散在各处的耦合收敛到工厂类,把「改 N 处」变成「改 1 处」,是耦合的优化而非恶化;
  2. 工厂方法模式解决「修改工厂逻辑」的问题:通过抽象工厂 + 具体工厂,新增实现类时只需新增工厂类,无需修改原有工厂代码,符合开闭原则;
  3. 实战中可结合配置 + 反射:让调用方完全依赖接口,连具体工厂类都不耦合,实现「零代码修改」切换实现。
五.总结
  1. 传统 new 的核心问题:调用方与具体类强耦合、创建逻辑冗余、无法统一管理对象、暴露不必要的细节;
  2. 工厂类的解决思路:将对象创建逻辑封装到专门的工厂类中,调用方只依赖工厂的统一接口,不依赖具体实现;
  3. 工厂类的核心价值:解耦(符合开闭原则)、封装复杂逻辑、统一管理对象生命周期、简化调用方代码。

简单来说,工厂类把「怎么造对象」和「用对象」分离开,让代码更易维护、更灵活。

二:工厂类的分类

1. 简单工厂(Simple Factory)

也叫「静态工厂」,是最基础的工厂模式,核心是用一个工厂类封装所有产品的创建逻辑,类比上面的例子,就是说该工厂类不仅生产MySql连接,还生成Redis连接等等,即一个工厂生产多个产品

核心特点
  • 只有一个工厂类,通常用 static 方法创建产品;
  • 调用方传「类型参数」(如字符串 / 枚举),工厂通过 if/else/switch 返回具体产品;
  • 不属于 GoF 23 种设计模式,但实际开发中最常用(简单易上手)。
代码示例(数据库连接场景)
// 产品接口
public interface Connection {
    void connect();
}

// 具体产品
class MySQLConnection implements Connection {
    @Override
    public void connect() { System.out.println("MySQL连接"); }
}
class OracleConnection implements Connection {
    @Override
    public void connect() { System.out.println("Oracle连接"); }
}

// 简单工厂类(核心)
public class SimpleConnectionFactory {
    // 静态方法:根据类型创建产品
    public static Connection createConnection(String dbType) {
        switch (dbType) {
        //用户传递mysql就生产mysql的连接
            case "mysql": return new MySQLConnection();
            case "oracle": return new OracleConnection();
            default: throw new IllegalArgumentException("不支持的数据库类型");
        }
    }
}

// 调用方
public class Client {
    public static void main(String[] args) {
        Connection conn = SimpleConnectionFactory.createConnection("mysql");
        conn.connect();
    }
}
适用场景
  • 产品类型少(比如固定 2-3 种),且几乎不会新增;
  • 不想让调用方关心产品创建细节,只想快速获取对象。
优缺点
优点缺点
简单易实现,调用方只需传参数新增产品必须修改工厂类的 if/else,违反「开闭原则」
封装了产品创建逻辑,解耦调用方和具体产品工厂类会随着产品增多变得臃肿(「上帝类」问题

2. 工厂方法(Factory Method)

工厂方法模式(Factory Method Pattern)是创建型设计模式的核心之一,也是对「简单工厂模式」的优化升级。它的核心思想是:定义一个创建对象的抽象接口(工厂接口),让具体的子类(具体工厂)决定实例化哪一个产品类—— 简单说就是「把对象的创建权,交给具体的工厂子类」。

(1)、先搞懂工厂方法模式的核心角色

为了方便理解,我们先明确模式中的 4 个核心角色(用数据库连接场景举例):

角色作用示例(数据库场景)
产品接口(Product)定义所有产品的通用行为,是具体产品的抽象Connection 接口(定义 connect() 方法)
具体产品(Concrete Product)实现产品接口的具体类,是最终要创建的对象MySQLConnectionOracleConnection
工厂接口(Creator)定义创建产品的「工厂方法」,返回产品接口类型ConnectionFactory 接口(定义 createConnection() 方法)
具体工厂(Concrete Creator)实现工厂接口,重写工厂方法,创建对应的具体产品MySQLConnectionFactoryOracleConnectionFactory

image.png

(2)、工厂方法模式的核心逻辑(通俗解释)

你可以把工厂方法模式理解为:

  1. 你(调用方)需要一个「数据库连接」,但不想自己 new 具体的连接类;
  2. 你找到一个「连接工厂总店」(工厂接口),总店规定所有分店(具体工厂)都必须能「造连接」;
  3. 「MySQL 分店」(MySQL 工厂)只造 MySQL 连接,「Oracle 分店」(Oracle 工厂)只造 Oracle 连接;
  4. 你想要哪种连接,就找对应的分店要 —— 你只认「总店的规则」(工厂接口),不认具体分店,也不用管
(3)、完整代码示例
// 1. 产品接口:定义所有数据库连接的通用行为
public interface Connection {
   void connect(); // 核心行为:连接数据库
}

// 2. 具体产品1:MySQL连接(实现产品接口)
public class MySQLConnection implements Connection {
   @Override
   public void connect() {
       System.out.println("创建MySQL数据库连接");
   }
}

// 3. 具体产品2:Oracle连接(实现产品接口)
public class OracleConnection implements Connection {
   @Override
   public void connect() {
       System.out.println("创建Oracle数据库连接");
   }
}

// 4. 工厂接口:定义创建产品的规范(工厂方法)
public interface ConnectionFactory {
   // 工厂方法:返回产品接口类型,具体造什么由子类决定
   Connection createConnection();
}

// 5. 具体工厂1:MySQL工厂(只造MySQL连接)
public class MySQLConnectionFactory implements ConnectionFactory {
   @Override
   public Connection createConnection() {
       // 这里可以封装复杂的创建逻辑(比如加载驱动、校验参数)
       return new MySQLConnection();
   }
}

// 6. 具体工厂2:Oracle工厂(只造Oracle连接)
public class OracleConnectionFactory implements ConnectionFactory {
   @Override
   public Connection createConnection() {
       return new OracleConnection();
   }
}

// 7. 调用方使用(核心:只依赖接口,不依赖具体实现)
public class Client {
   public static void main(String[] args) {
       // 步骤1:选择具体工厂(想要MySQL连接,就用MySQL工厂)
       ConnectionFactory factory = new MySQLConnectionFactory();
       // 步骤2:通过工厂方法创建产品(拿到的是接口类型)
       Connection conn = factory.createConnection();
       // 步骤3:使用产品(无需关心具体是哪种连接)
       conn.connect();

       // 切换成Oracle连接:只需换工厂,调用逻辑完全不变
       factory = new OracleConnectionFactory();
       conn = factory.createConnection();
       conn.connect();
   }
}

deepseek_mermaid_20260322_a2fcc7.png

(4)、工厂方法模式的核心优势(解决了什么问题)
  1. **严格遵守「开闭原则」**新增一种数据库连接(比如 PostgreSQL),只需新增 PostgreSQLConnection(具体产品) + PostgreSQLConnectionFactory(具体工厂),原有代码一行都不用改—— 而简单工厂需要修改工厂类的 if/else,违反开闭原则。
  2. 单一职责原则每个具体工厂只负责创建一种产品:MySQL 工厂只造 MySQL 连接,Oracle 工厂只造 Oracle 连接,工厂类的逻辑不会随着产品增多而臃肿(避免了简单工厂的「上帝类」问题)。
  3. 解耦程度更高调用方只依赖「工厂接口」和「产品接口」,完全不依赖具体的工厂 / 产品实现类。哪怕底层换了数据库连接的实现逻辑,调用方代码也不受影响。
  4. 灵活的扩展性可以为不同产品定制不同的创建逻辑:比如 MySQL 连接需要加载特定驱动,Oracle 连接需要校验特殊参数,这些逻辑都封装在各自的工厂里,互不干扰。
(5)、工厂方法模式的适用场景
  • 当你不知道(或不想固定)要创建哪种具体产品时;
  • 当产品类型较多,且需要频繁新增产品时;
  • 当希望把对象创建逻辑和业务逻辑分离时;
  • 当需要让子类决定创建哪种对象时(比如框架开发中,让用户自定义扩展)。
(6)、和简单工厂的核心区别(一张表看懂)
维度简单工厂工厂方法
工厂数量只有 1 个工厂类一个产品对应一个具体工厂类
开闭原则新增产品需修改工厂类(违反)新增产品只需新增工厂类(符合)
代码复杂度低(类少)高(类数量翻倍)
适用场景产品少、几乎不新增产品多、频繁新增
(7)、总结
  1. 核心定义:工厂方法模式通过「抽象工厂接口 + 具体工厂子类」,把对象创建的逻辑分散到各个具体工厂,让调用方只依赖抽象接口;
  2. 核心价值:符合开闭原则,新增产品无需修改原有代码,解耦调用方与具体产品;
  3. 核心思想:「把创建权交给子类」,让每个具体工厂专注创建自己的产品,做到职责单一。

简单来说,工厂方法模式就是「为每个产品配一个专属工厂」,既保证了扩展性,又让代码逻辑更清晰。

3.抽象工厂

抽象工厂模式(Abstract Factory Pattern)是创建型设计模式中更高级的工厂模式,是工厂方法模式的「升级版」。它的核心思想是:定义一个创建「产品族」的抽象接口,让具体的工厂子类负责创建某一整个产品族的所有产品—— 简单说就是「工厂方法造单个产品,抽象工厂造一套产品」。

(1)、先搞懂抽象工厂模式的核心概念

在理解抽象工厂前,必须先分清两个关键概念(用「电子产品」举例更易理解):

表格

概念定义示例(数据库场景)
产品等级结构同一类产品的不同实现(比如「手机」下的华为手机、苹果手机)Connection 接口下的 MySQLConnectionOracleConnectionDataSource 接口下的 MySQLDataSourceOracleDataSource
产品族同一品牌 / 场景下的多个不同等级的产品(比如「华为全家桶」:华为手机 + 华为平板 + 华为手表)MySQL 产品族:MySQLConnection(连接) + MySQLDataSource(数据源) + MySQLTransaction(事务);Oracle 产品族:OracleConnection + OracleDataSource + OracleTransaction

抽象工厂的核心就是「造产品族」,而工厂方法只是「造单个产品等级结构里的产品

(2)、抽象工厂模式的核心角色

延续数据库场景,抽象工厂包含 5 个核心角色(比工厂方法多了「多产品等级结构」):

表格

角色作用示例(数据库场景)
抽象产品 A(Product A)第一个产品等级结构的接口Connection 接口(定义 connect()
具体产品 A1(Concrete Product A1)产品 A 的具体实现(属于产品族 1)MySQLConnection
具体产品 A2(Concrete Product A2)产品 A 的具体实现(属于产品族 2)OracleConnection
抽象产品 B(Product B)第二个产品等级结构的接口DataSource 接口(定义 getDataSource()
具体产品 B1(Concrete Product B1)产品 B 的具体实现(属于产品族 1)MySQLDataSource
具体产品 B2(Concrete Product B2)产品 B 的具体实现(属于产品族 2)OracleDataSource
抽象工厂(Abstract Factory)定义创建所有产品等级结构的「工厂方法」(造产品族的规范)DBFactory 接口(定义 createConnection() + createDataSource()
具体工厂 1(Concrete Factory 1)实现抽象工厂,创建产品族 1 的所有产品MySQLFactory(造 MySQLConnection + MySQLDataSource
具体工厂 2(Concrete Factory 2)实现抽象工厂,创建产品族 2 的所有产品OracleFactory(造 OracleConnection + OracleDataSource

工厂方法模式:

                                  
    ConnectionFactory(工厂接口)             
    ├─ createConnection() 接口方法1                
                                             
    ├─ MySqlConnectionFactory 具体工厂实现类A    
       └─ createConnection()实现接口方法1                          
           └─connect()实现产品接口
               └─MySqlConnection创建产品A1                   
                           
    ├─ RedisConnectionFactory 具体工厂实现类B    
       └─ createConnection()实现接口方法1                          
           └─connect()实现产品接口
               └─RedisConnection创建产品B1    

抽象工厂:

   DBFactory(工厂接口)             
   ├─ createConnection()    接口方法1                
   │─ createDataSource()    接口方法2    
   
   ├─ MySqlFactory    具体工厂实现类A     
      └─ createConnection()   实现工厂接口方法1                          
          └─connect()   实现产品接口1
              └─MySqlConnection   创建产品A1  
      └─ createDataSOurce()   实现工厂接口方法2                          
          └─dataSource()   实现产品接口2
              └─MySqlDataSource   创建产品A2
                          
   ├─ RedisFactory    具体工厂实现类B     
      └─ createConnection()   实现工厂接口方法1                          
          └─connect()   实现产品接口1
              └─RedisConnection   创建产品B1  
      └─ createDataSOurce()   实现工厂接口方法2                          
          └─dataSource()   实现产品接口2
              └─RedisDataSource   创建产品B2   

可以看见,相比于工厂模式中,不同工厂只能创建产品:数据库连接connection,抽象工厂的不同工厂可以创建多种产品,如数据库连接,数据源等等,这些多个产品的集合就是一个产品族,每个工厂可以生产自己的产品族

(3)、抽象工厂模式的核心逻辑(通俗解释)

你可以把抽象工厂理解为:

  1. 你(调用方)需要一套「数据库解决方案」(不是单个连接,而是连接 + 数据源 + 事务);
  2. 你找到「数据库解决方案总店」(抽象工厂接口),总店规定所有分店必须能造「连接 + 数据源 + 事务」这一套产品;
  3. 「MySQL 分店」(具体工厂 1)只造 MySQL 全套产品(MySQL 连接 + MySQL 数据源 + MySQL 事务);
  4. 「Oracle 分店」(具体工厂 2)只造 Oracle 全套产品(Oracle 连接 + Oracle 数据源 + Oracle 事务);
  5. 你想要哪套解决方案,就找对应的分店 —— 你只认「总店的规则」,不用管每套产品是怎么造的,也不用担心「MySQL 连接配了 Oracle 数据源」这种不兼容的情况。
(4)、完整代码示例
// ========== 第一套产品等级结构:Connection(连接) ==========
// 抽象产品A
public interface Connection {
    void connect(); // 核心行为:连接数据库
}
// 具体产品A1(MySQL产品族)
public class MySQLConnection implements Connection {
    @Override
    public void connect() {
        System.out.println("创建MySQL数据库连接");
    }
}
// 具体产品A2(Oracle产品族)
public class OracleConnection implements Connection {
    @Override
    public void connect() {
        System.out.println("创建Oracle数据库连接");
    }
}

// ========== 第二套产品等级结构:DataSource(数据源) ==========
// 抽象产品B
public interface DataSource {
    void getDataSource(); // 核心行为:获取数据源
}
// 具体产品B1(MySQL产品族)
public class MySQLDataSource implements DataSource {
    @Override
    public void getDataSource() {
        System.out.println("获取MySQL数据源");
    }
}
// 具体产品B2(Oracle产品族)
public class OracleDataSource implements DataSource {
    @Override
    public void getDataSource() {
        System.out.println("获取Oracle数据源");
    }
}

// ========== 抽象工厂:定义造产品族的规范 ==========
public interface DBFactory {
    // 造第一套产品(Connection)
    Connection createConnection();
    // 造第二套产品(DataSource)
    DataSource createDataSource();
}

// ========== 具体工厂1:造MySQL产品族 ==========
public class MySQLFactory implements DBFactory {
    @Override
    public Connection createConnection() {
        return new MySQLConnection(); // 造MySQL连接
    }
    @Override
    public DataSource createDataSource() {
        return new MySQLDataSource(); // 造MySQL数据源
    }
}

// ========== 具体工厂2:造Oracle产品族 ==========
public class OracleFactory implements DBFactory {
    @Override
    public Connection createConnection() {
        return new OracleConnection(); // 造Oracle连接
    }
    @Override
    public DataSource createDataSource() {
        return new OracleDataSource(); // 造Oracle数据源
    }
}

// ========== 调用方使用(核心:拿整套产品族) ==========
public class Client {
    public static void main(String[] args) {
        // 步骤1:选择产品族(想要MySQL全套,就用MySQL工厂)
        DBFactory factory = new MySQLFactory();
        
        // 步骤2:获取产品族的所有产品(连接+数据源)
        Connection conn = factory.createConnection();
        DataSource ds = factory.createDataSource();
        
        // 步骤3:使用产品(无需关心具体是哪个产品族)
        conn.connect();
        ds.getDataSource();

        // 切换成Oracle产品族:只需换工厂,全套产品自动配套
        factory = new OracleFactory();
        conn = factory.createConnection();
        ds = factory.createDataSource();
        conn.connect();
        ds.getDataSource();
    }
}

deepseek_mermaid_20260322_6202cb.png

(5)、抽象工厂模式的核心优势(解决了什么问题)
  1. **保证产品族的「配套性」和「兼容性」**抽象工厂确保你拿到的所有产品都属于同一个产品族 —— 永远不会出现「MySQL 连接 + Oracle 数据源」这种不兼容的组合,这是工厂方法做不到的(工厂方法只能保证单个产品的正确性)。
  2. 整体切换产品族的成本极低比如你的系统要从 MySQL 全套换成 Oracle 全套,只需把 new MySQLFactory() 改成 new OracleFactory()所有产品都会自动切换成 Oracle 版本,调用方代码完全不用改。
  3. 更高层次的封装和解耦调用方不仅不依赖具体产品,甚至不依赖「单个产品的创建逻辑」—— 只依赖「产品族的创建规范」(抽象工厂接口),底层产品的实现逻辑再怎么变,调用方都不受影响。
  4. **符合「单一职责」**每个具体工厂只负责创建一个产品族的所有产品,逻辑边界清晰(MySQL 工厂只管 MySQL 全家桶,Oracle 工厂只管 Oracle 全家桶)。
(6)、抽象工厂模式的适用场景
  • 当你需要创建「配套使用的一套产品」(产品族),且希望保证产品之间的兼容性时;
  • 当你的系统需要整体切换「产品族」(比如从 MySQL 全套换成 Oracle 全套)时;
  • 当产品等级结构相对固定(新增产品族容易,新增产品等级难)时;
  • 框架开发中(比如 Spring 的 BeanFactory、MyBatis 的 DataSourceFactory),需要提供整套组件的扩展能力时。
(7)、抽象工厂 vs 工厂方法(核心区别)
维度工厂方法模式抽象工厂模式
创建目标单个产品(一个产品等级结构)一套产品(多个产品等级结构组成的产品族)
工厂接口只有一个工厂方法(造单个产品)多个工厂方法(造产品族的每个产品)
扩展难度新增产品容易(加具体产品 + 具体工厂)新增产品族容易(加具体工厂 + 对应产品);新增产品等级难(需修改抽象工厂接口)
核心价值解耦单个产品的创建保证产品族的配套性,整体切换产品族
适用场景单个产品的灵活扩展整套产品的配套使用和整体

deepseek_mermaid_20260322_3a60cb.png

三:spring中的bean注入机制(补充点,可忽略)

1. 手动注入(显式配置,可控性强)

手动注入是通过 XML / 注解显式指定要注入的 Bean,是最基础、最可控的方式,也是 BeanFactory 最核心的注入逻辑。

(1)构造方法注入(推荐,强制依赖)

核心逻辑:BeanFactory 通过 Bean 的构造方法,将依赖 Bean 作为参数传入,完成注入。适合「必须存在的依赖」(比如数据源、工厂类),能保证 Bean 创建时依赖已初始化

适用场景:依赖不可为空、需要保证 Bean 初始化时依赖完整。

代码示例(注解版,Spring Boot 主流)

// 步骤1:定义被依赖的 Bean(比如数据库连接工厂)
@Component
public class MySQLConnectionFactory implements ConnectionFactory {
    @Override
    public Connection createConnection() {
        return new MySQLConnection();
    }
}

// 步骤2:目标 Bean 通过构造方法注入依赖
@Component
public class UserService {
    // 依赖的接口(面向接口编程,符合工厂模式思想)
    private final ConnectionFactory connectionFactory;

    // 构造方法注入(Spring 会自动匹配类型对应的 Bean)
    // 注:Spring 4.3+ 中,单构造方法无需加 @Autowired
    public UserService(ConnectionFactory connectionFactory) {
        this.connectionFactory = connectionFactory;
    }

    public void queryUser() {
        Connection conn = connectionFactory.createConnection();
        conn.connect();
    }
}

XML 配置版(传统方式)

<!-- 定义依赖 Bean -->
<bean id="mysqlConnectionFactory" class="com.example.MySQLConnectionFactory"/>

<!-- 构造方法注入 -->
<bean id="userService" class="com.example.UserService">
    <constructor-arg ref="mysqlConnectionFactory"/>
</bean>
(2)Setter 方法注入(可选依赖,依赖可以不存在)

核心逻辑:BeanFactory 调用 Bean 的 Setter 方法,将依赖 Bean 赋值给属性。适合「可选依赖」(可通过 Setter 设置默认值)。

适用场景:依赖非必须、需要动态修改依赖的场景。

代码示例(注解版)

@Component
public class UserService {
    private ConnectionFactory connectionFactory;

    // Setter 方法注入(@Autowired 标识需要注入的方法)
    @Autowired
    public void setConnectionFactory(ConnectionFactory connectionFactory) {
        this.connectionFactory = connectionFactory;
    }
}
(3)字段注入(简化写法,不推荐)

核心逻辑:直接在字段上标注 @Autowired,BeanFactory 通过反射直接赋值给私有字段(跳过 Setter / 构造方法)。

优缺点:写法极简,但破坏封装性、难以单元测试(无法手动传入依赖),Spring 官方不推荐在生产环境使用。

代码示例

@Component
public class UserService {
    // 字段注入(仅演示,不推荐)
    @Autowired
    private ConnectionFactory connectionFactory;
}
2. 自动注入(自动匹配,简化配置)

自动注入是让 BeanFactory 自动识别并注入依赖,无需显式指定,核心依赖「Bean 的类型 / 名称」匹配。

(1)按类型注入(@Autowired,默认)

核心逻辑:BeanFactory 根据「字段 / 方法参数的类型」,在容器中查找匹配的 Bean 并注入。如果同类型有多个 Bean,会抛出异常(需配合 @Qualifier 指定名称)。

代码示例(解决多实例冲突)

// 多实例:MySQL 和 Oracle 工厂都实现 ConnectionFactory
@Component("mysqlFactory")
public class MySQLConnectionFactory implements ConnectionFactory { ... }

@Component("oracleFactory")
public class OracleConnectionFactory implements ConnectionFactory { ... }

// 目标 Bean:按类型+名称注入
@Component
public class UserService {
    // @Qualifier 指定要注入的 Bean 名称
    @Autowired
    @Qualifier("mysqlFactory")
    private ConnectionFactory connectionFactory;
}
(2)按名称注入(@Resource)

核心逻辑:BeanFactory 根据「字段名称 /@Resource 的 name 属性」匹配 Bean 名称,完成注入(优先按名称,名称匹配失败再按类型)。

代码示例

@Component
public class UserService {
    // 方式1:按字段名称(mysqlFactory)匹配 Bean
    @Resource
    private ConnectionFactory mysqlFactory;

    // 方式2:显式指定 Bean 名称
    @Resource(name = "oracleFactory")
    private ConnectionFactory connectionFactory;
}
3. 特殊注入形式
(1)方法参数注入(适合批量注入)

核心逻辑:BeanFactory 将依赖注入到非 Setter 方法的参数中,适合需要批量处理依赖的场景(比如初始化方法)。

@Component
public class DataService {
    private List<ConnectionFactory> factoryList;

    // 方法参数注入:注入所有实现 ConnectionFactory 的 Bean
    @Autowired
    public void initFactories(List<ConnectionFactory> factoryList) {
        this.factoryList = factoryList;
    }
}
(2)延迟注入(ObjectFactory/Provider)

核心逻辑:通过 ObjectFactoryjavax.inject.Provider 延迟获取 Bean,避免循环依赖或提前初始化。

@Component
public class UserService {
    // 延迟注入:调用 get() 时才真正获取 Bean
    @Autowired
    private ObjectFactory<ConnectionFactory> factoryObject;

    public void query() {
        // 延迟初始化
        ConnectionFactory factory = factoryObject.getObject();
        factory.createConnection();
    }
}
(3)循环依赖注入(Spring 内置解决)

当 A 依赖 B、B 又依赖 A 时,BeanFactory 通过「三级缓存」解决构造方法注入外的循环依赖(构造方法注入无法解决,会抛异常):

@Component
public class A {
    @Autowired
    private B b;
}

@Component
public class B {
    @Autowired
    private A a;
}
4、Spring Bean 注入的核心原则
  1. 优先构造方法注入:保证依赖不可变、Bean 初始化时依赖完整,符合「不可变对象」设计思想;
  2. 面向接口注入:注入接口而非具体实现(比如注入 ConnectionFactory 而非 MySQLConnectionFactory),符合工厂模式解耦思想;
  3. 避免字段注入:破坏封装性,单元测试困难,仅适合简单演示场景;
  4. 自动注入慎用:多实例场景需配合 @Qualifier/@Resource 指定名称,避免匹配失败