【问题标题】:Inject anonymous classes with spring用spring注入匿名类
【发布时间】:2013-07-29 11:32:47
【问题描述】:

我已经阅读了很多关于 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;
    }
}

现在使用之前的类层次结构,当且仅当我使用匿名类实例化 Generic&lt;Anything&gt; 时,我才能拥有一个有效的 getGenericType 方法。事实上,在这个测试中,只有前两个断言有效:

@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: &lt;class java.lang.String&gt; but: was &lt;class java.lang.Object&gt;

现在我想使用带有 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&lt;String&gt;() 实例化它

我已经尝试让 GenericImpl&lt;T&gt; 的构造函数受保护,并声明 GenericImpl&lt;String&gt; 本身是抽象的,但在这两种情况下,spring 都会失败并出现无法实例化 bean 异常。

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

其他细节

最后一个类将使用 Jackson 将 json 流转换为 POJO,并且 Jackson 库需要 Class&lt;T&gt; 字段来解组对象。

// 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&lt;T&gt; 参数,稍后使用它来执行转换。在春季环境中,我有这个用于自动装配

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

【问题讨论】:

  • 请问您为什么需要获取类型以及您打算如何使用它?如果您描述实际问题,也许有人会想出一个不同于弄乱类型的解决方案
  • 您使用的是 java @Configuration 还是 XML 上下文?
  • @c.s.我已经添加了我的用例。
  • @AndreyBalaguta 目前我正在使用 XML,我希望保留它。
  • 不要认为能够使用 Spring 就地创建和实例化匿名类会很好地消除这种冗长。即使使用匿名类,您也必须传递具体类型参数,对吗?那么为什么不将它作为构造函数参数传递呢。

标签: java spring instantiation anonymous-class


【解决方案1】:

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

【讨论】:

  • 谢谢。 @Configuration 类可以吗?或者如果没有@Qualifier,spring 能够更好地区分 Retriever&lt;Artifact&gt;Retriever&lt;OtherClass&gt;
【解决方案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&lt;T&gt; 构造函数,事实上我已经进行了自动装配(没有@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,也许春天有类似的东西。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-08-27
    • 2021-01-07
    • 1970-01-01
    • 2021-10-27
    • 1970-01-01
    相关资源
    最近更新 更多