1.2.4 프로퍼티 값 설정 방법
- DI를 통해 빈에 주입되는 것은 두가지
- 다른 빈 오브젝트의 래퍼런스
- 단순 값 (빈이 아닌 모든 것)
메타정보 종류에 따른 값 설정 방법
- 값을 넣는 방법도 네가지로 구분이 가능
1. XML:<property>와 전용 태그
- <property>는 ref 애트리뷰트를 이용해 다른 빈의 아이디 지정이 가능
- ref 대신 value 애트리뷰트를 사용하면 런타임시 주입할 값으로 인식
- String이 가장 간단한 타입이며, int, float, double, boolean, 등 기본 타입이거나 Class Resource같은 단순하지 않은 오브젝트는 적절한 변환이 필요
- 스프링 컨테이너는 XML의 문자열로 된 값을 프로퍼티 타입으로 변환하는 서비스를 내장
2. 애노테이션:@Value
- 빈이 사용해야할 단순한 값이나 오브젝트를 런타임시에 주입해야하는 이유
- 첫번째, 환경이 매번 달라질 수 있는 값일 경우
- 대표적으로 DataSource 타입의 빈에 제공하는 DriverClass, URL, UserName, Password, 등과 같이 환경에 의존적인 정보이거나 상황에 따라 달라 질 수있는 값을 지정하기 위함
- 두번째, 초기값을 미리 가져야하는 경우
- 테스트나 특별한 이벤트 때는 다른 값을 지정하고 싶은 경우가 발생, 이런 경우 사용
- 첫번째, 환경이 매번 달라질 수 있는 값일 경우
- 디폴트 값의 사용 예
public class Hello { private String name = "Everyone"; public void setName(String name) { this.name = name; }
- 코드와 외부 설정을 분리해서 얻을 수 있는 장점은 소스코드를 다시 컴파일 하지 않는 점
- 값의 경우 빈같이 자동와이어링 방법이 없기 떄문에 항상 명시적으로 지정
- 소스코드의 애노테이션을 이용해서 프로퍼티 값을 지정하는 방법 예시 (@Value)
public class Hello { private String name; @Value("Everyone") public void setName(String name) { this.name = name; }
@Value("Everyone")
은<proeprty ... value="Everyone" />
과 동일
- 자바 코드를 통해 직접 필드 값을 초기화할 때와 분명한 차이가 존재
- 스프링 컨테이너가 참조하는 정보이지 클래스 필드에 값을 넣는 기능은 없음
- 테스트 코드와 같이 컨테이너 밖에서 사용된다면 @Value은 무시
@Value
의 주요 용도는 자바 코드 외부의 리소스나 환경정보에 담긴 값을 사용하도록 지정
@Value
를 이용해 시스템 프로퍼티의 os.name 값을 가져와 넣어주는 예시@Value("#{systemProperties['os.name']}") String name;
- 환경정보를 담은 프로퍼티 파일에 값을 가져오는 예시
@Value("${database.username}") String username;
database.username
속성이 정의된database.properties
파일을 XML에 지정이 필요<context:property-placeholder location="classpath:database.properties"/>
- @Value는 필드와 수정자, 메소드 파라미터에 사용 가능
3. 자바 코드: @Value
- @Configuration과 @Bean을 사용하는 경우에도 프로퍼티 값을 외부로 독립이 가능
- 클래스 자체가 메타정보가 되며, 환경에 종속적인 정보는 환경정보나 프로퍼티 파일에서 가져오는 것이 바람직한 방법
- @Autowired처럼 @Value도 사용 가능 (@Configuration이 붙은 클래스를 일반 빈으로 취급)
@Configuration public class Config { @Value("${database.username}") private String name; @Bean public Hello hello() { hello.setName(this.name); return hello; }
- 메소드에 파라미터에 적용한 @Value 예시
@Bean public Hello hello(@Value("${database.username}") String name) { Hello hello = new Hello(); hello.setName(name); return hello; }
PropertyEditor와 ConversionService
- XML의 value나 @Value의 엘리먼트는 문자로 작성, 그 외의 타입인 경우라면 변경 과정이 필요
- 스프링은 두 가지 종류의 타입 변환 서비스를 제공 (default는
PropertyEditor
)
기본 타입
- 스프링의 내장 프로퍼티 에디터가 변환을 지원하는 기본 타입들
boolean, Boolean, byte, Byte, short, Short, int, Integer, long, Long, float, Float double, Double, BigDecimal, BigInteger, char, Character, String
boolean
변환하는 예시 (XML)boolean flag; public void setFlag(boolean flag) { this.flag = flag; }
//XML <property name="flag" name"true"/>
//@Value @Value("true") booealn flag;
배열
- 기본 타입의 배열로 선언된 프로퍼티에는 주입이 가능, 값을 콤마(,)로 구분
byte[], char[], short[], int[], long[]
- 4개의 정수 값을 가진 배열로 변환해주는 예시
@Value("1,2,3,4") int[] intarr;
기타
- 스프링은 기본 타입외에도 다른 타입의 변환을 지원 (사용할 가능성이 많은 타입)
- 기본 타입과 달리 각 타입을 문자열로 표현하는 포맷 지정이 필요
Chartset
- UTF-8과 같은 값을
java.nio.charset.Charset
타입으로 만듬
- UTF-8과 같은 값을
Class
- JDBC 드라이버 클래스를 선언할 때 자주 사용했던 타입
- "java.lang.String"과 같이 문자열로 된 클래스 이름을 Class 타입으로 변경
Currency
- ISO 4217 코드를 따르는
java.util.Currency
타입으로 변환
- ISO 4217 코드를 따르는
File
java.io.File
타입으로 변환, 스프링의 리소스 로더에 사용하는 표현 사용이 가능
file:, classpath:
와 같은 접두어 사용 가능
InputStream
java.io.InputStream
타입으로 변환
Locale
java.util.Locale
타입으로 변환
Pattern
java.util.regex.Pattern
타입으로 변환
Resource
- 스프링의 리소스 타입으로 변환, 배열도 지원
Timezone
java.util.Timezone
으로 변환
URI, URL
java.net.URI
또는java.net.URL
로 변환
- 문자열로 각 타입의 값을 어떻게 나타낼수 있는지는 각 프로퍼티 에디터의 API 문서를 참조
- 타입 이름 뒤에 Editor를 붙여주면 찾는 것이 가능 (Ex:
Charset
타입 변환은CharsetEditor
)
- 지원하지 않는 오브젝트를 주입하려면
PropertyEditor
인터페이스를 구현하여 사용 (권장X) 자주 사용되는 오브젝트라면 빈으로 등록하고 DI받아서 사용하는 것을 추천 또는 SpEL 추천
ConversionService
- 스프링 3.0부턴
PropertyEditor
대신 사용 가능한ConversionService
를 지원
- 자바빈에서 차용해서 사용해오던 PropertyEditor와 달리 스프링이 직접 제공하는 API
- PropertyEditor보다 변환기 작성이 간편, 멀티스레드 환경에서 공유해 사용이 가능
- 하지만 스프링 빈의 값을 주입하는 작업에는 기본변환기(PropertyEditor)도 충분
- ConversionService를 사용하고 싶다면
conversionSevice
빈 선언이 필요//conversionSErvice를 등록하면 컨테이너가 PropertyEditor 대신 사용 <bean id="conversionService" class="org.springframework.context.support.ConversionServiceFactoryBean"> <property name="converters"> <list> //직접 정의한 타입 변환기 등록이 가능 <bean class="springbook.converter.MyTypeConverter" /> </list> </property> </bean>
컬렉션
- 스프링은 List, Set, Map, Properties와 같은 컬렉션을 XML로 작성해 프로퍼티 주입하는 방법 제공
- value 애트리뷰트를 통해 스트링 값을 넣는 대신 컬렉션 선언용 태그를 사용
List, Set
- <list>와 <value>를 이용해서 선언
<property name="names"> <list> <value>Spring</value> <value>IoC</value> <value>DI</value> </list> </property>
- 프로퍼티가 Set이라면 <list> 대신 <set>을 사용
Map
- 맵은 <map>과 <entry> 태그를 이용
<property name="ages"> <map> <entry key="kim" value"30" /> <entry key="Lee" value"35" /> <entry key="Ahn" value"40" /> </map> </property>
Properties
- java.util.Properties 타입은 <props>와 <prop>을 이용
<property name="settings"> <props> <prop key="username">Spring</prop> <prop key="password">Book</prop> </props> </property>
컬렉션에 빈 정의
- 컬렉션에는 값뿐만 아니라 다른 빈의 래퍼런스에 대한 컬렉션 정의도 가능
<property name="beans"> <list> <ref bean="beanA" /> <ref bean="beanB" /> </list> </property>
- 여러 가지 타입의 오브젝트로 컬렉션을 구성한다면 각각 타입 정보를 프로퍼티 선언에 제공할 수 없기 때문에 문제가 될 수 있음
- 컬렉션을 사용할 떄는 가능한 한가지 타입의 파라미터를 제공, 컨테이너가 적합한 타입 변환기를 적용할 수 있도록 하기 위함
util 스키마
- 컬렉션을 프로퍼티의 값으로 선언하는 대신 독립적인 빈을 만드는 것이 가능
- 컬렉션을 별도로 선언할 때는 util 스키마의 전용 태그를 활용
<util:list>, <util:set>
- List를 빈으로 선언해주는 예시
<util:list id="names"> <value>Spring</value> <value>IoC</value> <value>DI</value> </util:list>
- <util:list>를 사용하면 List 구현 클래스를 직접 정의가 가능
<util:list id="names" list-class="java.util.LinkedList">
- Set은 <util:set>을 이용
<util:map>
- Map은 <util:map> 태그를 이용, map-class 애트리뷰트로 Map 클래스 직저 지정 가능
<util:map id="ages" map-class="java.util.TreeMap"> <entry key="Kim" value="30" /> <entry key="Lee" value="35" /> <entry key="Ahn" value="40" /> </util:map>
<util:properties>
- Properties는 <util:properties>를 이용
<util:properties id="settings"> <prop key="username">Spring</prop> <prop key="password">Book</prop> </util:properties>
- Prop[erties는 XML에 직접 내용으 등록하는 대신 외부 프로퍼티 파일을 지정해서 사용이 가능
<util:properties id="settings" location="classpath:settings.properties" />
Null과 빈 문자열
- 스트링 타입에는 null 값과 빈 문자열("")이 비슷한 용도로 사용되기는 하지만 동일하지 않기 떄문에 구분해서 사용이 필요
- 빈 문자열인 경우
<property name="name" value="" />
- null값은 value로 표현이 불가능, <null /> 태그를 사용
<property name="name"><null /></property>
- 일반적인 경우에는 null을 명시적 선언이 필요 없음
- 프로퍼티 값을 주입하지 않으면 default null (명시적으로 표현하고 싶은 경우 태그 사용)
프로퍼티 파일을 이용한 값 설정
- 설정정보를 XML로 분리하면 소스코드 수정 없이 간단히 조작이 가능
- XML에서 다시 일부 설정정보를 별도의 파일로 분리하면 유용할 떄가 존재
- 서버환경에 종속적인 정보가 있는 경우
- XML의 빈 설정 메타정보는 애플리케이션 구조가 바뀌지 않으면 자주 변경되지 않음
- 프로퍼티 값으로 제공되는 설정정보는 환경에 따라서 자주 바뀌는 경우가 많음
- 환경에 따라 자주 변경될 수 있는 내용은 프로퍼티 파일로 분리하는 것이 깔끔한 방법
- 또 다른 장점은 @Value를 효과적으로 사용이 가능하다는 점
- @Value는 소스코드 안에 포함되어 있어 값을 수정하면 새로 컴파일이 필요, 하지만 프로퍼티 파일의 내용을 참조하게 되면 소스코드 수정 없이 프로퍼티에 주입되는 값 변경이 가능
- dataSource 빈의 설정 내용을 분리하는 예시
- value 애트리뷰트를 이용한 빈 설정정보
<bean id="dataSource" class="org.springframework.jdbc.datasource.SimpleDriverDataSource" > <property name="driverClass" value="com.mysql.jdbc.Driver" /> <property name="url" value="jdbc:mysql://localhost/testdb" /> <property name="username" value="spring" /> <property name="password" value="book" /> </bean>
- database.properties 파일
db.driverclass="com.mysql.jdbc.Driver db.url=jdbc:mysql://localhost/testdb db.username=spring db.password=book
- 프로퍼티 값을 사용할 수 있는 방법은 XML의 value나 @Value에 대체 가능한 이름 또는 표현식을 넣어두고 스프링 컨테이너가 그 자리에 프로퍼티 파일에서 읽은 값을 넣어서 사용
수동 변환: PropertyPlaceHolderConfigurer
- 프로퍼티 치환자를 이용하는 방법
- 프로퍼티 치환자는 프로퍼티 파일의 키 값을 ${} 안에 넣어서 사용
<bean id="dataSource" class="...SimpleDriverDataSource" > <property name="driverClass" value="${db.driverClass}" /> <property name="url" value="${db.url}" /> <property name="username" value="${db.username}" /> <property name="password" value="${db.password}" /> </bean>
- context 네임스페이스의 property-placeholder 태그를 추가하고 파일 위치를 지정
<context:property-placeholder location="classpath:database.properties" />
- 스프링은 ${}으로 선언된 값을 database.properties에서 찾아서 치환작업을 처리
- 스프링은
<context:property-placeholder>
태그를 통해 자동으로 등록되는PropertyPlaceHolderConfigurer
빈을 통해 변환을 처리
PropertyPlaceHolderConfiguer
의 특징- 이 빈은 빈 팩토리 후처리기
- 프로퍼티 파일의 내용을 읽은 뒤에 빈 메타정보의 프로퍼티 값 정보에서 ${}로 치환
- 빈 후처리기와 빈 팩토리 후처리기의 차이
- 빈 후처리기: 매 빈 오브젝트가 만들어진 직후에 오브젝트의 내용이나 오브젝트 자체를 변경할 때 사용
- 빈 팩토리 후처리기: 빈 설정 메타정보가 모두 준비됐을 때 빈 메타정보 자체를 조작하기 위해 사용
- 대체할 위치를 치환자로 지정해두고 별도의 후처리기가 치환자 값을 변경해주기를 기대하는 것이기 때문에 수동적
- @Value를 사용한 애노테이션 사용도 가능
@Value("${db.username}") String username;
능동 변환: SpEL
- 다른 빈 오브젝트에 직접 접근할 수 있는 표현식을 이용해 원하는 프로퍼티를 가져오는 방법
- 대표적인 것이 바로 스프링의 빈 메타정보의 값 설정을 이용하는 경우
- SpEL은 기본적으로 #{} 안에 표현식을 사용
<bean id="hello" ...> <property name="name" value="Spring" /> </bean> <bean> <proeprty name="helloname" value="#{hello.name}" /> </bean>
- SpEL은 일반 프로그래밍 언어 수준에 가까운 강력한 표현식 기능을 지원
- 다른 빈의 프로퍼티 접근 가능
- 메소드 호출 가능
- 다양한 연산도 지원
- 클래스 정보 접근 가능
- 생성자를 호출해서 오브젝트 생성 가능
- Properties는 Map 인터페이스를 구현한 클래스
- SpEl에서는 Map의 get() 메소드를 호출해주는 표현식이 가능 [] 안에 키를 넣음
<bean id="dataSource" class="...SimpleDriverDataSource" > <property name="driverClass" value="#{dbprops['db.driverclass']}" /> <property name="url" value="#{dbprops['db.url']}" /> <property name="username" value="#{dbprops['db.username']}" /> <property name="password" value="#{dbprops['db.password']}" /> </bean>
- SpEl을 이용한 방법은 @Value에도 적용 가능
'개발서적 > 토비 스프링 3.1-Vol.2' 카테고리의 다른 글
[토비의 스프링 - Vol.2] 1장 - 1.4 기타 빈 설정 메타정보 (0) | 2022.01.17 |
---|---|
[토비의 스프링 - Vol.2] 1장 - 1.3 프로토타입과 스코프 (0) | 2022.01.17 |
[토비의 스프링 - Vol.2] 1장 - 1.2.5 컨테이너가 자동등록하는 빈 (0) | 2022.01.17 |
[토비의 스프링 - Vol.2] 1장 - 1.2.3 빈 의존관계 설정 방법 (0) | 2022.01.17 |
[토비의 스프링 - Vol.2] 1장 - 1.2.2 빈 등록 방법 (0) | 2021.07.06 |