在现有项目上实施 Guice

Implementing Guice on existing project

我目前正在尝试在我的 resteasy 解决方案中实施 Guice。 使用静态方法 8 个月后......我不得不改变以实施正确的单元测试。 我已经成功地启动了一小段代码。而且我很了解DI的过程。

我正在进一步寻找通过业务逻辑实现的方法,但我卡住了,我不知道如何使用 Guice 来实现它。

我的应用程序有一个单例名称元模型,用于 Guice 构建的示例并包含元素映射

有我的元模型

@Singleton
public class MetamodelImpl implements Metamodel{
    Map<String, Element> elements;

    public MetamodelImpl(){
        // load data from XML files in order to create my Elements
        // using new Element1() etc... function of the type.
    }

    public Element getElement(String name){
        return elements.get(name);
    }
}

然后,这是我的Element抽象class,Element可以包含其他元素,目的是调用generateResult以生成RestWrapper(json结果,但我们不关心).

public abstract class Element{
    List<Element> elements;

    public RestWrapper generateResult(Context context){
        RestWrapper restWrapper = new RestWrapper();

        for (Element element : elements) {
            restWrapper.add(element.generateResult(context));
        }

        return restWrapper;
    }
}

有实现的例子。

public class Element1{
    public RestWrapper generateResult(Context context){

        RestWrapper restWrapper = super.generateResult(context);

        // Add some custom logic using services
        // old code with static use
        Object object = ServiceExample.getResult(property, context);

        // Wanted, by injecting the service, but I can't inject service on element that I create...

        Object object = serviceExampleImpl.getResult(property);
    }
}

Context 是一个 Guice 对象,包含我的当前用户和一些其他内容。正如您可能看到的,我的问题是关于元素的 generateResult 方法,我必须调用一些现在由 Guice 实现的服务。例如:

public class ServiceExampleImpl implements ServiceExample{
    @Inject
    Context context;

    public Object getResult(String property){
        return ...
    }
}

我该怎么办?如何用 Guice 构建元素?并使上下文动态...

这是你需要的。

@Singleton
public class MetamodelImpl implements Metamodel{
    Map<String, Element> elements;

    @Inject
    public MetamodelImpl(Injector injector){
        Element element = new Element1("Initialize-Me");
        injector.injectMembers(element);
        elements = new HashMap<String, Element>();
        elements.put("Initialize-Me", element);
    }

    public Element getElement(String name){
        return elements.get(name);
    }
}

public class Element1 extends Element {
    @Inject
    private ServiceExample serviceExampleImpl;

    private final String property;

    public Element1(String property) {
        this.property = property;
    }
    @Override
    public RestWrapper generateResult(Context context){

        RestWrapper restWrapper = super.generateResult(context);

        // Wanted, by injecting the service, but I can't inject service on element that I create...

        Object object = serviceExampleImpl.getResult(property);
        //do something with object

        return restWrapper;
    }
}

您初始化 MetamodelImpl 并使用您需要的构造函数 "new" 创建所有元素。 MetamodelImpl 获得单一依赖项,即注入到构造函数中的 Guice 注入器。使用 "new" 创建元素后,调用它们方法 injector.injectMembers(...),将新创建的元素作为参数传递。这将导致 Guice 设置 member

private ServiceExample serviceExampleImpl; 

因为现在用@Inject 注释了。这意味着一旦您进入 generateResult 方法,服务现在就已正确设置,可以使用了。

已编辑 - 关于@Assisted 注射的附加信息

辅助注入允许您混合创建组件,其中一些值是注入的,而其他值仅在运行时才知道。要使用它,你需要创建一个未实现的工厂接口,它有一个方法:"create",它的return类型是你想要注入的混合组件的类型(有注入的,有你指定的在运行时)并且其参数正是用@Assisted 注释的依赖项。在我下面的示例中,IElement1Factory 是这个未实现的接口。 Element1 只有 1 个用 @Assisted 注释的参数,因此工厂中的方法只有 1 个参数。使用辅助注入的优点之一是您创建的组件可以被拦截,如果您需要的话。在使用 "new" 创建的对象上使用 setter 或字段注入不会被子class 编辑或代理以进行拦截。当您使用 guice 辅助注入创建它时,guice 将子class 它,包装适当的拦截器,如果您需要的话。

要使用辅助注射,您需要以下 jar。这是 Maven 依赖项。我的代码使用 4.0 版进行了测试。

<dependency>
    <groupId>com.google.inject.extensions</groupId>
    <artifactId>guice-assistedinject</artifactId>
</dependency>

在您的一个 Guice 模块中,我添加了以下绑定规则。

install(new FactoryModuleBuilder().build(IElement1Factory.class));

连同界面:

public interface IElement1Factory {
    Element1 create(String property);
}

现在,我的 MetamodelImpl 看起来像这样:

@Singleton
public class MetamodelImpl implements Metamodel{
    Map<String, Element> elements;

    @Inject
    public MetamodelImpl(IElement1Factory element1Factory){
        Element element = element1Factory.create("Initialize-Me");
        elements = new HashMap<String, Element>();
        elements.put("Initialize-Me", element);
    }

    public Element getElement(String name){
        return elements.get(name);
    }
}

以及调用它的 Main 方法:

public static void main(String ... params) {
    Injector injector = Guice.createInjector(new AbstractModule() {
        @Override
        protected void configure() {
            install(new FactoryModuleBuilder().build(IElement1Factory.class));
        }
    });

    MetamodelImpl model = injector.getInstance(MetamodelImpl.class);
    Element element = model.getElement("Initialize-Me");
    System.out.println(element);
}

产生输出:

Whosebug.guice.Element1@487a1576