Spring源码学习篇3 - XML配置了解 Profiles
2021/7/14 1:05:45
本文主要是介绍Spring源码学习篇3 - XML配置了解 Profiles,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!
开篇
对于XML配置文件来说,一般情况下根元素就是beans。XML配置文件被加载为document之后,会从根元素开始读取bean配置为BeanDefinition对象。但是,<beans>的解析开始前,其实还有profile属性的判断,只有profile匹配才会继续解析。
可以在整个配置文件的根元素配置profile属性,比如:
<beans profile="development" xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="..."> </beans>
也可以在嵌套的beans元素上定义,比如:
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:jdbc="http://www.springframework.org/schema/jdbc" xmlns:jee="http://www.springframework.org/schema/jee" xsi:schemaLocation="..."> <!-- other bean definitions --> <beans profile="development"> <jdbc:embedded-database id="dataSource"> <jdbc:script location="classpath:com/bank/config/sql/schema.sql"/> <jdbc:script location="classpath:com/bank/config/sql/test-data.sql"/> </jdbc:embedded-database> </beans> <beans profile="production"> <jee:jndi-lookup id="dataSource" jndi-name="java:comp/env/jdbc/datasource"/> </beans> </beans>
Profile如何起作用
不管是根元素还是嵌套的beans元素,解析逻辑都是DefaultBeanDefinitionDocumentReader#doRegisterBeanDefinitions
protected void doRegisterBeanDefinitions(Element root) { // Any nested <beans> elements will cause recursion in this method. In // order to propagate and preserve <beans> default-* attributes correctly, // keep track of the current (parent) delegate, which may be null. Create // the new (child) delegate with a reference to the parent for fallback purposes, // then ultimately reset this.delegate back to its original (parent) reference. // this behavior emulates a stack of delegates without actually necessitating one. 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); // We cannot use Profiles.of(...) since profile expressions are not supported // in XML config. See SPR-12458 for details. if (!getReaderContext().getEnvironment().acceptsProfiles(specifiedProfiles)) { if (logger.isDebugEnabled()) { logger.debug("Skipped XML bean definition file due to specified profiles [" + profileSpec + "] not matching: " + getReaderContext().getResource()); } return; } } } preProcessXml(root); parseBeanDefinitions(root, this.delegate); postProcessXml(root); this.delegate = parent; }
从代码可以看到,beans元素的profile属性可以配置多个值。
如果当前环境不接受specifiedProfiles,那么beans元素不会解析。
如果没有配置profile属性,那么会直接解析,不会做拦截。
Spring默认情况下,Environment的具体实现类是StandardEnvironment,那么来看下acceptsProfiles的具体实现:
@Deprecated public boolean acceptsProfiles(String... profiles) { Assert.notEmpty(profiles, "Must specify at least one profile"); for (String profile : profiles) { if (StringUtils.hasLength(profile) && profile.charAt(0) == '!') { if (!isProfileActive(profile.substring(1))) { return true; } } else if (isProfileActive(profile)) { return true; } } return false; } protected boolean isProfileActive(String profile) { validateProfile(profile); Set<String> currentActiveProfiles = doGetActiveProfiles(); return (currentActiveProfiles.contains(profile) || (currentActiveProfiles.isEmpty() && doGetDefaultProfiles().contains(profile))); }
首先,比较明显的是方法已经被标记为已过期,目前它也只有在xml文件解析的地方才有用到。从使用处的注解也可以了解到XML配置有些不足之处,而且现在毋庸置疑已经是注解的天下了,但是这不影响我们先来了解下Spring的机制。
// We cannot use Profiles.of(…) since profile expressions are not supported
// in XML config. See SPR-12458 for details.
代码的执行逻辑还是比较清楚的,简单来说就是,beans元素的profile属性中可以配置多个,只要这些profiles有任何一个满足条件,就认为beans元素匹配。而且,支持配置非("!")操作。
两种profile生效的属性配置
从上面isProfileActive方法可以看出,如果存在ActiveProfiles 那么会使用currentActiveProfiles 来匹配beans元素的profile,如果没有ActiveProfiles 那么将使用DefaultProfiles来匹配。
进入doGetActiveProfiles可以看到,读取的是spring.profiles.active属性,依然可以配置多值;
protected Set<String> doGetActiveProfiles() { synchronized (this.activeProfiles) { if (this.activeProfiles.isEmpty()) { String profiles = getProperty(ACTIVE_PROFILES_PROPERTY_NAME); if (StringUtils.hasText(profiles)) { setActiveProfiles(StringUtils.commaDelimitedListToStringArray( StringUtils.trimAllWhitespace(profiles))); } } return this.activeProfiles; } }
进入doGetDefaultProfiles可以看到, 读取的是spring.profiles.default属性,也可以配置多值;
protected Set<String> doGetDefaultProfiles() { synchronized (this.defaultProfiles) { if (this.defaultProfiles.equals(getReservedDefaultProfiles())) { String profiles = getProperty(DEFAULT_PROFILES_PROPERTY_NAME); if (StringUtils.hasText(profiles)) { setDefaultProfiles(StringUtils.commaDelimitedListToStringArray( StringUtils.trimAllWhitespace(profiles))); } } return this.defaultProfiles; } }
接触过Spring Boot的话,想必对spring.profiles.active不会陌生。
硬编码配置Profile
接口ConfigurableEnvironment提供了修改ActiveProfiles和DefaultProfiles的方法,所以除了属性配置,也可以通过硬编码的方式改变:
ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("testa_bean_config.xml", TestA.class); assertFalse(ctx.containsBean("bookShelf1")); ctx.getEnvironment().setActiveProfiles("dev"); ctx.refresh(); assertTrue(ctx.containsBean("bookShelf1"));
XML 配置Profile的限制
- profile的定义比较简单,多个值在一个beans元素上,相当于or,任意一个满足条件即可
- 可以使用非操作
- 可以通过beans元素嵌套的方式来达成and逻辑, 下面是官网的例子
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:jdbc="http://www.springframework.org/schema/jdbc" xmlns:jee="http://www.springframework.org/schema/jee" xsi:schemaLocation="..."> <!-- other bean definitions --> <beans profile="production"> <beans profile="us-east"> <jee:jndi-lookup id="dataSource" jndi-name="java:comp/env/jdbc/datasource"/> </beans> </beans> </beans>
@Profile注解支持表达式要比XML配置更加灵活
- !: A logical “not” of the profile
- &: A logical “and” of the profiles
- |: A logical “or” of the profiles
- 可以通过()组合成比较长的逻辑,比如:production & (us-east | eu-central)
当前先只是通过简单的XML配置来初步了解Spring的工作原理,后续再来学习注解部分。
这篇关于Spring源码学习篇3 - XML配置了解 Profiles的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!
- 2024-11-16MyBatis-Plus资料入门教程:快速上手指南
- 2024-11-16Mybatis资料入门教程:新手必看指南
- 2024-11-16MyBatis资料详解:新手入门与初级实战指南
- 2024-11-16MyBatisPlus资料:初学者入门指南与实用教程
- 2024-11-16MybatisPlus资料详解:初学者入门指南
- 2024-11-16MyBatisX资料:新手入门与初级教程
- 2024-11-16RESTful接口资料详解:新手入门指南
- 2024-11-16RESTful接口资料详解:新手入门教程
- 2024-11-16Spring Boot资料:新手入门与初级教程
- 2024-11-16Springboot资料:新手入门与初级教程