摘自Environment
注释:
A profile is a named, logical group of bean definitions to be registered with the container only if the given profile is active . Beans may be assigned to a profile whether defined in XML or via annotations; see the spring-beans 3.1 schema or the @Profile
annotation for syntax details. The role of the Environment
object with relation to profiles is in determining which profiles (if any) are currently active, and which profiles (if any) should be active by default.
profiles
类似Maven中的profiles
,Spring允许配置多个profile
,但只加载激活的profile
,通过profiles
配置,可以实现不同环境使用不同的配置(例如不同环境使用不同的数据库配置)
配置 XML方式 1 2 3 <beans profile ="local" > <bean id ="localBeanService" class ="xyz.iyichen.misc.learning.spring.tp.LocalBeanService" /> </beans >
注解方式 1 2 3 4 5 6 7 8 9 @Configuration public class Application { @Bean @Profile("local") public LocalBeanService localBeanService () { return new LocalBeanService (); } }
使用 通过spring.profiles.active 1 spring.profiles.active=local
例如在启动命令中添加-Dspring.profiles.active=local
通过方法设置 1 ConfigurableEnvironment.setActiveProfiles(String... profiles);
例如:
1 2 3 ClassPathXmlApplicationContext ac = new ClassPathXmlApplicationContext (new String []{"spring-context.xml" }, false );ac.getEnvironment().setActiveProfiles("local" ); ac.refresh();
实现 XML方式 当使用xml
配置时,Spring
是通过解析xml
实现Bean
的注册的。那么当解析到<beans profile="local">
,根据profile
是否可用决定是否注册Bean
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 protected void doRegisterBeanDefinitions (Element root) { BeanDefinitionParserDelegate parent = this .delegate; this .delegate = createDelegate(getReaderContext(), root, parent); if (this .delegate.isDefaultNamespace(root)) { String profileSpec = root.getAttribute(PROFILE_ATTRIBUTE); if (StringUtils.hasText(profileSpec)) { String[] specifiedProfiles = StringUtils.tokenizeToStringArray( profileSpec, BeanDefinitionParserDelegate.MULTI_VALUE_ATTRIBUTE_DELIMITERS); if (!getReaderContext().getEnvironment().acceptsProfiles(specifiedProfiles)) { return ; } } } preProcessXml(root); parseBeanDefinitions(root, this .delegate); postProcessXml(root); this .delegate = parent; }
方法调用顺序如下(新标签打开图片):
注解实现 当使用注解时,在注册Bean
时,判断是否包含@Profile
,如果不包含,则直接注册;如果没有,则判断该profile
是否可用,可用则注册,否则不做注册。判断逻辑在ProfileCondition.matches()
中实现:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 class ProfileCondition implements Condition { @Override public boolean matches (ConditionContext context, AnnotatedTypeMetadata metadata) { if (context.getEnvironment() != null ) { MultiValueMap<String, Object> attrs = metadata.getAllAnnotationAttributes(Profile.class.getName()); if (attrs != null ) { for (Object value : attrs.get("value" )) { if (context.getEnvironment().acceptsProfiles(((String[]) value))) { return true ; } } return false ; } } return true ; } }
方法调用顺序如下(新标签打开图片):
番外 当初始化ClassPathXmlApplicationContext
时,看下setConfigLocations()
方法:
1 2 3 4 5 6 7 8 9 10 public ClassPathXmlApplicationContext (String[] configLocations, boolean refresh, ApplicationContext parent) throws BeansException { super (parent); setConfigLocations(configLocations); if (refresh) { refresh(); } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 public void setConfigLocations (String... locations) { if (locations != null ) { Assert.noNullElements(locations, "Config locations must not be null" ); this .configLocations = new String [locations.length]; for (int i = 0 ; i < locations.length; i++) { this .configLocations[i] = resolvePath(locations[i]).trim(); } } else { this .configLocations = null ; } } protected String resolvePath (String path) { return getEnvironment().resolveRequiredPlaceholders(path); }
如果再看getEnvironment()
方法,会发现默认情况下使用的就是StandardEnvironment
(初始化时加载系统配置和启动配置),而且可以看到,配置文件路径也支持占位符替换
因此,可以根据不同启动参数,加载不同的配置文件。例如:
1.创建两个Spring
配置文件:spring-context-local.xml
和spring-context-rc.xml
2.启动类
1 2 3 ClassPathXmlApplicationContext ac = new ClassPathXmlApplicationContext (new String []{"spring-context-${custom.profile}.xml" }, false );ac.refresh();
3.启动命令
1 2 // 使用配置文件`spring-context-rc.xml` -Dcustom.profile=rc
参考 beans-definition-profiles