我是Spring的新手并试图理解这个概念" 将原型bean注入单个bean "。根据我的理解在单例中,每个Spring IoC容器只有一个实例,无论你检索它多少次。 validator.validate(requestId);
,因为仍未private RequestValidator validator
实例化。
我开发了以下示例,在单例bean中,我给出了如下所示的原型bean的引用:
<bean id="requestProcessor" class="com.injection.testing.RequestProcessor">
<property name="validator" ref="validator" />
</bean>
<bean id="validator" class="com.injection.testing.RequestValidator" scope="prototype" />
RequestProcessor.java
public class RequestProcessor {
private RequestValidator validator;
public RequestProcessor(){
System.out.println("Constructor:: RequestProcessor instance created!");
}
public void handleRequest(String requestId){
System.out.println("Request ID : "+ requestId);
validator.validate(requestId);
}
public RequestValidator getValidator() {
return validator;
}
public void setValidator(RequestValidator validator) {
this.validator= validator;
}
}
RequestValidator.java
public class RequestValidator {
private List<String> errorMessages = new ArrayList<String>();
public RequestValidator() {
System.out.println("Constructor:: RequestValidator instance created!");
}
// Validates the request and populates error messages
public void validate(String requestId){
System.out.println("RequestValidator :"+requestId);
}
public List<String> getErrorMessages() {
return errorMessages;
}
}
现在当我调用main方法时,我看到以下输出: 的 MainDemo.java
public class MainDemo {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("spring.xml");
RequestProcessor processor = (RequestProcessor) context.getBean("requestProcessor");
processor.handleRequest("1212");
System.out.println("------------------------");
processor.handleRequest("1213");
}
}
输出是:
Constructor:: RequestProcessor instance created!
Constructor:: RequestValidator instance created!
Request ID : 1212
RequestValidator :1212
------------------------
Request ID : 1213
RequestValidator :1213
现在查看输出,看起来第二次调用processor.handleRequest("1213");
bean没有实例化,而是已经实例化的bean被使用,这就是为什么构造函数不会被再次调用。所以原型bean validator
仅作为单例bean使用。
对我来说:我希望当我从应用程序上下文中获取requestProcessor时,它将与new validator
连接,因为我们声明验证器bean是原型范围。但这不会发生。
如何解决?我的理解是否正确?
另一种方式:
<!-- Lookup way -->
<bean id="requestProcessor" class="com.injection.testing.RequestProcessor" >
<lookup-method name="getValidator" bean="validator" />
</bean>
<bean id="validator" class="com.injection.testing.RequestValidator" scope="prototype" />
如果我调用我的main方法,我会在下面看到输出+错误:这里执行代码validator.validate(requestId);
,private RequestValidator validator;
没有实例化,为什么会出现空指针异常。
我已在以下代码中显示:
public class MainDemo {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("spring.xml");
RequestValidator requestValidator = (RequestValidator) context.getBean("validator");
RequestProcessor processor = (RequestProcessor) context.getBean("requestProcessor");
processor.handleRequest("1212");
System.out.println("------------------------");
processor.handleRequest("1213");
}
}
现在我看到以下错误:
Constructor:: RequestProcessor instance created!
Constructor:: RequestValidator instance created!
Request ID : 1212
Exception in thread "main" java.lang.NullPointerException
at com.injection.testing.RequestProcessor.handleRequest(RequestProcessor.java:12)
at com.injection.testing.MainDemo.main(MainDemo.java:14)
答案 0 :(得分:6)
当Spring上下文启动时,注入只发生一次。如果bean具有prototype
范围,Spring将为每次注入创建新的原型bean。但每次调用其方法时都不会创建原型bean。让我们考虑下一个例子:
<bean id="firstRequestProcessor" class="com.injection.testing.RequestProcessor">
<property name="validator" ref="validator" />
</bean>
<bean id="secondRequestProcessor" class="com.injection.testing.RequestProcessor">
<property name="validator" ref="validator" />
</bean>
<bean id="validator" class="com.injection.testing.RequestValidator" scope="prototype" />
在这种情况下,两个RequestProcessor
bean都有自己的RequestValidator
bean实例。
Lookup方法是方法,每次需要原型bean的新实例时都应该调用。最好使这个方法abstract
,因为无论如何Spring会自动覆盖这个方法。例如:
public class abstract RequestProcessor {
public void handleRequest(String requestId){
System.out.println("Request ID : "+ requestId);
RequestValidator validator = createValidator(); //here Spring will create new instance of prototype bean
validator.validate(requestId);
}
protected abstract RequestValidator createValidator();
}
注意,createValidator
返回RequestValidator
的实例并且没有任何参数。此外,您不需要私有类变量validator
。在这种情况下,bean的配置如下所示:
<bean id="requestProcessor" class="com.injection.testing.RequestProcessor" >
<lookup-method name="createValidator" bean="validator" />
</bean>
<bean id="validator" class="com.injection.testing.RequestValidator" scope="prototype" />
现在每次调用createValidator
方法时,Spring都会创建validator
bean的新实例。
您可以在documentation中找到更多详情。
答案 1 :(得分:0)
当spring创建上下文时,它将实例化RequestProcessor的一个实例。在该实例的实例化期间,创建RequestValidator的实例并将其注入RequestProcessor中。
因为对RequestProcessor Bean的任何后续引用都将在上下文中访问RequestProcessor的同一实例 - 并且它已经完全构造 - 所以永远不会调用创建RequestValidator的新实例。虽然你的Validator的范围是原型 - 在上面的例子中 - 你只需要一个副本 - 当你创建RequestProcessor单例时。
对于问题的第二部分 - 您误解了查找方法的使用。 Spring允许您覆盖RequestProcessor上的方法 - 因此当您调用requestProcessor.getValidator()时,spring将返回该RequestValidator的新实例。但是 - 在构造requestProcessor实例期间 - 您从未初始化验证器字段。 spring在实例化期间也没有注入验证器。因此NullPointerException。
答案 2 :(得分:0)
查找方法注入
如前所述,查找方法注入是一项高级功能, 您应该很少使用。在单例作用域的情况下很有用 bean依赖于原型作用域的bean。为此使用Java 配置类型提供了实现此目标的自然方法 模式。
public abstract class CommandManager {
public Object process(Object commandState) {
// grab a new instance of the appropriate Command interface
Command command = createCommand();
// set the state on the (hopefully brand new) Command instance
command.setState(commandState);
return command.execute();
}
// okay... but where is the implementation of this method?
protected abstract Command createCommand();
}
使用Java配置支持,您可以创建以下子类 覆盖抽象createCommand()方法的CommandManager 以某种方式查找新的(原型)命令对象:
@Bean
@Scope("prototype")
public AsyncCommand asyncCommand() {
AsyncCommand command = new AsyncCommand();
// inject dependencies here as required
return command;
}
@Bean
public CommandManager commandManager() {
// return new anonymous implementation of CommandManager with command() overridden
// to return a new prototype Command object
return new CommandManager() {
protected Command createCommand() {
return asyncCommand();
}
}
}
答案 3 :(得分:0)
在大多数应用场景中,容器中的大多数bean是单例的。当单例Bean需要与另一个单例Bean协作或非单例Bean需要与另一个非单例Bean协作时,通常可以通过将一个Bean定义为另一个Bean的属性来处理依赖性。当bean的生命周期不同时会出现问题。假设单例bean A需要使用非单例(原型)bean B,也许在对A的每个方法调用上都使用它。容器仅创建一次单例bean A,因此只有一次机会来设置属性。每次需要一个容器时,容器都无法向Bean A提供新的Bean B实例。
一个解决方案是放弃某些控制反转。您可以通过实现ApplicationContextAware接口,并通过对容器进行getBean(“ B”)调用来使bean A知道该容器,每次豆A需要它时都请求一个(通常是新的)bean B实例。
通过这种方法,我们的业务逻辑与Spring conatiner(应用程序上下文)结合在一起,这似乎不是一个好的解决方案。替代方法是查找方法注入。