본문 바로가기
공부/프로그래밍

[spring] 테스트 중 Unable to initialize 'javax.el.ExpressionFactory' 해결하기 - validator 2.0.0(JSR-380) 적용

by demonic_ 2020. 3. 17.
반응형

레거시 프로그램에 validator를 적용하기로 했다.

validator는 총 3가지 버전이 있는데 다음과 같다

 

Bean Validation 1.0(JSR-303)

Bean Validation 1.1(JSR-349)

Bean Validation 2.0(JSR-380)

 

그래서 추가하기 위해서 다음의 패키지를 추가한다

<dependency>
	<groupId>javax.validation</groupId>
	<artifactId>validation-api</artifactId>
	<version>2.0.1.Final</version>
</dependency>
<dependency>
	<groupId>org.hibernate</groupId>
	<artifactId>hibernate-validator</artifactId>
	<version>6.0.17.Final</version>
</dependency>

 

그리고 servlet.xml 에 bean을 등록했다

<bean id="validator" class="org.springframework.validation.beanvalidation.LocalValidatorFactoryBean" />

 

테스트를 위해 Controller에 Post API를 하나 생성한다.

@RestController
public class TestValidatorController {

    @PostMapping("/test/validator")
    public String testValidator(@RequestBody @Valid TestValidatorRequest request){

        System.out.println("request = " + request);
        return request.toString();
    }

    @Getter
    @NoArgsConstructor(access = AccessLevel.PROTECTED)
    @ToString
    static class TestValidatorRequest {
        @NotEmpty(message = "이름은 필수 입니다")
        private String name;
        private int age;
    }
}

 

 

POSTMAN이나 CURL을 통해 /test/validator 를 호출하면 @Valid 가 동작하는지 확인한다

 

정상건

curl -X POST localhost:8080/test/validator \
-H "Content-Type: application/json" \
-d '{"name":"테스터","age":"10"}'


request = TestValidatorController.TestValidatorRequest(name=테스터, age=10, gender=null)

 

validator 오류 확인하기

curl -X POST localhost:8080/test/validator \
-H "Content-Type: application/json" \
-d '{"age":"10"}'


Resolved [org.springframework.web.bind.MethodArgumentNotValidException: Validation failed for argument [0] in public java.lang.String kr.dgpark.test.TestValidatorController.testValidator(kr.dgpark.test.TestValidatorController$TestValidatorRequest): [Field error in object 'testValidatorRequest' on field 'name': rejected value [null]; 
codes [NotEmpty.testValidatorRequest.name,NotEmpty.name,NotEmpty.java.lang.String,NotEmpty]; 
arguments [org.springframework.context.support.DefaultMessageSourceResolvable: 
codes [testValidatorRequest.name,name]; 
arguments []; 
default message [name]]; default message [이름은 필수 입니다]] ]

 

 

의도한대로 이름값을 넣지않아 에러가 발생함을 확인했다.

문제는 테스트를 작성하면서였다. (사실 테스트 먼저 작성하다 에러가 발생해서 완전 헛다리 짚고 있었다....)

Test를 하는데 다음의 에러가 발생한다.

@Test
@DisplayName("validator 테스트")
void validatorTest() throws Exception {
    // given
    Map param = new HashMap<>();
    param.put("age", 10);
    String content = objectMapper.writeValueAsString(param);

    // when
    MvcResult mvcResult = mockMvc.perform(post("/test/validator")
            .contentType(MediaType.APPLICATION_JSON_VALUE)
            .content(content))
            .andReturn();

    // then
    MethodArgumentNotValidException resolvedException = (MethodArgumentNotValidException) mvcResult.getResolvedException();
    List<FieldError> fieldErrors = resolvedException.getBindingResult().getFieldErrors();
    for (FieldError fieldError : fieldErrors) {
        System.out.println("fieldError.getDefaultMessage() = " + fieldError.getDefaultMessage());
    }
}

 

'Unable to initialize 'javax.el.ExpressionFactory'. Check that you have the EL dependencies on the classpath, or use' 문구 그대로 검색하면 maven이나 gradle에 다음의 패키지를 추가하라고 한다.

<dependency>
	<groupId>javax.el</groupId>
	<artifactId>javax.el-api</artifactId>
	<version>3.0.0</version>
</dependency>
<dependency>
	<groupId>org.glassfish</groupId>
	<artifactId>javax.el</artifactId>
	<version>3.0.0</version>
</dependency>

 

그럼에도 불구하고 동일한 현상이 반복되었다.

 

그러다 몇몇 블로그에서 버전을 낮추면(Bean Validation 2.0(JSR-380) => 1.1(JSR-349)) 사용할 수 있다고 해서 해봤는데 되긴된다.

만약 버전을 낮춰서 사용할 거라면 의존성을 다음으로 교체하면 된다.

<dependency>
	<groupId>javax.validation</groupId>
	<artifactId>validation-api</artifactId>
	<version>1.1.0.Final</version>
</dependency>
<dependency>
	<groupId>org.hibernate</groupId>
	<artifactId>hibernate-validator</artifactId>
	<version>5.1.3.Final</version>
</dependency>

 

 

다만 개인적으로 Validation 2.0을 써보고 싶어서 좀더 알아보니 우연히 stackoverflow에서 jsp-api 버전을 변경해보라고 써 있었다.

이전엔 2.1 이었는데 2.2로 변경해봤다.

<dependency>
	<groupId>javax.servlet.jsp</groupId>
	<artifactId>jsp-api</artifactId>
<!--	<version>2.1</version> -->
		<version>2.2</version>
	<scope>provided</scope>
</dependency>

 

테스트를 돌려보니 이전의 오류는 없어졌고 테스트가 완료되었다.

fieldError.getDefaultMessage() = 이름은 필수 입니다

 

stackoverflow에 있는 글을 여기다 복사한다.

There are newer versions of that dependency that you might want to take a look at. When using the 2.1 version there were no validators being registered/added to ExtendedServletRequestDataBinder or more precisely the SpringValidatorAdapter targetValidator was null.

The root cause was that the exception below was being thrown in OptionalValidatorFactoryBean.afterPropertiesSet

javax.validation.ValidationException: HV000183: Unable to initialize 'javax.el.ExpressionFactory'. Check that you have the EL dependencies on the classpath, or use ParameterMessageInterpolator instead

 

 

2.1 버전에서는 SpringValidatorAdapter targetValidator가 null이고 본 문제는 OptionalValidatorFactoryBean에서 HV000183: Unable to initialize 'javax.el.ExpressionFactory' 가 발생했기 때문이다.

 

혹시나 jsp-api 패키지를 빼보니 다음 에러가 발생했다.

Caused by: java.lang.NoClassDefFoundError: javax/servlet/jsp/JspFactory
	at org.springframework.web.servlet.view.tiles3.TilesConfigurer$SpringTilesContainerFactory.createAttributeEvaluatorFactory(TilesConfigurer.java:386)
	at org.apache.tiles.factory.BasicTilesContainerFactory.createContainer(BasicTilesContainerFactory.java:88)
	at org.apache.tiles.startup.AbstractTilesInitializer.createContainer(AbstractTilesInitializer.java:114)
	at org.apache.tiles.startup.AbstractTilesInitializer.initialize(AbstractTilesInitializer.java:64)
	at org.springframework.web.servlet.view.tiles3.TilesConfigurer.afterPropertiesSet(TilesConfigurer.java:279)
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1837)
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1774)
	... 68 more
Caused by: java.lang.ClassNotFoundException: javax.servlet.jsp.JspFactory
	at java.net.URLClassLoader.findClass(URLClassLoader.java:382)
	at java.lang.ClassLoader.loadClass(ClassLoader.java:424)
	at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:349)
	at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
	... 75 more

 

 

Springboot 에는 없어도 잘 돌드만... 왜 그런지는 좀더 공부해봐야 할거 같다.

 

 

정리하자면,

1)

JSR-380 버전을 사용할거라면 체크해봐야 할 dependency는

<dependency>
	<groupId>javax.el</groupId>
	<artifactId>javax.el-api</artifactId>
	<version>3.0.0</version>
</dependency>
<dependency>
	<groupId>org.glassfish</groupId>
	<artifactId>javax.el</artifactId>
	<version>3.0.0</version>
</dependency>
<dependency>
	<groupId>javax.validation</groupId>
	<artifactId>validation-api</artifactId>
	<version>2.0.1.Final</version>
</dependency>
<dependency>
	<groupId>org.hibernate</groupId>
	<artifactId>hibernate-validator</artifactId>
	<version>6.0.17.Final</version>
</dependency>

 

 

 

2)

jsp-api 버전을 확인한다

<dependency>
	<groupId>javax.servlet.jsp</groupId>
	<artifactId>jsp-api</artifactId>
		<version>2.2</version>
	<scope>provided</scope>
</dependency>

 

 

끝.

 

 

 

참조:

https://medium.com/@gaemi/java-%EC%99%80-spring-%EC%9D%98-validation-b5191a113f5c

 

Java 와 Spring 의 Validation

이 글은 Bean Validation 과 그 구현체인 Hibernate Validator 그리고 Spring Validation 에 대해서 다루고 있습니다.

medium.com

https://knight76.tistory.com/entry/javaxvalidationValidationException-HV000183-Unable-to-initialize-javaxelExpressionFactory-%ED%95%B4%EA%B2%B0%ED%95%98%EA%B8%B0

 

javax.validation.ValidationException: HV000183: Unable to initialize 'javax.el.ExpressionFactory'. 해결하기

javax.validation.ValidationException: HV000183: Unable to initialize 'javax.el.ExpressionFactory'. 에러가 발생하면 라이브러를 추가한다. 2018-07-30T16:04:06,790 DEBUG o.s.b.f.s.DefaultListableBeanFac..

knight76.tistory.com

https://stackoverflow.com/questions/44984909/test-valid-annotation-with-springmvc-maven?noredirect=1&lq=1

 

Test @Valid annotation with SpringMVC Maven

I am working on the chapter 5 example of the book Spring in Action 4. When I am running the Test classes I found one of the @Valid test is not working, it appears as if the validation never occured(

stackoverflow.com

 

반응형

댓글