@Test publicvoidtest5(){ ArrayList arrayList = new ArrayList(); arrayList.setNames(Arrays.asList("woman", "man"));
ExpressionParser parser = new SpelExpressionParser(); Expression exp = parser.parseExpression("names[0]"); EvaluationContext evaluationContext = new StandardEvaluationContext(arrayList); // rootObject를 여기에 바로 쓸수도 있다. String message = exp.getValue(evaluationContext, String.class);
System.out.println(message); }
1
woman
값을 가져오는 것 말고도 값을 변경할수도 있습니다. 유의 해야할 점은 값을 추가해주는게 아닌 변경해주는 것입니다.
1 2 3 4 5 6 7 8 9 10 11 12
@Test publicvoidtest6(){ ArrayLists arrayLists = new ArrayLists(); arrayLists.setNames(Arrays.asList("man"));
ExpressionParser parser = new SpelExpressionParser(); Expression exp = parser.parseExpression("names[0]"); EvaluationContext evaluationContext = new StandardEvaluationContext(arrayLists); exp.setValue(evaluationContext, "woman");
값을 변경해주는 기능은 배열 뿐 아니라 일반 객체에서도 사용 가능합니다. 이때 필드는 public 이거나 setter 메소드가 구현되어 있어야 합니다.
1 2 3 4 5 6 7 8 9 10 11
@Test publicvoidtest7(){ SpELVO spELVO = new SpELVO("jesi");
ExpressionParser parser = new SpelExpressionParser(); Expression exp = parser.parseExpression("name"); EvaluationContext evaluationContext = new StandardEvaluationContext(spELVO); exp.setValue(evaluationContext, "sara");
System.out.println(spELVO.getName()); }
SpelParserConfiguration
SpelParserConfiguration를 사용 하면 Collection에서 index가 null일 경우에 자동으로 생성해주는 기능을 활성화 할수 있습니다.
아래의 예제에서는 배열에서 index가 null인 요소에 접근하려고 할때 자동으로 배열에 빈값이 추가된것을 확인 할수 있습니다. SpelParserConfiguration 생성자의 두번째 파라미터를 true로 전달하면 기능을 활성화 할수 있습니다(기본값은 두개 모두 false).
1 2 3 4 5 6 7 8 9 10 11 12
@Test publicvoidtest8(){ ArrayLists arrayLists = new ArrayLists();
SpelParserConfiguration configuration = new SpelParserConfiguration(false, true); ExpressionParser parser = new SpelExpressionParser(configuration); Expression expression = parser.parseExpression("names[4]"); String name = expression.getValue(arrayLists, String.class);
@Test publicvoidtestValidation(){ Event event = new Event(); // 타겟 객체 EventValidator eventValidator = new EventValidator(); // 검증 Validator
// BeanPropertyBindingResult는 Erros와 BindingResult의 구현체로써 보통은 웹에서는 MVC가 해당 객체를 생성하기 때문에 직접 생성할 일은 적다. Errors errors = new BeanPropertyBindingResult(event, "event");
스프링에서 제공하는 ApplicationEventPublisher를 이용해서 이벤트를 퍼블리싱 할수 있습니다. ApplicationEventPublisher는 스프링에서 Bean으로 등록 되어 있어 @Autowired로 바로 사용할수 있습니다. publishEvent 메소드를 통해서 퍼블리싱 하면 등록 된 리스너 객체가 호출되게 됩니다.
사용자 입력값을 어플리케이션 도메인 모델이 동적으로 변환해 넣어주는 기능입니다. 클라이언트 입력값은 대부분 문자열인데, 그 값을 객체가 가지고 있는 int, long, boolean, date 등 심지어 Event, Book 과 같은 도메인 타입으로 변환해서 넣어주는 기능입니다. MVC할때 클라이언트에서 받은 데이터를 객체에 넣어주는 기능이 해당 기능입니다.
PropertyEditor
스프링 3.0 이전까지 DataBinder가 변환 작업시에 사용하던 인터페이스입니다.
단점
PropertyEditor는 값을 set 하는 경우 상태를 저장
PropertyEditor가 값을 set하는 경우 쓰레드마다 값을 공유할수 있어 쓰레드-세이프하지 않습니다. 그렇기 때문에 PropertyEditor의 구현체는 여러 쓰레드에서 공유해서 사용하서는 안됩니다.
@Test public void test() { ApplicationContext ctx = new ClassPathXmlApplicationContext("spring/spring-bean.xml"); DataBindingBean dataBindingBean = (DataBindingBean) ctx.getBean("dataBindingBean"); System.out.println(dataBindingBean.toString()); } }
위의 코드를 작성 한 후에 실행 해보면 아래와 같이 타입이 맞지 않다고 에러를 발생시킵니다. xml에서 모든 값을 문자열로 넘겨주었으니 타입이 맞지 않다는 에러가 나오는 것이 당연합니다. 이제 문자열로 값이 넘어오더라도 타입게 맞게 데이터 바인딩이 되도록 수정해 보도록 하겠습니다.
1
...Failed to convert property value of type [java.lang.String] to required type [java.util.Date] for property 'date'...
CustomEditorConfigurer 등록하기
4.0 이전 버전
DataBinding에서 사용이 되는 CustomEditorConfigurer의 변화로 인해서 4.0 이전 버전과 이후 버전의 설정이 변경 되었습니다.
spring-bean.xml
4.0 이전 버전에서는 customEditors의 type이 Map<String, ?> 이었습니다. 아래와 같이 PropertyEditor를 구현한 클래스들을 Bean으로 등록해 주면 됩니다.
변화가 되면서 PropertyEditor 구현체가 default 생성자가 없으면 아래처럼 에러가 발생합니다.
1
No default constructor found; nested exception is java.lang.NoSuchMethodException: org.springframework.beans.propertyeditors.CustomDateEditor.<init>()
위의 방법은 모든 데이터바인딩에 적용이 되는 것임. DataBindingBean를 위해서 작성 된게 아니라 문자열로 넘어온 값을 Date 또는 Boolean으로 데이터바인딩해야 할 모든 상황에서 사용되게 됨. 데이터바인딩에서 사용될 사용자 PropertyEditor 구현체를 등록 하는 절차임.
PropertyEditorRegistrar
PropertyEditorRegistrar를 구현함으로써 PropertyEditor 등록을 커스텀하게 작성할수 있습니다. 이를 이용하면 default 생성자가 없는 경우에도 등록 할수 있습니다.
스프링의 Resource 객체는 java.net.URL을 추상화한 인터페이스입니다. Resource 객체는 스프링 내부에서 가장 많이 사용이 되는 인터페이스이며 스프링 IoC 컨테이너가 생성 될때, 컨테이너 설정 정보를 담는 파일들을 가져올때도 사용합니다.
Resource 인터페이스를 통해 추상화한 이유 java.net.URL의 한계로 클래스 패스를 기준으로 리소스를 읽어오는 기능이 존재하지 않기 때문입니다.
주요 메소드
exists
isOpen
isFile
isDirectory
getFile(항상 파일로 가져올수 있는 것은 아님)
구현체
UrlResource : URL을 기준으로 리소스를 읽어들이며 기본으로 제공하는 프로토콜에는 http, https, ftp, file, jar
ClassPathResource : 클래스패스를 기준으로 리소스를 읽어들이며 접두어로 classapth:를 사용
FileSystemResource : 파일 시스템을 기준으로 읽어들임
ServletContextResource : 웹 어플리케이션 루트에서 상대경로로 리소스를 읽어들임
ResourceLoader
ResourceLoader는 리소스를 읽어오는 기능을 제공하는 인터페이스 입니다. ApplicationContext도 ResourceLoader를 상속하고 있습니다. 기능은 말그대로 리소스를 읽어오는 기능만 제공하고 있습니다.
구현체
DefaultResourceLoader : UrlResource(경로가 http, https 등 프로토콜로 시작)와 ClassPathResource(경로가 classapth:로 시작)를 가져올때 사용
FileSystemResourceLoader : DefaultResourceLoader를 상속하고 있으며 경로가 /로 시작하는 경우 FileSystemResource를 반환.
GenericWebApplicationContext : DefaultResourceLoader를 상속하고 있으며 경로가 /로 시작하는 경우 ServletContextResource를 반환
DefaultResourceLoader를 직접 상속 하고 있지는 않음
리소스를 가져오는 코드는 아래와 같습니다.
1
resourceLoader.getResource("location 문자열");
Resource의 타입과 ApplicationContext 타입의 관계
Resource의 타입은 location 문자열과 ApplicationContext의 타입에 따라 결정됩니다. 위의 ResourceLoader 구현체를 통해서 Resource를 얻어오는게 아니라 bean으로 등록 된 ApplicationContext를 통해서 Resource를 가져올때는 아래와 같이 적용됩니다.
class org.springframework.context.support.GenericApplicationContext class org.springframework.core.io.DefaultResourceLoader$ClassPathContextResource class org.springframework.core.io.ClassPathResource
스프링 메시지소는 국제화(i18n)을 제공하는 인터페이스입니다. 메시지 설정 파일을 통해서 각 국가에 해당하는 언어로 메세지를 제공할수 있습니다. ApplicationContext는 MessageSource를 구현하고 있습니다.
메시지 설정 파일
메시지 설정 파일은 프로퍼티파일을 사용하며 파일 이름에 [파일이름][언어][국가].properties 형식으로 파일을 추가해주면 됩니다. 아래와 같이 2개의 파일을 생성하게 되면 인텔리제이에서는 Bundle로 묶이는 것을 확인 할수 있습니다.
1 2
messages.properties : 기본 메시지 messages_ko_KR.properties: 한국 메시지
파일이름이 messages로 시작하지 않아도 된다. 위의 형식만 맞춰주면 된다. 스프링 부트를 쓸 경우에는 messages로 시작하면 자동으로 등록 해준다.
메시지 가져오기
위와 같은 형식으로 파일을 생성한 후에 프로퍼티 작성 방식인 key=value 형식으로 값을 입력합니다.
1 2
//messages.properties greeting=Hello, so good {0}
1 2
//messages_ko_KR.properties greeting=안녕하세요 {0}
ReloadableResourceBundleMessageSource를 Bean으로 등록 해 줍니다. 여기에서 basename은 경로를 포함한 파일이름까지 적어주면 됩니다. 예를 들어 클래스패스에서 common/message-common_ko_KR.properties로 구성할 예정이라면 messageSource.setBasename("classpath:common/message-common") 이렇게 입력해주면 됩니다.
@Bean public MessageSource messageSource(){ ReloadableResourceBundleMessageSource messageSource = new ReloadableResourceBundleMessageSource(); messageSource.setBasename("classpath:messages"); messageSource.setDefaultEncoding("UTF-8"); return messageSource; } }
위처럼 설정이 완료가 되면 메시지를 가져올 준비가 되었습니다. messageSource.getMessage(“이름”, new String[]{“파라미터1”, “파라미터2..”}, Locale) 순으로 작성해주면 됩니다. 두번째 파라미터인 배열을 넘기면 프로퍼티에서 작성했었던 {0}에 값이 설정이 됩니다.
ApplicationContext는 EnvironmentCapable 인터페이스를 구현하고 있습니다. 이 인터페이스는 getEnvironment 메소드를 제공하며 호출시 Environment를 반환해 줍니다. Environment 클래스는 프로파일 및 프로퍼티 값과 관련이 있습니다.
프로파일
개발을 하다보면 로컬, 개발, 운영등 각 환경마다 설정을 달리 해주어야 하는 경우가 발생합니다. 이때 각 환경마다 활성화할 Bean을 관리해주는 역할을 하는게 프로파일입니다.
프로파일을 설정하면 앱 구동시에 설정된 프로파일 active 값에 따라서 해당 bean을 등록 할지 여부를 결정하게 됩니다. 예를 들어 A라는 Bean은 테스트시에만 쓰고 싶다면, 해당 Bean을 테스트 프로파일로 구성하면 테스트시에만 활성화 되게 됩니다.
설정 방법
설정 파일에 클래스에 정의를 하면 해당 설정 파일에서 정의한 모든 Bean을 한번에 정의할수 있습니다.
1 2 3 4 5 6
@Configuration @ComponentScan @Profile("test") public class BeanConfig { }
ApplicationContext 클래스도 Bean으로 등록되어 있기 때문에 의존주입 받을수 있습니다. 이 뿐 아니라 ApplicationContext가 상속하고 있는 BeanFactory, ResourceLoader 등도 Bean으로 등록되어 있기 때문에 ApplicationContext를 의존주입 받지 않고 필요한 기능을 가진 Bean을 의존주입해서 사용하는 것이 가시성에 좋습니다(ex : Bean과 관련된 처리를 할때는 BeanFactory사용).