【问题标题】:Spring Profiles on method level?方法级别的Spring Profiles?
【发布时间】:2014-04-16 10:42:00
【问题描述】:

我想介绍一些只在开发过程中执行的方法。

我想我可能会在这里使用Spring @Profile 注释?但是如何在类级别应用此注释,以便仅在属性中配置了特定配置文件时才调用此方法?

spring.profiles.active=dev

将以下内容作为伪代码。如何做到这一点?

class MyService {

    void run() { 
       log();
    }

    @Profile("dev")
    void log() {
       //only during dev
    }
}

【问题讨论】:

    标签: java spring spring-profiles


    【解决方案1】:

    您可以阅读http://docs.spring.io/spring/docs/3.1.x/javadoc-api/org/springframework/context/annotation/Profile.html

    @Profile 注解可以通过以下任何方式使用:

    直接或间接作为任何类的类型级注释 用@Component 注释,包括@Configuration 类作为 元注释,用于编写自定义构造型 annotations 如果一个@Configuration 类被标记为@Profile,那么所有的 与该类关联的 @Bean 方法和 @Import 注释 除非一个或多个指定的配置文件处于活动状态,否则将被绕过。 这与 Spring XML 中的行为非常相似:如果配置文件 提供了 beans 元素的属性,例如,除非配置文件,否则 beans 元素将不会被解析 “p1”和/或“p2”已被激活。同样,如果 @Component 或 @Configuration 类用@Profile({"p1", "p2"}) 标记,那个类 除非配置文件“p1”和/或“p2”具有 已激活。

    因此,类上的@Profile 注释适用于它的所有方法和导入。不上课。

    您尝试做的事情可能通过让两个类实现相同接口并根据配置文件注入一个或另一个来实现。看看这个问题的答案。

    Annotation-driven dependency injection which handles different environments

    【讨论】:

    • 好的,所以我想要的可能无法使用 spring 注释。但是,您知道如何实现这样的要求吗?
    • 我只是在写。
    • 您可能希望同一接口的多个实现,其中每个接口仅在某个配置文件处于活动状态时才包含在 Spring 上下文中。根据我收集的信息,您可能希望生产实现对相关方法有一个空实现
    • 如何让控制器处理程序方法仅在激活配置文件时可用?方法上的@Profile 注释似乎被忽略了。
    【解决方案2】:

    可能在 4.1 中

    @Profile 注解可以通过以下任何方式使用:

    直接或间接作为任何类的类型级注释 用@Component 注释,包括@Configuration 类。作为一个 元注释,用于编写自定义构造型 注释。 作为任何@Bean 方法的方法级注释

    http://docs.spring.io/spring/docs/4.1.x/javadoc-api/org/springframework/context/annotation/Profile.html

    【讨论】:

    【解决方案3】:

    对于不想让多个@Beans 使用@Profile 注释的未来读者,这也可能是一个解决方案:

    class MyService {
    
       @Autowired
       Environment env;
    
       void run() { 
          if (Arrays.asList(env.getActiveProfiles()).contains("dev")) {
             log();
          }
       }
    
       void log() {
          //only during dev
       }
    }
    

    【讨论】:

    • Enviroment 中的错字,应该是 Environment。 Arrays.asList(env.getActiveProfiles()).contains("dev") 也可以替换为 env.acceptsProfiles("dev")
    • 我更喜欢 Arrays.asList().contains() 解决方案,acceptProfiles 在这种情况下不是很容易理解..
    • @funder7,请解释如何创建一个新列表并在其上调用 contains() 比询问 env 是否接受某个配置文件更“易于理解”或性能更好?
    • 我的意思是,env.acceptsProfiles("dev") 更重要,而另一种方式更明确:env.getActiveProfiles 获取列表,.contains("dev") 检查感兴趣的配置文件是否包含在活动配置文件列表中.这只是一种风格偏好,没有别的。 :-)
    【解决方案4】:

    只是想补充一点,answer saying this is possible with current spring on method-level 是公然错误的。通常对方法使用@Profile 是行不通的——它唯一可以使用的方法是在带有@Bean 注解的@Configuration 类中。

    我使用 Spring 4.2.4 进行了快速测试,结果发现

    • @Configuration 类 bean 创建方法中的@Profile 起作用
    • @Profile 在 bean 的方法中不起作用(并且预计不会起作用 - 文档有点模棱两可)
    • 类级@Profile 工作当且仅当它与 bean 定义在相同的上下文中,在配置类中,或者如果使用上下文扫描,则有
    • env.acceptsProfiles("profile"), Arrays.asList(env.getActiveProfiles()).contains("profile") 有效

    测试类:

    import org.junit.Test;
    import org.junit.runner.RunWith;
    import org.springframework.beans.factory.NoSuchBeanDefinitionException;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.context.ApplicationContext;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Profile;
    import org.springframework.core.env.Environment;
    import org.springframework.stereotype.Component;
    import org.springframework.test.context.ActiveProfiles;
    import org.springframework.test.context.ContextConfiguration;
    import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
    
    import java.util.Arrays;
    
    import static org.junit.Assert.assertNotNull;
    import static org.junit.Assert.assertNull;
    
    @RunWith(SpringJUnit4ClassRunner.class)
    @ContextConfiguration(classes = { ProfileTest.ProfileTestConfiguration.class })
    @ActiveProfiles("test")
    public class ProfileTest {
        static class SomeClass {}
        static class OtherClass {}
        static class ThirdClass {
            @Profile("test")
            public void method() {}
        }
        static class FourthClass {
            @Profile("!test")
            public void method() {}
        }
    
        static class ProfileTestConfiguration {
            @Bean
            @Profile("test")
            SomeClass someClass() {
                return new SomeClass();
            }
            @Bean
            @Profile("!test")
            OtherClass otherClass() {
                return new OtherClass();
            }
            @Bean
            ThirdClass thirdClass() {
                return new ThirdClass();
            }
            @Bean
            FourthClass fourthClass() {
                return new FourthClass();
            }
        }
    
        @Autowired
        ApplicationContext context;
    
        @Test
        public void testProfileAnnotationIncludeClass() {
            context.getBean(SomeClass.class);
        }
        @Test(expected = NoSuchBeanDefinitionException.class)
        public void testProfileAnnotationExcludeClass() {
            context.getBean(OtherClass.class);
        }
        @Test
        public void testProfileAnnotationIncludeMethod() {
            context.getBean(ThirdClass.class).method();
        }
        @Test(expected = Exception.class)  // fails
        public void testProfileAnnotationExcludeMethod() {
            context.getBean(FourthClass.class).method();
        }
    }
    

    【讨论】:

      【解决方案5】:

      @Profile 可以和方法一起使用,也可以和Java Based Configuration一起使用

      例如PostgreSQL (LocalDateTime) 和 HSQLDB (2.4.0 Timestamp 之前) 的单独数据库时间戳:

      @Autowired
      private Function<LocalDateTime, T> dateTimeExtractor;
      
      @Bean
      @Profile("hsqldb")
      public Function<LocalDateTime, Timestamp> getTimestamp() {
          return Timestamp::valueOf;
      }
      
      @Bean
      @Profile("postgres")
      public Function<LocalDateTime, LocalDateTime> getLocalDateTime() {
          return dt -> dt;
      }
      

      还可以查看Spring Profiles example:3. 更多...(示例 Spring @Profile 在方法级别)

      【讨论】:

      • 我很抱歉这个评论在堆栈溢出中不是很好的形式......但这太酷了!!!
      【解决方案6】:

      使用Environment 配置文件可以实现:

      class MyClass {
      
         @Autowired
         Environment env;
      
         void methodA() { 
            if (env.acceptsProfiles(Profiles.of("dev"))) {
               methodB();
            }
         }
      
         void methodB() {
            // when profile is 'dev'
         }
      }
      

      Profiles.of 也可以与逻辑表达式一起使用:

      static Profiles of(String...profiles) 创建一个新的 Profiles 实例 根据给定的配置文件字符串检查匹配项。这 如果给定的配置文件字符串中的任何一个,则返回的实例将匹配 匹配。

      配置文件字符串可能包含一个简单的配置文件名称(例如 “生产”)或配置文件表达式。配置文件表达式允许 要表达的更复杂的配置文件逻辑,例如 “生产和云”。

      配置文件表达式支持以下运算符:

      ! - 配置文件的逻辑非 & - 配置文件的逻辑与 | - 配置文件的逻辑或 请注意 & 和 |运营商 不使用括号不能混合。例如“a & b | c”是 不是一个有效的表达式;它必须表示为“(a & b) | c”或“a & (b | c)"。

      【讨论】:

        【解决方案7】:

        @Profile 不能用于此,但您不妨编写自己的 @RunOnProfile 注释和方面。您的方法如下所示:

        @RunOnProfile({"dev", "uat"})
        public void doSomething() {
            log.info("I'm doing something on dev and uat");
        }
        

        或者

        @RunOnProfile("!prod")
        public void doSomething() {
            log.info("I'm doing something on profile other than prod");
        }
        

        这是@RunOnProfile注解和方面的代码:

        @Target(ElementType.METHOD)
        @Retention(RetentionPolicy.RUNTIME)
        public @interface RunOnProfile {
            String[] value();
        
            @Aspect
            @Configuration
            class RunOnProfileAspect {
        
                @Autowired
                Environment env;
        
                @Around("@annotation(runOnProfile)")
                public Object around(ProceedingJoinPoint joinPoint, RunOnProfile runOnProfile) throws Throwable {
                    if (env.acceptsProfiles(runOnProfile.value()))
                        return joinPoint.proceed();
                    return null;
                }
            }
        }
        

        注意 1:如果您期望得到回报而配置文件不适用,您将收到 null

        注意 2:这项工作与任何其他 Spring 方面一样。您需要调用自动装配的 bean 方法,以便 bean 代理启动。换句话说,您不能直接从其他类方法调用该方法。如果需要,您需要在类上自行自动装配组件并调用 self.doSomething()

        【讨论】:

          【解决方案8】:

          使用acceptProfiles(配置文件)

          class Test {
          
            @Autowired
            Environment env
          
            public void method1(){
               if(env.acceptsProfiles(Profiles.of("dev")){
                  //invoke method
                  doStuff();
               }
            }
          }
          

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2021-02-09
            • 1970-01-01
            • 2020-02-23
            • 2020-11-30
            • 1970-01-01
            相关资源
            最近更新 更多