java

关注公众号 jb51net

关闭
首页 > 软件编程 > java > SpringBoot通过Map实现天然的策略模式

SpringBoot如何通过Map实现天然的策略模式

作者:一恍过去

文章介绍了如何在Spring框架中使用@Resource注解和Map集合来实现策略模式,并详细解释了Spring如何处理集合类型的依赖注入,通过这种方式,可以在运行时动态地选择算法的行为,使得代码更加灵活和可扩展

前言

策略模式是一种行为设计模式,它允许在运行时选择算法的行为。在Spring框架中,我们可以利用@Resource注解和Map集合来优雅地实现策略模式。

在Spring框架中,当你使用@Resource注解注入一个Map<String, T>或List时,Spring会自动将所有类型为T的bean收集到这个Map或者List集合中,其中:

Map

List

底层机制解析

Spring的集合类型自动装配

Spring框架对集合类型的依赖注入有特殊处理:

@Resource注解的行为

@Resource注解默认按名称装配,但当目标是一个Map时,Spring会特殊处理:

实现原理

Spring在依赖注入时的处理流程:

使用

直接使用Map<String,T>

我们直接定义一个Controller,并且在Controller中使用@ResourceMap<String,T>

@RestController
@RequestMapping("/test")
public class TestController {
    @Resource
    private Map<String, Object> beanMap = new ConcurrentHashMap<>();
    
    public void beanMap() {
        System.out.println(beanMap.size());
    }
}

验证:

可以看到map中存了项目中所有的bean对象

指定Map中的bean类型

在实际的开发中,我们希望Map中只是存储需要的Bean,并且Controller中可以根据beanName进行转发到不同的Service中,步骤如下:

定义策略接口

public interface PaymentStrategy {
    void pay();
}

定义实现类

	@Service("ALI")
	@Slf4j
	public class AliStrategyService implements PaymentStrategy {
	
	    @Override
	    public void pay() {
	        log.info("使用支付宝支付");
	    }
	}


	@Service("WX")
	@Slf4j
	public class WxStrategyService implements PaymentStrategy {
	
	    @Override
	    public void pay() {
	        log.info("使用微信支付");
	    }
	}

策略使用

@RestController
@RequestMapping("/test")
public class TestController {

    @Resource
    private Map<String, PaymentStrategy> beanMap = new ConcurrentHashMap<>();
    
    public void beanMap() {
        PaymentStrategy wx = beanMap.get("WX");
        wx.pay();
        PaymentStrategy ali = beanMap.get("ALI");
        ali.pay();
    }
}

验证

可以看到map中,就只有两个Bean,并且key就是我们通过@Service(value)定义的名称

自定义注解实现

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Component
public @interface PaymentType {
    String value();
}
@PaymentType("CARD")
@Slf4j
public class CardStrategyService implements PaymentStrategy {

    @Override
    public void pay() {
        log.info("使用银行卡支付");
    }
}

结合List进行优化

在上面直接使用Map来注入Bean的场景下,如果Service(“xxName”)的名称和某个枚举或者常量一致的情况,并且会在多处使用,那么枚举或者常量变更后Service(“xxName”)没有修改到就容易出现问题(启动不会报错,容易忽略修改);

public interface PayWayConstants {
    String ALI_PAY = "ALI";

    String WX_PAY = "WX";
}

@Service(“ALI”)中不再需要写value值

// 接口
public interface PaymentStrategy {
	String getWay();
	
    void pay();
}


	// 各个实现类
	@Service
	@Slf4j
	public class AliStrategyService implements PaymentStrategy {
		@Override
	    public String getWay() {
	       return PayWayConstants.ALI_PAY;
	    }
	
	    @Override
	    public void pay() {
	        log.info("使用支付宝支付");
	    }
	}


	@Service
	@Slf4j
	public class WxStrategyService implements PaymentStrategy {
		@Override
	    public String getWay() {
	      return PayWayConstants.WX_PAY;
	    }
	    
	    @Override
	    public void pay() {
	        log.info("使用微信支付");
	    }
	}

@Component
public class PaymentStrategyComponent {

    private Map<String, PaymentStrategy> beanMap = new ConcurrentHashMap<>();

    @Resource
    private List<PaymentStrategy> list;

    @PostConstruct
    public void init() {
        for (PaymentStrategy service : list) {
            beanMap.put(service.getWay(), service);
            log.info("Registered: {}", service.getWay());
        }
    }

    public PaymentStrategy getPaymentStrategy(String way) {
        return beanMap.get(way);
    }
}
@RestController
@RequestMapping("/test")
public class TestController {

    @Resource
    private PaymentStrategyComponent strategyComponent;
    
    public void beanMap() {
        PaymentStrategy wx = strategyComponent.getPaymentStrategy(PayWayConstants.WX_PAY);
        wx.pay();
        PaymentStrategy ali = strategyComponent.getPaymentStrategy(PayWayConstants.ALI_PAY);
        ali.pay();
    }
}

总结

以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。

您可能感兴趣的文章:
阅读全文