利用springboot初始化机制三种实现策略模式的应用

简介: 面试时总被问,spring中使用了哪些设计模式,你在实际开发中又使用哪些设计模式。给他手指一个方向跟他说,这就是一个模式:go out!。

面试时总被问,spring中使用了哪些设计模式,你在实际开发中又使用哪些设计模式。给他手指一个方向跟他说,这就是一个模式:go out!。
这就是一种模式:策略模式,一个接口的多个实现方式(算法)。本文梳理了使用springboot实现的三种实现策略模式的应用
我们知道,springboot应用初始化的过程是通过事件机制进行的。主要是通过 EventPublishingRunListener 在不同的初始化阶段发送不同的 SpringApplicationEvent (不同的子)事件,触发相应逻辑(这里的逻辑指class的加载)的加载和初始化。
当 ApplicationPreparedEvent 事件发送后,对于应用来讲,说明整个初始化过程已完成,也意味着所有的类已放入spring ioc 中。
这时我们就可以结合自己的业务逻辑实现策略模式的应用,我们通过以下三种方式实现策略的应用
方式一:使用ApplicationListener 和 ContextRefreshedEvent
核心使用的是 ApplicationContext.getBeanNamesForAnnotation(Class annotationType)方法,基于注解类,获取标有指定注解类的所有实例
我们的业务逻辑是这样的:应用Api接收规则参数(authType),Api 根据authType 值的不同,使用不同的auth service,执行相应的规则业务逻辑。
public interface UserValidator<D, R> {

String check(D data, R rule);œ

}
@Service
@Validator(authType = AuthType.B_USER)
public class BUserValidator implements UserValidator<String, String> {

@Override
public String check(String data, String rule) {
    System.out.println("客官,这里B端用户逻辑");
    return "";
}

}
@Service
@Validator(authType = AuthType.C_USER)
public class CUserValidator implements UserValidator<String, String> {

@Override
public String check(String data, String rule) {
    System.out.println("客官,这里C端用户逻辑");
    return "";
}

}
public enum AuthType {

B_USER(1, "b端用户"),
C_USER(2, "c端用户");

public final int type;

public final String code;

AuthType(int type, String code) {
    this.type = type;
    this.code = code;
}

}
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
@Inherited
public @interface Validator {

AuthType authType();

}
@Component
public class AuthContainer implements ApplicationListener {

private ConcurrentHashMap<Integer, UserValidator> validatorMap = new ConcurrentHashMap<>();

@Override
public void onApplicationEvent(ContextRefreshedEvent contextRefreshedEvent) {
    String[] names = context.getBeanNamesForAnnotation(Validator.class);
    if (names.length > 0) {
        for (String name : names) {
            System.out.println("UserValidator:" + name);
            UserValidator userValidator = context.getBean(name, UserValidator.class);
            Validator validator = userValidator.getClass().getAnnotation(Validator.class);
            validatorMap.put(validator.authType().type, userValidator);
        }
    }
}

public ConcurrentHashMap<Integer, UserValidator> getValidatorMap() {
    return validatorMap;
}

}
复制代码
Api 接口定义如下,根据不同的authType 值,执行不同的auth service
@RequestMapping("/client_auth")
@RestController
public class Client3 {

@Autowired
private AuthContainer authContainer;

@RequestMapping("getAuth")
public String getRule(@RequestParam("authType") Integer authType) {
    // if authType=1,B_USER; if authType=2,C_USER;
    ConcurrentHashMap<Integer, UserValidator> map = authContainer.getValidatorMap();
    UserValidator userValidator = map.get(authType);
    String res = userValidator.check("hi", "看看什么规则");
    return res;
}

}
复制代码
方式二:使用ApplicationContextAware 和 @PostConstruct
我们的业务逻辑是这样的:应用Api接收规则参数(ruleType),Api 根据ruleType 值的不同,使用不同的rule service,执行相应的规则业务逻辑。
核心使用的是 ApplicationContext.getBeanNamesForAnnotation(Class annotationType)方法,基于注解类,获取标有指定注解类的所有实例
代码结构:

核心代码如下:
public abstract class RuleValidator<D, R> {

public abstract String check(D data, R rule);

}
@RuleMapping(ruleCodeEnum = RuleCodeEnum.COUNT)
@Service
public class CountRuleValidator extends RuleValidator<String, String> {

@Override
public String check(String data, String rule) {
    System.out.println("客官,这里是数量规则区域");
    return "";
}

}
@RuleMapping(ruleCodeEnum = RuleCodeEnum.PRICE)
@Service
public class PriceRuleValidator extends RuleValidator<String, String> {

@Override
public String check(String data, String rule) {
    System.out.println("客官,这里是价格规则区域");
    return "";
}

}
public enum RuleCodeEnum {

/** 价格规则 */
PRICE(1, "price"),
/** 数量规则 */
COUNT(2, "count");

public final int type;
public final String code;

RuleCodeEnum(int type, String code) {
    this.type = type;
    this.code = code;
}

}
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
@Inherited
public @interface RuleMapping {

String ruleCode() default "";
String ruleDesc() default "";
RuleCodeEnum ruleCodeEnum();

}

@Component
public class RuleValidatorInit implements ApplicationContextAware {

private static ApplicationContext context;
private Map<Integer, RuleValidator> validatorMap = new HashMap<>();

@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
    this.context = applicationContext;
}
public Map<Integer, RuleValidator> getValidatorMap() {
    return validatorMap;
}
@PostConstruct
public void afterPropertiesSet() {
    String[] names = context.getBeanNamesForAnnotation(RuleMapping.class);
    if (names.length > 0) {
        for (String name : names) {
            RuleValidator ruleValidator = context.getBean(name, RuleValidator.class);
            RuleMapping ruleMapping = ruleValidator.getClass().getAnnotation(RuleMapping.class);
            validatorMap.put(ruleMapping.ruleCodeEnum().type, ruleValidator);
        }
    }
}

}
复制代码
Api 接口定义如下,根据不同的ruleType 值,执行不同的rule service
@RequestMapping("/client")
@RestController
public class Client {

@Autowired
private RuleValidatorInit ruleValidatorInit;

@RequestMapping("getRule")
public String getRule(@RequestParam("ruleType") Integer ruleType) {
    Map<Integer, RuleValidator> map = ruleValidatorInit.getValidatorMap();
    System.out.println(map);
    RuleValidator<String, String> ruleValidator = map.get(ruleType);
    String res = ruleValidator.check("hi", "看看什么规则");
    return res;
}

}
复制代码
方式三:使用ApplicationContextAware
对比方式一,这里不需要定义指定注解了
核心使用的是 ApplicationContext.getBeansOfType(classType)方法,获取接口 classType 的所有子类实例
代码结构:

核心代码如下:
public interface RoleValidator<D, R> {

String check(D data, R rule);
RoleCodeEnum source();

}
@Service
public class BRoleValidator implements RoleValidator<String, String> {

@Override
public String check(String data, String rule) {
    System.out.println("客官,这里B端用户逻辑");
    return "";
}
@Override
public RoleCodeEnum source() {
    return RoleCodeEnum.B_USER;
}

}
@Service
public class CRoleValidator implements RoleValidator<String, String> {

@Override
public String check(String data, String rule) {
    System.out.println("客官,这里C端用户逻辑");
    return "";
}
@Override
public RoleCodeEnum source() {
    return RoleCodeEnum.C_USER;
}

}
public enum RoleCodeEnum {

B_USER(1, "b端用户"),
C_USER(2, "c端用户");

public final int type;
public final String code;

RoleCodeEnum(int type, String code) {
    this.type = type;
    this.code = code;
}

}
@Component
public class RoleValidatorInit implements ApplicationContextAware {

private static Map<RoleCodeEnum, RoleValidator> builderMap = new HashMap<>();

@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
    for (RoleValidator roleValidator : applicationContext.getBeansOfType(RoleValidator.class).values()) {
        builderMap.put(roleValidator.source(), roleValidator);
    }
}
public static RoleValidator getRoleValidator(RoleCodeEnum role) {
    return builderMap.get(role);
}

}
复制代码
Api 接口定义如下,根据不同的roleType 值,执行不同的role service
@RequestMapping("/client_role")
@RestController
public class Client2 {

@Autowired
private RoleValidatorInit roleValidatorInit;

@RequestMapping("getRole")
public String getRule(@RequestParam("roleType") Integer roleType) {
    // if roleType=1,B_USER; if roleType=2,C_USER;
    RoleValidator roleValidator = roleValidatorInit.getRoleValidator(RoleCodeEnum.B_USER);
    String res = roleValidator.check("hi", "看看什么规则");
    return res;
}

}
复制代码
总结
通过三种方式的对比,对于核心部分,其实就是几行代码的不同。无论是利用 Event 事件还是通过 Aware,本质都是拿到或利用 ApplicationContext 去解析接口,拿到实现类的实例,放入集合,然后在客户端(或其他)获取集合,根据枚举标识拿到对应的子类,执行对应的业务逻辑。

相关文章
|
2月前
|
缓存 Java 数据库连接
Spring Boot奇迹时刻:@PostConstruct注解如何成为应用初始化的关键先生?
【8月更文挑战第29天】作为一名Java开发工程师,我一直对Spring Boot的便捷性和灵活性着迷。本文将深入探讨@PostConstruct注解在Spring Boot中的应用场景,展示其在资源加载、数据初始化及第三方库初始化等方面的作用。
53 0
|
2月前
|
Java 数据库 开发者
深入剖析 SpringBoot 的 SPI 机制
【8月更文挑战第10天】在软件开发中,SPI(Service Provider Interface)机制是一种重要的服务发现和加载机制,尤其在构建模块化、可扩展的系统时尤为重要。SpringBoot作为Spring家族的一员,其内置的SPI机制不仅继承了Java SPI的设计思想,还进行了优化和扩展,以适应Spring Boot特有的需求。本文将深入剖析SpringBoot中的SPI机制,揭示其背后的原理与应用。
62 7
|
2月前
|
Java 开发者 Spring
"揭秘SpringBoot魔法SPI机制:一键解锁服务扩展新姿势,让你的应用灵活飞天!"
【8月更文挑战第11天】SPI(Service Provider Interface)是Java的服务提供发现机制,用于运行时动态查找和加载服务实现。SpringBoot在其基础上进行了封装和优化,通过`spring.factories`文件提供更集中的配置方式,便于框架扩展和组件替换。本文通过定义接口`HelloService`及其实现类`HelloServiceImpl`,并在`spring.factories`中配置,结合`SpringFactoriesLoader`加载服务,展示了SpringBoot SPI机制的工作流程和优势。
42 5
|
2月前
|
安全 Java UED
掌握SpringBoot单点登录精髓,单点登录是一种身份认证机制
【8月更文挑战第31天】单点登录(Single Sign-On,简称SSO)是一种身份认证机制,它允许用户只需在多个相互信任的应用系统中登录一次,即可访问所有系统,而无需重复输入用户名和密码。在微服务架构日益盛行的今天,SSO成为提升用户体验和系统安全性的重要手段。本文将详细介绍如何在SpringBoot中实现SSO,并附上示例代码。
43 0
|
2月前
|
消息中间件 Java Kafka
深入SpringBoot的心脏地带:掌握其核心机制的全方位指南
【8月更文挑战第29天】这段内容介绍了在分布式系统中起到异步通信与解耦作用的消息队列,并详细探讨了三种流行的消息队列产品:RabbitMQ、RocketMQ 和 Kafka。RabbitMQ 是一个基于 AMQP 协议的开源消息队列系统,支持多种消息模型,具有高可靠性及稳定性;RocketMQ 则是由阿里巴巴开源的高性能分布式消息队列,支持事务消息等多种特性;而 Kafka 是 LinkedIn 开源的分布式流处理平台,以其高吞吐量和良好的可扩展性著称。文中还提供了使用这三种消息队列产品的示例代码。总之,这三款产品各有优势,适用于不同场景。
11 0
|
2月前
|
消息中间件 Java Kafka
SpringBoot大揭秘:如何轻松掌握其核心机制?
【8月更文挑战第29天】这段内容介绍了在分布式系统中起到异步通信与解耦作用的消息队列,并详细探讨了三种流行的消息队列产品:RabbitMQ、RocketMQ 和 Kafka。RabbitMQ 是一个基于 AMQP 协议的开源消息队列系统,支持多种消息模型,具有高可靠性及稳定性;RocketMQ 则是由阿里巴巴开源的高性能分布式消息队列,支持事务消息等多种特性;而 Kafka 是 LinkedIn 开源的分布式流处理平台,以其高吞吐量和良好的可扩展性著称。文中还提供了使用这三种消息队列产品的示例代码。
13 0
WXM
|
2月前
|
存储 缓存 Java
|
2月前
|
Java Spring
Java SpringBoot Bean InitializingBean 项目初始化
Java SpringBoot Bean InitializingBean 项目初始化
41 0
|
2月前
|
Java 数据安全/隐私保护
SpringBoot 自定义初始化任务 Runner
SpringBoot 自定义初始化任务 Runner
11 0
|
2月前
|
消息中间件 Java Kafka
SpringBoot Kafka SSL接入点PLAIN机制收发消息
SpringBoot Kafka SSL接入点PLAIN机制收发消息
30 0
下一篇
无影云桌面