千家信息网

怎么使用@Profile

发表于:2026-11-03 作者:千家信息网编辑
千家信息网最后更新 2026年11月03日,这篇文章主要讲解了"怎么使用@Profile",文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习"怎么使用@Profile"吧!1.13 Environmen
千家信息网最后更新 2026年11月03日怎么使用@Profile

这篇文章主要讲解了"怎么使用@Profile",文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习"怎么使用@Profile"吧!

1.13 Environment抽象

Environment接口是在容器中抽象集成,应用环境包括两个重要的方面:profiles 和 properties。配置文件是一个命名的bean定义逻辑组,只有在给定的配置文件处于激活状态时才在容器中注册。可以将bean分配给配置文件,不管它是用XML定义的还是用注解定义的。与profile文件相关的环境对象的角色是确定哪些profile文件(如果有的话)当前是激活的,以及哪些profile文件(如果有的话)在默认情况下应该是激活的。

Properties几乎在所有应用中扮演一个重要的角色并且可能来自于多个源:属性文件、JVM系统属性、系统环境变量、JNDI、servlet上下文参数、Properties对象、Map对象等等。Environment对象的角色与properties关联去提供给用户一个便利的服务接口去配置属性源和解析属性。

1.13.1 Bean定义Profiles

bean定义属性文件在核心容器中提供一个机制,它允许在不同的环境中注册不同bean。环境这个词对于不同的用户可能意味着不同的东西,这个特性可以帮助许多使用场景,包括:

  • 在开发中针对内存中的数据源进行工作,而不是在进行QA或生产时从JNDI查找相同的数据源。

  • 当部署应用到执行环境使用注册健康基础设施

  • 为客户A和客户B部署注册定制的bean实现。

在实践应用中考虑第一个使用场景,它需要获取一个DataSource。在测试环境中,配置假设如下:

@Beanpublic DataSource dataSource() {    return new EmbeddedDatabaseBuilder()        .setType(EmbeddedDatabaseType.HSQL)        .addScript("my-schema.sql")        .addScript("my-test-data.sql")        .build();}

现在考虑这个应用怎样部署到QA或生产环境,假设应用程序数据源注册在生成应用服务JNDI目录。我们的dataSource bean看起来类似下面清单:

@Bean(destroyMethod="")public DataSource dataSource() throws Exception {    Context ctx = new InitialContext();    return (DataSource) ctx.lookup("java:comp/env/jdbc/datasource");}

问题是如何根据当前环境在使用这两种变体之间进行切换。随着时间的流逝,Spring用户已经设计出多种方法来完成此任务,通常依赖系统环境变量和包含$ {placeholder}占位符的XML 语句的组合,这些语句根据值解析为正确的配置文件路径环境变量。Bean定义配置文件是一项核心容器功能,可解决此问题。

如果我们概括前面环境特定的bean定义示例中所示的用例,我们最终需要在特定上下文中注册特定的bean定义,而不是在其他上下文中注册。可以这样说,你希望在情形A中注册bean定义的某个配置文件,而在情形B中注册另一个配置文件。我们开始更新配置以反映这种需求。

使用@Profile

@Profile注解允许你去指明哪些组件适合去注册,当一个或多个指定profile处于激活状态时。使用我们前面的例子,我们可以重写dataSource配置如下:

@Configuration@Profile("development")public class StandaloneDataConfig {    @Bean    public DataSource dataSource() {        return new EmbeddedDatabaseBuilder()            .setType(EmbeddedDatabaseType.HSQL)            .addScript("classpath:com/bank/config/sql/schema.sql")            .addScript("classpath:com/bank/config/sql/test-data.sql")            .build();    }}
@Configuration@Profile("production")public class JndiDataConfig {    @Bean(destroyMethod="")    public DataSource dataSource() throws Exception {        Context ctx = new InitialContext();        return (DataSource) ctx.lookup("java:comp/env/jdbc/datasource");    }}

如前所述,对于@Bean方法,你典型的选择使用编程式的JNDI查找,通过使用Spring的JndiTemplate/JndiLocatorDelegate帮助类或直接使用前面展示的JNDI InitialContext,而不是使用JndiObjectFactoryBean变体,这将迫使你将返回类型声明为FactoryBean类型。

配置文件字符串可能包含一个简单的配置名称(例如,production)或一个配置表达式。一个配置表达式允许更复杂的配置逻辑去表达(例如,production & us-east),下面的操作符在profile表达式中是被支持的:

  • !:逻辑非

  • &:逻辑与

  • |:逻辑或

你不能混合&和!操作符而不使用括号。例如,production & us-east | eu-central是无效表达式。它必须被表达类似production & (us-east | eu-central)。

你可以使用@Profile作为一个元数据注解去创建你自定义的注解。以下示例定义了一个自定义@Production注解,你可以将其用作@Profile(" production")的替代。

@Target(ElementType.TYPE)@Retention(RetentionPolicy.RUNTIME)@Profile("production")public @interface Production {}

如果@Configuration类被标注@Profile,所有的@Bean方法和@Import注解关联的类都将被绕过,除非一个或多个指定的配置文件被激活。如果@Component或@Configuration类被标记@Profile({"p1", "p2"}),这个类不会被注册或处理除非配置文件 p1或 p2被激活。如果给的配置前缀是NOT操作符(!),注解元素仅仅在配置文件没有被激活时被注册。例如,@Profile({"p1", "!p2"}),如果配置 p1被激活或者配置p2没有被激活时注册才会发生。

@Profile也可以被声明在方法级别去包含一个特定的配置bean类(例如,用于特定bean的替代),类似下面例子展示:

@Configurationpublic class AppConfig {    @Bean("dataSource")    @Profile("development") //1    public DataSource standaloneDataSource() {        return new EmbeddedDatabaseBuilder()            .setType(EmbeddedDatabaseType.HSQL)            .addScript("classpath:com/bank/config/sql/schema.sql")            .addScript("classpath:com/bank/config/sql/test-data.sql")            .build();    }    @Bean("dataSource")    @Profile("production") //2    public DataSource jndiDataSource() throws Exception {        Context ctx = new InitialContext();        return (DataSource) ctx.lookup("java:comp/env/jdbc/datasource");    }}
  1. standaloneDataSource方法仅仅在development配置中有效

  2. jndiDataSource方法仅仅在production配置中有效

对于@Bean方法上的@Profile,可以应用一个特殊的场景:在重载相同Java方法名的@Bean方法(类似于构造函数重载)的情况下,需要一致地在所有重载方法上声明@Profile条件。如果条件不一致,则只有重载方法中第一个声明的条件重要。因此,@Profile不能被使用去选择具有特定参数签名重载方法。在创建时,同一bean的所有工厂方法之间的解析都遵循Spring的构造函数解析方法。

如果你想去定义不同配置条件的bean,使用不同的Java方法名称,通过使用@Bean name属性指向相同名称的bean,类似前面展示例子。如果参数前面都相同(例如,所有的变体有无参构造函数),这是在一个有效的Java类中表示这种安排的唯一方法(因为只能有一个具有特定名称和参数签名的方法)。

XML bean定义配置文件

XML对应项是元素的profile属性。我们前面的相同配置能被重写在两个XML文件中,类似下面:

                        
    

也可以避免在同一文件中拆分和嵌套元素,如以下示例所示:

                                                                    

spring-bean.xsd已被限制为仅允许这些元素作为文件中的最后一个。这应该有助于提供灵活性,而不会在XML文件中引起混乱。

XML对应项不支持前面描述的配置文件表达式:然而,它可能通过!操作符否定一个配置文件。它也可能通过嵌入配置文件应用逻辑and,类型下面例子显示:

                     

在前面的例子中,如果production和us-east配置都被激活,则dataSource bean被暴露。

激活profile

现在我们已经更新了配置文件,我们仍然需要指示Spring哪一个配置文件激活。如果我们已经启动了我们的应用程序,我们将看到一个NoSuchBeanDefinitionException抛出,因为容器不能找到名称为dataSource的bean。

可以通过多种方式来激活配置文件,但最直接的方法是可通过ApplicationContext获得的Environment API以编程方式进行配置。下面例子展示怎样去做:

AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();ctx.getEnvironment().setActiveProfiles("development");ctx.register(SomeConfig.class, StandaloneDataConfig.class, JndiDataConfig.class);ctx.refresh();

此外,你也可以声明式地激活配置文件通过spring.profiles.active属性,也可通过系统环境变量、JVM属性、在web.xml中servlet上下文参数,甚至作为JNDI中的条目(查看PropertySource抽象)。在集成测试中,激活配置文件可以通过使用@ActiveProfiles声明在spring-test模块(查看上下文配置通过环境配置文件)。

请注意,配置文件不是非此即彼的命题。你可以一次性激活多个配置文件。编程式地,你可以提供多个配置文件名称给setActiveProfiles()方法,它可以接受String… 可变参数。下面例子激活多个配置文件:

ctx.getEnvironment().setActiveProfiles("profile1", "profile2");

声明式地,spring.profiles.active可以接收配置名称逗号分隔列表,类似下面例子展示:

-Dspring.profiles.active="profile1,profile2"

默认profile

默认配置文件表示默认情况下启用的配置文件。考虑下面例子:

@Configuration@Profile("default")public class DefaultDataConfig {    @Bean    public DataSource dataSource() {        return new EmbeddedDatabaseBuilder()            .setType(EmbeddedDatabaseType.HSQL)            .addScript("classpath:com/bank/config/sql/schema.sql")            .build();    }}

如果没有profile被激活,dataSource实例被创建。你可以看到这种方式提供一个默认的定义为一个或多个bean。如果任何profile被激活,这个默认profile不被使用。

你可以通过在Environment上使用setDefaultProfiles()改变默认profile名称或者,声明式地,通过使用spring.profiles.default属性。

1.13.2 PropertySource抽象

Spring的Environment抽象提供了可配置属性源层次结构上的搜索操作。考虑下面清单:

ApplicationContext ctx = new GenericApplicationContext();Environment env = ctx.getEnvironment();boolean containsMyProperty = env.containsProperty("my-property");System.out.println("Does my environment contain the 'my-property' property? " + containsMyProperty);

在前面的片段中,我们看到一个高级别的询问Spring的方式,是否my-property属性在当前环境中被定义:去回答这个问题,Environment对象执行搜索PropertySource 集合对象。PropertySource是一个简单key-value对源的抽象,并且Spring的StandardEnvironment被配置两个PropertySource 对象,一个描述JVM系统(System.getProperties())属性集合另一个描述系统环境变量集合(System.getenv())。

这些默认的属性源适用StandardEnvironment,为在独立的应用中使用。 StandardServletEnvironment是通过附加默认属性源填充,包括servlet配置和servlet上下文参数。它可以选择启用JndiPropertySource。查看javadock详情。

具体地说,当你使用StandardEnvironment时,如果my-property系统属性或my-property环境变量在运行时被描述,调用env.containsProperty("my-property")方法将返回true。

执行的搜索是分层的,默认情况,系统属性优先级高于环境变量。因此,如果my-property属性在两个地方被设置,在调用env.getProperty("my-property")时,系统属性值将被返回。请注意,属性值不会合并,而是会被前面的值完全覆盖。

对于通用的StandardServletEnvironment,完整的层次结构如下,优先级最高的条目位于顶部:

1.ServletConfig参数(如果使用的-例如,如果是DispatcherServlet上下文)

2.ServletContext参数(web.xml 上下文参数)

3.JNDI 环境变量(java:comp/env/)

4.JVM系统参数(-D 命令行参数)

5.JVM系统变量(操作系统环境变量)

最重要地,整个机制是可配置的。也许你有一个自定义的属性源,你想整合到此搜索中。这样做,实现和实例化你的PropertySource并且添加到当前的Environment PropertySources集合中。下面例子展示怎样去做:

ConfigurableApplicationContext ctx = new GenericApplicationContext();MutablePropertySources sources = ctx.getEnvironment().getPropertySources();sources.addFirst(new MyPropertySource());

在前面的代码中,MyPropertySource被添加到最高索引优先级中。如果它包含my-property属性,则会检测到并返回该属性,从而支持任何其他PropertySource中的my-property属性。MutablePropertySourcesAPI暴露了一些方法,这些方法允许去精确操作属性源集合。

1.13.3 使用@PropertySource

@PropertySource注解提供一个便捷的和陈述式的机制去添加PropertySource到Spring的Environment中。

给定一个名叫app.properties文件,它包含健值对testbean.name=myTestBean,下面的@Configuration类使用@PropertySource,以这种方式调用testBean.getName()并返回myTestBean:

@Configuration@PropertySource("classpath:/com/myco/app.properties")public class AppConfig {    @Autowired    Environment env;    @Bean    public TestBean testBean() {        TestBean testBean = new TestBean();        testBean.setName(env.getProperty("testbean.name"));        return testBean;    }}

@PropertySource资源位置中出现的任何${}占位符都将根据已经在环境中注册的属性源集进行解析,如下面的示例所示:

@Configuration@PropertySource("classpath:/com/${my.placeholder:default/path}/app.properties")public class AppConfig {    @Autowired    Environment env;    @Bean    public TestBean testBean() {        TestBean testBean = new TestBean();        testBean.setName(env.getProperty("testbean.name"));        return testBean;    }}

假设my.placeholder在一个已经被注册的属性源中被描述(例如,系统属性或环境变量),则占位符被解析为对应值。如果没有,则使用一个默认值default/path。如果没有指定默认值并且属性不能被解析,一个IllegalArgumentException被抛出。

根据Java8约定,@PropertySource注解是可以重复的。然而,所有的@PropertySource注解需要在相同等级被声明,要么直接地在配置类上,要么作为元数据注解在相同自定义注解中。不建议将直接注释和元注释混合使用,因为直接注释会有效地覆盖元注释。

参考代码:com.liyong.ioccontainer.starter.EnvironmentIocContainer

1.13.4 语句中的占位符解析

在以前,元素中占位符的值只能根据JVM系统属性或环境变量来解析。现在情况已经不同了。因为Environment抽象已经集成到容器,很容易通过它来路由占位符的解析。这意味着你可以按照自己喜欢的任何方式配置解析过程。你可以更改搜索系统属性和环境变量的优先级,也可以完全删除它们。你还可以适当地将你自己的属性源添加到组合中。

具体地说,不论在何处定义customer属性,只要在环境中可用,以下语句就可以工作:

    

感谢各位的阅读,以上就是"怎么使用@Profile"的内容了,经过本文的学习后,相信大家对怎么使用@Profile这一问题有了更深刻的体会,具体使用情况还需要大家实践验证。这里是,小编将为大家推送更多相关知识点的文章,欢迎关注!

配置 文件 属性 环境 方法 激活 系统 变量 参数 注解 应用 例子 上下 上下文 名称 不同 相同 多个 对象 容器 数据库的安全要保护哪些东西 数据库安全各自的含义是什么 生产安全数据库录入 数据库的安全性及管理 数据库安全策略包含哪些 海淀数据库安全审计系统 建立农村房屋安全信息数据库 易用的数据库客户端支持安全管理 连接数据库失败ssl安全错误 数据库的锁怎样保障安全 百单网互联网科技有限公司怎么样 广州大一互联网络科技有限公司 互联网科技公司起名 为想互联网科技怎么样 新华互联网科技学校 互联网和科技技术 互联网科技有限公司图片 远拓互联网科技 长亮科技互联网金融 中国互联网金融科技基金 顺网科技2021年互联网排名 恒生科技互联网指数分析 上海电发互联网科技有限公司 玖二柒互联网科技公司待遇 成都互联网科技公司工作好不好 亳州好赞互联网科技有限公司 滨湖区互联网智能科技产品有哪些 互联网领先科技宣传片 浙南科技城互联网大厦招聘 众联互联网络科技 互联网络科技 互联网科技公司排名 互联网科技网 互联网金融 科技金融 互联网科技公司电话 互联网大会黑科技排名 中国航天科技大学国际工业互联网 宜昌元轩互联网科技有限公司 威龙国际互联网科技公司 智帮互联网科技公司最近动向
0