我们如何告诉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");
}
}
答案 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,因为它更整洁。而且我正在使用自己的检查异常。