设置来自XML文件的绑定

设置来自XML文件的绑定

本文介绍了Guice:设置来自XML文件的绑定的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试使用Guice并借助XML文件进行所有绑定.在我的模块中(假设为"CustomModule"),我想加载一个XML文件并解析它以设置所有绑定.

I'm trying to use Guice and make all the bindings with the help of an XML file. In my module (let's say "CustomModule"), I would like to load an XML file and parse it to set all the bindings.

我能够加载XML文件并检索所有需要的值(以下是我的XML文件的一个示例),但是我无法将这些值用于bind(interfaceValue).to(implementationValue);.

I'm able to load the XML file and retrieve all the values needed (below is an exemple of my XML file), but I'm unable to use those values to bind(interfaceValue).to(implementationValue);.

到目前为止我已经尝试过的:

What I've tried so far:

  1. 加载XML文件,检索所有值并将其用作:bind(Class.fromName(Ivalue)).to(Class.fromName(Value));,其中IvalueInterfaceFooValueFoo.
  2. 将XML文件作为属性文件加载并使用Names.bindProperties(binder(), properties);.
  3. 手动绑定,这不是我想要的.
  1. Load the XML file, retrieve all the values and use them as :bind(Class.fromName(Ivalue)).to(Class.fromName(Value)); where Ivalue is InterfaceFoo and Value is Foo.
  2. Load the XML file as a Properties file and use Names.bindProperties(binder(), properties);.
  3. Bind manually, which is not what I want.

结果:

  1. 不起作用,因为Guice无法验证实现是否为接口的实现.
  2. 给出错误No implementation for interface was bound.
  3. 可以,但是不需要,因为我必须编辑CustomModule来更改绑定(在这种情况下,如果我希望BarInterfaceFoo的实现).
  1. Doesn't work, because Guice cannot verify if the implementation is an implementation of the interface.
  2. Gives an error No implementation for interface was bound.
  3. Works, but it is not wanted since I have to edit my CustomModule to change the bindings (in the case if I want Bar to be the implementation of InterfaceFoo).

我已经看过了,但没有取得太大的成功因为没有太多的文档.我也在这里寻求关于SO的解决方案,但是大多数时候,问题都与属性或注释的使用有关.

I've looked at this, but with not so much success since there is not very much documentation on it. I also looked for a solution here on SO, but most of the time the questions are about properties or the use of annotation.

是否有一种简单的方法可以在文件中指定接口/实现并将其作为配置"提供给Guice?

Is there a simple way to specify the Interfaces / Implementations in a file and give it to Guice as a "configuration"?

<bindings>
  <binding>
    <interface>interfaces.IReaderService</interface>
    <implementation>implementation.MemsReaderService</implementation>
  </binding>
  <binding>
    <interface>interfaces.IReportService </interface>
    <implementation>implementation.PdfReportService</implementation>
  </binding>
  <binding>
    <interface>interfaces.ISerializerService </interface>
    <implementation>implementation.JsonSerializerService</implementation>
  </binding>
  <binding>
    <interface>interfaces.ILoggerService </interface>
    <implementation>implementation.LoggerService</implementation>
  </binding>
</bindings>

CustomModule.java:

public class GuiceModule extends AbstractModule{

    private HashMap<String, String> classNames = new HashMap<String, String>();

    public GuiceModule(){
    }

    @Override
    protected void configure() {
        /* === Test 1 [NOK : Module doesn't know if A implements B] */
        for(Entry<String, String> entry : classNames.entrySet()){
            try {
                Class<?> itf = Class.forName(entry.getKey());
                Class<?> concrete = Class.forName(entry.getValue());
                bind(itf).to(concrete);
            } catch (ClassNotFoundException ex) {
                Logger.getLogger(GuiceModule.class.getName()).log(Level.SEVERE, null, ex);
            }
        }
        /* === Test 2 [NOK : Not bound] */
        try{
            File file = new File(getClass().getResource("guiceBindings.xml").toURI());
            Properties properties = new Properties();
            properties.load(new FileReader(file));
            Names.bindProperties(binder(), properties);
        } catch (Exception ex) {
            Logger.getLogger(GuiceModule.class.getName()).log(Level.SEVERE, null, ex);
        }
        /* === Test 3 [OK : Manual edition] */
        bind(IReaderService.class).to(MemsReaderService.class);
        bind(IReportService.class).to(PdfReportService.class);
        bind(ISerializerService.class).to(JsonSerializerService.class);
        bind(ILoggerService.class).to(LoggerService.class);
    }
}

ServiceProvider.java:

public class ServiceProvider {
    // declaration of the services available [FOR NOW]
    @Inject IReaderService iReaderService;
    @Inject IReportService iReportService;
    @Inject ISerializerService iSerializerService;
    @Inject ILoggerService iLoggerService;

    public ServiceProvider(){
    }

    // getters of the services injected
    public IReaderService getReaderService() {
        return iReaderService;
    }

    public IReportService getReportService() {
        return iReportService;
    }

    public ISerializerService getSerializerService() {
        return iSerializerService;
    }

    public ILoggerService getLoggerService() {
        return iLoggerService;
    }
}

推荐答案

Guice并非为此专门设计的.

这个想法是,通过在类中进行操作,您可以获得在类/@Provides方法,Provider<T>实现,AOP等中执行此操作的所有功能和灵活性.正如您所观察到的,它确实具有 Named.bindProperties ,但这不是您所需要的出于您陈述的原因而试图这样做.

Guice is not really designed for this.

The idea is that by doing it in classes, you get all the power and flexibility of being able to do it in a class / @Provides methods, Provider<T> implementations, AOP, and so forth. It does have Named.bindProperties, as you've observed, but this is not what you're trying to do for the reasons you've stated.

但是,如果您愿意使用原始类型,则实际上可以执行方法1,然后自己检查类.这不是最干净的代码,但是请注意,您的问题是Class<?>中的通用键入,而不是Guice.这是一个示例,其中注释掉的伪代码指出了使此代码在生产中工作所需进行的更改.我认为,如果您已经走了这么远,那么您可以自己弄清楚.这是说明此想法的代码:

However, you can actually do method #1 if you're willing to use raw types, and then check the classes yourself. It's not the cleanest code, but note that your problem is the generic typing in Class<?>, not Guice. Here's an example, with commented out pseudocode pointing out the changes you'd need to make to make this code work in production. I figure if you've gotten this far you can figure that out yourself though. Here's the code illustrating the idea:

import com.google.inject.AbstractModule;
import com.google.inject.Guice;
import com.google.inject.Injector;

public class DynamicBinding {
  static final String interfaceExample = "DynamicBinding$Foo";
  static final String implementationExample = "DynamicBinding$FooBar";

  public static void main(String... args) throws ClassNotFoundException {
    Injector inj = Guice.createInjector(new CustomModule());
    Foo blue = inj.getInstance(Foo.class);
    blue.doSomething();
  }

  static class CustomModule extends AbstractModule {

    @Override
    protected void configure() {
      // for (Entry<interface, implementation> : xml file) {
      bindFromStrings(interfaceExample, implementationExample);
      // }
    }

    private void bindFromStrings(String interfaceClass, String implClass) {
      try {
        Class fooClass = Class.forName(interfaceClass);
        // I recommend defining a custom exception here with a better message
        if (!fooClass.isInterface()) {
          throw new Exception("fooClass must be an interface!");
        }

        Class fooBarClass = Class.forName(implClass);
        // I recommend defining a custom exception here with a better message
        if (!fooClass.isAssignableFrom(fooBarClass)) {
          throw new Exception("classes must be in same inheritance hierarchy");
        }

        bind(fooClass).to(fooBarClass);
      } catch (Exception e) {
        // Logger.getLogger().log(blah);
        e.printStackTrace();
      }
    }
  }

  public static interface Foo {
    void doSomething();
  }

  public static class FooBar implements Foo {
    @Override
    public void doSomething() {
      System.out.println(this.getClass());
    }
  }
}

输出:

class DynamicBinding$FooBar

这篇关于Guice:设置来自XML文件的绑定的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

07-24 04:34