别再写“面条代码”了!Spring 大神级设计模式指南

嘿,各位码农同胞们!

每天用着 @Autowired@Service@Transactional,你有没有想过,这些看似简单的注解背后,其实隐藏着软件工程几十年来的智慧结晶——设计模式

我们常常把“写出优雅、可维护、可扩展的代码”挂在嘴边,但一到实际开发,代码就容易变成一坨难以维护的“面条”。究其原因,往往是因为我们缺少了“内功心法”,而设计模式,就是这套心法。

Spring Framework 和 Spring Boot 本身就是设计模式的集大成者。它不仅在内部大量使用了经典的设计模式,更通过其强大的 IoC 和 AOP 容器,让我们能够极其方便地在自己的业务代码中应用这些模式。

目录

一. 代理模式 (Proxy Pattern):万物皆可“代理”的 Spring AOP

Spring Boot 中的体现

架构图解

实战代码:自定义一个方法耗时监控切面

二. 工厂模式 (Factory Pattern):Bean 的“制造工厂”

Spring Boot 中的体现

架构图解

实战代码:使用 FactoryBean 创建一个复杂的支付客户端

三. 装饰器模式 (Decorator Pattern):给对象动态“穿上”新功能

Spring Boot 中的体现

架构图解

实战代码:一杯咖啡的“七十二变”

四. 策略模式 (Strategy Pattern):自由切换的算法“锦囊”

Spring Boot 中的体现

架构图解

实战代码:优雅地实现多种支付方式

结论:设计模式是内功,Spring 是神兵利器


一. 代理模式 (Proxy Pattern):万物皆可“代理”的 Spring AOP

一句话概括:为你真正的目标对象创建一个代理“替身”,客户端访问的是这个替身,从而让你有机会在不修改原对象代码的情况下,对访问进行控制或增强。

这就像你想见一位大明星(目标对象),但你不能直接联系他,而是要通过他的经纪人(代理)。经纪人可以帮你过滤掉不重要的请求、安排日程、甚至在你见面前做一些准备工作。

Spring Boot 中的体现

代理模式是 Spring AOP (面向切面编程) 的核心。当你使用 @Transactional@Async@Cacheable 甚至自定义的切面时,Spring 并没有改变你原有类的代码。相反,它在运行时为你创建了一个代理对象。

当你从容器中获取这个 Bean(比如通过 @Autowired)时,你拿到的其实是这个代理。当你调用 Bean 的方法时,实际上是代理在执行。代理会先执行它自己的增强逻辑(比如开启事务、检查缓存),然后再去调用你真正的业务方法,最后再执行一些收尾工作(比如提交/回滚事务)。

架构图解

实战代码:自定义一个方法耗时监控切面

让我们来写一个简单的 AOP 切面,用于记录所有 Service 层方法的执行时间。

1. 添加 AOP 依赖pom.xml 中确保有 spring-boot-starter-aop

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-aop</artifactId>
</dependency>

2. 创建一个注解

// PerformanceLog.java
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface PerformanceLog {}

3. 编写切面类

// PerformanceLogAspect.java
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

@Aspect
@Component
public class PerformanceLogAspect {

    private static final Logger log = LoggerFactory.getLogger(PerformanceLogAspect.class);

    // 环绕通知,拦截所有标注了 @PerformanceLog 的方法
    @Around("@annotation(PerformanceLog)")
    public Object logPerformance(ProceedingJoinPoint pjp) throws Throwable {
        long startTime = System.currentTimeMillis();
        // 调用原始方法
        Object result = pjp.proceed();
        long endTime = System.currentTimeMillis();
        log.info("Method {} executed in {} ms", pjp.getSignature(), (endTime - startTime));
        return result;
    }
}

4. 在业务代码中使用

// MyBusinessService.java
import org.springframework.stereotype.Service;

@Service
public class MyBusinessService {

    @PerformanceLog
    public void doSomething() {
        try {
            // 模拟业务耗时
            Thread.sleep(200);
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        }
        System.out.println("Doing something important...");
    }
}

现在,每次调用 doSomething 方法时,Spring 都会通过代理先执行 logPerformance 的逻辑,你会在控制台看到方法的执行耗时,而 MyBusinessService 本身完全无感知。

 

二. 工厂模式 (Factory Pattern):Bean 的“制造工厂”

一句话概括:定义一个用于创建对象的接口,让子类决定实例化哪一个类。工厂方法使一个类的实例化延迟到其子类。

你想要一杯咖啡,你不会自己去种咖啡豆、研磨、冲泡,而是去咖啡店(工厂),告诉店员你要拿铁还是美式,店员(工厂方法)会为你做好。你不用关心咖啡的具体制作过程。

Spring Boot 中的体现

整个 Spring 的 IoC 容器本质上就是一个巨大的、超级灵活的工厂ApplicationContext 就是这个工厂的门面。当你调用 applicationContext.getBean("myBean") 时,你就是在命令工厂生产一个叫 "myBean" 的产品。

Spring 会根据你的配置(XML、注解)来决定如何创建这个 Bean:是创建一个全新的实例(Prototype),还是返回一个已经存在的单例(Singleton)。

更具体的体现是 FactoryBean 接口。如果你想让一个 Bean 的创建过程变得非常复杂,或者想在创建前后做一些特殊处理,你可以实现 FactoryBean 接口。这个 Bean 自己就成了一个小工厂,专门用来生产另一种类型的 Bean。

架构图解

实战代码:使用 FactoryBean 创建一个复杂的支付客户端

假设我们需要根据配置来创建不同的支付客户端(比如 AliPayClientWeChatPayClient)。

1. 定义产品接口和实现

// PaymentClient.java
public interface PaymentClient {
    void pay(long amount);
}

// AliPayClient.java
public class AliPayClient implements PaymentClient {
    public AliPayClient(String endpoint, String appId) { /* ... complex initialization ... */ }
    @Override public void pay(long amount) { System.out.println("AliPay paying: " + amount); }
}

// WeChatPayClient.java
public class WeChatPayClient implements PaymentClient {
    public WeChatPayClient(String apiKey, String mchId) { /* ... complex initialization ... */ }
    @Override public void pay(long amount) { System.out.println("WeChatPay paying: " + amount); }
}

2. 实现 FactoryBean

// PaymentClientFactoryBean.java
import org.springframework.beans.factory.FactoryBean;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

@Component("paymentClient") // 注意这里的 Bean 名字
public class PaymentClientFactoryBean implements FactoryBean<PaymentClient> {

    @Value("${payment.type}")
    private String type;

    @Override
    public PaymentClient getObject() throws Exception {
        // 在这里实现复杂的创建逻辑
        if ("alipay".equalsIgnoreCase(type)) {
            return new AliPayClient("alipay.com", "app123");
        } else if ("wechat".equalsIgnoreCase(type)) {
            return new WeChatPayClient("wechat_key", "mch456");
        } else {
            throw new IllegalArgumentException("Unsupported payment type");
        }
    }

    @Override
    public Class<?> getObjectType() {
        return PaymentClient.class;
    }

    @Override
    public boolean isSingleton() {
        return true; // 我们希望它是单例的
    }
}

3. 配置和使用application.properties 中:

payment.type=alipay

在业务代码中,你可以像注入普通 Bean 一样注入 PaymentClient

// OrderService.java
@Service
public class OrderService {
    private final PaymentClient paymentClient;

    // Spring 注入的不是 FactoryBean 本身,而是它 getObject() 返回的对象
    public OrderService(PaymentClient paymentClient) {
        this.paymentClient = paymentClient;
    }

    public void placeOrder(long amount) {
        paymentClient.pay(amount);
    }
}

现在,你只需要修改配置文件里的 payment.typeOrderService 就能无缝切换支付方式,完全解耦了创建和使用。

三. 装饰器模式 (Decorator Pattern):给对象动态“穿上”新功能

一句话概括:在不改变原有对象接口的情况下,动态地给一个对象添加一些额外的职责。就功能而言,装饰器模式相比生成子类更为灵活。

想象一个“套娃”,你有一个核心的娃娃(原对象),然后你可以一层一层地给它套上更大的娃娃(装饰器),每一层都给它增加一点新的外观,但它本质上还是那个娃娃。

Spring Boot 中的体现

装饰器模式在 Spring 核心中不那么显眼,但在其依赖的技术中非常常见。最经典的例子是 Java IO 类,比如 new BufferedReader(new FileReader("file.txt"))

在 Spring Web 体系中,HttpServletRequestWrapperHttpServletResponseWrapper 就是标准的装饰器。你可以创建一个 Wrapper 来修改原始请求或响应的行为,比如修改 Header,或者对输出内容进行压缩。

此外,Spring Session 使用装饰器模式来包装 HttpSession,以实现分布式会话管理。

架构图解

实战代码:一杯咖啡的“七十二变”

我们来模拟一个制作咖啡的场景,基础是一杯清咖啡,然后可以动态地加奶、加糖。

1. 定义组件接口和基础实现

// Coffee.java
public interface Coffee {
    double getCost();
    String getDescription();
}

// SimpleCoffee.java
public class SimpleCoffee implements Coffee {
    @Override public double getCost() { return 10.0; }
    @Override public String getDescription() { return "Simple Coffee"; }
}

2. 创建抽象装饰器和具体装饰器

// CoffeeDecorator.java
public abstract class CoffeeDecorator implements Coffee {
    protected final Coffee decoratedCoffee;

    public CoffeeDecorator(Coffee coffee) {
        this.decoratedCoffee = coffee;
    }

    public double getCost() { return decoratedCoffee.getCost(); }
    public String getDescription() { return decoratedCoffee.getDescription(); }
}

// MilkDecorator.java
public class MilkDecorator extends CoffeeDecorator {
    public MilkDecorator(Coffee coffee) { super(coffee); }
    @Override public double getCost() { return super.getCost() + 3.0; }
    @Override public String getDescription() { return super.getDescription() + ", with Milk"; }
}

// SugarDecorator.java
public class SugarDecorator extends CoffeeDecorator {
    public SugarDecorator(Coffee coffee) { super(coffee); }
    @Override public double getCost() { return super.getCost() + 1.5; }
    @Override public String getDescription() { return super.getDescription() + ", with Sugar"; }
}

3. 在 Spring Service 中使用

// CoffeeShopService.java
@Service
public class CoffeeShopService {
    public void orderCoffee() {
        // 点一杯基础咖啡
        Coffee coffee = new SimpleCoffee();
        System.out.println(coffee.getDescription() + " costs $" + coffee.getCost());

        // 加奶
        Coffee milkCoffee = new MilkDecorator(coffee);
        System.out.println(milkCoffee.getDescription() + " costs $" + milkCoffee.getCost());

        // 再加糖
        Coffee sweetMilkCoffee = new SugarDecorator(milkCoffee);
        System.out.println(sweetMilkCoffee.getDescription() + " costs $" + sweetMilkCoffee.getCost());

        // 也可以这样套娃:一杯加了双份奶和一份糖的咖啡
        Coffee myFavoriteCoffee = new SugarDecorator(new MilkDecorator(new MilkDecorator(new SimpleCoffee())));
        System.out.println(myFavoriteCoffee.getDescription() + " costs $" + myFavoriteCoffee.getCost());
    }
}

这种方式让你能以任意组合来扩展功能,而不需要创建 CoffeeWithMilkCoffeeWithSugarCoffeeWithMilkAndSugar 等等无穷无尽的子类。

四. 策略模式 (Strategy Pattern):自由切换的算法“锦囊”

一句话概括:定义一系列算法,把它们一个个封装起来,并且使它们可以相互替换。此模式让算法的变化独立于使用算法的客户。

你出门旅游,可以根据路况和预算选择不同的出行策略:坐飞机(速度快,价格高)、坐火车(性价比高)、自驾(自由度高)。你的目的地不变,但到达目的地的策略可以随时切换。

Spring Boot 中的体现

策略模式是在 Spring 应用中最常被开发者主动使用的模式之一,因为 Spring 的依赖注入(DI)简直是为它量身定做的。

最经典的场景就是支付。一个电商系统需要支持多种支付方式:支付宝、微信支付、信用卡支付。你可以定义一个 PaymentStrategy 接口,然后为每种支付方式创建一个实现类。

在你的支付服务中,你不需要写一堆 if-else 来判断用哪种支付方式。你可以直接注入一个 Map<String, PaymentStrategy>,Spring 会自动把所有实现了 PaymentStrategy 接口的 Bean 收集起来,以它们的 Bean Name 为 Key 放入 Map。这样,你只需要根据传入的支付类型字符串,就能从 Map 中动态获取并执行对应的策略。

架构图解

实战代码:优雅地实现多种支付方式

1. 定义策略接口和具体策略

// PaymentStrategy.java
public interface PaymentStrategy {
    String getPayType();
    void pay(double amount);
}

// AliPaymentStrategy.java
@Component
public class AliPaymentStrategy implements PaymentStrategy {
    @Override public String getPayType() { return "alipay"; }
    @Override public void pay(double amount) { System.out.println("Processing AliPay payment of " + amount); }
}

// WechatPaymentStrategy.java
@Component
public class WechatPaymentStrategy implements PaymentStrategy {
    @Override public String getPayType() { return "wechat"; }
    @Override public void pay(double amount) { System.out.println("Processing WeChat Pay payment of " + amount); }
}

2. 创建策略的“上下文”(即使用者)

// PaymentService.java
import org.springframework.stereotype.Service;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
import java.util.stream.Collectors;

@Service
public class PaymentService {
    private final Map<String, PaymentStrategy> strategyMap;

    // Spring 会注入所有 PaymentStrategy 类型的 Bean
    public PaymentService(List<PaymentStrategy> strategies) {
        this.strategyMap = strategies.stream()
            .collect(Collectors.toMap(PaymentStrategy::getPayType, Function.identity()));
    }

    public void processPayment(String payType, double amount) {
        PaymentStrategy strategy = strategyMap.get(payType);
        if (strategy == null) {
            throw new IllegalArgumentException("Invalid payment type: " + payType);
        }
        strategy.pay(amount);
    }
}

看,PaymentService 里没有任何 if-else。如果你想增加一种新的支付方式,比如“信用卡支付”,你只需要新增一个 CreditCardPaymentStrategy 类并实现接口,不需要修改 PaymentService 的任何代码!这完美符合“开闭原则”。

结论:设计模式是内功,Spring 是神兵利器

  • 代理模式 让你无侵入地增强代码,是 AOP 的基石。

  • 工厂模式 将创建与使用解耦,是 IoC 容器的核心思想。

  • 装饰器模式 提供了比继承更灵活的功能扩展方式。

  • 策略模式 让你可以优雅地管理和切换算法,告别 if-else 地狱。

so,下次当你写代码时,不妨停下来想一想:这里是不是可以用某种设计模式来优化一下?当你开始这样思考时,你就走在了通往“代码艺术家”的路上。

now,打开你的 IDE,去重构那段让你头疼的“面条代码”吧!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

nextera-void

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值