简体   繁体   English

使用bean定义配置文件的Spring 3.1 bean可见性

[英]Spring 3.1 bean visibility using bean definition profiles

I have been experimenting with using Spring 3.1's bean definition profiles and nested beans. 我一直在尝试使用Spring 3.1的bean定义配置文件和嵌套bean。 I had hoped that I could define different beans depending on the active profile. 我希望我可以根据活动配置文件定义不同的bean。 Consider the following heavily over simplified example such that my Spring context contains something like 请考虑以下重点简化示例,以便我的Spring上下文包含类似的内容

<bean id="say" class="test.Say" p:hello-ref="hello"/>

<beans profile="prod">
    <bean id="hello" class="test.Hello" p:subject="Production!"/>
</beans>

<beans profile="dev">
    <bean id="hello" class="test.Hello" p:subject="Development!"/>
</beans>

I get the following error: 我收到以下错误:

Exception in thread "main" org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'say' defined in class path resource [applicationContext.xml]: Cannot resolve reference to bean 'hello' while setting bean property 'hello'; 线程“main”中的异常org.springframework.beans.factory.BeanCreationException:在类路径资源[applicationContext.xml]中定义名称为'say'的bean时出错:在设置bean属性'hello'时无法解析对bean'hello'的引用; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No bean named 'hello' is defined at org.springframework.beans.factory.support.BeanDefinitionValueResolver.resolveReference(BeanDefinitionValueResolver.java:328) at org.springframework.beans.factory.support.BeanDefinitionValueResolver.resolveValueIfNecessary(BeanDefinitionValueResolver.java:106) at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.applyPropertyValues(AbstractAutowireCapableBeanFactory.java:1360) aJava Result: 1 嵌套异常是org.springframework.beans.factory.NoSuchBeanDefinitionException:在org.springframework.beans.factory的org.springframework.beans.factory.support.BeanDefinitionValueResolver.resolveReference(BeanDefinitionValueResolver.java:328)中没有定义名为'hello'的bean。 .support.BeanDefinitionValueResolver.resolveValueIfNecessary(BeanDefinitionValueResolver.java:106)at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.applyPropertyValues(AbstractAutowireCapableBeanFactory.java:1360)aJava Result:1

I was expecting that the hello bean would be defined according to the active Maven profile (in my case prod or dev ). 我希望根据活动的Maven配置文件(在我的例子中为proddev )来定义hello bean。 I'm starting to think that the Spring active profiles ( spring.profiles.active ) may be completely unrelated to Maven profiles. 我开始认为Spring活动配置文件( spring.profiles.active )可能与Maven配置文件完全无关。

Could somebody please explain where I am going wrong? 有人可以解释我哪里出错吗? (Is this even possible using profiles?). (甚至可以使用配置文件?)。

I was expecting that the hello bean would be defined according to the active Maven profile (in my case prod or dev). 我希望根据活动的Maven配置文件(在我的例子中为prod或dev)来定义hello bean。 I'm starting to think that the Spring active profiles (spring.profiles.active) may be completely unrelated to Maven profiles. 我开始认为Spring活动配置文件(spring.profiles.active)可能与Maven配置文件完全无关。

That's true, they are unrelated. 这是真的,他们是无关的。

Here is how you can fix it: 以下是如何解决它:

Make sure that the web.xml that you have in src/main/webapp/WEB-INF/ folder has the following context setting: 确保src/main/webapp/WEB-INF/文件夹中的web.xml具有以下上下文设置:

<context-param>
    <param-name>spring.profile.active</param-name>
    <param-value>${profileName}</param-value>
</context-param>

And then make sure that the maven-war-plugin has filtering turned on for the web.xml : 然后确保maven-war-pluginweb.xml启用了过滤功能:

<plugin>
    <artifactId>maven-war-plugin</artifactId>
    <version>2.3</version>
    <configuration>
        <filteringDeploymentDescriptors>true</filteringDeploymentDescriptors>
    </configuration>
</plugin>

And then lastly in your profiles: 最后在你的个人资料中:

<profiles>
    <profile>
        <id>dev</id>
        <properties>
            <profileName>dev</profileName>
        </properties>
    </profile>
    <profile>
        <id>prod</id>
        <properties>
            <profileName>prod</profileName>
        </properties>
    </profile>
</profiles>

You could also add a default value in the normal properties section: 您还可以在普通属性部分中添加默认值:

<properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <profileName>dev</profileName>
</properties>

So if you run without the -P option the dev spring profile will be used. 因此,如果在没有-P选项的情况下运行,将使用dev弹簧配置文件。

When running mvn package the web.xml will have the correct value for the spring.profile.active . 运行mvn packageweb.xml将具有spring.profile.active的正确值。

Thanks to maba (whose answer I shall accept), I started thinking about this in a different way. 感谢maba (我将接受他的回答),我开始以不同的方式思考这个问题。

I've modified the parent bean "say" because it needs to be lazily initialized because when it is initially encountered the nested bean contexts do not yet exist. 我已经修改了 bean “说”,因为它需要被懒惰地初始化,因为当它最初遇到时,嵌套的bean上下文还不存在。 So the new version adds a new bean and changes the "say" definition such that it now looks like: 所以新版本添加了一个新bean并更改了“说”定义,现在它看起来像:

<bean class="test.InitProfile" p:profiles="dev"/>

<bean id="say" class="test.Say" lazy-init="true" p:hello-ref="hello"/>

The new InitProfile bean is an InitializingBean responsible for setting up the active profiles. 新的InitProfile bean是一个InitializingBean,负责设置活动的配置文件。

It contains: 它包含:

package test;

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.util.StringUtils;

public class InitProfile implements InitializingBean, ApplicationContextAware {

    private ConfigurableApplicationContext ctx;
    private String[] profiles;

    public void setApplicationContext(ApplicationContext ac) throws BeansException {
        ctx = (ConfigurableApplicationContext) ac;
    }

    public void setProfiles(String inprofiles) {
        if (inprofiles.contains(",")) {
            profiles = StringUtils.split(inprofiles, ",");
        } else {
            profiles = new String[]{inprofiles};
        }
    }

    public void afterPropertiesSet() throws Exception {
        String[] activeProfiles = ctx.getEnvironment().getActiveProfiles();
        if (profiles != null && activeProfiles.length == 0) {
            ctx.getEnvironment().setActiveProfiles(profiles);
            ctx.refresh();
        }
    }
}

Using this approach has the added advantage of being able to set the active spring profile using a classpath properties file (this can differ depending on my active Maven profile). 使用此方法的另一个优点是能够使用类路径属性文件设置活动弹簧配置文件(这可能因我的活动Maven配置文件而异)。 I also like this approach because I can use it for both web application and command line applications. 我也喜欢这种方法,因为我可以将它用于Web应用程序和命令行应用程序。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM