簡體   English   中英

春季動態依賴注入

[英]Dynamic dependency injection in spring

我正在尋找一種方法來創建一個可以“部分”自動裝配的非單件彈簧豆。

@Component
class Example {
    private SpringBean1 bean1;
    private SpringBean2 bean2;
    private String dynamicDependancy;

    @Autowired
    public Example(SpringBean1 bean1, SpringBean2 bean2, String dynamicDepedency) {
        this.bean1 = bean1;
        this.bean2 = bean2;
        this.dynamicDepedency = dynamicDepedency;
    }
}

我想要這樣的事情,因為有時只有在運行時才知道依賴。 我有一種方法可以創建一種創建靜態成員類的工廠,這樣我就可以測試靜態成員類:

@Component
class ExampleFactory {
    private SpringBean1 bean1;
    private SpringBean2 bean2;

    @Autowired
    public ExampleFactory(SpringBean1 bean1, SpringBean2 bean2) {
        this.bean1 = bean1;
        this.bean2 = bean2;
    }

    public Example from(String dynamicDependency) {
        return new Example(bean1, bean2, dynamicDependency);
    }

    static class Example {
        private SpringBean1 bean1;
        private SpringBean2 bean2;
        private String dynamicDependancy;

        public Example(SpringBean1 bean1, SpringBean2 bean2, String 
            dynamicDependancy) {
            this.bean1 = bean1;
            this.bean2 = bean2;
            this.dynamicDependancy = dynamicDependancy;
        }
    }
}

我正在查看Prototype范圍,並使用getBean(java.lang.String,java.lang.Object)使得更難使用依賴注入。 我想知道是否有任何“春天的方式”來做這些事情。

謝謝。

更新:我找到了另一個解決方案,並在另一篇文章中發布了答案: https//stackoverflow.com/a/52021965/2580829

使用由Spring注入的工廠然后公開創建Example實例的方法的基本方法是我如何做到這一點,所以它基本上是正確的。 如果您希望Spring使用其現代功能透明地完成此操作,您可以將@Configuration查找方法注入結合使用,以從單例范圍的bean創建Example on-demand的實例。


一,配置類:

@Configuration
public class DemoConfiguration {
    @Autowired IFooBean fooBean;
    @Autowired IBarBean barBean;

    @Bean()
    @Scope("prototype")
    Example newExample(String name) {
        return new Example(fooBean, barBean, name);
    }
}

這里應該沒有什么太令人驚訝的了,除了newExamplename參數。 您可以像我上面那樣自動裝配容器可以滿足的依賴項( fooBeanbarBean ),但是由於配置類的實例與Spring的任何其他bean一樣,您還可以使用任何其他機制:將ObjectFactoryObjectProvider注入配置讓它實現ApplicationContextAware ,甚至為它們使用查找方法注入。 如果您需要避免fooBeanbarBean初始化,如果它們被自動裝配到配置bean中,那么這將非常有用。

不要忘記將工廠方法的范圍設置為"prototype" ,否則Spring將返回您使用的第一個bean,即使您為name傳遞了不同的值。


Example本身的實現類似於你問題中的實現:

public class Example {
    IFooBean fooBean;
    IBarBean barBean;
    String name;

    public Example(IFooBean fooBean, IBarBean barBean, String name) {
        System.out.printf("%s(fooBean=%s, barBean=%s, name=%s)\n", this, fooBean, barBean, name);
        this.fooBean = fooBean;
        this.barBean = barBean;
        this.name = name;
    }
}

然后,在您實際需要Example實例的位置,使用@Lookup注入工廠方法:

public interface IUsesExample {
    void doThing();
}

@Component
public class UsesExample implements IUsesExample {
    @Lookup
    protected Example getExample(String name) {return null;};

    public void doThing() {
        System.out.printf("%s.doThing(getExample() = %s)\n", this, getExample("aaa"));
        System.out.printf("%s.doThing(getExample() = %s)\n", this, getExample("bbb"));
    }
}

要使用@Component和掃描,這必須是一個具體的類,這意味着我們需要一個getExample()的虛擬實現; Spring將使用CGLIB替換它,調用上面DemoConfiguration定義的工廠方法。 Spring將正確地將參數從lookup方法傳遞給factory方法。

出於測試目的,我只使用不同的name值調用getExample()兩次,以證明我們每次都注入了正確的東西,從而得到一個不同的實例。


使用以下小型Spring Boot應用程序對此進行測試:

@SpringBootApplication
public class DemoApplication {
    @Autowired IUsesExample usesExample;

    public static void main(String[] args) {
        SpringApplication.run(DemoApplication.class, args);
    }

    @PostConstruct
    void run() {
        usesExample.doThing();
    }
}

給出以下輸出:

com.example.demo.FooBean@fd46303
com.example.demo.BarBean@6a62689d
com.example.demo.Example@66629f63(fooBean=com.example.demo.FooBean@fd46303, barBean=com.example.demo.BarBean@6a62689d, name=aaa)
com.example.demo.UsesExample$$EnhancerBySpringCGLIB$$68b994e8@6c345c5f.doThing(getExample() = com.example.demo.Example@66629f63)
com.example.demo.Example@6b5966e1(fooBean=com.example.demo.FooBean@fd46303, barBean=com.example.demo.BarBean@6a62689d, name=bbb)
com.example.demo.UsesExample$$EnhancerBySpringCGLIB$$68b994e8@6c345c5f.doThing(getExample() = com.example.demo.Example@6b5966e1)

那是:

  • 一個FooBean被創建
  • 一個BarBean被創建
  • 使用上面的兩個bean和name創建一個Example
  • Example返回到UseExample
  • 創建一個不同的Example ,使用相同的FooBeanBarBean ,並且這次將name設置為"bbb"

我假設您熟悉如何設置基於Java的配置和組件掃描以及上述示例所依賴的所有其他管道。 我使用Spring Boot以簡單的方式獲得整個shebang。

如果您正在從其他原型范圍的bean創建Example s,那么可能有一種方法可以通過范圍傳遞僅運行時依賴項的值,但我不知道在哪里開始回答如何執行此操作,尤其是在不知道的情況下bean的實際范圍以及它們之間的關系。 無論哪種方式,上述解決方案似乎更簡單易懂。

暫無
暫無

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

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