简体   繁体   English

将原型注入单例(java配置+注释)

[英]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)

PS 聚苯乙烯

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

PS2 PS2

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

PS3 PS3

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: 最后,定义弹跳豆有三种不同的方法:

  • XML XML格式
  • annotated classes (eg @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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM