Spring在运行时选择bean实现

我正在使用带有注释的Spring Beans,我需要在运行时选择不同的实现。

@Service public class MyService { public void test(){...} } 

例如对于windows的平台,我需要MyServiceWin extending MyService ,对于linux平台,我需要MyServiceLnx extending MyService

现在我只知道一个可怕的解决方案:

 @Service public class MyService { private MyService impl; @PostInit public void init(){ if(windows) impl=new MyServiceWin(); else impl=new MyServiceLnx(); } public void test(){ impl.test(); } } 

请考虑我只使用注释而不是XML配置。

您可以将bean注入移动到配置中,如下所示:

 @Configuration public class AppConfig { @Bean public MyService getMyService() { if(windows) return new MyServiceWin(); else return new MyServiceLnx(); } } 

或者,您可以使用配置文件windowslinux ,然后使用@Profile注释(如@Profile("linux")@Profile("windows")注释您的服务实现,并为您的应用程序提供此配置文件之一。

1.实现自定义Condition

 public class LinuxCondition implements Condition { @Override public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) { return context.getEnvironment().getProperty("os.name").contains("Linux"); } } 

对于Windows

2.在Configuration类中使用@Conditional

 @Configuration public class MyConfiguration { @Bean @Conditional(LinuxCondition.class) public MyService getMyLinuxService() { return new LinuxService(); } @Bean @Conditional(WindowsCondition.class) public MyService getMyWindowsService() { return new WindowsService(); } } 

3.像往常一样使用@Autowired

 @Service public class SomeOtherServiceUsingMyService { @Autowired private MyService impl; // ... } 

让我们创造漂亮的配置。

想象一下,我们有Animal界面,我们有DogCat实现。 我们想写写:

 @Autowired Animal animal; 

但我们应该返回哪个实施?

在此处输入图像描述

那么解决方案是什么? 有很多方法可以解决问题。 我将一起编写如何使用@Qualifier和Custom Conditions。

首先,让我们创建自定义注释:

 @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.METHOD, ElementType.FIELD, ElementType.TYPE}) public @interface AnimalType { String value() default ""; } 

和配置:

 @Configuration @EnableAutoConfiguration @ComponentScan public class AnimalFactoryConfig { @Bean(name = "AnimalBean") @AnimalType("Dog") @Conditional(AnimalCondition.class) public Animal getDog() { return new Dog(); } @Bean(name = "AnimalBean") @AnimalType("Cat") @Conditional(AnimalCondition.class) public Animal getCat() { return new Cat(); } } 

注意我们的bean名称是AnimalBean为什么我们需要这个豆? 因为当我们注入Animal接口时,我们只会编写@Qualifier(“AnimalBean”)

我们还创建了自定义注释来将值传递给我们的自定义条件

现在我们的条件看起来像这样(假设“Dog”名称来自配置文件或JVM参数或…)

  public class AnimalCondition implements Condition { @Override public boolean matches(ConditionContext conditionContext, AnnotatedTypeMetadata annotatedTypeMetadata) { if (annotatedTypeMetadata.isAnnotated(AnimalType.class.getCanonicalName())){ return annotatedTypeMetadata.getAnnotationAttributes(AnimalType.class.getCanonicalName()) .entrySet().stream().anyMatch(f -> f.getValue().equals("Dog")); } return false; } } 

最后注射:

 @Qualifier("AnimalBean") @Autowired Animal animal; 

使用@Qualifier注释将所有实现自动@Qualifier到工厂中,然后从工厂返回所需的服务类。

 public class MyService { private void doStuff(); } 

我的Windows服务:

 @Service("myWindowsService") public class MyWindowsService implements MyService { @Override private void doStuff() { //Windows specific stuff happens here. } } 

我的Mac服务:

 @Service("myMacService") public class MyMacService implements MyService { @Override private void doStuff() { //Mac specific stuff happens here } } 

我的工厂:

 @Component public class MyFactory { @Autowired @Qualifier("myWindowsService") private MyService windowsService; @Autowired @Qualifier("myMacService") private MyService macService; public MyService getService(String serviceNeeded){ //This logic is ugly if(serviceNeeded == "Windows"){ return windowsService; } else { return macService; } } } 

如果你想变得非常棘手,你可以使用枚举来存储你的实现类类型,然后使用枚举值来选择你想要返回的实现。

 public enum ServiceStore { MAC("myMacService", MyMacService.class), WINDOWS("myWindowsService", MyWindowsService.class); private String serviceName; private Class clazz; private static final Map, ServiceStore> mapOfClassTypes = new HashMap, ServiceStore>(); static { //This little bit of black magic, basically sets up your //static map and allows you to get an enum value based on a classtype ServiceStore[] namesArray = ServiceStore.values(); for(ServiceStore name : namesArray){ mapOfClassTypes.put(name.getClassType, name); } } private ServiceStore(String serviceName, Class clazz){ this.serviceName = serviceName; this.clazz = clazz; } public String getServiceBeanName() { return serviceName; } public static  ServiceStore getOrdinalFromValue(Class clazz) { return mapOfClassTypes.get(clazz); } } 

然后您的工厂可以进入应用程序上下文并将实例拉入其自己的地图。 添加新服务类时,只需在枚举中添加另一个条目,这就是您所要做的。

  public class ServiceFactory implements ApplicationContextAware { private final Map myServices = new Hashmap(); public MyService getInstance(Class clazz) { return myServices.get(ServiceStore.getOrdinalFromValue(clazz).getServiceName()); } public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { myServices.putAll(applicationContext.getBeansofType(MyService.class)); } } 

现在,您可以将所需的类类型传递到工厂,它将为您提供所需的实例。 非常有用,特别是如果你想使服务通用。