一、@Profile 注解的作用
- @Profile : 在开发项目的时候,一个项目可能存在多种环境。比如:生产环境、开发环境、测试环境。其中,每种环境所对应不同的配置,为了提高开发效率,不一直进行配置的修改,还有就是在不同的环境下想要执行的方法不一致,SpringBoot提供了一个可以动态指定配置文件的注解,这个注解就是用来动态指定配置。
- @Profile :该注解的作用就是指定某个类或者某个方法在特定的配置环境下生效,只要是被 @Component或 @Configuration注解的类都可以使用 @Profile注解。在使用DI来依赖注入的时候,能够根据@profile标明的环境,将注入符合当前运行环境的相应的bean。
- 重点:没有使用该注解说明此时的类和方法在任何的环境下都可以生效
该注解生效的条件:
- 被 @Component或 @Configuration注解的类可以使用该注解
该注解需要的参数:
- @Profile中需要指定一个字符串,指定该Bean生效的环境, @profile("dev")。
二、使用 @Profile
@Profile 注解让你表明当一个或多个指定的配置文件处于活动状态时,一个组件就有资格注册。使用我们前面的例子,我们可以重写 dataSource 配置如下。
Java
@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(); } }
Java
@Configuration @Profile("production") public class JndiDataConfig { @Bean(destroyMethod = "") //@Bean(destroyMethod = "") 禁用默认的销毁方法推理。 public DataSource dataSource() throws Exception { Context ctx = new InitialContext(); return (DataSource) ctx.lookup("java:comp/env/jdbc/datasource"); } }
如前所述,对于 @Bean 方法,你通常会选择使用程序化的JNDI查找,通过使用Spring的 JndiTemplate/JndiLocatorDelegat helper 或前面显示的直接使用JNDI InitialContext,但不使用 JndiObjectFactoryBean 的变体,这将迫使你将返回类型声明为 FactoryBean 类型。
profile 字符串可以包含一个简单的 profile 名称(例如,production)或一个 profile 表达式。profile 表达式允许表达更复杂的 profile 逻辑(例如,production & us-east)。profile 表达式中支持以下运算符。
- !: profile的 NOT 逻辑
- &: profile的 AND 的逻辑
- |: profile的 OR 的逻辑
你不能在不使用括号的情况下混合使用 & 和 | 运算符。例如,production & us-east | eu-central 不是一个有效的表达。它必须表示为 production & (us-east | eu-central)。
你可以使用 @Profile 作为 元注解,以创建一个自定义的组成注解。下面的例子定义了一个自定义的 @Production 注解,你可以把它作为 @Profile("production") 的直接替换。
Java
@Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Profile("production") public @interface Production { }
如果一个 @Configuration 类被标记为 @Profile,所有与该类相关的 @Bean 方法和 @Import 注解都会被绕过,除非一个或多个指定的 profiles 处于激活状态。如果一个 @Component 或 @Configuration 类被标记为 @Profile({"p1", "p2"}),该类不会被注册或处理,除非 profiles "p1" 或 "p2" 已经被激活。如果一个给定的profiles前缀为NOT操作符(!),那么只有在该profiles没有激活的情况下,才会注册被注解的元素。例如,给定 @Profile({"p1", "!p2"}),如果profile 'p1' 被激活或 profile 'p2' 未被激活,注册将发生。
@Profile 也可以在方法层面上声明,以便只包括一个配置类的一个特定Bean(例如,对于一个特定Bean的备选变体),正如下面的例子所示。
Java
@Configuration public class AppConfig { @Bean("dataSource") @Profile("development") //StandaloneDataSource 方法只在 development profile 中可用。 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") //jndiDataSource 方法只在 production profile 中可用。 public DataSource jndiDataSource() throws Exception { Context ctx = new InitialContext(); return (DataSource) ctx.lookup("java:comp/env/jdbc/datasource"); } }
对于 @Bean 方法的 @Profile,一个特殊的情况可能适用。在同一Java方法名的重载 @Bean 方法的情况下(类似于构造函数重载),@Profile 条件需要在所有重载的方法上一致声明。如果条件不一致,那么在重载的方法中,只有第一个声明的条件才是重要的。因此,@Profile 不能被用来选择具有特定参数签名的重载方法而不是另一个。同一个Bean的所有工厂方法之间的解析遵循Spring在创建时的构造函数解析算法。
如果你想定义具有不同概况条件的备选Bean,请使用不同的Java方法名,通过使用 @Bean name 属性指向同一个Bean名称,如前面的例子所示。如果参数签名都是一样的(例如,所有的变体都有无参数的工厂方法),这是首先在一个有效的Java类中表示这种安排的唯一方法(因为一个特定名称和参数签名的方法只能有一个)。
三、激活一个 Profile
现在我们已经更新了我们的配置,我们仍然需要指示Spring哪个profile是激活的。如果我们现在启动我们的示例应用程序,我们会看到一个 NoSuchBeanDefinitionException 被抛出,因为容器找不到名为 dataSource 的Spring Bean。
激活一个 profile 可以通过几种方式进行,但最直接的是以编程方式对环境API进行激活,该API可以通过 ApplicationContext 获得。下面的例子显示了如何做到这一点。
Java
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(); ctx.getEnvironment().setActiveProfiles("development"); ctx.register(SomeConfig.class, StandaloneDataConfig.class, JndiDataConfig.class); ctx.refresh();
此外,你还可以通过 spring.profiles.active 属性声明性地激活profiles,它可以通过系统环境变量、JVM系统属性、web.xml 中的servlet上下文参数,甚至作为JNDI中的一个条目来指定(参见 PropertySource 抽象)。在集成测试中,可以通过使用 spring-test 模块中的 @ActiveProfiles 注解来声明活动profiles(见 environment profiles 的 context 配置)。
请注意,profiles 不是一个 "非此即彼" 的命题。你可以同时激活多个profiles。在程序上,你可以向 setActiveProfiles() 方法提供多个profiles名称,该方法接受 String… 可变参数。下面的例子激活了多个profiles。
Java
ctx.getEnvironment().setActiveProfiles("profile1", "profile2");
在声明上,spring.profiles.active 可以接受一个用逗号分隔的 profile 名称列表,正如下面的例子所示。
-Dspring.profiles.active="profile1,profile2"
四、默认 Profile
默认 profile 代表默认启用的 profile。考虑一下下面的例子。
Java
@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就不应用。
你可以通过在环境中使用 setDefaultProfiles() 来改变默认配置文件的名称,或者通过声明性地使用 spring.profiles.default 属性。