簡體   English   中英

將原型注入單例(java配置+注釋)

[英]inject prototype into singleton(java configuration + annotation)

我在采訪中被問到有關將單體原型注入單例的問題。 我很難回答,現在我正在嘗試對此進行研究。

我寫了以下代碼(啟動時啟動)

豆1:

@Service
@Scope(value = "prototype")
public class MyValidator {
}

豆2:

@Service
public class ValidatorHolder {

    @Autowired
    MyValidator myValidator;

    public MyValidator getMyValidator() {
        return myValidator;
    }
}

組態:

@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("=====================================");

    }
}

該代碼為true。

據我了解,在閱讀文章鏈接時 ,可以配置為返回false。

如何在我的代碼中執行? (沒有xml)

聚苯乙烯

我試圖像文章中那樣重寫代碼:

   <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>

在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("=====================================");

無論如何我都認為是true

PS2

讓我們重新研究Sean Patrick Floyd的答案(范圍代理,b))

我使用以下主要方法類:

@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("=====================================");         
}

當我運行應用程序時-我看到了

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

PS2問題與構造函數上缺少@Autowired有關

解決此問題后

System.out.println(validatorHolder1.getMyValidator() == validatorHolder2.getMyValidator());

返回true

但是如果稍微替換一下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());

真正

 System.out.println(validatorHolder1.getMyValidator().getObject() == validatorHolder2.getMyValidator().getObject());

乃至

System.out.println(validatorHolder1.getMyValidator().getObject() == validatorHolder1.getMyValidator().getObject());

您誤解了方法注入技術。 您需要使bean抽象才能工作:

public class MyValidator {}

public abstract class ValidatorHolder {
    public abstract MyValidator getMyValidator();
}

現在,您可以按以下方式在XML中定義bean:

<bean class="com.somepackage.MyValidator" scope="prototype" />
<bean class="com.somepackage.ValidatorHolder">
    <lookup-method name="getMyValidator" bean="myValidator" />
</bean>

在這種情況下,Spring將創建ValidatorHolder的匿名子類,該子類將在每次調用它的類型時返回原型bean(新副本)。

使用帶注釋的服務類,無法進行查找方法注入,但這是使用@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();
            }
        };
    }
}

在這種情況下,您將自己創建ValidatorHolder的子類,並且可以清楚地看到會發生什么。

但是只有當您使bean和提供者方法成為抽象時,這兩個版本才有效。

最后,定義彈跳豆有三種不同的方法:

  • XML格式
  • 帶組件掃描的帶注釋的類(例如@Service @Component@Component Component)
  • 具有@Bean方法的@Configuration類。

在示例代碼中,您將這三種樣式混合在一起,這幾乎從來不是一個好主意。 選擇一種技術並堅持下去。


關於作用域代理,這可以通過所有三種bean注冊技術來實現。

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)帶注釋的服務等級

@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類,類似於XML版本的Bean類

@Configuration
public class MyConfiguration{

    @Bean
    @Scope("prototype")
    public MyValidator myValidator(){
        return new MyValidator();
    }

    @Bean
    public ValidatorHolder validatorHolder(){
        return new ValidatorHolder(myValidator());
    }

}

請注意,所有代理解決方案將始終返回相同的對象即代理。 但是底層功能將委托給不同的對象。 通過將以下代碼添加到MyValidator來進行嘗試:

private int counter = 1;
public int counter(){
    return counter ++;
}

現在,與調用此代碼的頻率無關:

validatorHolder.getMyValidator().counter();

它將始終返回1

@Service
public class ValidatorHolder {

@Autowired
ApplicatioContext context;

public MyValidator getMyValidator() {
    return context.getBean(MyValidator.class);
}
}

另請閱讀http://shekhargulati.com/tag/method-injection/

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM