Ваш базовый подход с использованием фабрики, созданной 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-объектов с прототипом, возможно, есть способ передать значение для зависимости только во время выполнения через область действия, но я понятия не имею, где вообще начать отвечать, как это сделать это, особенно не зная реальных границ бобов и того, как они связаны друг с другом. В любом случае вышеприведенное решение кажется более простым и понятным.