如何指示 Spring 自动装配紧耦合对象链中的字段
How to instruct Spring to Autowire a field in a chain of tightly coupled objects
有点难以解释...希望问题不含糊...
你可以看看代码来了解一下...
ClassA.java
public class ClassA {
@Autowired
InterA abcd;
public void dododo() {
abcd.doit();
}
}
ClassB.java
@Component
public class ClassB implements InterA {
@Override
public void doit() {
System.out.println("hoo hoo");
}
}
ClassC.java
@Component("classc")
public class ClassC {
public void doFromAbove() {
ClassA cls = new ClassA();
cls.dododo();
}
}
界面InterA.java
public interface InterA {
public void doit();
}
配置ClassConfig.java(在其他javaclass文件的同一个包上)
@Configuration
@ComponentScan
public class ClassConfig {
}
主要方法
public static void main(String[] args) {
try(AbstractApplicationContext appctx = new AnnotationConfigApplicationContext(ClassConfig.class)) {
ClassC obj = (ClassC) appctx.getBean("classc");
obj.doFromAbove();
}
}
当我执行 main 方法时,ClassA
中的 Autowired 字段 "abcd" 没有被注入并导致 NullPointerException
只有当我将 ClassA
声明为 @Component
并获取它的 bean 时它才有效...间接自动装配没有发生
我是否应该将 ClassA
与 ClassC
分离并使所有内容松散耦合?
是否有任何简单的注解可用于告诉 Spring 自动注入 @Autowired 字段,即使对象是以紧密耦合的方式创建的?
备注
请不要告诉我在 ClassC
中使用 ApplicationContext 来创建 ClassA
的 bean。
哪位Spring极客能找到答案?
问题在ClassC
:
ClassA cls = new ClassA();
如果你像这样调用 ClassA
的构造函数,Spring 将不会发挥它的魔力。如果您需要带有注入字段的 ClassA
实例,请向 Spring 索要实例(使用注入或 getBean()
)。
(为避免 null
假定注入的字段,我建议使用构造函数注入。)
在 Spring 容器中声明的 beans(通过 XML 或像 @Component
这样的注解)是 Spring-managed - Spring 会处理它们,当您通过 ApplicationContext.getBean()
请求它们时会确保它们存在,并且它们的依赖项也会被注入。
当您自己创建实例 (cls = new ClassA()
) 时,该实例不受 Spring 管理,因此 Spring 不会对其执行任何操作。事实上,Spring 甚至不会(也不能)知道对象的存在。
一些混淆可能源于您使用 Spring 注释对 class 进行注释 - 但它实际上是 objects(实例)在Java中实际使用;即使 class 被注解,注解也只会应用于由 Spring.
创建和管理的实例
如果启用加载时编织,则可以使用 @Configurable
,这将使对象的每个新实例成为托管 spring 组件,因此您使用的新语句将起作用。
除此之外,您可以创建一个原型范围的 bean 定义和一个引用该 bean 的工厂 bean,这意味着它每次都会给您一个新的 bean,因此您可以注入工厂并只调用 get 方法新实例。
经过大量谷歌搜索,Spring 文档略读,我相信有更多可能的解决方案来解决这个难题...
可能的解决方案:
- 将 JSR 330
Provider<T>
与 @Autowired
结合使用
- 使用
FactoryBean<T>
和 getObject()
中的初始化代码(但是工厂 returned 的 bean 不是 spring 管理的,因此原型中的任何自动装配字段 class 将 return NullPointerException)
- 使用查找方法注入(包括CGLIB库)(我不喜欢
这个,因为它修改了编译后的代码,听起来像是在创建 bean 对象
摘要 classes)(Java 的纯度被违反)
- 实现
ApplicationContextAware
接口并获取上下文
对象(不推荐)
- 自动装配
ApplicationContext
并使用 getBean()
(不推荐)
上述方法中最微妙的方法是 JSR330 Provider
A类
@Component("classa")
@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public class ClassA implements InterB {
private static int counter=0;
private int objectid = 0;
@Autowired
InterA abcd;
public ClassA() {
super();
this.objectid = ++counter;
}
@Override
public void dododo() {
System.out.println("instance number "+objectid++);
abcd.doit();
}
}
B级
@Component
public class ClassB implements InterA {
@Override
public void doit() {
System.out.println("hoo hoo");
}
}
C类
@Component("classc")
public class ClassC {
@Autowired
Provider<InterB> classAPrototypeobj;
public void doFromAbove() {
//you can do a for loop here and get a set of objects for use
InterB cls = (InterB) classAPrototypeobj.get();
InterB cls1 = (InterB) classAPrototypeobj.get();
cls.dododo();
cls1.dododo();
System.out.println(cls);
System.out.println(cls1);
}
}
现在它可以完美地工作并且初始化的对象也被 spring 管理...
注:
必须在 maven pom.xml
中设置 JSR330 依赖项
<dependency>
<groupId>javax.inject</groupId>
<artifactId>javax.inject</artifactId>
<version>1</version>
</dependency>
为什么不使用@Lookup 注解?根据已接受的答案,我假设您每次 ClassC
.
中都需要一个 ClassA
的新实例
@Component("classA")
@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public class ClassA {
@Autowired
InterA abcd;
private ObjectA objA;
private ObjectB objB;
public ClassA(objA, objB) {
this.objA = objA;
this.objB = objB;
}
public void dododo() {
abcd.doit();
}
}
@Component("classc")
public class ClassC {
public void doFromAbove() {
ClassA cls = getNewInstanceOfClassA(objA, objB);
cls.dododo();
}
@Lookup("classA")
private getNewInstanceOfClassA(ObjectA objA, ObjectB objB) {
//Spring creates a runtime implementation of this method
return null;
}
}
其余 class 实现保持不变。我在实现中包含了 objA 和 objB,以便清楚地了解注入构造函数参数。
所以main方法获取classC的一个springbean,调用doFromAbove()
方法。这依次调用 getNewInstanceOfClassA
方法,该方法 returns 类型为 classA 的 spring bean 具有构造函数参数 objA、objB。由于我们将其注释为原型 bean,因此每次调用此方法时我们都会得到一个新的 classA 实例。您不需要实施 getNewInstanceOfClassA
方法。 Spring 在运行时添加它自己的代码。
从本质上讲,您的问题归结为在单例 bean 中注入原型 bean。查找注释是解决该问题的最佳方法。
有点难以解释...希望问题不含糊...
你可以看看代码来了解一下...
ClassA.java
public class ClassA {
@Autowired
InterA abcd;
public void dododo() {
abcd.doit();
}
}
ClassB.java
@Component
public class ClassB implements InterA {
@Override
public void doit() {
System.out.println("hoo hoo");
}
}
ClassC.java
@Component("classc")
public class ClassC {
public void doFromAbove() {
ClassA cls = new ClassA();
cls.dododo();
}
}
界面InterA.java
public interface InterA {
public void doit();
}
配置ClassConfig.java(在其他javaclass文件的同一个包上)
@Configuration
@ComponentScan
public class ClassConfig {
}
主要方法
public static void main(String[] args) {
try(AbstractApplicationContext appctx = new AnnotationConfigApplicationContext(ClassConfig.class)) {
ClassC obj = (ClassC) appctx.getBean("classc");
obj.doFromAbove();
}
}
当我执行 main 方法时,ClassA
中的 Autowired 字段 "abcd" 没有被注入并导致 NullPointerException
只有当我将 ClassA
声明为 @Component
并获取它的 bean 时它才有效...间接自动装配没有发生
我是否应该将 ClassA
与 ClassC
分离并使所有内容松散耦合?
是否有任何简单的注解可用于告诉 Spring 自动注入 @Autowired 字段,即使对象是以紧密耦合的方式创建的?
备注
请不要告诉我在 ClassC
中使用 ApplicationContext 来创建 ClassA
的 bean。
哪位Spring极客能找到答案?
问题在ClassC
:
ClassA cls = new ClassA();
如果你像这样调用 ClassA
的构造函数,Spring 将不会发挥它的魔力。如果您需要带有注入字段的 ClassA
实例,请向 Spring 索要实例(使用注入或 getBean()
)。
(为避免 null
假定注入的字段,我建议使用构造函数注入。)
在 Spring 容器中声明的 beans(通过 XML 或像 @Component
这样的注解)是 Spring-managed - Spring 会处理它们,当您通过 ApplicationContext.getBean()
请求它们时会确保它们存在,并且它们的依赖项也会被注入。
当您自己创建实例 (cls = new ClassA()
) 时,该实例不受 Spring 管理,因此 Spring 不会对其执行任何操作。事实上,Spring 甚至不会(也不能)知道对象的存在。
一些混淆可能源于您使用 Spring 注释对 class 进行注释 - 但它实际上是 objects(实例)在Java中实际使用;即使 class 被注解,注解也只会应用于由 Spring.
创建和管理的实例如果启用加载时编织,则可以使用 @Configurable
,这将使对象的每个新实例成为托管 spring 组件,因此您使用的新语句将起作用。
除此之外,您可以创建一个原型范围的 bean 定义和一个引用该 bean 的工厂 bean,这意味着它每次都会给您一个新的 bean,因此您可以注入工厂并只调用 get 方法新实例。
经过大量谷歌搜索,Spring 文档略读,我相信有更多可能的解决方案来解决这个难题...
可能的解决方案:
- 将 JSR 330
Provider<T>
与@Autowired
结合使用
- 使用
FactoryBean<T>
和getObject()
中的初始化代码(但是工厂 returned 的 bean 不是 spring 管理的,因此原型中的任何自动装配字段 class 将 return NullPointerException) - 使用查找方法注入(包括CGLIB库)(我不喜欢 这个,因为它修改了编译后的代码,听起来像是在创建 bean 对象 摘要 classes)(Java 的纯度被违反)
- 实现
ApplicationContextAware
接口并获取上下文 对象(不推荐) - 自动装配
ApplicationContext
并使用getBean()
(不推荐)
上述方法中最微妙的方法是 JSR330 Provider
A类
@Component("classa")
@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public class ClassA implements InterB {
private static int counter=0;
private int objectid = 0;
@Autowired
InterA abcd;
public ClassA() {
super();
this.objectid = ++counter;
}
@Override
public void dododo() {
System.out.println("instance number "+objectid++);
abcd.doit();
}
}
B级
@Component
public class ClassB implements InterA {
@Override
public void doit() {
System.out.println("hoo hoo");
}
}
C类
@Component("classc")
public class ClassC {
@Autowired
Provider<InterB> classAPrototypeobj;
public void doFromAbove() {
//you can do a for loop here and get a set of objects for use
InterB cls = (InterB) classAPrototypeobj.get();
InterB cls1 = (InterB) classAPrototypeobj.get();
cls.dododo();
cls1.dododo();
System.out.println(cls);
System.out.println(cls1);
}
}
现在它可以完美地工作并且初始化的对象也被 spring 管理...
注: 必须在 maven pom.xml
中设置 JSR330 依赖项<dependency>
<groupId>javax.inject</groupId>
<artifactId>javax.inject</artifactId>
<version>1</version>
</dependency>
为什么不使用@Lookup 注解?根据已接受的答案,我假设您每次 ClassC
.
ClassA
的新实例
@Component("classA")
@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public class ClassA {
@Autowired
InterA abcd;
private ObjectA objA;
private ObjectB objB;
public ClassA(objA, objB) {
this.objA = objA;
this.objB = objB;
}
public void dododo() {
abcd.doit();
}
}
@Component("classc")
public class ClassC {
public void doFromAbove() {
ClassA cls = getNewInstanceOfClassA(objA, objB);
cls.dododo();
}
@Lookup("classA")
private getNewInstanceOfClassA(ObjectA objA, ObjectB objB) {
//Spring creates a runtime implementation of this method
return null;
}
}
其余 class 实现保持不变。我在实现中包含了 objA 和 objB,以便清楚地了解注入构造函数参数。
所以main方法获取classC的一个springbean,调用doFromAbove()
方法。这依次调用 getNewInstanceOfClassA
方法,该方法 returns 类型为 classA 的 spring bean 具有构造函数参数 objA、objB。由于我们将其注释为原型 bean,因此每次调用此方法时我们都会得到一个新的 classA 实例。您不需要实施 getNewInstanceOfClassA
方法。 Spring 在运行时添加它自己的代码。
从本质上讲,您的问题归结为在单例 bean 中注入原型 bean。查找注释是解决该问题的最佳方法。