Loading...
Spring Framework Reference Documentation 7.0.2의 Environment Abstraction의 한국어 번역본입니다.
아래의 경우에 피드백에서 신고해주신다면 반영하겠습니다.
감사합니다 :)
Environment 인터페이스는
애플리케이션 환경의 두 가지 주요 측면인 profiles와
properties를 모델링하는, 컨테이너에 통합된 추상화입니다.
profile은 주어진 profile이 active인 경우에만 컨테이너에 등록되는 bean definition의 이름이 있는 논리적 그룹입니다. Bean은 XML에서 정의되었든 어노테이션으로 정의되었든 profile에 할당될 수 있습니다.
profile과 관련된 Environment 객체의 역할은 현재 어떤 profile(있는 경우)이 active 상태인지, 그리고 어떤 profile(있는 경우)이 기본적으로 active 상태여야 하는지를 결정하는 것입니다.
Properties는 거의 모든 애플리케이션에서 중요한 역할을 하며 다양한 소스에서 올 수 있습니다. 예를 들어, properties 파일, JVM 시스템 properties, 시스템 환경 변수, JNDI, 서블릿 컨텍스트 매개변수, ad-hoc Properties 객체, Map 객체 등이 있습니다.
properties와 관련된 Environment 객체의 역할은 property 소스를 구성하고 그들로부터 properties를 resolve하기 위한 편리한 서비스 인터페이스를 사용자에게 제공하는 것입니다.
Bean definition profiles는 코어 컨테이너에서 서로 다른 환경에서 서로 다른 bean을 등록할 수 있게 해 주는 메커니즘을 제공합니다. “environment”라는 단어는 사용자에 따라 서로 다른 의미를 가질 수 있으며, 이 기능은 다음을 포함한 많은 use case에 도움이 될 수 있습니다:
DataSource가 필요한 practical 애플리케이션에서 첫 번째 use case를 고려해 보겠습니다. test 환경에서 configuration은 다음과 유사할 수 있습니다:
1@Bean 2public DataSource dataSource() { 3 return new EmbeddedDatabaseBuilder() 4 .setType(EmbeddedDatabaseType.HSQL) 5 .addScript("my-schema.sql") 6 .addScript("my-test-data.sql") 7 .build(); 8}
1@Bean 2fun dataSource(): DataSource { 3 return EmbeddedDatabaseBuilder() 4 .setType(EmbeddedDatabaseType.HSQL) 5 .addScript("my-schema.sql") 6 .addScript("my-test-data.sql") 7 .build() 8}
이제 이 애플리케이션이 QA 또는 production 환경에 어떻게 deploy될 수 있는지 생각해 보겠습니다. 애플리케이션의 datasource가 production 애플리케이션 서버의 JNDI directory에 등록되어 있다고 가정합니다. 우리의 dataSource bean은 이제 다음과 같은 모습이 됩니다:
1@Bean(destroyMethod = "") 2public DataSource dataSource() throws Exception { 3 Context ctx = new InitialContext(); 4 return (DataSource) ctx.lookup("java:comp/env/jdbc/datasource"); 5}
1@Bean(destroyMethod = "") 2fun dataSource(): DataSource { 3 val ctx = InitialContext() 4 return ctx.lookup("java:comp/env/jdbc/datasource") as DataSource 5}
문제는 현재 환경에 따라 이 두 variation 사이를 어떻게 전환하느냐입니다. 시간이 지나면서 Spring 사용자들은 보통 시스템 환경 변수와, 환경 변수의 값에 따라 올바른 설정 파일 경로로 resolve되는 ${placeholder} 토큰을 포함한 XML <import/> statements의 조합에 의존하여 이를 수행하는 여러 가지 방법을 고안해 왔습니다.
Bean definition profiles는 이 문제에 대한 해결책을 제공하는 코어 컨테이너 기능입니다.
앞에서 설명한 환경별 bean definition 예제를 일반화하면, 특정 컨텍스트에서는 특정 bean definition을 등록해야 하지만 다른 컨텍스트에서는 등록하지 않아야 한다는 요구에 도달하게 됩니다. 상황 A에서는 특정 profile의 bean definition을 등록하고, 상황 B에서는 다른 profile을 등록하고 싶다고 말할 수 있습니다.
우리는 이러한 필요를 반영하도록 설정을 업데이트하는 것부터 시작합니다.
@Profile@Profile 어노테이션을 사용하면 하나 이상의 지정된 profile이 active일 때 컴포넌트가 등록 대상이 되도록 표시할 수 있습니다. 앞의 예제를 사용하여 dataSource 설정을 다음과 같이 다시 작성할 수 있습니다:
1@Configuration 2@Profile("development") 3public class StandaloneDataConfig { 4 5 @Bean 6 public DataSource dataSource() { 7 return new EmbeddedDatabaseBuilder() 8 .setType(EmbeddedDatabaseType.HSQL) 9 .addScript("classpath:com/bank/config/sql/schema.sql") 10 .addScript("classpath:com/bank/config/sql/test-data.sql") 11 .build(); 12 } 13}
1@Configuration 2@Profile("development") 3class StandaloneDataConfig { 4 5 @Bean 6 fun dataSource(): DataSource { 7 return EmbeddedDatabaseBuilder() 8 .setType(EmbeddedDatabaseType.HSQL) 9 .addScript("classpath:com/bank/config/sql/schema.sql") 10 .addScript("classpath:com/bank/config/sql/test-data.sql") 11 .build() 12 } 13}
1@Configuration 2@Profile("production") 3public class JndiDataConfig { 4 5 @Bean(destroyMethod = "") (1) 6 public DataSource dataSource() throws Exception { 7 Context ctx = new InitialContext(); 8 return (DataSource) ctx.lookup("java:comp/env/jdbc/datasource"); 9 } 10}
| 1 | @Bean(destroyMethod = "")는 기본 destroy method 추론을 비활성화합니다. |
1@Configuration 2@Profile("production") 3class JndiDataConfig { 4 5 @Bean(destroyMethod = "") (1) 6 fun dataSource(): DataSource { 7 val ctx = InitialContext() 8 return ctx.lookup("java:comp/env/jdbc/datasource") as DataSource 9 } 10}
| 1 | @Bean(destroyMethod = "")는 기본 destroy method 추론을 비활성화합니다. |
앞에서 언급했듯이,
@Bean메서드에서는 일반적으로 programmatic JNDI lookup을 선택하며,<br>이는 Spring의JndiTemplate/JndiLocatorDelegatehelpers 또는 앞에서 보여준<br>직접적인 JNDIInitialContext사용을 통해 수행하지만, return type을FactoryBeantype으로<br>선언하도록 강제하는JndiObjectFactoryBeanvariant는 사용하지 않습니다.
profile 문자열에는 단순한 profile 이름(예: production)이나 profile expression이 포함될 수 있습니다. profile expression을 사용하면 더 복잡한 profile logic(예: production & us-east)을 표현할 수 있습니다.
profile expressions에서 지원되는 연산자는 다음과 같습니다:
!: profile에 대한 논리 NOT&: profiles에 대한 논리 AND|: profiles에 대한 논리 OR괄호를 사용하지 않고
&와|연산자를 섞어 사용할 수 없습니다. 예를 들어,<br>production & us-east | eu-central는 유효한 expression이 아닙니다. 이는<br>production & (us-east | eu-central)로 표현해야 합니다.
@Profile을 meta-annotation으로 사용하여 custom composed 어노테이션을 만들 수 있습니다. 다음 예제는
@Profile("production")의 drop-in replacement로 사용할 수 있는 custom @Production 어노테이션을 정의합니다:
1@Target(ElementType.TYPE) 2@Retention(RetentionPolicy.RUNTIME) 3@Profile("production") 4public @interface Production { 5}
1@Target(AnnotationTarget.CLASS) 2@Retention(AnnotationRetention.RUNTIME) 3@Profile("production") 4annotation class Production
@Configuration클래스가@Profile로 표시된 경우, 해당 클래스와 관련된 모든@Bean메서드 및<br>@Import어노테이션은 지정된 profile 중 하나 이상이 active 상태가 아닌 한 무시됩니다.<br>@Component또는@Configuration클래스가@Profile({"p1", "p2"})로 표시된 경우,<br>profiles 'p1' 또는 'p2'가 활성화되지 않으면 해당 클래스는 등록되거나 처리되지 않습니다.<br>주어진 profile이 NOT 연산자(!)로 prefix된 경우, 어노테이션이 달린 element는 해당 profile이 active가 아닐 때에만<br>등록됩니다. 예를 들어,@Profile({"p1", "!p2"})의 경우 profile 'p1'이 active이거나 profile 'p2'가<br>active가 아닌 경우 registration이 발생합니다.
@Profile은 또한 설정 클래스의 특정 bean 하나만 포함하도록 메서드 수준에 선언할 수 있습니다(예: 특정 bean의 대체 variant). 다음 예제와 같습니다:
1@Configuration 2public class AppConfig { 3 4 @Bean("dataSource") 5 @Profile("development") (1) 6 public DataSource standaloneDataSource() { 7 return new EmbeddedDatabaseBuilder() 8 .setType(EmbeddedDatabaseType.HSQL) 9 .addScript("classpath:com/bank/config/sql/schema.sql") 10 .addScript("classpath:com/bank/config/sql/test-data.sql") 11 .build(); 12 } 13 14 @Bean("dataSource") 15 @Profile("production") (2) 16 public DataSource jndiDataSource() throws Exception { 17 Context ctx = new InitialContext(); 18 return (DataSource) ctx.lookup("java:comp/env/jdbc/datasource"); 19 } 20}
| 1 | standaloneDataSource 메서드는 development profile에서만 사용 가능합니다. |
| 2 | jndiDataSource 메서드는 production profile에서만 사용 가능합니다. |
1@Configuration 2class AppConfig { 3 4 @Bean("dataSource") 5 @Profile("development") (1) 6 fun standaloneDataSource(): DataSource { 7 return EmbeddedDatabaseBuilder() 8 .setType(EmbeddedDatabaseType.HSQL) 9 .addScript("classpath:com/bank/config/sql/schema.sql") 10 .addScript("classpath:com/bank/config/sql/test-data.sql") 11 .build() 12 } 13 14 @Bean("dataSource") 15 @Profile("production") (2) 16 fun jndiDataSource() = 17 InitialContext().lookup("java:comp/env/jdbc/datasource") as DataSource 18}
| 1 | standaloneDataSource 메서드는 development profile에서만 사용 가능합니다. |
| 2 | jndiDataSource 메서드는 production profile에서만 사용 가능합니다. |
@Bean메서드에서의@Profile에는 특수한 scenario가 적용될 수 있습니다: 동일한 Java 메서드 이름을<br>가지는 overloaded@Bean메서드(생성자 overloading과 유사)의 경우,@Profile조건은 모든<br>overloaded 메서드에 일관되게 선언되어야 합니다. 조건이 일관되지 않으면 overloaded 메서드 중<br>첫 번째 선언에 대한 조건만 중요합니다. 따라서@Profile은 특정 argument signature를 가진<br>overloaded 메서드를 다른 메서드보다 선택하는 데 사용할 수 없습니다. 동일한 bean에 대한 모든 factory 메서드 사이의<br>resolution은 생성 시점에 Spring의 constructor resolution algorithm을 따릅니다.<br>서로 다른 profile 조건을 가진 대체 bean을 정의하려면, 앞의 예제에서와 같이@Beanname attribute를<br>사용하여 동일한 bean name을 가리키는 서로 다른 Java 메서드 이름을 사용하십시오.<br>argument signatures가 모두 동일한 경우(예: 모든 variant가 no-arg factory 메서드를 가지는 경우),<br>이는 우선 유효한 Java 클래스에서 이러한 구성을 표현하는 유일한 방법입니다(특정 이름과 argument signature를<br>가지는 메서드는 하나만 있을 수 있기 때문입니다).
XML에서의 counterpart는 <beans> element의 profile attribute입니다. 앞의 sample 설정은 다음과 같이 두 개의 XML 파일로 다시 작성할 수 있습니다:
1<beans profile="development" 2 xmlns="http://www.springframework.org/schema/beans" 3 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 4 xmlns:jdbc="http://www.springframework.org/schema/jdbc" 5 xsi:schemaLocation="..."> 6 7 <jdbc:embedded-database id="dataSource"> 8 <jdbc:script location="classpath:com/bank/config/sql/schema.sql"/> 9 <jdbc:script location="classpath:com/bank/config/sql/test-data.sql"/> 10 </jdbc:embedded-database> 11</beans>
1<beans profile="production" 2 xmlns="http://www.springframework.org/schema/beans" 3 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 4 xmlns:jee="http://www.springframework.org/schema/jee" 5 xsi:schemaLocation="..."> 6 7 <jee:jndi-lookup id="dataSource" jndi-name="java:comp/env/jdbc/datasource"/> 8</beans>
또한 이러한 split을 피하고 동일한 파일 내에서 <beans/> elements를 중첩하는 것도 가능합니다. 다음 예제와 같습니다:
1<beans xmlns="http://www.springframework.org/schema/beans" 2 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 3 xmlns:jdbc="http://www.springframework.org/schema/jdbc" 4 xmlns:jee="http://www.springframework.org/schema/jee" 5 xsi:schemaLocation="..."> 6 7 <!-- other bean definitions --> 8 9 <beans profile="development"> 10 <jdbc:embedded-database id="dataSource"> 11 <jdbc:script location="classpath:com/bank/config/sql/schema.sql"/> 12 <jdbc:script location="classpath:com/bank/config/sql/test-data.sql"/> 13 </jdbc:embedded-database> 14 </beans> 15 16 <beans profile="production"> 17 <jee:jndi-lookup id="dataSource" jndi-name="java:comp/env/jdbc/datasource"/> 18 </beans> 19</beans>
spring-bean.xsd는 이러한 elements를 파일의 마지막에만 허용하도록 제한되었습니다. 이는 XML 파일에 혼란을 초래하지 않으면서 유연성을 제공하는 데 도움이 됩니다.
XML counterpart는 앞에서 설명한 profile expressions를 지원하지 않습니다. 그러나
!연산자를 사용하여<br>profile을 부정하는 것은 가능합니다. 또한 다음 예제와 같이 profiles를 중첩하여 논리적 “and”를<br>적용하는 것도 가능합니다:<br><br>xml<br><beans xmlns="http://www.springframework.org/schema/beans"<br> xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"<br> xmlns:jdbc="http://www.springframework.org/schema/jdbc"<br> xmlns:jee="http://www.springframework.org/schema/jee"<br> xsi:schemaLocation="..."><br><br> <!-- other bean definitions --><br><br> <beans profile="production"><br> <beans profile="us-east"><br> <jee:jndi-lookup id="dataSource" jndi-name="java:comp/env/jdbc/datasource"/><br> </beans><br> </beans><br></beans><br><br><br>위의 예에서dataSourcebean은production과us-eastprofiles가 모두 active인 경우에만<br>exposed됩니다.
이제 설정을 업데이트했으므로, 어떤 profile이 active인지 Spring에 지시해야 합니다. 지금 sample 애플리케이션을 시작하면 컨테이너가 dataSource라는 이름의 Spring bean을 찾을 수 없기 때문에 NoSuchBeanDefinitionException이 발생하는 것을 보게 될 것입니다.
profile을 활성화하는 방법은 여러 가지가 있지만, 가장 직접적인 방법은 ApplicationContext를 통해 사용할 수 있는 Environment API를 대상으로 programmatic하게 수행하는 것입니다. 다음 예제는 그 방법을 보여줍니다:
1AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(); 2ctx.getEnvironment().setActiveProfiles("development"); 3ctx.register(SomeConfig.class, StandaloneDataConfig.class, JndiDataConfig.class); 4ctx.refresh();
1val ctx = AnnotationConfigApplicationContext().apply { 2 environment.setActiveProfiles("development") 3 register(SomeConfig::class.java, StandaloneDataConfig::class.java, JndiDataConfig::class.java) 4 refresh() 5}
또한 spring.profiles.active property를 통해 profiles를 선언적으로 활성화할 수도 있습니다. 이 property는 시스템 환경 변수, JVM 시스템 properties, web.xml의 서블릿 컨텍스트 매개변수 또는 JNDI의 entry로 지정할 수 있습니다(PropertySource Abstraction 참조).
integration tests에서는 spring-test 모듈의 @ActiveProfiles 어노테이션을 사용하여 active profiles를 선언할 수 있습니다(context configuration with environment profiles
참조).
profiles는 “either-or” 방식이 아니라는 점에 유의하십시오. 여러 profile을 한 번에 활성화할 수 있습니다. Programmatic하게는 String… varargs를 받는 setActiveProfiles() 메서드에 여러 profile 이름을 제공할 수 있습니다. 다음 예제는 여러 profiles를 활성화합니다:
1ctx.getEnvironment().setActiveProfiles("profile1", "profile2");
1ctx.getEnvironment().setActiveProfiles("profile1", "profile2")
선언적으로는 spring.profiles.active에 다음 예제와 같이 comma로 구분된 profile 이름 목록을 지정할 수 있습니다:
-Dspring.profiles.active="profile1,profile2"
default profile은 active profile이 없을 때 활성화되는 profile을 나타냅니다. 다음 예제를 고려해 보십시오:
1@Configuration 2@Profile("default") 3public class DefaultDataConfig { 4 5 @Bean 6 public DataSource dataSource() { 7 return new EmbeddedDatabaseBuilder() 8 .setType(EmbeddedDatabaseType.HSQL) 9 .addScript("classpath:com/bank/config/sql/schema.sql") 10 .build(); 11 } 12}
1@Configuration 2@Profile("default") 3class DefaultDataConfig { 4 5 @Bean 6 fun dataSource(): DataSource { 7 return EmbeddedDatabaseBuilder() 8 .setType(EmbeddedDatabaseType.HSQL) 9 .addScript("classpath:com/bank/config/sql/schema.sql") 10 .build() 11 } 12}
no profile is active한 경우, dataSource가 생성됩니다. 이를 하나 이상의 bean에 대한 default definition을 제공하는 방법으로 볼 수 있습니다.
어떤 profile이든 활성화되면 default profile은 적용되지 않습니다.
default profile의 이름은 default입니다. Environment에서 setDefaultProfiles()를 사용하거나 선언적으로 spring.profiles.default property를 사용하여 default profile의 이름을 변경할 수 있습니다.
PropertySource AbstractionSpring의 Environment 추상화는 구성 가능한 property 소스 계층에 대한 search operations를 제공합니다. 다음 listing을 고려해 보십시오:
1ApplicationContext ctx = new GenericApplicationContext(); 2Environment env = ctx.getEnvironment(); 3boolean containsMyProperty = env.containsProperty("my-property"); 4System.out.println("Does my environment contain the 'my-property' property? " + containsMyProperty);
1val ctx = GenericApplicationContext() 2val env = ctx.environment 3val containsMyProperty = env.containsProperty("my-property") 4println("Does my environment contain the 'my-property' property? $containsMyProperty")
앞의 snippet에서 우리는 현재 환경에 대해 my-property property가 정의되어 있는지 여부를 Spring에 묻는 high-level 방식을 볼 수 있습니다. 이 질문에 답하기 위해 Environment 객체는 PropertySource 객체 집합에 대한 search를 수행합니다.
PropertySource는 key-value pairs의 어떤 소스에 대해서도 적용 가능한 simple 추상화이며, Spring의 StandardEnvironment는 JVM 시스템 properties(System.getProperties()) 집합을 나타내는 하나의 PropertySource 객체와 시스템 환경 변수(System.getenv()) 집합을 나타내는 하나의 PropertySource 객체, 이렇게 두 개의 PropertySource 객체로 구성됩니다.
이러한 default property 소스는 standalone 애플리케이션에서 사용하기 위해
StandardEnvironment에 존재합니다.<br>StandardServletEnvironment는<br>서블릿 config, 서블릿 컨텍스트 매개변수, 그리고 JNDI가 사용 가능한 경우<br>JndiPropertySource를 포함하는 추가 default property 소스로 채워집니다.
구체적으로 StandardEnvironment를 사용할 때 env.containsProperty("my-property") 호출은 runtime에 my-property 시스템 property 또는 my-property 환경 변수가 존재하는 경우 true를 반환합니다.
수행되는 search는 hierarchical합니다. 기본적으로 시스템 properties는 환경 변수보다 우선합니다.<br>따라서
env.getProperty("my-property")호출 중에my-propertyproperty가 두 곳에 모두 설정되어 있는 경우,<br>시스템 property 값이 “승리”하여 반환됩니다. property 값은 merge되지 않고,<br>preceding entry에 의해 완전히 override된다는 점에 유의하십시오.<br>일반적인StandardServletEnvironment의 경우 전체 hierarchy는 다음과 같으며,<br>우선순위가 가장 높은 entries가 맨 위에 있습니다:<br><br>1. ServletConfig 매개변수(해당되는 경우 — 예:DispatcherServlet컨텍스트의 경우)<br>2. ServletContext 매개변수(web.xml context-param entries)<br>3. JNDI 환경 변수(java:comp/env/entries)<br>4. JVM 시스템 properties(-Dcommand-line arguments)<br>5. JVM 시스템 환경(운영 체제 환경 변수)
가장 중요한 점은 전체 메커니즘이 configurable하다는 것입니다. search에 통합하고 싶은 custom source of properties가 있을 수 있습니다. 이를 위해서는 직접 PropertySource를 구현하고 인스턴스화한 후, 현재 Environment의 PropertySources 집합에 추가하면 됩니다. 다음 예제는 그 방법을 보여줍니다:
1ConfigurableApplicationContext ctx = new GenericApplicationContext(); 2MutablePropertySources sources = ctx.getEnvironment().getPropertySources(); 3sources.addFirst(new MyPropertySource());
1val ctx = GenericApplicationContext() 2val sources = ctx.environment.propertySources 3sources.addFirst(MyPropertySource())
위 코드에서 MyPropertySource는 search에서 가장 높은 precedence로 추가되었습니다. 이 소스에 my-property property가 포함되어 있으면, 다른 어떤 PropertySource의 my-property property보다 우선하여 해당 property가 감지되고 반환됩니다.
MutablePropertySources API는 property 소스 집합을 정밀하게 조작할 수 있는 여러 메서드를 제공합니다.
@PropertySource@PropertySource 어노테이션은 Spring의 Environment에 PropertySource를 추가하기 위한 편리하고 선언적인 메커니즘을 제공합니다.
testbean.name=myTestBean이라는 key-value pair를 포함하는 app.properties라는 파일이 있다고 가정하면, 다음 @Configuration 클래스는 @PropertySource를 사용하여 testBean.getName() 호출이 myTestBean을 반환하도록 합니다:
1@Configuration 2@PropertySource("classpath:/com/myco/app.properties") 3public class AppConfig { 4 5 @Autowired 6 Environment env; 7 8 @Bean 9 public TestBean testBean() { 10 TestBean testBean = new TestBean(); 11 testBean.setName(env.getProperty("testbean.name")); 12 return testBean; 13 } 14}
1@Configuration 2@PropertySource("classpath:/com/myco/app.properties") 3class AppConfig { 4 5 @Autowired 6 private lateinit var env: Environment 7 8 @Bean 9 fun testBean() = TestBean().apply { 10 name = env.getProperty("testbean.name")!! 11 } 12}
@PropertySource resource location에 존재하는 모든 ${…} placeholders는 다음 예제와 같이 환경에 이미 등록된 property 소스 집합을 기준으로 resolve됩니다:
1@Configuration 2@PropertySource("classpath:/com/${my.placeholder:default/path}/app.properties") 3public class AppConfig { 4 5 @Autowired 6 Environment env; 7 8 @Bean 9 public TestBean testBean() { 10 TestBean testBean = new TestBean(); 11 testBean.setName(env.getProperty("testbean.name")); 12 return testBean; 13 } 14}
1@Configuration 2@PropertySource("classpath:/com/${my.placeholder:default/path}/app.properties") 3class AppConfig { 4 5 @Autowired 6 private lateinit var env: Environment 7 8 @Bean 9 fun testBean() = TestBean().apply { 10 name = env.getProperty("testbean.name")!! 11 } 12}
my.placeholder가 이미 등록된 property 소스(예: 시스템 properties 또는 환경 변수) 중 하나에 존재한다고 가정하면, placeholder는 해당 값으로 resolve됩니다. 그렇지 않으면 default/path가 default로 사용됩니다.
default가 지정되지 않았고 property를 resolve할 수 없는 경우 IllegalArgumentException이 발생합니다.
@PropertySource는 repeatable 어노테이션으로 사용할 수 있습니다.@PropertySource는<br>attribute overrides를 사용하여 custom composed 어노테이션을 생성하기 위한 meta-annotation으로도<br>사용할 수 있습니다.
역사적으로 elements에서 placeholders의 값은 JVM 시스템 properties 또는 환경 변수에 대해서만 resolve될 수 있었습니다. 더 이상 그렇지 않습니다. Environment 추상화가 컨테이너 전반에 통합되어 있으므로, placeholders의 resolution을 이를 통해 쉽게 route할 수 있습니다.
이는 resolution process를 원하는 방식으로 구성할 수 있음을 의미합니다. 시스템 properties와 환경 변수를 통해 search하는 precedence를 변경하거나 완전히 제거할 수 있습니다. 또한 필요에 따라 자체 property 소스를 mix에 추가할 수도 있습니다.
구체적으로 다음 statement는 customer property가 Environment에서 사용 가능한 한, 해당 property가 어디에 정의되어 있든 상관없이 동작합니다:
1<beans> 2 <import resource="com/bank/service/${customer}-config.xml"/> 3</beans>
Programmatic Bean Registration
Registering a LoadTimeWeaver