在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>
答案 0 :(得分:12)
可以通过@Component
注释声明和实例化内部类bean,但解决方案很难看,但我稍后会介绍它。首先,这里是如何使用XML中的<bean>
声明来完成的。给定
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。这里描述的概念也适用于反思。用于初始化Constructor
类的Inner
必须使其第一个参数属于封闭类的类型。这就是我们在此明确宣布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可以将其注入内部类的构造函数中。