【问题标题】:spring beans map with command pattern带有命令模式的 spring beans 映射
【发布时间】:2019-02-11 17:23:56
【问题描述】:

我有具有不同实现的ProductHandler 类,例如ABCProductHandlerDEFProductHandler 等。它们是使用here 所示的命令模式从ProductServiceImpl 类调用的。

ProductServiceImpl 类:

@Service
public class ProductServiceImpl implements ProductService {

    private Map<ProductType,ProductHandler> productHandlersMap = 
                                         new EnumMap<>(ProductType.class);

    private ABCProductHandler abcProductHandler;

    private DEFProductHandler defProductHandler;

    //....10 other product handlers goes here

    @Autowired
    public ProductServiceImpl(ABCProductHandler abcProductHandler, 
                                  DEFProductHandler defProductHandler, .....) {
        this.abcProductHandler = abcProductHandler;
        this.defProductHandler = defProductHandler;
        //....10 other product handlers goes here
    }

    @PostConstruct() 
    public void init() {
        productHandlersMap.put(ProductType.ABC, abcProductHandler);
        productHandlersMap.put(ProductType.DEF, defProductHandler);
        //....10 other product handlers goes here
    }

    @Override
    public ProductDetails calculateProductPrice(ProductType productType) {
        productHandlersMap.get(productType).calculate();
        //..some otehr code goes here
        return productDetails;
    }
}

但是,我对上面的 ProductServiceImpl 类不满意,因为有很多带有样板代码的 productHandlersMap.put 调用。

现在,我的问题是有什么方法可以轻松加载productHandlersMap

@Service
public class ProductServiceImpl implements ProductService {

    private Map<ProductType,ProductHandler> productHandlersMap = 
                        new EnumMap<>(ProductType.class);

    @PostConstruct() 
    public void init() {
         //How to laod productHandlersMap easily with 
         // different ProductHandler types here?
    }

    @Override
    public ProductDetails calculateProductPrice(ProductType productType) {
        productHandlersMap.get(productType).calculate();
        //..some other code goes here
        return productDetails;
    }
}

【问题讨论】:

    标签: java spring dependency-injection


    【解决方案1】:

    Spring 可以自动将实现接口 I 的 bean 的不同实现自动连接到类型为 Map&lt;String, I&gt; 的属性,其中键是 bean 的名称,值是 bean 实例.由于您已经有一个枚举来定位每个 ProductHandler 实现,您可以利用它:

    public enum ProductType {
        ABC(ProductType.ABC_BEAN_NAME),
        DEF(ProductType.DEF_BEAN_NAME);
    
        public static final String ABC_BEAN_NAME = "abcProductHandler";
        public static final String DEF_BEAN_NAME = "defProductHandler";
    
        private String beanName;
    
        ProductType(String beanName) { this.beanName = beanName; }
    
        public String beanName() { return beanName; }
    }
    

    然后,在 @Configuration 工厂类中或通过 @Service@Component 注释定义不同的 ProductHandler 实现:

    @Service(ProductType.ABC_BEAN_NAME)
    public class ABCProductHandler implements ProductHandler {
    
        // ...
    }
    
    @Service(ProductType.DEF_BEAN_NAME)
    public class DEFProductHandler implements ProductHandler {
    
        // ...
    }
    

    现在,在您的 ProductServiceImpl bean 中,简单地自动装配一个 Map&lt;String, ProductHandler&gt;

    @Service
    public class ProductServiceImpl implements ProductService {
    
        private final Map<String, ProductHandler> productHandlersMap;
    
        @Autowired
        public ProductServiceImpl(Map<String, ProductHandler> productHandlersMap) {
            this.productHandlersMap = productHandlersMap;
        }
    
        @Override
        public ProductDetails calculateProductPrice(ProductType productType) {
            productHandlersMap.get(productType.beanName()).calculate();
            //..some otehr code goes here
            return productDetails;
        }
    }
    

    这样一来,你就可以让 Spring 完成所有的注入工作,甚至不需要使用 @PostConstruct 方法。

    注意productType.beanName()calculateProductPrice 方法中的使用。这可确保您使用正确的 bean 来计算价格。

    【讨论】:

    • 为什么还要维护一个地图,因为 spring bean 工厂隐式地维护了这一点。我们所能做的就是调用 context.getBean("bean name") 来获取实例。想法?
    • @siva 因为,如果您使用context.getBean(...),您将在业务逻辑类中使用 Spring 代码。这是不鼓励的,也被认为是不好的做法。映射只是一个映射,从String 到您的 bean,包含更多的业务逻辑。有时您只需要一张地图
    【解决方案2】:

    可以创建spring配置组件

    @Configuration
    public class CollectionConfig {
    
        @Bean
        public ProductHandler  getABC() {
            return new ABCProductHandler(ProductType.ABC);
        }
    
        @Bean
        public ProductHandler  getDEF() {
            return new DEFProductHandler(ProductType.DEF);
        }
    
        @Bean
        public ProductHandler  getXYZ() {
            return new XYZProductHandler(ProductType.XYZ);
    
        }
     
    
        // other factory methods
    
    }
    

    然后:

        @Service 
    public class ProductServiceImpl implements ProductService { 
    private Map<ProductType,ProductHandler> productHandlersMap = new EnumMap<>(ProductType.class); 
    
     @Autowired(required = false)
     private List<ProductHandler> beanList;
    
     @PostConstruct() 
    public void init() { 
        beanList.foreach(b->
        productHandlersMap.put(b.getType(), b))
         }
    }
    

     

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2016-04-22
      • 1970-01-01
      • 1970-01-01
      • 2015-04-18
      • 1970-01-01
      • 2013-11-17
      • 2013-05-18
      • 2012-05-11
      相关资源
      最近更新 更多