Tob*_*bia 40 java spring spring-bean
我正在使用带有注释的Spring Beans,我需要在运行时选择不同的实现.
@Service
public class MyService {
public void test(){...}
}
Run Code Online (Sandbox Code Playgroud)
例如对于我需要的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();
}
}
Run Code Online (Sandbox Code Playgroud)
请考虑我只使用注释而不是XML配置.
nob*_*beh 64
Conditionpublic class LinuxCondition implements Condition {
@Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
return context.getEnvironment().getProperty("os.name").contains("Linux"); }
}
Run Code Online (Sandbox Code Playgroud)
同样的Windows.
@Conditional在Configuration课堂上使用@Configuration
public class MyConfiguration {
@Bean
@Conditional(LinuxCondition.class)
public MyService getMyLinuxService() {
return new LinuxService();
}
@Bean
@Conditional(WindowsCondition.class)
public MyService getMyWindowsService() {
return new WindowsService();
}
}
Run Code Online (Sandbox Code Playgroud)
@Autowired照常使用@Service
public class SomeOtherServiceUsingMyService {
@Autowired
private MyService impl;
// ...
}
Run Code Online (Sandbox Code Playgroud)
gre*_*rep 23
让我们创建漂亮的配置.
想象一下,我们有Animal界面,我们有Dog和Cat实现.我们想写写:
@Autowired
Animal animal;
Run Code Online (Sandbox Code Playgroud)
但我们应该返回哪个实施?
那么解决方案是什么?有很多方法可以解决问题.我将一起编写如何使用 @Qualifier和Custom Conditions.
首先,让我们创建自定义注释:
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD, ElementType.FIELD, ElementType.TYPE})
public @interface AnimalType {
String value() default "";
}
Run Code Online (Sandbox Code Playgroud)
和配置:
@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();
}
}
Run Code Online (Sandbox Code Playgroud)
注意我们的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;
}
}
Run Code Online (Sandbox Code Playgroud)
最后注射:
@Qualifier("AnimalBean")
@Autowired
Animal animal;
Run Code Online (Sandbox Code Playgroud)
Sta*_*lav 16
您可以将bean注入移动到配置中,如下所示:
@Configuration
public class AppConfig {
@Bean
public MyService getMyService() {
if(windows) return new MyServiceWin();
else return new MyServiceLnx();
}
}
Run Code Online (Sandbox Code Playgroud)
另外,您也可以使用配置文件windows和linux,然后与注释您的服务实现@Profile批注,如@Profile("linux")或@Profile("windows"),并提供此配置文件用于您的应用程序之一.
Jam*_*ENL 12
将您的所有实现自动装配到带有@Qualifier注释的工厂中,然后从工厂返回您需要的服务类。
public class MyService {
private void doStuff();
}
Run Code Online (Sandbox Code Playgroud)
我的 Windows 服务:
@Service("myWindowsService")
public class MyWindowsService implements MyService {
@Override
private void doStuff() {
//Windows specific stuff happens here.
}
}
Run Code Online (Sandbox Code Playgroud)
我的 Mac 服务:
@Service("myMacService")
public class MyMacService implements MyService {
@Override
private void doStuff() {
//Mac specific stuff happens here
}
}
Run Code Online (Sandbox Code Playgroud)
我的工厂:
@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;
}
}
}
Run Code Online (Sandbox Code Playgroud)
如果你想变得非常棘手,你可以使用枚举来存储你的实现类类型,然后使用枚举值来选择你想要返回的实现。
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);
}
}
Run Code Online (Sandbox Code Playgroud)
然后你的工厂可以进入应用程序上下文并将实例拉入它自己的地图。当您添加一个新的服务类时,只需向枚举添加另一个条目,这就是您要做的全部工作。
public class ServiceFactory implements ApplicationContextAware {
private final Map<String, MyService> myServices = new Hashmap<String, MyService>();
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));
}
}
Run Code Online (Sandbox Code Playgroud)
现在,您只需将所需的类类型传递给工厂,它就会为您提供所需的实例。非常有帮助,特别是如果您想让服务通用。
只需将@Service带注释的类设置为有条件即可:
仅此而已。不需要其他显式@Bean方法。
public enum Implementation {
FOO, BAR
}
@Configuration
public class FooCondition implements Condition {
@Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
Implementation implementation = Implementation.valueOf(context.getEnvironment().getProperty("implementation"));
return Implementation.FOO == implementation;
}
}
@Configuration
public class BarCondition implements Condition {
@Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
Implementation implementation = Implementation.valueOf(context.getEnvironment().getProperty("implementation"));
return Implementation.BAR == implementation;
}
}
Run Code Online (Sandbox Code Playgroud)
神奇的事情发生了。条件就在它所属的地方:在实现类中。
@Conditional(FooCondition.class)
@Service
class MyServiceFooImpl implements MyService {
// ...
}
@Conditional(BarCondition.class)
@Service
class MyServiceBarImpl implements MyService {
// ...
}
Run Code Online (Sandbox Code Playgroud)
然后您可以Dependency Injection照常使用,例如Lombokvia@RequiredArgsConstructor或@Autowired。
@Service
@RequiredArgsConstructor
public class MyApp {
private final MyService myService;
// ...
}
Run Code Online (Sandbox Code Playgroud)
将其放入您的 application.yml 中:
implementation: FOO
Run Code Online (Sandbox Code Playgroud)
只有用 注释的实现 FooCondition 才会被实例化。没有幻像实例化。
只是在这个问题上添加我的 2 美分。请注意,不必像其他答案所示那样实现那么多的 java 类。人们可以简单地使用@ConditionalOnProperty。例子:
@Service
@ConditionalOnProperty(
value="property.my.service",
havingValue = "foo",
matchIfMissing = true)
class MyServiceFooImpl implements MyService {
// ...
}
@ConditionalOnProperty(
value="property.my.service",
havingValue = "bar")
class MyServiceBarImpl implements MyService {
// ...
}
Run Code Online (Sandbox Code Playgroud)
将其放入您的 application.yml 中:
property.my.service: foo
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
33478 次 |
| 最近记录: |