[英]inject prototype into singleton(java configuration + annotation)
I was asked on interview question about injectin prototype into singleton. 我在采访中被问到有关将单体原型注入单例的问题。 I difficult to diificult to answer and now I am trying to research this.
我很难回答,现在我正在尝试对此进行研究。
I have wrote following code(pring boot) 我写了以下代码(启动时启动)
bean 1: 豆1:
@Service
@Scope(value = "prototype")
public class MyValidator {
}
bean 2: 豆2:
@Service
public class ValidatorHolder {
@Autowired
MyValidator myValidator;
public MyValidator getMyValidator() {
return myValidator;
}
}
configuration: 组态:
@SpringBootApplication
@Configuration
@ComponentScan("com.example.domain")
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
ApplicationContext context = new AnnotationConfigApplicationContext(DemoApplication.class);
ValidatorHolder validatorHolder1 = (ValidatorHolder) context.getBean("validatorHolder");
ValidatorHolder validatorHolder2 = (ValidatorHolder) context.getBean("validatorHolder");
System.out.println("=====================================");
System.out.println(validatorHolder1.getMyValidator() == validatorHolder2.getMyValidator());
System.out.println("=====================================");
}
}
This code retirns true. 该代码为true。
As I understood while reading article link It is possible to configure to return false. 据我了解,在阅读文章链接时 ,可以配置为返回false。
How can I do in my code? 如何在我的代码中执行? (without xml)
(没有xml)
I tryed to rewrite code like in article: 我试图像文章中那样重写代码:
<bean id="validatorHolder" class="com.example.domain.ValidatorHolder">
<property name="myValidator" ref="validator"/>
</bean>
<bean id="validator" scope="prototype" class="com.example.domain.MyValidator">
<!-- This instructs the container to proxy the current bean-->
<aop:scoped-proxy/>
</bean>
Inside main method I have wrote following code: 在main方法内部,我编写了以下代码:
ApplicationContext xmlContext = new FileSystemXmlApplicationContext("classpath:applicationContext.xml");
ValidatorHolder validatorHolder21 = (ValidatorHolder) xmlContext.getBean("validatorHolder");
ValidatorHolder validatorHolder22 = (ValidatorHolder) xmlContext.getBean("validatorHolder");
System.out.println("=====================================");
System.out.println(validatorHolder21.getMyValidator() == validatorHolder22.getMyValidator());
System.out.println("=====================================");
anyway I see true
无论如何我都认为是
true
lets reserch Sean Patrick Floyd answer (scope proxy, b)) 让我们重新研究Sean Patrick Floyd的答案(范围代理,b))
I use following main method class: 我使用以下主要方法类:
@SpringBootApplication
@ComponentScan("com.example.domain")
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
ApplicationContext context = new AnnotationConfigApplicationContext(DemoApplication.class);
ValidatorHolder validatorHolder1 = (ValidatorHolder) context.getBean("validatorHolder");
ValidatorHolder validatorHolder2 = (ValidatorHolder) context.getBean("validatorHolder");
System.out.println("=====================================");
System.out.println(validatorHolder1.getMyValidator() == validatorHolder2.getMyValidator());
System.out.println("=====================================");
}
when I run application - I see 当我运行应用程序时-我看到了
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'validatorHolder' defined in file [D:\freelance\demo\target\classes\com\example\domain\ValidatorHolder.class]: Instantiation of bean failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [com.example.domain.ValidatorHolder]: No default constructor found; nested exception is java.lang.NoSuchMethodException: com.example.domain.ValidatorHolder.<init>()
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateBean(AbstractAutowireCapableBeanFactory.java:1099)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1044)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:504)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:476)
at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:303)
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:230)
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:299)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:194)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:755)
at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:759)
at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:480)
at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:689)
at org.springframework.boot.SpringApplication.run(SpringApplication.java:321)
at org.springframework.boot.SpringApplication.run(SpringApplication.java:969)
at org.springframework.boot.SpringApplication.run(SpringApplication.java:958)
at com.example.DemoApplication.main(DemoApplication.java:20)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:483)
at com.intellij.rt.execution.application.AppMain.main(AppMain.java:134)
Caused by: org.springframework.beans.BeanInstantiationException: Failed to instantiate [com.example.domain.ValidatorHolder]: No default constructor found; nested exception is java.lang.NoSuchMethodException: com.example.domain.ValidatorHolder.<init>()
at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:85)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateBean(AbstractAutowireCapableBeanFactory.java:1092)
... 20 common frames omitted
Caused by: java.lang.NoSuchMethodException: com.example.domain.ValidatorHolder.<init>()
at java.lang.Class.getConstructor0(Class.java:3074)
at java.lang.Class.getDeclaredConstructor(Class.java:2170)
at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:80)
... 21 common frames omitted
PS2 issue was related with missed @Autowired
on constructor PS2问题与构造函数上缺少
@Autowired
有关
after fix this issue 解决此问题后
System.out.println(validatorHolder1.getMyValidator() == validatorHolder2.getMyValidator());
returns true 返回true
but if a bit replace MyValidator code: 但是如果稍微替换一下MyValidator代码:
@Service
@Scope(value="prototype", proxyMode=ScopedProxyMode.TARGET_CLASS)
public class MyValidator {
Object object = new Object();
public Object getObject() {
return object;
}
public void setObject(Object object) {
this.object = object;
}
}
System.out.println(validatorHolder1.getMyValidator() == validatorHolder2.getMyValidator());
true 真正
System.out.println(validatorHolder1.getMyValidator().getObject() == validatorHolder2.getMyValidator().getObject());
false 假
and even 乃至
System.out.println(validatorHolder1.getMyValidator().getObject() == validatorHolder1.getMyValidator().getObject());
false 假
You have misunderstood the method injection technique. 您误解了方法注入技术。 You need to make your bean abstract for it to work:
您需要使bean抽象才能工作:
public class MyValidator {}
public abstract class ValidatorHolder {
public abstract MyValidator getMyValidator();
}
Now you can define the beans in XML as follows: 现在,您可以按以下方式在XML中定义bean:
<bean class="com.somepackage.MyValidator" scope="prototype" />
<bean class="com.somepackage.ValidatorHolder">
<lookup-method name="getMyValidator" bean="myValidator" />
</bean>
In this case, Spring will create an anonymous subclass of ValidatorHolder
that returns the prototype bean (a new copy) every type it is called. 在这种情况下,Spring将创建
ValidatorHolder
的匿名子类,该子类将在每次调用它的类型时返回原型bean(新副本)。
With annotated service classes, lookup method injection is not possible, but this is how you can do it with @Configuration
classes: 使用带注释的服务类,无法进行查找方法注入,但这是使用
@Configuration
类的方法:
@Configuration
public class MyConfiguration{
@Bean
@Scope("prototype")
public MyValidator myValidator(){
return new MyValidator();
}
@Bean
public ValidatorHolder validatorHolder(){
return new ValidatorHolder(){
@Override public MyValidator getMyValidator(){
return myValidator();
}
};
}
}
In this case, you are creating the subclass of ValidatorHolder
yourself, and you can see clearly what happens. 在这种情况下,您将自己创建
ValidatorHolder
的子类,并且可以清楚地看到会发生什么。
But either version only works if you make bean and the provider method abstract. 但是只有当您使bean和提供者方法成为抽象时,这两个版本才有效。
On a final note, there are three different ways to define spring beans: 最后,定义弹跳豆有三种不同的方法:
@Service
, @Component
) with a component scan @Service
@Component
, @Component
Component) @Configuration
classes with @Bean
methods. @Bean
方法的@Configuration
类。 In your sample code, you are mixing these three styles, which is almost never a good idea. 在示例代码中,您将这三种样式混合在一起,这几乎从来不是一个好主意。 Pick one technique and stick with it.
选择一种技术并坚持下去。
Regarding the scoped proxy, this can be achieved in all three bean registration techniques. 关于作用域代理,这可以通过所有三种bean注册技术来实现。
a) XML a)XML
public class MyValidator {}
public class ValidatorHolder {
private MyValidator myValidator;
public void setMyValidator(MyValidator myValidator){
this.myValidator = myValidator;}
public MyValidator getMyValidator();
}
<bean class="com.somepackage.MyValidator" scope="prototype" />
<bean class="com.somepackage.ValidatorHolder">
<aop:scoped-proxy />
</bean>
b) annotated service class b)带注释的服务等级
@Service @Scope(value="prototype", proxyMode=ScopedProxyMode.TARGET_CLASS)
public class MyValidator {}
@Service
public class ValidatorHolder {
@Autowired
public ValidatorHolder(MyValidator myValidator){
this.myValidator=myValidator;
}
private final MyValidator myValidator;
public MyValidator getMyValidator(){ return myValidator; };
}
c) @Configuration
classes, Bean classes like in XML version c)
@Configuration
类,类似于XML版本的Bean类
@Configuration
public class MyConfiguration{
@Bean
@Scope("prototype")
public MyValidator myValidator(){
return new MyValidator();
}
@Bean
public ValidatorHolder validatorHolder(){
return new ValidatorHolder(myValidator());
}
}
Please note that all proxy solutions will always return the same object, the proxy. 请注意,所有代理解决方案将始终返回相同的对象即代理。 But the underlying functionality will delegate to different Objects.
但是底层功能将委托给不同的对象。 Try it out by adding this code to MyValidator:
通过将以下代码添加到MyValidator来进行尝试:
private int counter = 1;
public int counter(){
return counter ++;
}
Now, independent of how often you call this code: 现在,与调用此代码的频率无关:
validatorHolder.getMyValidator().counter();
it will always return 1
. 它将始终返回
1
。
@Service
public class ValidatorHolder {
@Autowired
ApplicatioContext context;
public MyValidator getMyValidator() {
return context.getBean(MyValidator.class);
}
}
also read http://shekhargulati.com/tag/method-injection/ 另请阅读http://shekhargulati.com/tag/method-injection/
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.