Динамическое внедрение зависимости весной - PullRequest
0 голосов
/ 02 июля 2018

Я искал способ создания не синглтонового пружинного боба, который может быть «частично» автоматически подключен.

@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

1 Ответ

0 голосов
/ 02 июля 2018

Ваш базовый подход с использованием фабрики, созданной Spring, которая затем предоставляет метод для создания Example экземпляров, - это то, как я бы это сделал, так что это, по сути, правильно. Если вы хотите, чтобы Spring позаботился об этом прозрачно, используя его современные функции, вы можете использовать @Configuration класс в сочетании с методом поиска инъекций для создания экземпляров Example по требованию от синглтон-бобов.


Во-первых, класс конфигурации:

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

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

Здесь не должно быть ничего удивительного, кроме, может быть, параметра name для newExample. Вы можете автоматически связать зависимости, которым может удовлетворять контейнер (fooBean и barBean), как я делал выше, но, поскольку экземпляры классов конфигурации создаются как любой другой компонент в Spring, вы также можете использовать любой другой механизм: внедрить ObjectFactory или ObjectProvider в конфигурацию, пусть он реализует ApplicationContextAware, или даже использует для них внедрение метода поиска. Это было бы полезно, если вам нужно избежать инициализации fooBean и barBean на ранней стадии, как если бы они были автоматически подключены к компоненту конфигурации.

Не забудьте установить область действия фабричного метода на "prototype", иначе Spring просто вернет первый созданный вами компонент, даже если вы передадите другое значение для 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 правильно передаст параметры из метода поиска в фабричный метод.

В целях тестирования я просто дважды вызываю getExample() с разными значениями для name, чтобы продемонстрировать, что мы получаем разные экземпляры с правильными вещами, вводимыми в него каждый раз.


Тестирование с помощью следующего небольшого приложения 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)

То есть:

  • a FooBean создается
  • a BarBean создается
  • и Example создаются с двумя вышеуказанными bean-компонентами и name
  • это Example возвращается UseExample
  • создается другое Example, с теми же FooBean и BarBean и name, установленными на "bbb" на этот раз.

Я предполагаю, что вы знакомы с тем, как настроить конфигурацию на основе Java и сканирование компонентов, и все остальные примеры, на которые опираются приведенные выше примеры. Я использовал Spring Boot, чтобы получить весь shebang простым способом.

Если вы создаете Example s из других bean-объектов с прототипом, возможно, есть способ передать значение для зависимости только во время выполнения через область действия, но я понятия не имею, где вообще начать отвечать, как это сделать это, особенно не зная реальных границ бобов и того, как они связаны друг с другом. В любом случае вышеприведенное решение кажется более простым и понятным.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...