2

我已经阅读了很多关于在运行时获取泛型类型的内容,并且我明白为了防止完全类型擦除并在不将其提供给构造函数的情况下获取泛型类型,我可以使用匿名类和实用方法,即

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>使用匿名类实例化 a 时,我才能拥有一个工作方法。事实上,在这个测试中,只有前两个断言有效:

@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),因为弹簧容器在内部使用new GenericImpl<String>()

我已经尝试过创建GenericImpl<T>受保护的构造函数并声明GenericImpl<String>自己是抽象的,但在这两种情况下,spring 都会失败并出现无法实例化 bean 异常。

有什么简单的方法可以告诉 spring 使用匿名类来实例化类吗?

额外细节

最后一个类将使用 Jackson 将 json 流转换为 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 配备一个 Retriever,并且这些检索器通常会自动装配到其他类中。

@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,我都需要其中一个。这种方法有效,但有点冗长,并且用无用的行使应用程序上下文混乱。所以我一直在寻找一种方法来消除应用程序上下文中的所有这些噪音。

4

2 回答 2

2

好的,我的用例的最终解决方案将使用此答案中描述的方法。这会更好,因为它可以跟踪使用情况,并且我将摆脱当前方法遇到的所有问题。

这样,我可以执行以下操作

@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 支持通用注入,也许 spring 有类似的东西。

于 2013-07-29T13:42:54.317 回答
2

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

于 2013-07-29T13:11:55.427 回答