Spring Java Config: 런타임 인수를 사용하여 프로토타입 범위 @Bean을 만드는 방법
Spring의 Java Config를 사용하여, 런타임에만 얻을 수 있는 생성자 인수가 있는 프로토타입 범위 콩을 획득/인스턴트해야 한다.다음 코드 예제를 고려하십시오(간결성을 위해 단순화됨).
@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;
}
}
공지name
이다final
할 수 시공자를 통해서만 공급할 수 있으며, 불변성을 보장한다.또 다.Thing
요청 처리기 구현에 대해 (연관적으로) 알 수 없어야 한다.
이 코드는 다음과 같은 Spring XML 구성과 완벽하게 호환된다.
<bean id="thing", class="com.whatever.Thing" scope="prototype">
<!-- other post-instantiation properties omitted -->
</bean>
Java 구성을 사용하여 동일한 작업을 수행하는 방법다음은 Spring 3.x를 사용하지 않는다.
@Bean
@Scope("prototype")
public Thing thing(String name) {
return new Thing(name);
}
이제 공장을 만들 수 있을 겁니다. 예를 들어:
public interface ThingFactory {
public Thing createThing(String name);
}
그러나 이는 Spring을 사용하여 ServiceLocator 및 Factory 설계 패턴을 대체하는 전체 포인트를 능가하는 것으로, 이 사용 사례에 이상적일 것이다.
Spring Java Config가 이 작업을 수행할 수 있다면 다음을 피할 수 있을 것이다.
- 공장 인터페이스 정의
- 공장 구현 정의
- 공장 구현을 위한 필기 테스트
스프링이 XML 구성을 통해 이미 지원하는 사소한 일에는 엄청난 양의 작업(상대적으로 말하면)을 할 수 있다.
@Configuration
,@Bean
그와 같은 방법
@Bean
@Scope("prototype")
public Thing thing(String name) {
return new Thing(name);
}
콩 정의를 등록하고 콩을 만드는 공장을 제공하기 위해 사용된다.정의한 콩은 직접 또는 스캔을 통해 결정된 인수를 사용하여 요청 시에만 인스턴스화된다.ApplicationContext
.
의 .prototype
콩, 새로운 물체가 매번 생성되어 그에 상응하는 것@Bean
방법 또한 실행된다.
콩 한 냥을 건져올 수 있다.ApplicationContext
그 방법에 의하여.
명시적 생성자 인수/공장 방법 인수를 지정하고 빈 정의에서 지정된 기본 인수(있는 경우)를 재정의할 수 있다.
매개 변수:
정적 공장 방법에 대한 명시적 인수를 사용하여 프로토타입을 작성할 경우 사용할 아그스 인수.다른 경우에는 null이 아닌 arg 값을 사용하는 것이 무효다.
다시 말해, 이것을 위해.prototype
의 범위,, 의 구성자가 의 팥의 팥이 사용될 @Bean
방법 호출(이 방법은 콩의 이름 조회를 사용하기 때문에 유형 보증이 매우 약하다.)
또는 종류별로 콩을 찾아보는 타이핑 방식을 사용할 수 있다.
적어도 봄 버전 4+에서는 그렇다.
다음 항목으로 시작하지 않으려면ApplicationContext
또는BeanFactory
콩을 회수하기 위해, 당신은 (4.3년 봄 이후)을 주입할 수 있다.
의의 .
ObjectFactory
프로그램적 옵션성과 관대한 미사용 취급이 가능하도록 특별히 주입 지점에 맞게 설계되었다.
그리고 그 방법을 사용하라.
이 공장에서 관리하는 객체의 인스턴스(공유 또는 독립)를 반환하십시오.
명시적
BeanFactory.getBean(String, Object)
.
예를 들어,
@Autowired
private ObjectProvider<Thing> things;
[...]
Thing newThing = things.getObject(name);
[...]
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
세 개 이상의 매개변수를 전달한다.
봄 4.3 이후, 그 문제를 위해 꿰맨 새로운 방법이 있다.
ObjectProvider - "주장된" 프로토타입 범위 콩에 의존성으로 추가하고 인수를 사용하여 콩을 인스턴스화할 수 있도록 해준다.
사용 방법의 간단한 예는 다음과 같다.
@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 문자열을 인쇄한다.
의견당 업데이트됨
첫째, 당신이 왜 "이것은 효과가 없다"고 말하는지 잘 모르겠다. 나는 당신의 구성이 어딘가 잘못된 것이 틀림없다고 의심한다.
이 작업은 다음과 같다.
-- 구성 파일:
@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
그래서 '싱글톤' 빈은 두 번 요청받는다.그러나 우리가 예상한 대로 봄은 그것을 단 한번만 만들어낸다.두 번째로 그것이 그 콩을 가지고 있는 것을 보고 기존의 물체를 돌려준다.생성자(@Bean method)는 두 번째로 호출되지 않는다.이에 대해, 동일한 컨텍스트 객체로부터 '프로토타이프' Bean을 두 번 요청할 때, 우리는 출력에서 참조가 변경되고 생성자(@Bean 방법) IS가 두 번 호출되는 것을 본다.
그렇다면 문제는 프로토타입에 어떻게 싱글톤을 주입하느냐 하는 겁니다.위의 구성 클래스에서도 이 작업을 수행하는 방법을 보여준다.그러한 모든 참조를 시공자에게 전달해야 한다.이렇게 하면 생성된 클래스가 순수한 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를 쓴다면 당신은 모든 @Autoored 없이 이 수업을 만들게 되어 매우 행복할 것이다.자동 생성된 구성 요소가 필요한 경우 해당 구성 요소를 Java 구성 파일에 로컬로 유지하십시오.
이것은 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;
내부 클래스만 사용해도 비슷한 효과를 얻을 수 있다.
@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);
}
}
}
적합한 콩을 생성해야 하는 경우 다음과 같이 하십시오.
@Configuration
public class ThingConfiguration {
@Bean
@Scope(SCOPE_PROTOTYPE)
public Thing simpleThing(String name) {
return new Thing(name);
}
@Bean
@Scope(SCOPE_PROTOTYPE)
public Thing specialThing(String name) {
Thing thing = new Thing(name);
// some special configuration
return thing;
}
}
// Usage
@Autowired
private ApplicationContext context;
AutowireCapableBeanFactory beanFactory = context.getAutowireCapableBeanFactory();
((DefaultListableBeanFactory) beanFactory).getBean("specialThing", Thing.class, "name");
약간 다른 접근으로 늦은 답변.그것은 이 질문 자체를 언급하는 최근의 질문의 후속이다.
네, 아까 말한 바와 같이 파라미터를 받아들이는 원두 원형을 선언할 수 있다.@Configuration
각 주입 시 새로운 콩을 만들 수 있는 클래스.
그렇게 되면 이렇게 될 것이다.@Configuration
공장을 분류하고 이 공장에 너무 많은 책임을 부여하지 않기 위해, 여기에는 다른 콩이 포함되지 않아야 한다.
@Configuration
public class ServiceFactory {
@Bean
@Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public Thing thing(String name) {
return new Thing(name);
}
}
하지만 구성 콩을 주입하여Thing
s:
@Autowired
private ServiceFactory serviceFactory;
public void onRequest(Request request) {
//request is already validated
String name = request.getParameter("name");
Thing thing = serviceFactory.thing(name); // create a new bean at each invocation
// ...
}
그것은 유형 안전하면서도 간결하다.
콩 xml 파일에서 속성 범위="complete"를 사용하십시오.
'Programing' 카테고리의 다른 글
선행 0으로 Java 문자열을 포맷하는 방법 (0) | 2022.05.15 |
---|---|
OpenGL 메서드만 사용하여 텍스트를 그리는 방법 (0) | 2022.05.15 |
vue-i18n - '알 수 없는' 토큰 유형 감지 (0) | 2022.05.14 |
vuejs에서 재사용 가능한 api-properties 구성 요소를 구현하는 방법? (0) | 2022.05.14 |
java : float를 String으로, String을 Float로 변환 (0) | 2022.05.14 |