我正在使用带有注释的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配置。
答案 0 :(得分:52)
Condition
public class LinuxCondition implements Condition {
@Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
return context.getEnvironment().getProperty("os.name").contains("Linux"); }
}
Windows
相同。
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();
}
}
@Autowired
@Service
public class SomeOtherServiceUsingMyService {
@Autowired
private MyService impl;
// ...
}
答案 1 :(得分:14)
让我们创造漂亮的配置。
想象一下,我们有动物界面,我们有 Dog 和 Cat 实现。我们想写写:
@Autowired
Animal animal;
但我们应该返回哪个实施?
那么什么是解决方案?有很多方法可以解决问题。我将一起编写如何使用 @Qualifier 和自定义条件。
首先,让我们创建自定义注释:
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD, ElementType.FIELD, ElementType.TYPE})
public @interface AnimalType {
String value() default "";
}
和config:
@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 。 为什么我们需要这个bean?因为当我们注入Animal接口时我们只会编写 @Qualifier(“AnimalBean”)
我们还创建了自定义注释,将值传递给自定义条件。
现在我们的条件看起来像这样(假设“狗”名称来自配置文件或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;
答案 2 :(得分:3)
将所有实施自动装配到带有123
4
5
678
...
注释的工厂中,然后从工厂返回所需的服务类。
@Qualifier
我的Windows服务:
public class MyService {
private void doStuff();
}
我的Mac服务:
@Service("myWindowsService")
public class MyWindowsService implements MyService {
@Override
private void doStuff() {
//Windows specific stuff happens here.
}
}
我的工厂:
@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<Class<?>, ServiceStore> mapOfClassTypes = new HashMap<Class<?>, 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 <T> ServiceStore getOrdinalFromValue(Class<?> clazz) {
return mapOfClassTypes.get(clazz);
}
}
现在您可以将所需的类类型传递给工厂,它将为您提供所需的实例。非常有用,特别是如果你想使服务通用。
答案 3 :(得分:1)
MyService.java:
public interface MyService {
String message();
}
MyServiceConfig.java:
@Configuration
public class MyServiceConfig {
@Value("${service-type}")
MyServiceTypes myServiceType;
@Bean
public MyService getMyService() {
if (myServiceType == MyServiceTypes.One) {
return new MyServiceImp1();
} else {
return new MyServiceImp2();
}
}
}
application.properties:
service-type=one
MyServiceTypes.java
public enum MyServiceTypes {
One,
Two
}
在任何Bean / Component / Service / etc中使用。喜欢:
@Autowired
MyService myService;
...
String message = myService.message()