[Spring] <mvc:annotation-driven>

rin·2020년 4월 30일
2
post-thumbnail

spring 설정을 하던 도중 <mvc:annotation driven />이 단순히 어노테이션 설정 방식을 사용하겠다는 것 외에 dispatcher-servlet에 대한 설정을 추가할 수 있음을 알게 되었다.

spring-webmvc 라이브러리를 이용해서 설정할 수 있는 것들은 위의 이미지와 같고 하나씩 알아보도록 하겠다.

annotation-driven

ref. https://gmlwjd9405.github.io/2018/12/18/spring-annotation-enable.html

mvc:annotation-driven

  • Spring MVC 컴포넌트들을 디폴트 설정을 통해 활성화한다.
  • Spring MVC @Controller에 요청을 보내기 위해 필요한 HandlerMappingHandlerAdapter를 Bean으로 등록한다.
    • HandlerMapping : HTTP 요청정보를 이용해서 컨트롤러를 찾아주는 기능
    • HandlerAdapter : HandlerMapping을 통해 찾은 컨트롤러를 직접 실행하는 기능을 수행
  • Bean을 생성하기 위해 xml 파일에 context:component-scan을 명시하면 이 태그를 포함하지 않아도 MVC 어플리케이션을 작동한다.

context:component-scan

  • 특정 패키지 내의 클래스를 스캔하고 Annotation(@Component @Controller @Service @Repository)을 확인한 후 Bean 인스턴스로 생성한다.
  • 이를 이용하면 @Autowired@Qualifier Annotation을 인식할 수 있다.
  • context:component-scan을 선언했다면 context:annotation-config를 선언할 필요가 없다.

context:annotation-config

  • ApplicationContext 안에 이미 등록된 Bean들의 Annotation을 활성화하기 위해 사용된다.
  • Component-scan과의 차이점은 이 설정은 Bean을 등록하는 작업을 수행하지 않는다는 것이다.

1. message-converters

메세지 컨버터는 XML이나 JSON을 이용한 AJAX 기능이나 웹 서비스를 개발할 때 사용할 수 있다.
HTTP 요청 프로퍼티를 모델 오브젝트의 프로퍼티에 개별적으로 바인딩하고 모델 오브젝트를 다시 뷰를 이용해 클라이언트로 보낼 콘텐츠를 만드는 대신, HTTP 요청 메세지 본문과 HTTP 응답 메세지 본문을 통째로 메세지로 다루는 방식이다. 메세지 컨버터는 파라미터의 @RequestBody와 메소드에 부여한 @ResponseBody를 이용한다.

자바 Configuration에서 Spring MVC에 의해 만들어진 기본 컨버터를 대체하기 위해 configureMessageConverters()를 오버라이딩 하거나, 기본 컨버터를 커스텀 마이징하거나 기본 컨버터에 추가적인 컨버터를 더하기 위해 extendMessageConverters()를 오버라이딩 함으로써 HttpMessageConverter를 커스텀 마이징할 수 있다.

다음 예제는 기본 ObjectMapper 대신 사용자 정의된 XML 및 Jackson JSON 변환기를 추가한다.

@Configuration
@EnableWebMvc
public class WebConfiguration implements WebMvcConfigurer {

    @Override
    public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
        Jackson2ObjectMapperBuilder builder = new Jackson2ObjectMapperBuilder()
                .indentOutput(true)
                .dateFormat(new SimpleDateFormat("yyyy-MM-dd"))
                .modulesToInstall(new ParameterNamesModule());
        converters.add(new MappingJackson2HttpMessageConverter(builder.build()));
        converters.add(new MappingJackson2XmlHttpMessageConverter(builder.createXmlMapper(true).build()));
    }
}

위의 예제에서 Jackson2ObjectMapperBuilder는 들여쓰기(indentation enabled), 사용자 지정 데이터 포맷, jackson-module-parameter-names 등록과 함께
MappingJackson2HttpMessageConverterMappingJackson2XmlHttpMessageConverter를 위한 공통 구성을 작성하는데 사용되며 파라미터 이름을 엑세스하는 것을 지원한다. (Java8에 추가)
이 빌더는 Jackson의 기본 프로퍼티들을 다음 설정을 통해 커스텀 마이징 할 수 있다. :

  • DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES → disabled
  • MapperFeature.DEFAULT_VIEW_INCLUSION → disabled

또한 다음과 같은 잘 알려진 모듈이 클래스 경로에서 감지되면 자동으로 등록된다.

  • jackson-datatype-joda : Joda-Time 타입을 서포팅한다.
  • jackson-datatype-jsr310 : Java 8 Date and Time API 타입을 서포팅한다.
  • jackson-datatype-jdk8 : Optional과 같은 다른 Java 8 타입을 서포팅한다.
  • jackson-module-kotlin : Kotlin클래스와 data클래스를 서포팅한다.

Jackson XML 지원으로 들여쓰기(indentation)를 활성화 하려면 jackson-dataformat-xml외에 woodstox-core-asl 의존성이 필요하다.

아래와 같은 다른 흥미로운 Jackson 모듈도 활용할 수 있다.

  • jackson-datatype-money : javax.money 타입을 지원한다. (비공식적 모듈)
  • jackson-datatype-hibernate : Hibernate-specific 타입과 프로퍼티를 지원한다. (aspects 지연로딩을 포함한다.)

다음은 같은 구성을 XML에서 활성화하는 예이다.

<mvc:annotation-driven>
    <mvc:message-converters>
        <bean class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter">
            <property name="objectMapper" ref="objectMapper"/>
        </bean>
        <bean class="org.springframework.http.converter.xml.MappingJackson2XmlHttpMessageConverter">
            <property name="objectMapper" ref="xmlMapper"/>
        </bean>
    </mvc:message-converters>
</mvc:annotation-driven>

<bean id="objectMapper" class="org.springframework.http.converter.json.Jackson2ObjectMapperFactoryBean"
      p:indentOutput="true"
      p:simpleDateFormat="yyyy-MM-dd"
      p:modulesToInstall="com.fasterxml.jackson.module.paramnames.ParameterNamesModule"/>

<bean id="xmlMapper" parent="objectMapper" p:createXmlMapper="true"/>

2. argument-resolvers

ref. https://develop-log-sj.tistory.com/31

Argument Resolver는 사용자가 컨트롤러의 메서드 인자값으로 임의의 값을 전달하려할 때 사용되며 HandlerMethodArgumentResolver를 구현한 클래스를 작성함으로써 사용할 수 있다.

HandlerMethodArgumentResolver는 컨트롤러에 들어오는 파라미터를 커스텀 마이징하는데 사용하는데, 컨트롤러에 들어가기 전에 클라이언트로부터 받은 파라미터들을 원하는 것으로 가공해서 컨트롤러로 전달한다.

interface인 HandlerMethodArgumentResolver를 구현한 클래스를 작성한다.
supportsParameter 메소드를 오버라이딩하여 원하는 타입의 Argument가 존재하는지 검사한 후 true/false를 리턴한다.
만약 supportsParameter의 반환값이 true일 경우 오버라이딩된 resolveArgument 메소드가 수행되고 ArgumentResolver를 사용하는 메소드에 전달할 값을 반환한다.

java Config에 설정:
WebMvcConfigureraddArgumentResolvers메소드를 오버라이딩한다.

@Bean
    public MyArgumentResolver getMyArgumentResolver(){
        return new MyArgumentResolver();
    }

    @Override
    public void addArgumentResolvers(List<HandlerMethodArgumentResolver> resolvers) {
        resolvers.add(getMyArgumentResolver());
    }

xml 파일에 설정:

    <mvc:annotation-driven>
        <mvc:argument-resolvers>
            <bean class="아규먼트리졸버클래스"></bean>      
        </mvc:argument-resolvers>
    </mvc:annotation-driven>

3. async-support

비동기 요청

ref. Asynchronous Request
Spring MVC는 Servlet 3.0 비동기 요청 처리와 광범위하게 통합되어 있다.

  • DeferredResultCallable는 컨트롤러 메소드 내에서 값을 반환하고, 하나의 비동기 반환 값에 대한 기본적인 지원을 제공한다.
  • 컨트롤러는 SSE낮은 수준의 데이터(raw data)를 포함한 여러 값을 스트리밍 할 수 있다.
  • 컨트롤러는 응답 처리를 위해 반응형 클라이언트를 사용하고, 반응 타입을 반환 할 수 있다.

Configuration

ref. Asynchronous Requests - Configuration
비동기 request를 처리하기위한 기능은 서블릿 컨테이너 수준에서 활성화되어야 한다. MVC configuration은 비동기 요청에 대한 몇 가지 옵션도 제공한다.

Servlet Container

필터와 서블릿 선언은 비동기 요청 처리를 활성화하는 것을 true로 설정하기 위한 비동기 지원 플래그를 가지고 있다. 또한, ASYNC javax.servlet.DispatchType를 처리하기 위해 필터 매핑을 선언해야 한다.

Java Configuration에서, 서블릿 컨테이너를 초기화하기 위해 AbstractAnnotationConfigDispatcherServletInitializer를 사용할 때, 이는 자동적으로 수행된다.

web.xml Configuration에서는 <async-supported>true</async-supported>DispatcherServletFilter 선언에 추가해야한다. 또한 필터 매핑에 <dispatcher>ASYNC</dispatcher>도 추가해야한다.

Spring MVC

MVC configuration에서 비동기 요청 처리와 관련하여 다음의 옵션들을 사용할 수 있다.

  • Java configuration : configureAsyncSupport 콜백을 WebMvcConfigurer에서 사용하라.
  • XML namespace : <async-support> 엘리먼트를 <mvc:annotation-driven>의 하위에 사용하라.

다음을 구성할 수 있다.:

  • 명시적으로 설정되지 않았다면 비동기 요청에 대한 기본 타임아웃 값은 기본 서블릿 컨테이너에 따라 다르다.
  • 다음과 같은 목적을 위해 AsyncTaskExecutor를 사용한다.
    • 반응형 타입으로 스트리밍할 때 쓰는 것을 차단하기 위해서
    • 컨트롤러 메서드로부터 반환된 Callable 인스턴스를 실행하기 위해서
  • 따라서 기본적으론 SimpleAsyncTaskExecutor으로 설정되므로, 반응형 타입으로 스트리밍하거나 Callable을 반환하는 컨트롤러 메서드가 있는 경우에는 AsyncTaskExecutor을 구성할 것을 적극 권장한다.
  • DeferredResultProcessingInterceptor 구현
  • CallableProcessingInterceptor 구현

DeferredResult, ResponseBodyEmitter, SseEmitter에 대한 기본적인 타임아웃 값을 정할 수 있다. Callable에서는 WebAsyncTask을 사용해 타임아웃 값을 제공 할 수 있다.

4. path-matching

Ant path pattern

근원은 자바 기반의 빌드 도구인 Ant에서 ?, *, **을 이용한 패턴으로 경로를 명시하는 것에서 시작한다.
URL mapping 등 경로를 지정할 때 사용하는 경로 패턴으로 현재는 Spring 등 다양한 곳에서 이용하고 있다.

패턴의미
?1개의 문자와 매칭
*0개 이상의 문자와 매칭
**0개 이상의 디렉토리와 매칭

스프링은 특정 경로가 Ant 경로 패턴 경로와 일치하는지 여부를 확인할 때 사용하는 AntPathMatcher 클래스를 제공한다. 이 클래스가 가지고 있는 boolean match(String pattern, String path) 메서드를 사용해 path가 Ant패턴인 pattern에 매칭되는지 확인한다.

interface PathMatcher
문자열 기반 경로 일치를 위한 전략 인터페이스.
기본 구현은 AntPathMatcher로, Ant-style 패턴 구문을 지원한다.

class AntPathMatcher
Ant 스타일 경로 패턴을 위한 PathMatcher 구현.
이 매핑 코드의 일부는 Apache Ant로부터 빌려왔다.
?, *, ** 외에 {spring:[a-z]+} 는 "spring"이라는 이름의 경로 변수로써 regexp [a-z]+와 일치한다.

패턴과 경로는 둘 다 절대적이거나 둘 다 상대적이어야 서로 일치한다. 따라서 이를 구현하는 사용자는 패턴을 정제하여 사용하는 맥락에서 "/"를 접두사로 사용하기를 권장한다.

path-matching

패스 매칭과 URL 처리와 관련된 옵션을 커스텀 마이징 할 수 있다. 개별 옵션에 대한 자세한 내용은 PathMatchConfigurer javadoc을 참조하라.

다음은 Java Configuration에서 패스 매칭을 커스텀 마이징 하는 방법에 대해 보여준다.

@Configuration
@EnableWebMvc
public class WebConfig implements WebMvcConfigurer {

    @Override
    public void configurePathMatch(PathMatchConfigurer configurer) {
        configurer
            .setUseTrailingSlashMatch(false)
            .setUseRegisteredSuffixPatternMatch(true)
            .setPathMatcher(antPathMatcher())
            .setUrlPathHelper(urlPathHelper())
            .addPathPrefix("/api", HandlerTypePredicate.forAnnotation(RestController.class));
    }

    @Bean
    public UrlPathHelper urlPathHelper() {
        //...
    }

    @Bean
    public PathMatcher antPathMatcher() {
        //...
    }
}

다음 예제는 같은 설정을 XML에서 활성화하는 방법을 보여준다.

<mvc:annotation-driven>
    <mvc:path-matching
        trailing-slash="false"
        registered-suffixes-only="true"
        path-helper="pathHelper"
        path-matcher="pathMatcher"/>
</mvc:annotation-driven>

<bean id="pathHelper" class="org.example.app.MyPathHelper"/>
<bean id="pathMatcher" class="org.example.app.MyPathMatcher"/>

5. return-value-handlers

Argument Resolver가 컨트롤러에 들어오는 파라미터를 가공하는데 사용되었다면, RetunValueHandler는 반대로 리턴 타입으로 판단하여 분기 시킬 수 있다.
즉, 컨트롤러를 실행한 결과값을 처리하는 클래스이다.
RequestMappingHanlderAdapter에서 전달받은 RetunValueHandler를 사용하여 핸들러 메서드의 리턴 값을 핸들링한다. 이 때, @ResponseBody 어노테이션이 사용된 핸들러 메서드라면 MessageConverter를 사용해 리턴 값을 Json 포맷으로 변환한다.

ModelAndViewResolver가 남아있기는 하나, HandlerMethodReturnValueHandler를 사용하는 것을 권장한다. HandlerMethodReturnValueHandler를 우선 검색한 뒤 리턴 된 클래스를 처리하는 핸들러를 찾지못한 경우에만 ModelAndViewResolver를 검색한다.


작동 방식이나 메서드는 Argument Resolver와 굉장히 유사하다.

  • boolean supportsReturnType(MethodParameter returnType) : MethodParameter로 주어진 타입이 이 핸들러가 서포팅 할 수 있는지 판단한다.
  • void handleReturnValue : supportsReturnType가 true인 경우에만 해당 메서드가 호출된다. Model에 어트리뷰트 값을 추가하고, 뷰를 셋팅하거나 ModelAndViewContainer 플래그를 true 셋팅함으로써 해당 응답이 직접 처리되었음을 명시하여 주어진 반환값을 처리한다.
	<mvc:annotation-driven>
		<mvc:return-value-handlers>
			<bean class="RequestMappingHanlderAdapter구현체 클래스"/>
			<ref bean="customHandler" />
		</mvc:return-value-handlers>
	</mvc:annotation-driven>

	<bean id="customHandler" class="RequestMappingHanlderAdapter구현체 클래스"/>
profile
🌱 😈💻 🌱

0개의 댓글