前言
一般在业务相对比较成熟的公司,都有一套很适合业务并且相对成熟的框架供业务开发使用,业务开发者在不知晓框架底层逻辑的情况下在熟悉业务的老员工的指导下,也可以完成日常的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("入参");
}
}
结果
什么是模板模式?模板模式就是在一个方法中定义一个算法骨架,而将一些步骤延迟到子类中去实现。模板模式使得子类在不改变算法结构的情况下,重新定义算法中的某些步骤。上面的例子就是前言中提到的接口调用的简化实现,只不过真正的入口是通过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();
}
}
结果: