AI摘要

文章以Spring Bean初始化逻辑被重复执行为引,展示Spring在doCreateBean、initializeBean等核心流程中如何用模板方法模式固定算法骨架、开放扩展点;结合JdbcTemplate、RestTemplate等源码,归纳钩子与回调的最佳实践,并给出统一支付API实战,帮助读者掌握框架级复用与扩展设计。
我以为模板方法模式只是个教科书上的概念,直到我在Spring的Bean创建过程中踩了一个坑——自定义的初始化逻辑总是莫名其妙地执行了两次,才意识到这个看似简单的模式背后藏着Spring框架的设计精髓。
那是在我们重构一个老项目时遇到的问题。我们需要在Bean初始化时加载一些外部配置,于是很自然地在@PostConstruct方法里写初始化逻辑。但测试时发现这个方法被执行了两次。排查了半天,才发现是因为我们同时实现了InitializingBean接口,而Spring的模板方法模式确保了初始化逻辑的固定执行顺序。这次经历让我决心深入研究Spring源码中的模板方法模式。

一、从一个初始化方法执行两次的问题说起

1.1 问题代码重现

先看看当时有问题的代码:

@Component
@Slf4j
public class ConfigLoader implements InitializingBean {
    
    private Map<String, String> configMap;
    
    @PostConstruct
    public void init() {
        log.info("@PostConstruct方法执行:开始加载配置");
        loadConfig();
    }
    
    @Override
    public void afterPropertiesSet() throws Exception {
        log.info("InitializingBean.afterPropertiesSet()方法执行:开始加载配置");
        loadConfig();  // 这里又被执行了一次!
    }
    
    private void loadConfig() {
        // 从数据库加载配置
        configMap = configRepository.loadAllConfigs();
        log.info("配置加载完成,共{}条配置", configMap.size());
    }
    
    @Bean
    public SomeBean someBean() {
        return new SomeBean(configMap);
    }
}

运行结果出乎意料:

2022-05-15 14:32:03 INFO  ConfigLoader - @PostConstruct方法执行:开始加载配置
2022-05-15 14:32:03  INFO  ConfigLoader - 配置加载完成,共50条配置
2022-05-15 14:32:03  INFO  ConfigLoader - InitializingBean.afterPropertiesSet()方法执行:开始加载配置
2022-05-15 14:32:03  INFO  ConfigLoader - 配置加载完成,共50条配置

​配置被加载了两次!​​ 这不仅浪费资源,还可能导致并发问题。

1.2 问题的根本原因

问题的根源在于对Spring Bean初始化流程理解不足。Spring使用模板方法模式定义了Bean的创建流程,这个流程中的每个步骤都有固定的执行顺序。让我带你看一下Spring源码中的关键部分:

// Spring Bean创建的核心流程
public abstract class AbstractAutowireCapableBeanFactory {
    
    protected Object doCreateBean(String beanName, RootBeanDefinition mbd, Object[] args) {
        // 1. 实例化Bean
        BeanWrapper instanceWrapper = createBeanInstance(beanName, mbd, args);
        
        // 2. 属性注入
        populateBean(beanName, mbd, instanceWrapper);
        
        // 3. 初始化Bean - 这里就是模板方法模式的核心
        Object exposedObject = initializeBean(beanName, exposedObject, mbd);
        
        return exposedObject;
    }
    
    protected Object initializeBean(String beanName, Object bean, RootBeanDefinition mbd) {
        // 3.1 Aware接口回调
        invokeAwareMethods(beanName, bean);
        
        // 3.2 BeanPostProcessor前置处理
        Object wrappedBean = applyBeanPostProcessorsBeforeInitialization(bean, beanName);
        
        // 3.3 调用初始化方法 - 模板方法
        invokeInitMethods(beanName, wrappedBean, mbd);
        
        // 3.4 BeanPostProcessor后置处理
        wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
        
        return wrappedBean;
    }
}

二、模板方法模式深度解析

2.1 模板方法模式的定义

模板方法模式在一个方法中定义一个算法的骨架,而将一些步骤延迟到子类中。模板方法使得子类可以在不改变算法结构的情况下,重新定义算法中的某些步骤。

// 模板方法模式的通用结构
public abstract class AbstractTemplate {
    
    // 模板方法 - 定义算法骨架(通常用final修饰,防止子类修改)
    public final void templateMethod() {
        step1();        // 固定步骤
        step2();        // 固定步骤
        hookMethod();   // 钩子方法(可选步骤)
        if (hook()) {   // 钩子方法控制流程
            step3();    // 条件执行步骤
        }
        primitiveOperation1();  // 抽象方法,由子类实现
        primitiveOperation2();  // 抽象方法,由子类实现
    }
    
    // 固定实现的方法
    private void step1() {
        System.out.println("执行步骤1");
    }
    
    private void step2() {
        System.out.println("执行步骤2");
    }
    
    // 钩子方法 - 空实现,子类可以覆盖
    protected void hookMethod() {
        // 空实现,子类可以选择性覆盖
    }
    
    // 钩子方法 - 返回布尔值控制流程
    protected boolean hook() {
        return true;  // 默认返回true
    }
    
    // 抽象方法 - 必须由子类实现
    protected abstract void primitiveOperation1();
    protected abstract void primitiveOperation2();
}

2.2 Spring中的模板方法模式应用场景

Spring框架中大量使用了模板方法模式,主要应用在:

  1. Bean生命周期管理​ - AbstractAutowireCapableBeanFactory
  2. 事务管理​ - PlatformTransactionManager
  3. 数据访问​ - JdbcTemplate
  4. 缓存抽象​ - CacheManager
  5. 安全框架​ - AbstractSecurityInterceptor

三、深入Spring源码:Bean创建流程中的模板方法

3.1 核心模板方法:doCreateBean()

让我们深入Spring源码,看看模板方法模式的具体实现:

public abstract class AbstractAutowireCapableBeanFactory 
        extends AbstractBeanFactory implements AutowireCapableBeanFactory {
    
    /**
     * 创建Bean的核心模板方法
     * 这个方法定义了Bean创建的完整算法骨架
     */
    @Override
    protected Object doCreateBean(String beanName, RootBeanDefinition mbd, Object[] args) {
        // 声明BeanWrapper,用于包装创建的Bean实例
        BeanWrapper instanceWrapper = null;
        
        // 步骤1:实例化Bean - 可能是工厂方法、构造器或通过Supplier
        if (mbd.isSingleton()) {
            // 从缓存中移除正在创建的Bean
            instanceWrapper = this.factoryBeanInstanceCache.remove(beanName);
        }
        if (instanceWrapper == null) {
            // 调用模板方法:创建Bean实例
            instanceWrapper = createBeanInstance(beanName, mbd, args);
        }
        
        Object bean = instanceWrapper.getWrappedInstance();
        Class<?> beanType = instanceWrapper.getWrappedClass();
        
        // 步骤2:应用MergedBeanDefinitionPostProcessor
        synchronized (mbd.postProcessingLock) {
            if (!mbd.postProcessed) {
                try {
                    // 调用模板方法:后处理合并的Bean定义
                    applyMergedBeanDefinitionPostProcessors(mbd, beanType, beanName);
                } catch (Throwable ex) {
                    throw new BeanCreationException(mbd.getResourceDescription(), beanName,
                            "Post-processing of merged bean definition failed", ex);
                }
                mbd.postProcessed = true;
            }
        }
        
        // 步骤3:解决循环依赖 - 早期引用暴露
        boolean earlySingletonExposure = (mbd.isSingleton() && 
                this.allowCircularReferences &&
                isSingletonCurrentlyInCreation(beanName));
        if (earlySingletonExposure) {
            // 将早期Bean引用添加到SingletonFactory中
            addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
        }
        
        Object exposedObject = bean;
        
        try {
            // 步骤4:属性注入 - 调用模板方法
            populateBean(beanName, mbd, instanceWrapper);
            
            // 步骤5:初始化Bean - 调用模板方法
            exposedObject = initializeBean(beanName, exposedObject, mbd);
        } catch (Throwable ex) {
            if (ex instanceof BeanCreationException && beanName.equals(((BeanCreationException) ex).getBeanName())) {
                throw (BeanCreationException) ex;
            } else {
                throw new BeanCreationException(mbd.getResourceDescription(), beanName, 
                        "Initialization of bean failed", ex);
            }
        }
        
        // 步骤6:处理循环依赖的早期引用
        if (earlySingletonExposure) {
            Object earlySingletonReference = getSingleton(beanName, false);
            if (earlySingletonReference != null) {
                if (exposedObject == bean) {
                    exposedObject = earlySingletonReference;
                } else if (!this.allowRawInjectionDespiteWrapping &&
                        hasDependentBean(beanName)) {
                    // 处理依赖关系...
                }
            }
        }
        
        // 步骤7:注册可销毁的Bean
        try {
            registerDisposableBeanIfNecessary(beanName, bean, mbd);
        } catch (BeanDefinitionValidationException ex) {
            throw new BeanCreationException(mbd.getResourceDescription(), beanName,
                    "Invalid destruction signature", ex);
        }
        
        return exposedObject;
    }
}

3.2 初始化阶段的模板方法:invokeInitMethods()

这是初始化阶段的核心,也是我们问题发生的地方:

public abstract class AbstractAutowireCapableBeanFactory {
    
    /**
     * 初始化方法的调用 - 模板方法
     */
    protected void invokeInitMethods(String beanName, Object bean, RootBeanDefinition mbd)
            throws Throwable {
        
        // 判断Bean是否实现了InitializingBean接口
        boolean isInitializingBean = (bean instanceof InitializingBean);
        
        // 如果实现了InitializingBean接口且不是由外部管理的初始化方法
        if (isInitializingBean && 
            !(mbd != null && mbd.isExternallyManagedInitMethod("afterPropertiesSet"))) {
            
            if (logger.isTraceEnabled()) {
                logger.trace("Invoking afterPropertiesSet() on bean with name '" + beanName + "'");
            }
            
            // 调用afterPropertiesSet()方法
            ((InitializingBean) bean).afterPropertiesSet();
        }
        
        // 处理自定义的init-method
        if (mbd != null && bean.getClass() != NullBean.class) {
            String initMethodName = mbd.getInitMethodName();
            
            if (StringUtils.hasLength(initMethodName) &&
                !(isInitializingBean && "afterPropertiesSet".equals(initMethodName)) &&
                !mbd.isExternallyManagedInitMethod(initMethodName)) {
                
                // 通过反射调用init-method
                invokeCustomInitMethod(beanName, bean, mbd);
            }
        }
    }
    
    /**
     * 调用自定义的初始化方法
     */
    protected void invokeCustomInitMethod(String beanName, Object bean, RootBeanDefinition mbd)
            throws Throwable {
        
        // 获取初始化方法
        Method initMethod = mbd.getResolvedInitMethod();
        if (initMethod == null) {
            initMethod = ClassUtils.getMethodIfAvailable(
                    bean.getClass(), mbd.getInitMethodName());
            if (initMethod == null) {
                throw new NoSuchMethodException("No init method found");
            }
            mbd.resolvedInitMethod = initMethod;
        }
        
        // 执行初始化方法
        ReflectionUtils.makeAccessible(initMethod);
        initMethod.invoke(bean);
    }
}

3.3 @PostConstruct的处理机制

那么@PostConstruct是在哪里处理的呢?这涉及到另一个重要的接口BeanPostProcessor

public class InitDestroyAnnotationBeanPostProcessor 
        implements DestructionAwareBeanPostProcessor, MergedBeanDefinitionPostProcessor {
    
    /**
     * 在初始化之前执行 - 处理@PostConstruct
     */
    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) 
            throws BeansException {
        
        // 查找Bean中所有@PostConstruct注解的方法
        LifecycleMetadata metadata = findLifecycleMetadata(bean.getClass());
        
        try {
            // 执行@PostConstruct方法
            metadata.invokeInitMethods(bean, beanName);
        } catch (InvocationTargetException ex) {
            throw new BeanCreationException(beanName, "Invocation of init method failed", ex.getTargetException());
        } catch (Throwable ex) {
            throw new BeanCreationException(beanName, "Failed to invoke init method", ex);
        }
        
        return bean;
    }
    
    /**
     * 查找生命周期元数据
     */
    private LifecycleMetadata findLifecycleMetadata(Class<?> clazz) {
        if (this.lifecycleMetadataCache == null) {
            return buildLifecycleMetadata(clazz);
        }
        
        LifecycleMetadata metadata = this.lifecycleMetadataCache.get(clazz);
        if (metadata == null) {
            synchronized (this.lifecycleMetadataCache) {
                metadata = this.lifecycleMetadataCache.get(clazz);
                if (metadata == null) {
                    metadata = buildLifecycleMetadata(clazz);
                    this.lifecycleMetadataCache.put(clazz, metadata);
                }
            }
        }
        return metadata;
    }
    
    /**
     * 构建生命周期元数据
     */
    private LifecycleMetadata buildLifecycleMetadata(Class<?> clazz) {
        List<LifecycleElement> initMethods = new ArrayList<>();
        
        // 递归查找父类中的@PostConstruct方法
        Class<?> targetClass = clazz;
        do {
            List<LifecycleElement> currInitMethods = new ArrayList<>();
            
            // 查找所有@PostConstruct注解的方法
            ReflectionUtils.doWithLocalMethods(targetClass, method -> {
                if (method.isAnnotationPresent(PostConstruct.class)) {
                    LifecycleElement element = new LifecycleElement(method);
                    currInitMethods.add(element);
                }
            });
            
            initMethods.addAll(0, currInitMethods);
            targetClass = targetClass.getSuperclass();
        } while (targetClass != null && targetClass != Object.class);
        
        return new LifecycleMetadata(clazz, initMethods, Collections.emptyList());
    }
}

四、解决初始化方法执行两次的问题

理解了Spring的初始化流程后,我们可以正确重写代码:

@Component
@Slf4j
public class ConfigLoader implements InitializingBean {
    
    private Map<String, String> configMap;
    
    // 方案1:只使用@PostConstruct
    @PostConstruct
    public void init() {
        log.info("初始化配置");
        loadConfig();
    }
    
    // 方案2:只实现InitializingBean接口
    // @Override
    // public void afterPropertiesSet() throws Exception {
    //     log.info("初始化配置");
    //     loadConfig();
    // }
    
    // 方案3:明确区分不同的初始化阶段(推荐)
    @PostConstruct
    public void postConstruct() {
        log.info("@PostConstruct阶段:执行前置初始化");
        preLoadConfig();
    }
    
    @Override
    public void afterPropertiesSet() throws Exception {
        log.info("afterPropertiesSet阶段:执行主初始化");
        loadConfig();
    }
    
    private void preLoadConfig() {
        // 加载基本配置
        log.info("加载基本配置");
    }
    
    private void loadConfig() {
        // 加载完整配置
        configMap = configRepository.loadAllConfigs();
        log.info("配置加载完成,共{}条配置", configMap.size());
    }
}

执行顺序​:

  1. @PostConstruct注解的方法(通过BeanPostProcessor
  2. InitializingBean.afterPropertiesSet()方法
  3. 自定义的init-method

五、实战:基于模板方法模式实现自定义框架

理解了Spring的模板方法模式后,我在实际项目中应用了这个模式。我们公司有个需求:统一处理各种外部API的调用,但不同的API有不同的认证、参数组装和错误处理逻辑。

5.1 设计API调用模板

/**
 * API调用模板 - 抽象类定义算法骨架
 */
public abstract class AbstractApiTemplate<T, R> {
    
    /**
     * 模板方法 - 定义API调用的完整流程(final防止子类修改)
     */
    public final ApiResponse<R> execute(ApiRequest<T> request) {
        // 1. 参数验证
        validateRequest(request);
        
        // 2. 构建请求
        HttpRequest httpRequest = buildRequest(request);
        
        // 3. 执行请求
        HttpResponse httpResponse = executeRequest(httpRequest);
        
        // 4. 处理响应
        ApiResponse<R> response = handleResponse(httpResponse);
        
        // 5. 后置处理
        postProcess(request, response);
        
        return response;
    }
    
    /**
     * 钩子方法:参数验证(子类可以覆盖)
     */
    protected void validateRequest(ApiRequest<T> request) {
        if (request == null) {
            throw new IllegalArgumentException("请求参数不能为空");
        }
        if (request.getData() == null) {
            throw new IllegalArgumentException("请求数据不能为空");
        }
        // 调用具体实现的验证
        doValidate(request);
    }
    
    /**
     * 抽象方法:具体验证逻辑(子类必须实现)
     */
    protected abstract void doValidate(ApiRequest<T> request);
    
    /**
     * 钩子方法:构建请求
     */
    protected HttpRequest buildRequest(ApiRequest<T> request) {
        // 构建基础请求
        HttpRequest.Builder builder = HttpRequest.newBuilder()
                .uri(URI.create(getApiUrl()))
                .timeout(Duration.ofSeconds(getTimeout()));
        
        // 添加公共头部
        addCommonHeaders(builder);
        
        // 添加认证信息
        addAuthentication(builder);
        
        // 构建请求体(抽象方法,子类实现)
        HttpRequest httpRequest = buildRequestBody(builder, request);
        
        return httpRequest;
    }
    
    /**
     * 抽象方法:构建请求体(子类必须实现)
     */
    protected abstract HttpRequest buildRequestBody(
            HttpRequest.Builder builder, ApiRequest<T> request);
    
    /**
     * 具体方法:执行HTTP请求
     */
    protected HttpResponse executeRequest(HttpRequest httpRequest) {
        try {
            HttpClient client = HttpClient.newBuilder()
                    .connectTimeout(Duration.ofSeconds(getConnectTimeout()))
                    .build();
            
            return client.send(httpRequest, HttpResponse.BodyHandlers.ofString());
            
        } catch (Exception e) {
            throw new ApiException("API请求失败", e);
        }
    }
    
    /**
     * 抽象方法:处理响应(子类必须实现)
     */
    protected abstract ApiResponse<R> handleResponse(HttpResponse httpResponse);
    
    /**
     * 钩子方法:后置处理(子类可以覆盖)
     */
    protected void postProcess(ApiRequest<T> request, ApiResponse<R> response) {
        // 默认实现:记录日志
        logRequestAndResponse(request, response);
        
        // 调用具体实现的后置处理
        doPostProcess(request, response);
    }
    
    /**
     * 钩子方法:具体后置处理逻辑(子类可以覆盖)
     */
    protected void doPostProcess(ApiRequest<T> request, ApiResponse<R> response) {
        // 默认空实现
    }
    
    // ========== 具体方法的默认实现 ==========
    
    /**
     * 获取API地址(抽象方法,子类必须实现)
     */
    protected abstract String getApiUrl();
    
    /**
     * 获取超时时间(钩子方法,子类可以覆盖)
     */
    protected int getTimeout() {
        return 30;  // 默认30秒
    }
    
    /**
     * 获取连接超时时间(钩子方法,子类可以覆盖)
     */
    protected int getConnectTimeout() {
        return 10;  // 默认10秒
    }
    
    /**
     * 添加公共头部(钩子方法,子类可以覆盖)
     */
    protected void addCommonHeaders(HttpRequest.Builder builder) {
        builder.header("Content-Type", "application/json")
               .header("User-Agent", "MyApiClient/1.0");
    }
    
    /**
     * 添加认证信息(钩子方法,子类可以覆盖)
     */
    protected void addAuthentication(HttpRequest.Builder builder) {
        // 默认不添加认证
    }
    
    /**
     * 记录请求和响应日志
     */
    private void logRequestAndResponse(ApiRequest<T> request, ApiResponse<R> response) {
        // 实际项目中可以使用SLF4J
        System.out.printf("[API调用] 请求: %s, 响应状态: %d%n",
                request.getData().getClass().getSimpleName(),
                response.getCode());
    }
}

5.2 具体API实现:微信支付API

/**
 * 微信支付API实现
 */
@Component
@Slf4j
public class WechatPayApiTemplate extends AbstractApiTemplate<WechatPayRequest, WechatPayResponse> {
    
    @Value("${wechat.pay.api.url}")
    private String apiUrl;
    
    @Value("${wechat.pay.appid}")
    private String appId;
    
    @Value("${wechat.pay.mchid}")
    private String mchId;
    
    @Value("${wechat.pay.apikey}")
    private String apiKey;
    
    @Autowired
    private WechatPaySignService signService;
    
    @Override
    protected String getApiUrl() {
        return apiUrl;
    }
    
    @Override
    protected int getTimeout() {
        return 60;  // 微信支付需要更长超时时间
    }
    
    @Override
    protected void doValidate(ApiRequest<WechatPayRequest> request) {
        WechatPayRequest wechatRequest = request.getData();
        
        if (StringUtils.isEmpty(wechatRequest.getOutTradeNo())) {
            throw new IllegalArgumentException("商户订单号不能为空");
        }
        
        if (wechatRequest.getTotalAmount() == null || 
            wechatRequest.getTotalAmount().compareTo(BigDecimal.ZERO) <= 0) {
            throw new IllegalArgumentException("订单金额必须大于0");
        }
        
        // 微信支付特定验证
        if (StringUtils.isEmpty(wechatRequest.getOpenid()) && 
            StringUtils.isEmpty(wechatRequest.getProductId())) {
            throw new IllegalArgumentException("必须提供openid或product_id");
        }
    }
    
    @Override
    protected void addAuthentication(HttpRequest.Builder builder) {
        // 添加微信支付特定的认证头部
        String timestamp = String.valueOf(System.currentTimeMillis() / 1000);
        String nonceStr = UUID.randomUUID().toString().replace("-", "");
        
        builder.header("Wechat-AppId", appId)
               .header("Wechat-MchId", mchId)
               .header("Wechat-Timestamp", timestamp)
               .header("Wechat-NonceStr", nonceStr);
    }
    
    @Override
    protected HttpRequest buildRequestBody(
            HttpRequest.Builder builder, ApiRequest<WechatPayRequest> request) {
        
        WechatPayRequest wechatRequest = request.getData();
        
        // 构建微信支付请求体
        Map<String, Object> requestBody = new HashMap<>();
        requestBody.put("appid", appId);
        requestBody.put("mch_id", mchId);
        requestBody.put("nonce_str", UUID.randomUUID().toString().replace("-", ""));
        requestBody.put("body", wechatRequest.getBody());
        requestBody.put("out_trade_no", wechatRequest.getOutTradeNo());
        requestBody.put("total_fee", wechatRequest.getTotalAmount()
                .multiply(BigDecimal.valueOf(100)).intValue());  // 转换为分
        requestBody.put("spbill_create_ip", wechatRequest.getClientIp());
        requestBody.put("notify_url", wechatRequest.getNotifyUrl());
        requestBody.put("trade_type", wechatRequest.getTradeType());
        
        if (StringUtils.isNotEmpty(wechatRequest.getOpenid())) {
            requestBody.put("openid", wechatRequest.getOpenid());
        }
        
        // 生成签名
        String sign = signService.generateSign(requestBody, apiKey);
        requestBody.put("sign", sign);
        
        // 转换为JSON
        String jsonBody = JSON.toJSONString(requestBody);
        
        // 构建POST请求
        return builder.POST(HttpRequest.BodyPublishers.ofString(jsonBody))
                     .build();
    }
    
    @Override
    protected ApiResponse<WechatPayResponse> handleResponse(HttpResponse httpResponse) {
        int statusCode = httpResponse.statusCode();
        String responseBody = (String) httpResponse.body();
        
        WechatPayResponse wechatResponse = JSON.parseObject(responseBody, WechatPayResponse.class);
        
        ApiResponse<WechatPayResponse> response = new ApiResponse<>();
        response.setData(wechatResponse);
        response.setSuccess(statusCode == 200);
        response.setCode(statusCode);
        response.setMessage(wechatResponse.getReturnMsg());
        
        // 验证微信支付返回的签名
        if (response.isSuccess()) {
            boolean signValid = signService.verifySign(wechatResponse, apiKey);
            if (!signValid) {
                response.setSuccess(false);
                response.setMessage("签名验证失败");
                log.warn("微信支付返回签名验证失败: {}", responseBody);
            }
        }
        
        return response;
    }
    
    @Override
    protected void doPostProcess(ApiRequest<WechatPayRequest> request, 
                                ApiResponse<WechatPayResponse> response) {
        // 微信支付特定的后置处理
        if (response.isSuccess()) {
            // 记录支付成功
            log.info("微信支付成功,订单号: {}, 微信支付订单号: {}", 
                    request.getData().getOutTradeNo(), 
                    response.getData().getTransactionId());
            
            // 发送支付成功事件
            eventPublisher.publishEvent(new WechatPaySuccessEvent(
                    request.getData().getOutTradeNo(),
                    response.getData().getTransactionId()
            ));
        } else {
            // 记录支付失败
            log.error("微信支付失败,订单号: {}, 错误信息: {}", 
                    request.getData().getOutTradeNo(), 
                    response.getMessage());
            
            // 发送支付失败事件
            eventPublisher.publishEvent(new WechatPayFailedEvent(
                    request.getData().getOutTradeNo(),
                    response.getMessage()
            ));
        }
    }
}

5.3 支付宝支付API实现

/**
 * 支付宝支付API实现
 */
@Component
@Slf4j
public class AlipayApiTemplate extends AbstractApiTemplate<AlipayRequest, AlipayResponse> {
    
    @Value("${alipay.api.url}")
    private String apiUrl;
    
    @Value("${alipay.appid}")
    private String appId;
    
    @Value("${alipay.private.key}")
    private String privateKey;
    
    @Value("${alipay.public.key}")
    private String publicKey;
    
    @Autowired
    private AlipaySignService signService;
    
    @Override
    protected String getApiUrl() {
        return apiUrl;
    }
    
    @Override
    protected int getTimeout() {
        return 45;  // 支付宝支付超时时间
    }
    
    @Override
    protected void doValidate(ApiRequest<AlipayRequest> request) {
        AlipayRequest alipayRequest = request.getData();
        
        if (StringUtils.isEmpty(alipayRequest.getOutTradeNo())) {
            throw new IllegalArgumentException("商户订单号不能为空");
        }
        
        if (alipayRequest.getTotalAmount() == null || 
            alipayRequest.getTotalAmount().compareTo(BigDecimal.ZERO) <= 0) {
            throw new IllegalArgumentException("订单金额必须大于0");
        }
        
        // 支付宝特定验证
        if (StringUtils.isEmpty(alipayRequest.getSubject())) {
            throw new IllegalArgumentException("订单标题不能为空");
        }
    }
    
    @Override
    protected void addCommonHeaders(HttpRequest.Builder builder) {
        // 支付宝需要特定的Content-Type
        builder.header("Content-Type", "application/x-www-form-urlencoded;charset=utf-8");
    }
    
    @Override
    protected HttpRequest buildRequestBody(
            HttpRequest.Builder builder, ApiRequest<AlipayRequest> request) {
        
        AlipayRequest alipayRequest = request.getData();
        
        // 构建支付宝请求参数
        Map<String, String> params = new HashMap<>();
        params.put("app_id", appId);
        params.put("method", "alipay.trade.app.pay");
        params.put("charset", "utf-8");
        params.put("sign_type", "RSA2");
        params.put("timestamp", LocalDateTime.now()
                .format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
        params.put("version", "1.0");
        params.put("notify_url", alipayRequest.getNotifyUrl());
        
        // 业务参数
        Map<String, Object> bizContent = new HashMap<>();
        bizContent.put("out_trade_no", alipayRequest.getOutTradeNo());
        bizContent.put("total_amount", alipayRequest.getTotalAmount().toString());
        bizContent.put("subject", alipayRequest.getSubject());
        bizContent.put("product_code", "QUICK_MSECURITY_PAY");
        
        params.put("biz_content", JSON.toJSONString(bizContent));
        
        // 生成签名
        String sign = signService.generateSign(params, privateKey);
        params.put("sign", sign);
        
        // 构建表单数据
        String formData = params.entrySet().stream()
                .map(entry -> entry.getKey() + "=" + URLEncoder.encode(entry.getValue(), StandardCharsets.UTF_8))
                .collect(Collectors.joining("&"));
        
        // 构建POST请求
        return builder.POST(HttpRequest.BodyPublishers.ofString(formData))
                     .build();
    }
    
    @Override
    protected ApiResponse<AlipayResponse> handleResponse(HttpResponse httpResponse) {
        int statusCode = httpResponse.statusCode();
        String responseBody = (String) httpResponse.body();
        
        ApiResponse<AlipayResponse> response = new ApiResponse<>();
        
        if (statusCode == 200) {
            // 解析支付宝响应
            Map<String, String> responseParams = parseResponse(responseBody);
            
            AlipayResponse alipayResponse = new AlipayResponse();
            alipayResponse.setTradeNo(responseParams.get("trade_no"));
            alipayResponse.setOutTradeNo(responseParams.get("out_trade_no"));
            alipayResponse.setTotalAmount(new BigDecimal(responseParams.get("total_amount")));
            
            // 验证签名
            boolean signValid = signService.verifySign(responseParams, publicKey);
            
            response.setData(alipayResponse);
            response.setSuccess("10000".equals(responseParams.get("code")) && signValid);
            response.setCode(statusCode);
            response.setMessage(responseParams.get("msg"));
            
            if (!signValid) {
                log.warn("支付宝返回签名验证失败: {}", responseBody);
            }
        } else {
            response.setSuccess(false);
            response.setCode(statusCode);
            response.setMessage("支付宝接口调用失败");
        }
        
        return response;
    }
    
    @Override
    protected void doPostProcess(ApiRequest<AlipayRequest> request, 
                                ApiResponse<AlipayResponse> response) {
        // 支付宝支付特定的后置处理
        if (response.isSuccess()) {
            // 生成支付参数(用于客户端调起支付宝)
            String payParams = generatePayParams(response.getData());
            response.getData().setPayParams(payParams);
            
            log.info("支付宝支付成功,订单号: {}, 支付宝交易号: {}", 
                    request.getData().getOutTradeNo(), 
                    response.getData().getTradeNo());
        } else {
            log.error("支付宝支付失败,订单号: {}, 错误信息: {}", 
                    request.getData().getOutTradeNo(), 
                    response.getMessage());
        }
    }
    
    private Map<String, String> parseResponse(String responseBody) {
        // 解析支付宝返回的URL编码字符串
        return Arrays.stream(responseBody.split("&"))
                .map(param -> param.split("="))
                .filter(pair -> pair.length == 2)
                .collect(Collectors.toMap(
                    pair -> pair[0],
                    pair -> URLDecoder.decode(pair[1], StandardCharsets.UTF_8)
                ));
    }
    
    private String generatePayParams(AlipayResponse response) {
        // 生成调起支付宝的支付参数
        Map<String, String> params = new HashMap<>();
        params.put("app_id", appId);
        params.put("method", "alipay.trade.app.pay");
        params.put("charset", "utf-8");
        params.put("sign_type", "RSA2");
        params.put("timestamp", LocalDateTime.now()
                .format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
        params.put("version", "1.0");
        
        Map<String, Object> bizContent = new HashMap<>();
        bizContent.put("out_trade_no", response.getOutTradeNo());
        bizContent.put("total_amount", response.getTotalAmount().toString());
        bizContent.put("subject", "订单支付");
        bizContent.put("product_code", "QUICK_MSECURITY_PAY");
        
        params.put("biz_content", JSON.toJSONString(bizContent));
        
        // 生成签名
        String sign = signService.generateSign(params, privateKey);
        params.put("sign", sign);
        
        // 转换为URL参数
        return params.entrySet().stream()
                .map(entry -> entry.getKey() + "=" + entry.getValue())
                .collect(Collectors.joining("&"));
    }
}

5.4 使用示例

@Service
@Slf4j
public class PaymentService {
    
    @Autowired
    private WechatPayApiTemplate wechatPayApiTemplate;
    
    @Autowired
    private AlipayApiTemplate alipayApiTemplate;
    
    /**
     * 微信支付
     */
    public WechatPayResponse wechatPay(WechatPayRequest request) {
        log.info("发起微信支付,订单号: {}", request.getOutTradeNo());
        
        ApiRequest<WechatPayRequest> apiRequest = new ApiRequest<>();
        apiRequest.setData(request);
        
        ApiResponse<WechatPayResponse> response = wechatPayApiTemplate.execute(apiRequest);
        
        if (!response.isSuccess()) {
            throw new PaymentException("微信支付失败: " + response.getMessage());
        }
        
        return response.getData();
    }
    
    /**
     * 支付宝支付
     */
    public AlipayResponse alipay(AlipayRequest request) {
        log.info("发起支付宝支付,订单号: {}", request.getOutTradeNo());
        
        ApiRequest<AlipayRequest> apiRequest = new ApiRequest<>();
        apiRequest.setData(request);
        
        ApiResponse<AlipayResponse> response = alipayApiTemplate.execute(apiRequest);
        
        if (!response.isSuccess()) {
            throw new PaymentException("支付宝支付失败: " + response.getMessage());
        }
        
        return response.getData();
    }
    
    /**
     * 统一支付接口 - 使用策略模式选择支付方式
     */
    public PaymentResponse pay(PaymentRequest paymentRequest) {
        AbstractApiTemplate<?, ?> apiTemplate;
        ApiRequest<?> apiRequest;
        
        switch (paymentRequest.getPayType()) {
            case WECHAT:
                WechatPayRequest wechatRequest = convertToWechatPayRequest(paymentRequest);
                apiTemplate = wechatPayApiTemplate;
                apiRequest = new ApiRequest<WechatPayRequest>().setData(wechatRequest);
                break;
                
            case ALIPAY:
                AlipayRequest alipayRequest = convertToAlipayRequest(paymentRequest);
                apiTemplate = alipayApiTemplate;
                apiRequest = new ApiRequest<AlipayRequest>().setData(alipayRequest);
                break;
                
            default:
                throw new IllegalArgumentException("不支持的支付类型: " + paymentRequest.getPayType());
        }
        
        // 使用模板方法执行支付
        ApiResponse<?> response = apiTemplate.execute(apiRequest);
        
        if (!response.isSuccess()) {
            throw new PaymentException("支付失败: " + response.getMessage());
        }
        
        return convertToPaymentResponse(response);
    }
}

六、模板方法模式的进阶应用

6.1 使用回调接口增强扩展性

在有些场景下,我们可能需要更灵活的扩展点。这时可以使用回调接口:

/**
 * 支持回调接口的模板方法抽象类
 */
public abstract class AbstractCallbackTemplate<T, R> {
    
    /**
     * 模板方法 - 支持回调
     */
    public final R execute(T request, TemplateCallback<T, R> callback) {
        // 1. 前置处理
        preProcess(request);
        
        // 2. 执行具体逻辑(通过回调接口)
        R result = callback.doInTemplate(request);
        
        // 3. 后置处理
        postProcess(request, result);
        
        return result;
    }
    
    /**
     * 钩子方法:前置处理
     */
    protected void preProcess(T request) {
        // 默认实现:记录日志
        log.info("开始处理请求: {}", request);
    }
    
    /**
     * 钩子方法:后置处理
     */
    protected void postProcess(T request, R result) {
        // 默认实现:记录日志
        log.info("请求处理完成: {}, 结果: {}", request, result);
    }
    
    /**
     * 回调接口
     */
    @FunctionalInterface
    public interface TemplateCallback<T, R> {
        R doInTemplate(T request) throws Exception;
    }
}

/**
 * 使用回调接口的示例
 */
@Service
public class TransactionService {
    
    @Autowired
    private AbstractCallbackTemplate<String, Boolean> transactionTemplate;
    
    @Transactional
    public void processWithTransaction(String businessId) {
        transactionTemplate.execute(businessId, request -> {
            // 在这里编写事务性的业务逻辑
            return doBusinessLogic(request);
        });
    }
    
    private Boolean doBusinessLogic(String businessId) {
        // 复杂的业务逻辑
        // 这个方法中的代码都在事务中执行
        return true;
    }
}

6.2 模板方法模式在Spring源码中的其他应用

除了Bean生命周期,模板方法模式在Spring的很多地方都有应用:

// 1. JdbcTemplate - 数据库操作模板
public class JdbcTemplate extends JdbcAccessor implements JdbcOperations {
    
    @Override
    public <T> T execute(ConnectionCallback<T> action) throws DataAccessException {
        // 获取连接
        Connection con = DataSourceUtils.getConnection(obtainDataSource());
        
        try {
            Connection conToUse = con;
            // 应用连接定制器
            if (this.nativeJdbcExtractor != null) {
                conToUse = this.nativeJdbcExtractor.getNativeConnection(con);
            }
            
            // 执行用户回调
            return action.doInConnection(conToUse);
            
        } catch (SQLException ex) {
            // 转换异常
            throw translateException("ConnectionCallback", ex);
        } finally {
            // 释放连接
            DataSourceUtils.releaseConnection(con, getDataSource());
        }
    }
    
    @Override
    public <T> T query(final String sql, final ResultSetExtractor<T> rse) throws DataAccessException {
        // 调用模板方法
        return execute(new ConnectionCallback<T>() {
            @Override
            public T doInConnection(Connection con) throws SQLException {
                PreparedStatement ps = null;
                ResultSet rs = null;
                try {
                    ps = con.prepareStatement(sql);
                    rs = ps.executeQuery();
                    return rse.extractData(rs);
                } finally {
                    JdbcUtils.closeResultSet(rs);
                    JdbcUtils.closeStatement(ps);
                }
            }
        });
    }
}

// 2. RestTemplate - HTTP请求模板
public class RestTemplate extends InterceptingHttpAccessor implements RestOperations {
    
    @Override
    public <T> T execute(String url, HttpMethod method, 
                        RequestCallback requestCallback,
                        ResponseExtractor<T> responseExtractor, 
                        Object... uriVariables) throws RestClientException {
        
        // 构建请求
        ClientHttpRequest request = createRequest(url, method);
        
        // 应用请求回调
        if (requestCallback != null) {
            requestCallback.doWithRequest(request);
        }
        
        // 执行请求
        ClientHttpResponse response = request.execute();
        
        // 处理响应
        return responseExtractor.extractData(response);
    }
}

// 3. Cache抽象 - 缓存操作模板
public abstract class AbstractCacheManager implements CacheManager, InitializingBean {
    
    @Override
    public Cache getCache(String name) {
        // 模板方法:获取缓存
        Cache cache = this.cacheMap.get(name);
        if (cache == null) {
            synchronized (this.cacheMap) {
                cache = this.cacheMap.get(name);
                if (cache == null) {
                    // 延迟创建缓存
                    cache = createCache(name);
                    this.cacheMap.put(name, cache);
                }
            }
        }
        return cache;
    }
    
    /**
     * 抽象方法:由子类实现具体的缓存创建逻辑
     */
    protected abstract Cache createCache(String name);
}

七、模板方法模式的最佳实践和陷阱

7.1 最佳实践

  1. 合理设计抽象类​:抽象类应该定义算法的骨架,具体步骤由子类实现
  2. 使用final保护模板方法​:防止子类修改算法结构
  3. 提供合适的钩子方法​:让子类能够适度扩展算法
  4. 保持方法单一职责​:每个方法只做一件事

7.2 常见陷阱

// 陷阱1:模板方法过于复杂
public abstract class ComplexTemplate {
    // 模板方法做了太多事情,违反了单一职责原则
    public final void complexTemplateMethod() {
        step1();
        step2();
        step3();
        step4();
        step5();
        step6();
        step7();
        step8();
        // ... 太多步骤
    }
}

// 陷阱2:钩子方法过多
public abstract class TooManyHooksTemplate {
    public final void templateMethod() {
        step1();
        hook1();  // 钩子方法太多,难以理解和维护
        hook2();
        hook3();
        if (hook4()) {
            step2();
        }
        hook5();
        // ...
    }
    
    protected void hook1() {}
    protected void hook2() {}
    protected void hook3() {}
    protected boolean hook4() { return true; }
    protected void hook5() {}
    // ...
}

// 陷阱3:子类破坏算法结构
public class EvilSubclass extends GoodTemplate {
    // 错误:试图绕过final限制
    public void templateMethod() {  // 编译错误,不能覆盖final方法
        // 直接调用父类步骤,破坏算法结构
        super.step3();
        super.step1();
        super.step2();
    }
}

7.3 设计原则

基于我在Spring源码中学到的经验,我总结了模板方法模式的设计原则:

  1. 好莱坞原则​:"不要调用我们,我们会调用你"。父类控制流程,子类提供实现。
  2. 开闭原则​:对扩展开放,对修改关闭。通过增加新的子类来扩展功能。
  3. 里氏替换原则​:子类必须能够替换它们的父类。
  4. 依赖倒置原则​:依赖于抽象,而不是具体实现。

总结

通过深入分析Spring源码中的模板方法模式,我获得了以下重要认知:

  1. 模板方法模式是框架设计的基石​:Spring通过这个模式固定了Bean生命周期、事务管理、数据访问等核心流程。
  2. 钩子方法是灵活性的关键​:Spring提供了大量的钩子方法(如BeanPostProcessor),让开发者可以在不修改框架源码的情况下扩展功能。
  3. 执行顺序很重要​:在Spring的Bean初始化中,@PostConstructInitializingBean.afterPropertiesSet()init-method的执行顺序是固定的,理解这个顺序可以避免很多坑。
  4. 模式组合使用更强大​:Spring中模板方法模式常与其他模式(如工厂方法、策略模式)结合使用,形成更灵活的设计。

最重要的收获​:设计模式不是孤立的语法规则,而是解决特定问题的经验总结。通过阅读Spring这样的优秀框架源码,我们可以学习到这些模式在真实项目中的最佳实践,从而在自己的项目中更好地应用它们。

这次对Spring源码的深入研究,不仅解决了初始化方法执行两次的问题,更重要的是让我掌握了通过源码学习设计模式的方法。现在,每当我遇到复杂的设计问题时,我都会想:Spring是怎么解决类似问题的?这种思考方式大大提升了我的设计能力。

版权声明 ▶ 本网站名称:黄磊的博客
▶ 本文标题:设计模式实战:从Spring源码中学习模板方法模式的应用
▶ 本文链接:https://www.huangleicole.com/backend-related/56.html
▶ 转载本站文章需要遵守:商业转载请联系站长,非商业转载请注明出处!!

如果觉得我的文章对你有用,请随意赞赏