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

这便是一种形式:战略形式,一个接口的多个完成办法(算法)。本文梳理了运用springboot完成的三种完成战略形式的运用

咱们知道,springboot运用初始化的进程是经过事情机制进行的。主要是经过 EventPublishingRunListener 在不同的初始化阶段发送不同的 SpringApplicationEvent (不同的子)事情,触发相应逻辑(这儿的逻辑指class的加载)的加载和初始化。

当 ApplicationPreparedEvent 事情发送后,对于运用来讲,阐明整个初始化进程已完成,也意味着一切的类已放入spring ioc 中。

这时咱们就可以结合自己的事务逻辑完成战略形式的运用,咱们经过以下三种办法完成战略的运用

办法一:运用ApplicationListenerContextRefreshedEvent

中心运用的是 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<ContextRefreshedEvent> {
    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)办法,依据注解类,获取标有指定注解类的一切实例

代码结构:

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

中心代码如下:

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 的一切子类实例

代码结构:

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

中心代码如下:

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 去解析接口,拿到完成类的实例,放入集合,然后在客户端(或其他)获取集合,依据枚举标识拿到对应的子类,履行对应的事务逻辑。

原文地址: 使用springboot初始化机制三种完成战略形式的运用