ServiceLocatorFactoryBean默认实例

时间:2016-07-01 17:31:52

标签: java spring spring-ioc

我们如何告诉Spring ServiceLocatorFactoryBean提供服务的默认实例? 我有这样的场景。

package strategy;

import model.Document;

public interface IPrintStrategy {
public void print(Document document);
}

和两种策略类

package strategy;

import model.Document;

import org.springframework.stereotype.Component;

@Component("A4Landscape")
public class PrintA4LandscapeStrategy implements IPrintStrategy{

 @Override
 public void print(Document document) {
  System.out.println("Doing stuff to print an A4 landscape document");
 }

}


package strategy;

import model.Document;

import org.springframework.stereotype.Component;

@Component("A5Landscape")
public class PrintA5LandscapeStrategy implements IPrintStrategy{

 @Override
 public void print(Document document) {
  System.out.println("Doing stuff to print an A5 landscape document");
 }

}

战略工厂界面如下

package strategy;

public interface PrintStrategyFactory {

 IPrintStrategy getStrategy(String strategyName);

}

和Spring配置如下

<beans xmlns:context="http://www.springframework.org/schema/context" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://www.springframework.org/schema/beans" xsi:schemalocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
  http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.1.xsd">

 <context:component-scan base-package="strategy">

  <bean class="org.springframework.beans.factory.config.ServiceLocatorFactoryBean" id="printStrategyFactory">
  <property name="serviceLocatorInterface" value="strategy.PrintStrategyFactory">
 </property></bean>

 <alias alias="A4P" name="A4Portrait">
 <alias alias="A4L" name="A4Landscape">
</alias></alias></context:component-scan></beans>

和我的测试类

import model.Document;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.testng.AbstractTestNGSpringContextTests;
import org.testng.annotations.Test;

import strategy.PrintStrategyFactory;


@ContextConfiguration(locations = {"classpath:/spring-config.xml"})
public class SpringFactoryPatternTest extends AbstractTestNGSpringContextTests{

 @Autowired
 private PrintStrategyFactory printStrategyFactory;

 @Test
 public void printStrategyFactoryTest(){
  Document doc = new Document();

  printStrategyFactory.getStrategy("A4L").print(doc);
  printStrategyFactory.getStrategy("A5L").print(doc);

  printStrategyFactory.getStrategy("Something").print(doc);

 }
}

当我像上次通话一样将一些文本传递给工厂时会发生什么

  printStrategyFactory.getStrategy("Something").print(doc);

有没有办法配置ServiceLocatorFactoryBean来发回我的Print Strategy的默认实例,就像下面类的实例一样。

package strategy;

import model.Document;

import org.springframework.stereotype.Component;

@Component("invalid")
public class InvalidLandscapeStrategy implements IPrintStrategy{

 @Override
 public void print(Document document) {
  System.out.println("INVALID DOCUMENT STRATEGY");
 }

}

1 个答案:

答案 0 :(得分:0)

我没有找到真正整洁的方法,但是这里有3种不太整洁的选项。

1:遇到相同问题时,我使用的选项是使用setServiceLocatorExceptionClass方法并设置自己的异常类,然后捕获它并使用默认值。

// Checked exception for service locator factory
public class PrintStrategyException extends Exception
{
    public PrintStrategyException(String message)
    {
        super(message);
    }

    public PrintStrategyException(String message, Throwable cause)
    {
        super(message, cause);
    }

    public PrintStrategyException(Throwable cause)
    {
        super(cause);
    }
}

我使用了JAVA API,但转换为您的XML配置:

<bean class="org.springframework.beans.factory.config.ServiceLocatorFactoryBean" id="printStrategyFactory">
    <property name="serviceLocatorInterface" value="strategy.PrintStrategyFactory"/>
    <property name="setServiceLocatorExceptionClass" value="strategy.PrintStrategyException"/>
</bean>

您可能在这里想要RuntimeException,但是由于我故意使用了检查异常,因此我可以(并且必须)捕获它:

try {
    strategy = printStrategyFactory.getStrategy("A4L");
}
catch (PrintStrategyException e) {
    // strategy was not found, use the default "invalid"
    strategy = printStrategyFactory.getStrategy("invalid");
}
strategy.print(doc);

2:与选项1相同,但是在找不到该bean时捕获标准的Spring异常之一,而不必麻烦创建自己的bean。

请注意,我的try-catch代码位于您的测试类中,因此,此方法实际上取决于创建包装器类以使您的Strategy像在测试中所做的那样自动连接工厂。 (使您的Test类成为生产类)。 这样做的需要-收集您的调用以将工厂实例化到一个位置,以便您可以处理异常-这是使此方法不理想的原因。它可能似乎只是将您的原始问题包装在了更好的包装中,但实际上,您可以在任何地方捕获该异常(您没有必须像我一样使用通用方法)并通过对其进行检查(相对于运行时)并将其添加到工厂方法签名中,您将其设为API的一部分,以便所有调用者都需要对其进行处理。

3:最后一招,非常棘手的方法是利用基于属性的方法setServiceMappings并重写java.util.Properties类:

// I'm sticking with the Annotation approach here, but you can transcribe it into XML
@Bean("strategyFactory")
public FactoryBean serviceLocatorFactoryBean() {
    ServiceLocatorFactoryBean factoryBean = new ServiceLocatorFactoryBean();
    factoryBean.setServiceLocatorInterface(PrintStrategyFactory.class);
    Properties myProperties = new MyProperties();
    myProperties.setProperty("A4P", "A4Portrait");
    myProperties.setProperty("A4L", "A4Landscape");
    factoryBean.setServiceMappings(myProperties);
    return factoryBean;
}

public static class MyProperties extends Properties
{
    @Override
    public String getProperty(String key)
    {
        String value = super.getProperty(key);

        // Strategy name is not known, default to "invalid" strategy
        return value == null ? "invalid" : value;
    }
}

这是很棘手的,因为它依赖于Spring类将在Properties对象上调用getProperty()的知识。而且,确实java.util.Properties不是 final 。如果Spring想要支持此解决方案,他们将使用 Map 之类的接口,而不是 Properties 之类的接口。

但是,虽然在合同中没有明确地调用getProperty(),但强烈暗示了这一点,并且不可避免地会调用getProperty()。而且该解决方案将无所不在地无处不在。

因此有效。我仍然更喜欢选项1,因为它更整洁。而且我正在使用自己的检查异常。