【问题标题】:Anyway to @Inject/@Autowire an inner class into an outer class?无论如何@Inject/@Autowire 内部类到外部类?
【发布时间】:2014-06-13 21:07:29
【问题描述】:

在 Spring/JSR-330 中,有没有办法正确声明需要依赖注入的内部类,以便我可以将其注入到外部类中?

例如:

@Component
public class TestClass{

    // How to declare this class?
    private class TestClassInner{
       @Autowired private SomeBean somebean;

       public boolean doSomeWork(){
          return somebean.doSomething();
       }               
    }

    // Inject the inner class here in the outer class such that the outer class can use an instance of it
    @Autowired TestClassInner innerClass;

    @PostConstruct
    public void init(){
        ...
    }

    public void someMethod(){
       innerClass.doSomeWork();
       ...
    }
}

我尝试使用@Component 注释内部类,使其成为公共类,使其成为公共静态等,但似乎我尝试过的每种组合最终都会引发一个或另一个错误。

作为私有内部类,Spring 抱怨它缺少一个构造函数,即使我定义了一个。

作为带注释的@Component 公共静态类,Spring 抱怨它找到了两个 bean - TestClass@TestClassInner 和 testClass.TestClassInner。如果我使用 @Qualifier,它会抱怨找不到 bean。

我想我误解了这些内部 bean 如何工作/与 Spring 交互以正确理解是否/如何声明它们。

这可能吗?

编辑

以下是我尝试过的一些组合(包括尝试实现基于@SotiriosDelimanolis 响应的新构造函数):

    // How to declare this class?
@Component
public class TestClassInner{
    @Autowired private ProviderService providerService;

    public TestClassInner(){
        super();
    }
    public TestClassInner( TestClass t){
        super();
    }
}

抛出错误(公共和私有内部类都抛出相同的错误):

Caused by: org.springframework.beans.BeanInstantiationException: Could not instantiate bean class [com.ia.exception.TestClass$TestClassInner]: No default constructor found; nested exception is java.lang.NoSuchMethodException: com.ia.exception.TestClass$TestClassInner.<init>()
    at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:83)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateBean(AbstractAutowireCapableBeanFactory.java:1030)
    ... 54 more

我刚刚尝试在我的测试类(上图)中使用静态公共嵌套类,它似乎可以正确注入。另一方面,在我的实际控制器中,它发现了 2 个匹配的类:

Caused by: org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type [com.ia.web.ContractController$InnerClass] is defined: expected single matching bean but found 2: com.ia.web.ContractController$InnerClass,contractController.InnerClass
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:865)

编辑 2

@Controller
public class ContractController {

    @Component
    static public class InnerClass extends AttachmentControllerSupport{

        /**
         * 
         */
        public InnerClass() {
            super();
            // TODO Auto-generated constructor stub
        }

        public InnerClass( ContractController c){
            super();
        }
    }

    @Autowired private InnerClass innerclass;

    @Autowired private AttachmentControllerSupport attachmentControllerSupport;
    @Autowired private ContractService contractService;

}

applicationContext.xml:

<context:component-scan base-package="com.ia">
    <context:exclude-filter expression=".*_Roo_.*" type="regex"/>
    <context:exclude-filter expression="org.springframework.stereotype.Controller" type="annotation"/>
</context:component-scan>
<context:spring-configured/>

restmvc-config.xml:

<mvc:annotation-driven conversion-service="applicationConversionService" >
  <mvc:argument-resolvers>
    <bean class="org.springframework.security.web.bind.support.AuthenticationPrincipalArgumentResolver" />
  </mvc:argument-resolvers>
</mvc:annotation-driven>

【问题讨论】:

  • 对于初学者来说,您不正确的术语会混淆一个本身就足够复杂的问题。在 Java 中,“内部类”具体来说是一个非静态嵌套类;嵌套类可以是内部的或静态的。
  • @chrylis 我实际上并没有意识到这一点。感谢您的澄清。我在这里使用术语“内部类”来指代嵌套类。我会考虑到这一点来尝试编辑帖子

标签: java spring dependency-injection


【解决方案1】:

可以通过@Component注解来声明和实例化内部类bean,但是解决方案很丑,但我稍后会讲到。首先,这里介绍如何使用 XML 中的 &lt;bean&gt; 声明来做到这一点。给定

package com.example;

public class Example {
    @Autowired
    private Inner inner;
    public class Inner {        
    }
}

你应该有

<bean name="ex" class="com.example.Example" />
<bean name="inner" class="com.example.Example$Inner">
    <constructor-arg ref="ex"></constructor-arg>
</bean>

对于内部类,任何构造函数都将其第一个参数隐式声明为封闭类型的实例。

所以

public Inner() {}

上面实际上会被编译为

public Inner (Example enclosingInstance) {}

对于 Java 代码,该参数的实参通过语法隐式提供

enclosingInstance.new Inner();

Spring 使用反射来实例化您的 bean 类并初始化您的 bean。而且这里描述的概念也适用于反射。用于初始化Inner 类的Constructor 的第一个参数必须是封闭类的类型。这就是我们在此处通过声明 constructor-arg 来明确执行的操作。

使用@Component 的解决方案取决于几件事。首先,您必须了解上面讨论的所有内容。基本上,对于Constructor 对象,当您调用newInstance() 时,您需要传递封闭类的一个实例作为第一个参数。其次,你必须知道 Spring 是如何处理注解的。当带注释的类有一个用@Autowired 注释的构造函数时,它将选择初始化bean 的构造函数。它还使用ApplicationContext 解析要作为参数注入构造函数的bean。

利用这两个事实,您可以编写这样的类

@Component
public class Example {
    @Component
    public class Inner {
        @Autowired
        public Inner() {}

    }
}

这里,我们的内部类有一个@Autowired 构造函数,所以Spring 确切地知道要使用哪个Constructor 对象。由于@Autowired,它还将尝试从ApplicationContext 中找到一个bean,以匹配并注入构造函数具有的每个参数。在这种情况下,唯一的参数是Example 类型,即封闭类。由于Example也是注解@Component,所以它也是上下文中的一个bean,所以Spring可以将其注入到内部类的构造函数中。

【讨论】:

  • 答案看起来很有趣。
  • 我肯定知道@Configuration 是可能的,如果这是特定于刻板印象的,我会感到惊讶。
  • @chrylis @chrylis @Configuration 你的意思是用@Bean 方法显式地创建一个内部类实例?
  • 对不起,我含糊其辞。我的意思是我经常使用带有@Configuration 注释的嵌套(但静态,而不是内部)类,完全没有问题。
  • @chrylis 这就是嵌套(静态)与内部的全部区别。嵌套类只是普通类。内部类具有对封闭实例的引用。
猜你喜欢
  • 1970-01-01
  • 2011-01-02
  • 1970-01-01
  • 2023-01-24
  • 2013-04-26
  • 2014-09-04
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多