[SPRING] Spring Java Config : 런타임 인수를 사용하여 프로토 타입 범위의 @Bean을 만드는 방법은 무엇입니까?
SPRINGSpring Java Config : 런타임 인수를 사용하여 프로토 타입 범위의 @Bean을 만드는 방법은 무엇입니까?
Spring의 Java Config를 사용하여 런타임에만 생성 가능한 생성자 인수를 사용하여 프로토 타입 범위 Bean을 획득 / 인스턴스화해야한다. 간략하게하기 위해 단순화 된 다음 코드 예제를 고려하십시오.
@Autowired
private ApplicationContext appCtx;
public void onRequest(Request request) {
//request is already validated
String name = request.getParameter("name");
Thing thing = appCtx.getBean(Thing.class, name);
//System.out.println(thing.getName()); //prints name
}
여기서 Thing 클래스는 다음과 같이 정의됩니다.
public class Thing {
private final String name;
@Autowired
private SomeComponent someComponent;
@Autowired
private AnotherComponent anotherComponent;
public Thing(String name) {
this.name = name;
}
public String getName() {
return this.name;
}
}
통지의 이름은 final입니다. 이것은 constructor을 통해서만 제공 할 수 있습니다 만, 불변성을 보증합니다. 다른 종속성은 Thing 클래스의 구현 별 종속성이며 요청 처리기 구현에 밀접하게 결합되어서는 안됩니다.
이 코드는 Spring XML 설정과 완벽하게 잘 작동합니다 (예 :
<bean id="thing", class="com.whatever.Thing" scope="prototype">
<!-- other post-instantiation properties omitted -->
</bean>
어떻게하면 자바 설정으로 같은 것을 얻을 수 있습니까? 다음은 작동하지 않습니다.
@Bean
@Scope("prototype")
public Thing thing(String name) {
return new Thing(name);
}
이제 공장을 만들 수 있습니다 (예 :
public interface ThingFactory {
public Thing createThing(String name);
}
그러나 이것은 ServiceLocator와 Factory 디자인 패턴을 대체하기 위해 Spring을 사용하는 전체적인 포인트를 무효화합니다. 이는이 사용 사례에 이상적입니다.
Spring Java Config가 이것을 할 수 있다면, 나는 피할 수있을 것이다 :
그것은 Spring이 XML 설정을 통해 이미 지원하고있는 사소한 일에 대한 많은 일 (상대적으로 말하기)입니다.
해결법
-
==============================
1.@Configuration 클래스에서, @Bean 메소드는
@Configuration 클래스에서, @Bean 메소드는
@Bean @Scope("prototype") public Thing thing(String name) { return new Thing(name); }
Bean 정의를 등록하고 Bean 작성을위한 팩토리를 제공하는 데 사용됩니다. 그것이 정의한 bean은 직접적으로 또는 ApplicationContext를 스캔함으로써 결정되는 인자를 사용하여 요청시 인스턴스화된다.
프로토 타입 빈의 경우 매번 새로운 객체가 생성되므로 해당 @Bean 메소드도 실행됩니다.
BeanFactory # getBean (String name, Object ... args) 메소드를 통해 ApplicationContext에서 Bean을 검색 할 수있다.
즉,이 프로토 타입 범위 Bean의 경우 Bean 클래스의 생성자가 아닌 @Bean 메서드 호출에서 사용할 인수를 제공합니다.
Spring 버전 4 이상에서는 사실입니다.
-
==============================
2.Spring> 4.0 및 Java 8을 사용하면보다 안전하게 유형을 안전하게 처리 할 수 있습니다.
Spring> 4.0 및 Java 8을 사용하면보다 안전하게 유형을 안전하게 처리 할 수 있습니다.
@Configuration public class ServiceConfig { @Bean public Function<String, Thing> thingFactory() { return name -> thing(name); // or this::thing } @Bean @Scope(value = "prototype") public Thing thing(String name) { return new Thing(name); } }
용법:
@Autowired private Function<String, Thing> thingFactory; public void onRequest(Request request) { //request is already validated String name = request.getParameter("name"); Thing thing = thingFactory.apply(name); // ... }
이제 런타임시 빈을 얻을 수 있습니다. 이것은 물론 팩토리 패턴이지만 ThingFactory와 같은 특정 클래스를 작성하는 데 시간을 절약 할 수 있습니다 (단, 두 개 이상의 매개 변수를 전달하려면 사용자 정의 @FunctionalInterface를 작성해야합니다).
-
==============================
3.댓글 당 업데이트 됨
댓글 당 업데이트 됨
첫째로, 나는 왜 이것이 Spring 3.x에서 잘 작동하는 무언가를 위해 "이것이 작동하지 않는다"는 것을 확신 할 수 없다. 어딘가에서 당신의 구성에 뭔가 틀린 것이 틀림 없다고 생각합니다.
작동 방식 :
- 구성 파일 :
@Configuration public class ServiceConfig { // only here to demo execution order private int count = 1; @Bean @Scope(value = "prototype") public TransferService myFirstService(String param) { System.out.println("value of count:" + count++); return new TransferServiceImpl(aSingletonBean(), param); } @Bean public AccountRepository aSingletonBean() { System.out.println("value of count:" + count++); return new InMemoryAccountRepository(); } }
- 실행할 테스트 파일 :
@Test public void prototypeTest() { // create the spring container using the ServiceConfig @Configuration class ApplicationContext ctx = new AnnotationConfigApplicationContext(ServiceConfig.class); Object singleton = ctx.getBean("aSingletonBean"); System.out.println(singleton.toString()); singleton = ctx.getBean("aSingletonBean"); System.out.println(singleton.toString()); TransferService transferService = ctx.getBean("myFirstService", "simulated Dynamic Parameter One"); System.out.println(transferService.toString()); transferService = ctx.getBean("myFirstService", "simulated Dynamic Parameter Two"); System.out.println(transferService.toString()); }
Spring 3.2.8과 Java 7을 사용하면 다음과 같은 결과를 얻을 수있다.
value of count:1 com.spring3demo.account.repository.InMemoryAccountRepository@4da8692d com.spring3demo.account.repository.InMemoryAccountRepository@4da8692d value of count:2 Using name value of: simulated Dynamic Parameter One com.spring3demo.account.service.TransferServiceImpl@634d6f2c value of count:3 Using name value of: simulated Dynamic Parameter Two com.spring3demo.account.service.TransferServiceImpl@70bde4a2
따라서 'Singleton'Bean은 두 번 요청됩니다. 그러나 우리가 예상 하듯이 Spring은 한 번만 생성합니다. 두 번째로 그 bean을 가지고 있고 기존 객체를 반환한다는 것을 알게됩니다. 생성자 (@Bean 메서드)가 두 번 호출되지 않습니다. 이 점을 고려하여 'Prototype'Bean이 동일한 컨텍스트 개체에서 두 번 요청되면 출력에서 참조가 변경되고 생성자 (@Bean 메서드)가 두 번 호출됨을 알 수 있습니다.
그래서 문제는 프로토 타입에 싱글 톤을 주입하는 방법입니다. 위의 구성 클래스는이를 수행하는 방법을 보여줍니다! 그러한 모든 참조를 생성자에 전달해야합니다. 이렇게하면 생성 된 클래스가 순수한 POJO 일뿐만 아니라 포함 된 참조 객체를 필요에 따라 변경할 수 없게됩니다. 따라서 전송 서비스는 다음과 같이 보일 수 있습니다.
public class TransferServiceImpl implements TransferService { private final String name; private final AccountRepository accountRepository; public TransferServiceImpl(AccountRepository accountRepository, String name) { this.name = name; // system out here is only because this is a dumb test usage System.out.println("Using name value of: " + this.name); this.accountRepository = accountRepository; } .... }
Unit Tests를 작성하면 모든 @Autowired없이 클래스를 생성하게되어 매우 기쁩니다. autowired 컴포넌트가 필요하다면 java config 파일에 로컬 컴포넌트를 보관하십시오.
이것은 BeanFactory에서 아래 메소드를 호출합니다. 이 설명이 정확한 사용 사례를위한 것임을 설명합니다.
/** * Return an instance, which may be shared or independent, of the specified bean. * <p>Allows for specifying explicit constructor arguments / factory method arguments, * overriding the specified default arguments (if any) in the bean definition. * @param name the name of the bean to retrieve * @param args arguments to use if creating a prototype using explicit arguments to a * static factory method. It is invalid to use a non-null args value in any other case. * @return an instance of the bean * @throws NoSuchBeanDefinitionException if there is no such bean definition * @throws BeanDefinitionStoreException if arguments have been given but * the affected bean isn't a prototype * @throws BeansException if the bean could not be created * @since 2.5 */ Object getBean(String name, Object... args) throws BeansException;
-
==============================
4.Spring 4.3 이후로, 그 문제를 해결하기위한 새로운 방법이 있습니다.
Spring 4.3 이후로, 그 문제를 해결하기위한 새로운 방법이 있습니다.
ObjectProvider - "인수가있는"Prototype 범위 bean에 의존성으로 추가하고 인수를 사용하여 인스턴스화 할 수 있습니다.
다음은이를 사용하는 간단한 예제입니다.
@Configuration public class MyConf { @Bean @Scope(BeanDefinition.SCOPE_PROTOTYPE) public MyPrototype createPrototype(String arg) { return new MyPrototype(arg); } } public class MyPrototype { private String arg; public MyPrototype(String arg) { this.arg = arg; } public void action() { System.out.println(arg); } } @Component public class UsingMyPrototype { private ObjectProvider<MyPrototype> myPrototypeProvider; @Autowired public UsingMyPrototype(ObjectProvider<MyPrototype> myPrototypeProvider) { this.myPrototypeProvider = myPrototypeProvider; } public void usePrototype() { final MyPrototype myPrototype = myPrototypeProvider.getObject("hello"); myPrototype.action(); } }
물론 이것은 usePrototype을 호출 할 때 hello 문자열을 출력합니다.
-
==============================
5.내부 클래스를 사용하는 것과 비슷한 효과를 얻을 수 있습니다.
내부 클래스를 사용하는 것과 비슷한 효과를 얻을 수 있습니다.
@Component class ThingFactory { private final SomeBean someBean; ThingFactory(SomeBean someBean) { this.someBean = someBean; } Thing getInstance(String name) { return new Thing(name); } class Thing { private final String name; Thing(String name) { this.name = name; } void foo() { System.out.format("My name is %s and I can " + "access bean from outer class %s", name, someBean); } } }
from https://stackoverflow.com/questions/22155832/spring-java-config-how-do-you-create-a-prototype-scoped-bean-with-runtime-argu by cc-by-sa and MIT license
'SPRING' 카테고리의 다른 글
[SPRING] java.lang.ClassNotFoundException : org.springframework.web.servlet.DispatcherServlet (0) | 2018.12.06 |
---|---|
[SPRING] Spring Boot에게 어떤 메인 클래스를 실행 가능한 jar 파일로 사용할 지 알려주려면 어떻게해야합니까? (0) | 2018.12.06 |
[SPRING] Spring에서 Session 객체를 얻으려면 어떻게해야합니까? (0) | 2018.12.06 |
[SPRING] 봄 XML 파일 구성 계층 도움말 / 설명 (0) | 2018.12.06 |
[SPRING] 스프링 XML 파일을 스프링 @Configuration 클래스로 변환 (0) | 2018.12.06 |