java设计模式-工厂模式-接口实现类无缝切换

背景

  因为项目需要,要实现一个数字签名的功能。数字签名功能,需要加签,验签两个方法。这两个方法,通常是通过专门的签名服务器实现。签名服务器的供应商不只一家。同时在开发初期,也不会购买签名服务器,那就需要一个临时方法,让开发可以正常进行。而且为了在以后的生产环境部署,可以使用不同供应商的签名服务器,需要数字签名实现类的无缝切换。就是不修改已有的代码,通过配置调用不同的实现类。
  具体的说,就是没有签名服务器的时候,通过配置调用临时实现。有签名服务器时,根据不同的签名服务器,配置不同的参数,调用对应的实现。并且这种调用,不需要修改已有的代码。
  如果新增一个新的供应商的签名服务器,那么就新增一个对应实例类,并定义一个对应参数。就可以通过配置改参数,实现无缝切换。

Maven依赖

1. 导入springboot

    <parent>
        <artifactId>spring-boot-dependencies</artifactId>
        <groupId>org.springframework.boot</groupId>
        <version>2.1.3.RELEASE</version>
    </parent>

2. 导入spring-boot-starter-web和spring-boot-starter-test

		<!-- Spring Boot Web -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <!-- Spring Boot Test -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>

3.项目结构

项目结构

项目启动类

package cn.com.soulfox;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

/**
 * @create 2023/11/6 16:17
 */
@SpringBootApplication
public class FactoryModelRun {

    public static void main(String[] args) {
        SpringApplication.run(FactoryModelRun.class, args);
    }
}

Spring上下文工具类

  这个类提供了通过BeanName从spring容器中获取实例的方法

package cn.com.soulfox.utils;

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.stereotype.Component;

/**
 * spring工具类 
 * @author jiyh
 */
@Component
public final class SpringContextHelper implements BeanFactoryPostProcessor {

    /**
     * Spring应用上下文环境
     */
    private static ConfigurableListableBeanFactory beanFactory;

    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
        SpringContextHelper.beanFactory = beanFactory;
    }

    /**
     * 获取对象
     *
     * @param name   实例名称
     * @return 一个以所给名字注册的bean的实例
     * @throws BeansException
     */
    public static <T> T getBean(String name, Class<T> clz) throws BeansException {
        return (T) beanFactory.getBean(name, clz);
    }
}

数字签名功能类

1. 数字签名接口

package cn.com.soulfox.sign;

/**
 * 数字签名接口
 * @create 2023/11/6 16:32
 */
public interface SignServer {

    /**
     *  签名
     * @param signText  签名明文串
     * @param dn        数字证书 DN
     * @return
     */
    String sign(String signText, String dn);

    /**
     * 核验签名
     * @param singText       签名明文串
     * @param certSignedText 签名结果串
     * @param dn             数字证书 DN
     * @return
     */
    boolean verify(String singText, String certSignedText, String dn);
}

2. 数字签名默认实现

package cn.com.soulfox.sign;

import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Component;

/**
 * 默认实现,返回一些默认值,让程序顺利执行
 * @create 2023/11/6 16:36
 */
@Component("sign-default")//这里的“sign-default”就是beanName
@Lazy//延迟加载就是在没有配置的情况,不初始化
public class DefaultSignServer implements SignServer {

    /**
     * 加签方法,返回原明文串
     * @param signText  签名明文串
     * @param dn        数字证书 DN
     * @return
     */
    @Override
    public String sign(String signText, String dn) {
        System.out.println("加签默认实现——————————————————————————");
        return signText;
    }

    /**
     * 验签方法,直接返回true
     * @param singText       签名明文串
     * @param certSignedText 签名结果串
     * @param dn             数字证书 DN
     * @return
     */
    @Override
    public boolean verify(String singText, String certSignedText, String dn) {
        System.out.println("验签默认实现——————————————————————————");
        return true;
    }
}

3. 数字签名工厂类-用于初始化/提供,数字签名实现类

package cn.com.soulfox.sign;

import cn.com.soulfox.utils.SpringContextHelper;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;

/**
 * @author jiyh
 * @create 2023/11/6 16:45
 */
@Component
public class SignServerFactory {

    /**
     * 数字签名实现类 beanName
     */
    private static String beanName;

    /**
     * 数字签名对象
     */
    private static SignServer signServer;

    /**
     * 初始化数字签名对象
     */
    @PostConstruct
    public void initSignServer(){
        if(beanName == null || "".equals(beanName)){
            beanName = "sign-default";
        }
        signServer = SpringContextHelper.getBean(beanName, SignServer.class);
        if(signServer == null){
            signServer = new DefaultSignServer();
        }
    }

    public static SignServer getSignServer(){
        return SignServerFactory.signServer;
    }

    /**
     * 获取配置的数字签名服务类参数
     * @param beanName
     */
    @Value("${sign-server.bean-name:sign-default}")
    public void setBeanName(String beanName) {
        System.out.println("数字签名配置参数[beanName->"+beanName+"]");
        SignServerFactory.beanName = beanName;
    }

}

4. application.yml 中参数配置

server:
  port: 19002

spring:
  application:
    name: sign
    
#这里配置数字签名实例的beanName
#如果使用默认实现类,这个参数可以不配置
sign-server:
  bean-name: sign-default

测试1

1. 测试类

package cn.com.soulfox.sign;

import cn.com.soulfox.FactoryModelRun;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;

/**
 * @author jiyh
 * @create 2023/11/6 16:40
 */
@RunWith(SpringRunner.class)
@SpringBootTest(classes = FactoryModelRun.class,
        webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public class DefaultSignServerTest {

    @Test
    public void Test(){
        SignServer signServer = SignServerFactory.getSignServer();
        String sign = signServer.sign("test", "DN");
        System.out.println(sign);

        boolean result = signServer.verify("test", "test","DN");
        System.out.println(result);
    }
}

2. 测试结果测试结果-默认

测试2

  现在我们把实现类切换到ExceptionSignServer类,这个类的作用是返回异常信息,比如验签失败。

1. ExceptionSignServer代码

package cn.com.soulfox.sign;

import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Component;

/**
 * 异常实现,返回一些异常值,测试异常流程
 * @create 2023/11/6 16:36
 */
@Component("sign-exception")//这里的“sign-exception”就是beanName
@Lazy//延迟加载就是在没有配置的情况,不初始化
public class ExceptionSignServer implements SignServer {

    /**
     * 加签方法,返回null. 标识加签异常
     * @param signText  签名明文串
     * @param dn        数字证书 DN
     * @return
     */
    @Override
    public String sign(String signText, String dn) {
        System.out.println("加签异常实现——————————————————————————");
        return null;
    }

    /**
     * 验签方法,直接返回fasle,标识验签失败
     * @param singText       签名明文串
     * @param certSignedText 签名结果串
     * @param dn             数字证书 DN
     * @return
     */
    @Override
    public boolean verify(String singText, String certSignedText, String dn) {
        System.out.println("验签异常实现——————————————————————————");
        return false;
    }
}

2. 修改 application.yml 中参数配置

server:
  port: 19002

spring:
  application:
    name: sign
#修改为异常实现类
sign-server:
  bean-name: sign-exception

3. 测试结果

数字签名异常实现

总结

  以上在DefaultSignServer 和ExceptionSignServer两个实现类的切换过程中,只修改了配置文件 application.yml 中的配置参数。调用代码的地方没有修改,这样就实现了无缝切换。后续有了签名服务器,先导入供应商提供的jar包,再实现SignServer接口。就可以在不修改代码的情况下切换到对应签名服务器的加签,验签功能。这也是JAVA开发原则中,开闭原则(对新增开放对修改封闭)的体现。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值