[spring] 테스트 중 Unable to initialize 'javax.el.ExpressionFactory...

[spring] 테스트 중 Unable to initialize 'javax.el.ExpressionFactory...

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

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

Bean Validation 1.0(JSR-303)

Bean Validation 1.1(JSR-349)

Bean Validation 2.0(JSR-380)

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

javax.validation validation-api 2.0.1.Final org.hibernate hibernate-validator 6.0.17.Final

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

테스트를 위해 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 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에 다음의 패키지를 추가하라고 한다.

javax.el javax.el-api 3.0.0 org.glassfish javax.el 3.0.0

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

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

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

javax.validation validation-api 1.1.0.Final org.hibernate hibernate-validator 5.1.3.Final

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

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

javax.servlet.jsp jsp-api 2.2 provided

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

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는

javax.el javax.el-api 3.0.0 org.glassfish javax.el 3.0.0 javax.validation validation-api 2.0.1.Final org.hibernate hibernate-validator 6.0.17.Final

2)

jsp-api 버전을 확인한다

javax.servlet.jsp jsp-api 2.2 provided

끝.

참조:

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

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

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

from http://lemontia.tistory.com/925 by ccl(A) rewrite - 2020-03-17 08:54:27

댓글

이 블로그의 인기 게시물

2020 LCK 롤챔스 Spring 경기 재개 및 일정

데이터 바인딩 추상화 - propertyEditor

Spring Web Form