设计模式之模板模式的实际使用案例

文章介绍了在业务成熟公司中,通常使用一套包含模板模式的框架,允许业务开发者通过继承抽象类并重写特定方法来完成业务逻辑,而无需关注底层细节。模板模式提供了一个固定流程,子类只需实现具体业务步骤。同时,文章回顾了SpringAOP的原生使用,通过MethodInterceptor实现方法拦截,展示了如何创建自定义拦截器并配置到Spring中,以监控和增强服务的执行。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

前言

一般在业务相对比较成熟的公司,都有一套很适合业务并且相对成熟的框架供业务开发使用,业务开发者在不知晓框架底层逻辑的情况下在熟悉业务的老员工的指导下,也可以完成日常的curd工作。比如笔者所在的公司就是这样一种情况,框架提供全流程的业务功能,业务开发只需要适配各个业务模块即可,比如封装一个对外接口供外渠道调用,业务开发只需要继承一个抽象类,然后在子类中各个方法中添加内容即可完成业务,而整个调用链的工作过程也完全不需要业务开发者去关心。那么这种模式的工作原理是什么呢?

模板模式

什么是模板模式?先看个代码例子,顺便复现一下上文提到的接口调用逻辑。

package com.cz.ims;

import okhttp3.Request;
import okhttp3.Response;

import java.util.Date;
import java.util.List;

/**
 * @program: Reids
 * @description: 抽象业务类
 * @author: Cheng Zhi
 * @create: 2023-04-19 15:42
 **/
public abstract class BaseBusiBean {

    private long trancId = 0;
    /**
     * 模拟sessionId
     */
    private final static long sessionId = new Date().getTime();

    final public void getResult(Object... input) throws Exception {
        recordRequest();
        publicParamParse();
        init(input);
        validate(input);
        createMain3hBeans(input);
        doBusiness(input);
        createResponse(input);
        recordResponse();
    }

    private void publicParamParse() {
        System.out.println("公共参数解析...");
    }
    private void recordRequest() {
        // 模拟sessionId
        trancId = sessionId;
        System.out.println("记录返回请求参数到数据库....");
    }
    private void recordResponse() {
        if (trancId == sessionId) {
            System.out.println("记录返回结果到数据库或其他存储介质...");
        }
    }
    /**
     * @Description: 进行初始化工作
     * @param input
     * @throws Exception
     */
    abstract public void init(Object... input) throws Exception;

    /**
     * @Description: 业务数据有效性校验,如果数据不合法,则需要抛出异常
     * @param input
     * @throws Exception
     */
    abstract public void validate(Object... input) throws Exception;

    /**
     * 校验用户信息
     * @param input
     * @return
     * @throws Exception
     */
    abstract public List<String> createMain3hBeans(Object... input) throws Exception;

    /**
     * @Description: 执行具体业务操作,如存数据库等
     * @param input
     * @return
     * @throws Exception
     */
    abstract public Object[] doBusiness(Object... input) throws Exception;

    /**
     * 构建返回参数
     * @param result
     * @return
     */
    abstract public Response createResponse(Object[] result);
}
package com.cz.ims;

import okhttp3.Response;

import java.util.List;

/**
 * @program: Reids
 * @description: 测试业务逻辑
 * @author: Cheng Zhi
 * @create: 2023-04-19 15:45
 **/
public class TestBusinessBean extends BaseBusiBean{

    @Override
    public void init(Object... input) throws Exception {
        System.out.println("初始化中....");
    }

    @Override
    public void validate(Object... input) throws Exception {
        System.out.println("参数有效性校验中...");
    }

    @Override
    public List<String> createMain3hBeans(Object... input) throws Exception {

        System.out.println("用户信息校验中...");
        return null;
    }

    @Override
    public Object[] doBusiness(Object... input) throws Exception {

        System.out.println("具体业务执行中...");
        return new Object[0];
    }

    @Override
    public Response createResponse(Object[] result) {
        System.out.println("构建返回结果....");
        return null;
    }
}

测试类

package com.cz.ims;

/**
 * @program: Reids
 * @description:
 * @author: Cheng Zhi
 * @create: 2023-04-19 16:05
 **/
public class Main {

    public static void main(String[] args) throws Exception {
        TestBusinessBean testBusinessBean = new TestBusinessBean();
        testBusinessBean.getResult("入参");
    }
}

结果

image.png

什么是模板模式?模板模式就是在一个方法中定义一个算法骨架,而将一些步骤延迟到子类中去实现。模板模式使得子类在不改变算法结构的情况下,重新定义算法中的某些步骤。上面的例子就是前言中提到的接口调用的简化实现,只不过真正的入口是通过spring的拦截器根据接口编号来找到对应的具体bean(比如TestBusinessBean),然后反射来实现的。

回顾spring aop的原生使用

既然提到了拦截器,不免让人想到了面向切面编程(AOP),顺便回想一下AOP的实现原理,AOP是基于动态代理实现的。AOP创建方式常用的有两种,一种是spring原生的,一种是基于Aspect的
经常使用这种继承框架或者springboot,导致如今好多spring的基本功能的实现原理以及使用方式都忘记了,下面回顾一下基于原生spring实现的aop,其实笔者公司的接口封装使用的就是这种方式配合模板模式来实现的。

package com.cz.aop;

import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;

import javax.annotation.Nonnull;
import javax.annotation.Nullable;

/**
 * @program: Reids
 * @description: 自定义拦截器
 * @author: Cheng Zhi
 * @create: 2023-04-19 15:11
 **/
public class IntfInterceptor implements MethodInterceptor {

    @Nullable
    @Override
    public Object invoke(@Nonnull MethodInvocation methodInvocation) throws Throwable {

        String name = methodInvocation.getMethod().getName();
        System.out.println("拦截到的方法名" + name);
        return methodInvocation.proceed();
    }
}
package com.cz.aop;

/**
 * @program: Reids
 * @description: 被监控的服务
 * @author: Cheng Zhi
 * @create: 2023-04-19 15:16
 **/
public class MonitoredIntf {

    public void test() {
        System.out.println("我正在运行....");
    }
}

xml配置:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

   <beans>

      <bean id="intf_outinterface_interceptor" class="com.cz.aop.IntfInterceptor"></bean>
      <bean id="imscnsh_cN_SHMgntService" class="com.cz.aop.MonitoredIntf"></bean>

      <bean class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator" id="intf_outIntfServiceAutoProxy">
         <!--Bean名称规则(数组):指定为哪些bean创建自动代理-->
         <property name="beanNames">
            <list>
               <!--拦截的目标服务-->
               <value>imscnsh_cN_SHMgntService</value>
            </list>
         </property>
         <!--通知列表:需要执行那些通知-->
         <property name="interceptorNames">
            <list>
               <value>intf_outinterface_interceptor</value>
            </list>
         </property>
      </bean>
   </beans>
</beans>

测试类:

package com.cz.aop;

import org.springframework.context.support.ClassPathXmlApplicationContext;

/**
 * @program: Reids
 * @description:
 * @author: Cheng Zhi
 * @create: 2023-04-19 14:48
 **/
public class Main {

    public static void main(String[] args) {
        ClassPathXmlApplicationContext classPathXmlApplicationContext = new ClassPathXmlApplicationContext("file:///D:\Java\Project\Reids\Queue\src\main\resources\CzSpringConfig.xml");
        MonitoredIntf user = (MonitoredIntf) classPathXmlApplicationContext.getBean("imscnsh_cN_SHMgntService");
        user.test();
    }
}

结果:

image.png

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值