티스토리 뷰
지난번에 이 글을 쓰면서 Spring의 전반적인 설정을 Xml Config에서 Java Config로 변환을 했었다. 다 쓰고 나서 혼자서만 '이렇게 하면 짠! 하고 변환이 됩니다.' 라고 글을 쓴것 같아 내심 찜찜했다. 사실 나도 급할때는 이런 결과만을 원할때가 있지만 그래도 기왕이면 어떻게 변환을 하는지, 물고기를 잡아주는 방법이 아닌 '낚시를 하는 방법'에 대해서 글을 쓰고자 한다. (그냥 물고기를 원한다면 위의 글에서 바로 확인!)
1. property 가 없는 bean
<bean id="localeResolver" class="org.springframework.web.servlet.i18n.AcceptHeaderLocaleResolver" />
Xml Config에서 위처럼 bean을 선언할때 어떠한 property도 가지고 있지 않은 경우이다.
@Bean
public AcceptHeaderLocaleResolver localeResolver() {
return new AcceptHeaderLocaleResolver();
}
Java Config에서는 @Bean을 하나 만들어주고 return type으로 Xml Config에서 class에 해당하는 class를 넣어준다. 그리고 메소드명은 bean id에 해당하는것을 넣어주면 된다. 만약 bean id 가 없는 bean이라면 클래스명의 앞을 소문자로 바꾼것을 메소드명으로 사용하면 된다. (acceptHeaderLocaleResolver)
그리고 return은 class의 객체를 생성해서 해주면 된다.
2. property가 있는 bean
<bean id="restTemplate" class="org.springframework.web.client.RestTemplate">
<property name="requestFactory">
<bean class="org.springframework.http.client.SimpleClientHttpRequestFactory">
<property name="connectTimeout" value="5000" />
<property name="readTimeout" value="5000" />
</bean>
</property>
<property name="messageConverters">
<list>
<bean class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter" />
</list>
</property>
</bean>
위의 변환의 응용 버전이다. 일단 구조만 만들어 놓자. RestTemplate을 결과적으로 return 해야 하니 다음과 같이 메소드를 만들어 놓는다.
step 1.
@Bean
public RestTemplate restTemplate(){
RestTemplate restTemplate = new RestTemplate();
return restTemplate;
}
아까와는 다르게 restTemplate에 속성을 넣어줘야 하므로 바로 return new restTemplate 하지 않고 객체를 하나 생성을 했다.
step 2.
restTemplate 정의를 한 바로 다음줄에 나오는 requestFactory의 속성값은 어디서 찾을수가 있을까? RestTemplate으로 들어가본다. 찾을수가 없다. 그렇다면 부모클래스에도 들어가보자. InterceptingHttpAccessor가 부모클래스인데 이곳에도 없다. 이것도 부모가 있다. HttpAccessor에 가서 찾을 수 있다.
HttpAccessor.java
public abstract class HttpAccessor {
/** Logger available to subclasses. */
protected final Log logger = HttpLogging.forLogName(getClass());
private ClientHttpRequestFactory requestFactory = new SimpleClientHttpRequestFactory();
... 생략 ...
이렇듯 property로 정의된 값의 속성은 어딘가에는 꼭 이렇게 정의가 되어 있다. 이것을 찾아야한다. 즉 requestFactory는 SimpleClientHttpRequestFactory의 값을 가지고 있어야 한다는 것이다.
@Bean
public RestTemplate restTemplate(){
RestTemplate restTemplate = new RestTemplate();
SimpleClientHttpRequestFactory requestFactory = new SimpleClientHttpRequestFactory();
requestFactory.setConnectTimeout(5000);
requestFactory.setReadTimeout(5000);
restTemplate.setRequestFactory(requestFactory);
return restTemplate;
}
restTemplate이라는 bean의 속성은 requestFactory이고 requestFactory도 속성을 가지고 있다. 따라서 requestFactory도 객체를 생성한 후 속성값을 넣어주고 이 객체를 restTemplate에 넣어주면 된다.
step 3.
property로 requestFactory에 이어 messageConverters를 가지고 있다. 이건 어디서 나온것인가? 바로 RestTemplate 안에 들어있는 속성이다. RestTemplate 안에 들어가면 속성 정보를 파악할 수 있다.
public class RestTemplate extends InterceptingHttpAccessor implements RestOperations {
... 생략 ...
private final List<HttpMessageConverter<?>> messageConverters = new ArrayList<>();
private ResponseErrorHandler errorHandler = new DefaultResponseErrorHandler();
private UriTemplateHandler uriTemplateHandler;
private final ResponseExtractor<HttpHeaders> headersExtractor = new HeadersExtractor();
... 생략 ...
messageConverters가 정의되어 있다. 이 속성의 type은 List<HttpMessageConverter<?>> 이다.
requestFactory와 마찬가지로 messageConverters라는 객체가 있는것이고 그 안에 속성을 정의를 해야한다. 따라서 messageConverters 객체를 먼저 생성해 주도록 하자.
List<HttpMessageConverter<?>> converterList = new ArrayList<HttpMessageConverter<?>>();
이 list에 넣을 값은 HttpMessageConverter type의 객체이다. 여기 예제에서는 MappingJackson2HttpMessageConverter 객체를 사용하기로 했다. 따라서 다음과 같이 Java Code로 작성할 수 있다.
List<HttpMessageConverter<?>> converterList = new ArrayList<HttpMessageConverter<?>>();
HttpMessageConverter<?> jacksonConverter = new MappingJackson2HttpMessageConverter();
converterList.add(jacksonConverter);
이렇게 해서 Java Config로 변환한것을 하나로 합치면
@Bean
public RestTemplate restTemplate(){
RestTemplate restTemplate = new RestTemplate();
SimpleClientHttpRequestFactory requestFactory = new SimpleClientHttpRequestFactory();
requestFactory.setConnectTimeout(5000);
requestFactory.setReadTimeout(5000);
restTemplate.setRequestFactory(requestFactory);
List<HttpMessageConverter<?>> converterList = new ArrayList<HttpMessageConverter<?>>();
HttpMessageConverter<?> jacksonConverter = new MappingJackson2HttpMessageConverter();
converterList.add(jacksonConverter);
restTemplate.setMessageConverters(converterList);
return restTemplate;
}
이렇게 변환을 할 수가 있는것이다.
3. property가 아닌 constructor-arg를 통해 속성을 주입받는 bean
<bean id="messageSourceAccessor" class="org.springframework.context.support.MessageSourceAccessor">
<constructor-arg ref="messageSource" />
</bean>
위처럼 속성값을 생성자를 통해 받는 경우도 있다. 하여간 변환하는 과정에 아리송하다면 해당 class로 직접 들어가서 본다.
MessageSourceAccessor.java
public class MessageSourceAccessor {
private final MessageSource messageSource;
... 생략 ...
public MessageSourceAccessor(MessageSource messageSource) {
this.messageSource = messageSource;
this.defaultLocale = null;
}
MessageSourceAccessor code를 보면 위와 같은 모습이다. 생성자가 있고 Argument로 messageSource를 받고 있다. 이 모습 그대로 우리의 Java Config로 가지고 가면 된다.
@Bean
public MessageSourceAccessor messageSourceAccessor() throws Exception {
return new MessageSourceAccessor(messageSource());
}
messageSource()는 bean으로 정의를 한 메소드이고 이것의 return type은 MessageSource이다. 따라서 이것을 MessageSourceAccessor의 파라미터로 넣어주면 된다.
4. Spring xml namespace를 사용해 생성한 bean
사실 위의 3가지 경우는 변환을 하는데 큰 어려움이 없다. 조금만 해보면 금방 익숙해진다. 하지만 xml namespace를 사용한 Xml Config의 Java Config로의 변환은 어렵다. 우리에게 조금 더 편하게 bean 생성을 할 수 있도록 제공을 하는 xml namespace의 tag들은 전환을 할때는 아주 골치가 아프다. 여기서 말하는 xml namespace의 tag는 <aop:~, <security:~, <mvc:~ 등 우리가 Xml Config에서 사용했던 tag들을 말한다. 이를 변환하려고 namespacehandler, xsd 파일부터 그에 해당하는 BeanDefinitionParser들을 찾아가봤지만 여기에서 우리가 위에서 봤던 방식으로 bean을 생성한다거나 하지는 않고 다른 규칙으로 bean을 바로 정의해버린다. Juergen Hoeller 형만 아는 미지의 세계로 들어가버린다. 그래서 이부분까지 다루지는 않고 이런 xml namespace가 나올때 공식(?)만 몇개 소개를 하려고 한다.
Xml Config | Java Config |
<mvc:~> | @EnableWebMVC or extends WebMvcConfigurationSupport 후 method override |
<context:component-scan> | @ComponentScan |
<beans profile="…"> | @Profile |
<aop:aspectj-autoproxy /> | @EnableAspectJAutoProxy |
<security:~> | @EnableWebSecurity and extends WebSecurityConfigurerAdapter 후 method override |
여기에 나오지 않는 표현들은 이곳을 참조하도록 하자. 웬만한 변환은 거의 다루고 있다.
끝!
'Framework > Spring' 카테고리의 다른 글
Springboot + Redis 연동하는 초간단 샘플 만들어보기 (3) | 2021.01.05 |
---|---|
Spring properties 사용시 UnsatisfiedDependencyException: Could not resolve placeholder 오류 조치 (0) | 2020.12.29 |
spring 프로젝트에서 springboot 프로젝트로 migration 하기 (6) - XML config to JAVA config (0) | 2020.12.16 |
Springboot static resource ERR_ABORTED 404 오류 (1) | 2020.12.16 |
java.lang.IllegalStateException: No thread-bound request found 오류 (0) | 2020.12.15 |