用spring注入匿名类

时间:2013-07-29 11:32:48

标签: java spring instantiation anonymous-class

我已经阅读了很多关于getting generic type at runtime的内容,并且我已经明白为了防止完全类型擦除并获得泛型类型而不将其赋予构造函数我可以使用匿名类加上实用方法,即

interface Generic<T> {
    public Class<T> getGenericType();
}

@Component
class GenericImpl<T> extends AbstractGenericImpl<T> {

}

abstract class AbstractGenericImpl<T> implements Generic<T> {

    protected Class<T> klass;

    @SuppressWarnings("unchecked")
    public Class<T> getGenericType() {
        if (klass == null) {
            // this is a spring utility method
            klass = (Class<T>) GenericTypeResolver.resolveTypeArgument(getClass(), AbstractGenericImpl.class);
        }
        return klass;
    }
}

现在使用以前的类层次结构,当且仅当我使用匿名类实例化getGenericType时,才能使用Generic<Anything>方法。事实上,在这个测试中,只有前两个断言正在起作用:

@Test
public void testGeneric() throws Exception {
    Generic<String> anonymous = new AbstractGenericImpl<String>() {};
    Generic<String> anonymous2 = new GenericImpl<String>() {};
    Generic<String> concrete = new GenericImpl<String>();
    // assertion
    assertThat("Anonymous of abstract class", anonymous.getGenericType(), equalTo(String.class));
    assertThat("Anonymous of concrete subclass", anonymous2.getGenericType(), equalTo(String.class));
    assertThat("With non anonymous class it fails", concrete.getGenericType(), equalTo(String.class));
}

第三个失败的是Expected: <class java.lang.String> but: was <class java.lang.Object>

现在我想使用带有spring @Autowired注释的Generic类,即

@Autowired Generic<String> auto;

@Test
public void testAutowiring() {
    assertThat(auto, instanceOf(Generic.class));
    assertThat(auto.getGenericType(), equalTo(String.class));
}

但是第二个断言失败并出现与上面相同的错误(Object而不是String),因为spring容器在内部用new GenericImpl<String>()

实例化它

我已经尝试使GenericImpl<T>的构造函数受到保护,并且还声明GenericImpl<String>本身是抽象的,但在这两种情况下,spring都会失败并且无法实例化bean异常。

有没有简单的方法告诉spring使用匿名类实例化类?

其他细节

最终的类将json流转换为与Jackson的POJO,而Jackson库需要Class<T>字段来解组对象。

// here I convert json stream to a POJO and I need the generic type
mapper.readValue(hit.source(), getGenericType());

由于我有多个POJO类要转换为JSON,因此我在一个公共类中使用名为Retriever的泛型实现了所有逻辑。最后,我将为每个POJO提供一个检索器,并且通常这些检索器在其他类中自动装配。

@Autowired Retriever<Artifact> retriever;

目前我在Retriever中有一个构造函数,它接受Class<T>参数并稍后使用它来执行转换。在春天的背景下,我将其用于自动装配

<!-- Since retriever has a Class<T> constructor this is the only way I found to resolve its dependency -->
<bean id="artifactRetriever" class="a.b.c.RetrieverImpl">
    <constructor-arg value="a.b.c.Artifact"/>
</bean>

我需要转换的每个POJO都需要其中一个。这种方法有效,但它有点冗长,它使应用程序上下文与无用的行混乱。所以我一直在寻找一种方法来摆脱应用程序环境中的所有噪音。

2 个答案:

答案 0 :(得分:2)

使用Spring就地创建和实例化匿名类是不可能的,而不是使用XML配置(因为它需要类名,而你没有类名)。

答案 1 :(得分:2)

好的,我的用例的最终解决方案将使用this answer中描述的方法。它会更好,因为它可以跟踪使用情况,我将摆脱目前的方法所遇到的每一个问题。

通过这种方式我可以做到以下

@Component
public class ArtifactImpl extends AbstractGenericImpl<Artifact> {
}

@Component
public class MaterialImpl extends AbstractGenericImpl<Material> {
}

@Component
class Usage {
  @Autowired ArtifactImpl foo;
  @Autowired MaterialImpl bar;
}

这样一切都在编译时检查,我摆脱了Class<T>构造函数实际上我已经自动装配(没有@Qualifier)并且以下测试正在运行:

@RunWith(SpringJUnit4ClassRunner.class)
public class AutowiringTest {
  @Autowired Usage test;

  public void testAutowiring() {
    assertThat(test.foo.getGenericType(), equalTo(Artifact.class));
    assertThat(test.bar.getGenericType(), equalTo(Material.class));
  }
}

原始答案

好吧,我发现我所要求的内容将毫无用处,因为自动装配在运行时发生,因此有两个带有不同对象的自动装配对象会导致弹簧错误,即这不起作用:

@Configuration
class RetrieverProvider {
    @Bean 
    Retriever<Artifact> getArtifact() {
        return new RetrieverImpl<Artifact>() {};
    }

    @Bean 
    Retriever<Material> getMaterial() {
        return new RetrieverImpl<Material>() {};
    }
}

class InjectedAttempt {
    // at injection time, i.e. runtime, type erasure prevent spring to distinguish
    @Autowired Retriever<Artifact> foo; // this type
    @Autowired Retriever<Material> bar; // from this type
    // so it cannot perform injection by type
}

实现这一目标的唯一方法是以这种方式使用限定符,但我不喜欢这种方法,所以我将继续使用xml配置和构造函数参数。

@Configuration
class RetrieverProvider {

    @Bean @Qualifier("artifact") Retriever<Artifact> getArtifact() {
        return new RetrieverImpl<Artifact>() {};
    }

    @Bean @Qualifier("material")
    Retriever<Material> getMaterial() {
        return new RetrieverImpl<Material>() {};
    }


}

class Injected {

    @Autowired @Qualifier("artifact") Retriever<Artifact> foo;

    @Autowired @Qualifier("material") Retriever<Material> bar;
}

作为旁注guice has support for generic injections,也许春天有类似的东西。