문제

I'd like to introduce some methods that are only executed during development.

I thought I might use Spring @Profile annotation here? But how can I apply this annotation on class level, so that this method is only invoked if the specific profile is configured in properties?

spring.profiles.active=dev

Take the following as pseudocode. How can this be done?

class MyService {

    void run() { 
       log();
    }

    @Profile("dev")
    void log() {
       //only during dev
    }
}
도움이 되었습니까?

해결책 2

AS you can read on http://docs.spring.io/spring/docs/3.1.x/javadoc-api/org/springframework/context/annotation/Profile.html

The @Profile annotation may be used in any of the following ways:

as a type-level annotation on any class directly or indirectly annotated with @Component, including @Configuration classes as a meta-annotation, for the purpose of composing custom stereotype annotations If a @Configuration class is marked with @Profile, all of the @Bean methods and @Import annotations associated with that class will be bypassed unless one or more the specified profiles are active. This is very similar to the behavior in Spring XML: if the profile attribute of the beans element is supplied e.g., , the beans element will not be parsed unless profiles 'p1' and/or 'p2' have been activated. Likewise, if a @Component or @Configuration class is marked with @Profile({"p1", "p2"}), that class will not be registered/processed unless profiles 'p1' and/or 'p2' have been activated.

So, a @Profile annotation on a class, aplies to all of it's methods and imports. Not to the class.

What you're trying to do could probably be achieved by having two classes that implement the same interface, and injecting one or another depending on the profile. Take a look at the answer to this question.

Annotation-driven dependency injection which handles different environments

다른 팁

For future readers who don't want to have multiple @Beans annotated with @Profile, this could also be a solution:

class MyService {

   @Autowired
   Environment env;

   void run() { 
      if (Arrays.asList(env.getActiveProfiles()).contains("dev")) {
         log();
      }
   }

   void log() {
      //only during dev
   }
}

@Profile can be used with method and with Java Based Configuration

e.g. separate DB timestamp for PostgreSQL (LocalDateTime) and for HSQLDB (prior to 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;
}

Have a look also at Spring Profiles example: 3. More…(sample Spring @Profile at method level)

Just wanted to add that answer saying this is possible with current spring on method-level is blatantly wrong. Using @Profile on methods in general will not work - the only methods it will work on are in the @Configuration class with @Bean annotation on them.

I run a quick test with Spring 4.2.4, where it was seen that

  • @Profile in @Configuration class bean creation methods work
  • @Profile in methods of a bean does not work (and is not expected to work - docs are a bit ambiguous)
  • Class-level @Profile works if and only if it's at the same context as bean definition, in the configuration class or if context-scan is used, there
  • env.acceptsProfiles("profile"), Arrays.asList(env.getActiveProfiles()).contains("profile") works

Test class:

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();
    }
}

Possible in 4.1

The @Profile annotation may be used in any of the following ways:

as a type-level annotation on any class directly or indirectly annotated with @Component, including @Configuration classes. As a meta-annotation, for the purpose of composing custom stereotype annotations. As a method-level annotation on any @Bean method

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

@Profile can't be used for that, but you might as well code your own @RunOnProfile annotation and aspect. Your method would look like this:

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

Or

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

Here's the code for the @RunOnProfile annotation and aspect:

@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;
        }
    }
}

Note 1: If you expect something in return and the profile does not apply, you would get null.

Note 2: This work like any other Spring aspect. You need to invoke the autowired bean method, for the bean proxy to kick in. In other words, you can't invoke the method directly from other class method. If you want that, you need to self autowire the component, on the class and invoke self.doSomething().

Use acceptsProfiles(Profile)

class Test {

  @Autowired
  Environment env

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

Using the Environment profile this can be achieved:

class MyClass {

   @Autowired
   Environment env;

   void methodA() { 
      if (env.acceptsProfiles(Profiles.of("dev"))) {
         methodB();
      }
   }

   void methodB() {
      // when profile is 'dev'
   }
}

Profiles.of can be used with logical expressions also:

static Profiles of(String... profiles) Create a new Profiles instance that checks for matches against the given profile strings. The returned instance will match if any one of the given profile strings matches.

A profile string may contain a simple profile name (for example "production") or a profile expression. A profile expression allows for more complicated profile logic to be expressed, for example "production & cloud".

The following operators are supported in profile expressions:

! - A logical not of the profile & - A logical and of the profiles | - A logical or of the profiles Please note that the & and | operators may not be mixed without using parentheses. For example "a & b | c" is not a valid expression; it must be expressed as "(a & b) | c" or "a & (b | c)".

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top