Java 无论如何@Inject/@Autowire 将内部类转换为外部类?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/24213823/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-14 10:50:45  来源:igfitidea点击:

Anyway to @Inject/@Autowire an inner class into an outer class?

javaspringdependency-injection

提问by Eric B.

In Spring/JSR-330, is there a way to properly declare an inner class which requires dependency injection, such that I can inject it into the outer class?

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

For example:

例如:

@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();
       ...
    }
}

I've tried annotating the inner class with @Component, making it a public class, making it public static, etc, but it seems that every combination I've tried always ends up throwing one error or another.

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

As a private inner class, Spring complains that it is missing a constructor, even if I define one.

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

As an annotated @Componentpublic static class, Spring complains that it find two beans - TestClass@TestClassInner and testClass.TestClassInner. And if I use a @Qualifier, it then complains that the bean cannot be found.

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

I presume I am misunderstanding how these inner beans work/interact with Spring to properly understand if/how to declare them.

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

Is this even possible?

这甚至可能吗?

Edit

编辑

So here are a few combinations I've tried (including tried implementing a new constructor based on @SotiriosDelimanolis response):

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

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

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

Throws error (both public and private inner class throw the same error):

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

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

I just tried using a static public nested class with my test class (above) and it seems to inject properly. On the other hand, in my actual controller, it discovers 2 matching classes:

我只是尝试在我的测试类(上面)中使用静态公共嵌套类,它似乎正确注入。另一方面,在我的实际控制器中,它发现了 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)

Edit 2

编辑 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:

应用上下文.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:

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>

采纳答案by Sotirios Delimanolis

It is possible to declare and instantiate inner class beans through @Componentannotations, but solution is ugly, but I will get to it later. First, here's how you can do it with <bean>declarations in XML. Given

可以通过@Component注解来声明和实例化内部类bean ,但是解决方案很丑,但我稍后会讲到。首先,这里说明了如何使用<bean>XML 中的声明来实现。给定的

package com.example;

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

you'd have

你会有

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

For inner classes, any constructor implicitly declares its first parameter as an instance of the enclosing type.

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

So

所以

public Inner() {}

above would actually be compiled to

上面实际上会被编译为

public Inner (Example enclosingInstance) {}

With Java code, the argument for that parameter is implicitly provided with the syntax

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

enclosingInstance.new Inner();

Spring uses reflection to instantiate your bean classes and initialize your beans. And the concept described here also applies to reflection. The Constructorused to initialize the Innerclass has to have its first parameter be of the type of the enclosing class. That's what we are doing explicitly here by declaring a constructor-arg.

Spring 使用反射来实例化您的 bean 类并初始化您的 bean。并且这里描述的概念也适用于反射。将Constructor用于初始化Inner类必须具有其第一个参数是封闭类的类型。这就是我们在这里通过声明一个constructor-arg.

The solution for using @Componentdepends on a few things. First, you have to know all the things discussed above. Basically, with a Constructorobject, when you call newInstance(), you need to pass an instance of the enclosing class as the first argument. Second, you have to know how Spring processes the annotation. When the annotated class has a single constructor annotated with @Autowired, that's the constructor it will choose to initialize the bean. It also uses the ApplicationContextto resolve beans to inject as arguments to the constructor.

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

Playing on those two facts, you can write a class like this

根据这两个事实,你可以写一个这样的类

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

    }
}

Here, our inner class has an @Autowiredconstructor, so Spring knows exactly which Constructorobject to use. Because of the @Autowiredit will also try to find a bean from the ApplicationContextto match and inject for every parameter the constructor has. In this case, the only parameter is of type Example, the enclosing class. Since Exampleis also annotated with @Component, it's also a bean in the context, so Spring can inject it into the constructor of the inner class.

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