引言
工作中我们会遇到一些实际有关配置不同的情况,比如生产数据库配置、开发环境数据库配置、测试环境数据库配置,还有一些特殊方法只需要开发环境执行生产环境不需要执行的。
这种情况的解决方式一般两种:
1、使用maven打包方式不同,不同的环境使用不同的配置文件打包(常用)
2、使用Spring提供的profile配置,不同的环境使用不同的运行参数。
介绍
笔者针对的是Spring提供的profile配置方式的介绍。
Springboot中实现Profile的配置如下
@Configuration
@Profile("dev")
public class DevelopmentProfileConfig{
@Bean
public UserInfo userInit(){
return ......;
}
}
那么在我们使用的Spring中是如何配置的?笔者提供了以下的配置方式以供参考:
Spring配置步骤如下
第一步:编写三个环境的Spring配置文件如下
applicationContest-dev.xml 配置如下:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://www.springframework.org/schema/beans"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.0.xsd">
<!-- 手动定义一个bean -->
<bean id="userInfo" name="userInfo2" class="com.leo.model.UserInfo" init-method="myInit"
destroy-method="myDestroy">
<property name="id" value="2"></property>
<property name="name" value="开发环境配置"></property>
<property name="age" value="18"></property>
</bean>
</beans>
applicationContest-dev.xml配置如下:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://www.springframework.org/schema/beans"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.0.xsd">
<!-- 手动定义一个bean -->
<bean id="userInfo" name="userInfo2" class="com.leo.model.UserInfo" init-method="myInit"
destroy-method="myDestroy">
<property name="id" value="2"></property>
<property name="name" value="测试环境配置"></property>
<property name="age" value="18"></property>
</bean>
</beans>
applicationContest-prod.xml配置如下:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://www.springframework.org/schema/beans"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.0.xsd">
<!-- 手动定义一个bean -->
<bean id="userInfo" name="userInfo2" class="com.leo.model.UserInfo" init-method="myInit"
destroy-method="myDestroy">
<property name="id" value="2"></property>
<property name="name" value="生产环境配置"></property>
<property name="age" value="18"></property>
</bean>
</beans>
第二步:配置Spring的主配置文件
配置applicationContest-mainTest.xml,将三个环境的配置文件引入
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://www.springframework.org/schema/beans"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.0.xsd">
<beans profile="test">
<import resource="applicationContext-test.xml"></import>
</beans>
<beans profile="prod">
<import resource="applicationContext-prod.xml"></import>
</beans>
<beans profile="dev">
<import resource="applicationContext-dev.xml"></import>
</beans>
</beans>
第三步:编写测试代码
/**
* 针对Profile的测试
*/
@Test
public void profileTest() throws InterruptedException {
ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext(CLASS_PATH_RESOURCE);
UserInfo userInfo = (UserInfo) applicationContext.getBean("userInfo");
System.out.println(userInfo);
TimeUnit.SECONDS.sleep(3);
applicationContext.registerShutdownHook();
}
运行单元测试,发现报错了
三月 01, 2020 1:23:18 下午 org.springframework.context.support.ClassPathXmlApplicationContext prepareRefresh
信息: Refreshing org.springframework.context.support.ClassPathXmlApplicationContext@6c629d6e: startup date [Sun Mar 01 13:23:18 CST 2020]; root of context hierarchy
"D:\Program Files\Java\jdk1.8.0_45\bin\java" -ea -Didea.launcher.port=7532 "-Didea.launcher.bin.path=D:\Program Files (x86)\JetBrains\IntelliJ IDEA 14.0.2\bin" -Dfile.encoding=UTF-8 -classpath "D:\Program Files (x86)\JetBrains\IntelliJ IDEA 14.0.2\lib\idea_rt.jar;D:\Program Files (x86)\JetBrains\IntelliJ IDEA 14.0.2\plugins\junit\lib\junit-rt.jar;D:\Program Files\Java\jdk1.8.0_45\jre\lib\charsets.jar;D:\Program Files\Java\jdk1.8.0_45\jre\lib\deploy.jar;D:\Program Files\Java\jdk1.8.0_45\jre\lib\javaws.jar;D:\Program Files\Java\jdk1.8.0_45\jre\lib\jce.jar;D:\Program Files\Java\jdk1.8.0_45\jre\lib\jfr.jar;D:\Program Files\Java\jdk1.8.0_45\jre\lib\jfxswt.jar;D:\Program Files\Java\jdk1.8.0_45\jre\lib\jsse.jar;D:\Program Files\Java\jdk1.8.0_45\jre\lib\management-agent.jar;D:\Program Files\Java\jdk1.8.0_45\jre\lib\plugin.jar;D:\Program Files\Java\jdk1.8.0_45\jre\lib\resources.jar;D:\Program Files\Java\jdk1.8.0_45\jre\lib\rt.jar;D:\Program Files\Java\jdk1.8.0_45\jre\lib\ext\access-bridge-64.jar;D:\Program Files\Java\jdk1.8.0_45\jre\lib\ext\cldrdata.jar;D:\Program Files\Java\jdk1.8.0_45\jre\lib\ext\dnsns.jar;D:\Program Files\Java\jdk1.8.0_45\jre\lib\ext\jaccess.jar;D:\Program Files\Java\jdk1.8.0_45\jre\lib\ext\jfxrt.jar;D:\Program Files\Java\jdk1.8.0_45\jre\lib\ext\localedata.jar;D:\Program Files\Java\jdk1.8.0_45\jre\lib\ext\nashorn.jar;D:\Program Files\Java\jdk1.8.0_45\jre\lib\ext\sunec.jar;D:\Program Files\Java\jdk1.8.0_45\jre\lib\ext\sunjce_provider.jar;D:\Program Files\Java\jdk1.8.0_45\jre\lib\ext\sunmscapi.jar;D:\Program Files\Java\jdk1.8.0_45\jre\lib\ext\sunpkcs11.jar;D:\Program Files\Java\jdk1.8.0_45\jre\lib\ext\zipfs.jar;E:\WorkSpace\Git\spring-framework-learning-example\chapter-4-springmvc-sourcecode-analysis\target\test-classes;E:\WorkSpace\Git\spring-framework-learning-example\chapter-4-springmvc-sourcecode-analysis\target\classes;D:\Library\maven\repository\junit\junit\4.12\junit-4.12.jar;D:\Library\maven\repository\org\hamcrest\hamcrest-core\1.3\hamcrest-core-1.3.jar;D:\Library\maven\repository\org\springframework\spring-webmvc\4.3.25.RELEASE\spring-webmvc-4.3.25.RELEASE.jar;D:\Library\maven\repository\org\springframework\spring-aop\4.3.25.RELEASE\spring-aop-4.3.25.RELEASE.jar;D:\Library\maven\repository\org\springframework\spring-beans\4.3.25.RELEASE\spring-beans-4.3.25.RELEASE.jar;D:\Library\maven\repository\org\springframework\spring-context\4.3.25.RELEASE\spring-context-4.3.25.RELEASE.jar;D:\Library\maven\repository\org\springframework\spring-core\4.3.25.RELEASE\spring-core-4.3.25.RELEASE.jar;D:\Library\maven\repository\commons-logging\commons-logging\1.2\commons-logging-1.2.jar;D:\Library\maven\repository\org\springframework\spring-expression\4.3.25.RELEASE\spring-expression-4.3.25.RELEASE.jar;D:\Library\maven\repository\org\springframework\spring-web\4.3.25.RELEASE\spring-web-4.3.25.RELEASE.jar;D:\Library\maven\repository\javax\servlet\javax.servlet-api\3.0.1\javax.servlet-api-3.0.1.jar;D:\Library\maven\repository\javax\servlet\jsp\jsp-api\2.2\jsp-api-2.2.jar;D:\Library\maven\repository\org\springframework\spring-test\4.3.25.RELEASE\spring-test-4.3.25.RELEASE.jar;D:\Library\maven\repository\org\aspectj\aspectjrt\1.8.9\aspectjrt-1.8.9.jar;D:\Library\maven\repository\org\aspectj\aspectjweaver\1.8.9\aspectjweaver-1.8.9.jar" com.intellij.rt.execution.application.AppMain com.intellij.rt.execution.junit.JUnitStarter -ideVersion5 BeanTest,profileTest
三月 01, 2020 1:23:18 下午 org.springframework.context.support.ClassPathXmlApplicationContext prepareRefresh
信息: Refreshing org.springframework.context.support.ClassPathXmlApplicationContext@6c629d6e: startup date [Sun Mar 01 13:23:18 CST 2020]; root of context hierarchy
三月 01, 2020 1:23:18 下午 org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions
信息: Loading XML bean definitions from class path resource [applicationContext-mainTest.xml]
三月 01, 2020 1:23:19 下午 org.springframework.beans.factory.xml.DefaultBeanDefinitionDocumentReader doRegisterBeanDefinitions
信息: Skipped XML bean definition file due to specified profiles [test] not matching: class path resource [applicationContext-mainTest.xml]
三月 01, 2020 1:23:19 下午 org.springframework.beans.factory.xml.DefaultBeanDefinitionDocumentReader doRegisterBeanDefinitions
信息: Skipped XML bean definition file due to specified profiles [prod] not matching: class path resource [applicationContext-mainTest.xml]
三月 01, 2020 1:23:19 下午 org.springframework.beans.factory.xml.DefaultBeanDefinitionDocumentReader doRegisterBeanDefinitions
信息: Skipped XML bean definition file due to specified profiles [dev] not matching: class path resource [applicationContext-mainTest.xml]
org.springframework.beans.factory.NoSuchBeanDefinitionException: No bean named 'userInfo' available
at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBeanDefinition(DefaultListableBeanFactory.java:682)
at org.springframework.beans.factory.support.AbstractBeanFactory.getMergedLocalBeanDefinition(AbstractBeanFactory.java:1218)
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:284)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:197)
at org.springframework.context.support.AbstractApplicationContext.getBean(AbstractApplicationContext.java:1082)
at BeanTest.profileTest(BeanTest.java:39)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:78)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:57)
at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:74)
at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:211)
at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:67)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at com.intellij.rt.execution.application.AppMain.main(AppMain.java:134)
第四步:报错原因分析
通过报错的日志分析,我们报错的直接原因:org.springframework.beans.factory.NoSuchBeanDefinitionException: No bean named 'userInfo' available
没有找到这个bean对象。
根本原因是:Skipped XML bean definition file due to specified profiles [test] not matching: class path resource [applicationContext-mainTest.xml]
翻译过来过来就是没有找到匹配的profiles为test的文件。
产生这个错误的原因是我们需要提前在环境变量中激活我们需要使用的配置文件
激活的配置的参数有两个:spring.profiles.default
和 spring.profiles.active
激活的方式有以下几种
- 在SpringMVC中作为DispatcherServlet的初始化参数
<servlet>
<servlet-name>myspringmvc</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<!-- 指定spring配置文件的位置,如果不配置默认/WEB-INF/[servlet-name]-servlet -->
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:myspringmvc-servlet.xml</param-value>
<param-name>spring.profiles.default</param-name>
<!-- 在这里激活dev的配置 -->
<param-value>dev</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
- 在Web应用中作为应用上下文参数
<context-param>
<param-name>spring.profiles.default</param-name>
<param-value>dev</param-value>
</context-param>
- 作为环境变量
- 作为JVM的系统属性
最后附一下测试结果:
spring.profiles.default=dev之后结果
spring.profiles.default=prod之后结果
总结
使用Spring提供的profile配置可以让不同环境使用不同的配置,本文也只是抛砖引玉给大家提供一种解题思路。本文介绍了spring.profiles.default
其实还有spring.profiles.active
,后者的用法和前者类似,如果共同使用则以spring.profiles.active的配置优先。
测试结果也证明了以上说法:
最后,笔者使用下面配置方式并未达到预期的效果,推测原因是bean初始化的时候已经通过环境变量或者JVM参数完成了bean参数的初始化,在此之后如果再使用applicationContext.getEnvironment().setActiveProfiles("prod")
修改的话,虽然能修改Profiles参数,但是bean的初始化已经完成了,因此这个时候获取的bean仍然是修改之前的bean。
ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext(CLASS_PATH_RESOURCE);
applicationContext.getEnvironment().setActiveProfiles("prod");