programing

봄: 프로필에서 AND를 수행하는 방법?

minecode 2023. 9. 2. 09:42
반응형

봄: 프로필에서 AND를 수행하는 방법?

스프링 종단 주석을 사용하여 종단을 선택할 수 있습니다.그러나 설명서를 읽는 경우 OR 작업을 통해 둘 이상의 프로파일만 선택할 수 있습니다.@Profile("A", "B")를 지정하면 프로파일 A 또는 프로파일 B가 활성화되면 빈이 작동합니다.

사용 사례가 다릅니다. 여러 구성의 TEST 및 PROD 버전을 지원하고자 합니다.따라서 간혹 TEST 및 CONFIG1 프로파일이 모두 활성화된 경우에만 Bean을 자동 배선하려고 합니다.

스프링으로 할 수 있는 방법이 없을까요?가장 간단한 방법은 무엇입니까?

Spring 5.1(Spring Boot 2.1에 통합됨) 이후 프로파일 문자열 주석 내부에서 프로파일 식을 사용할 수 있습니다.그래서:

Spring 5.1(Spring Boot 2.1) 이상에서는 다음과 같이 쉽게 사용할 수 있습니다.

@Component
@Profile("TEST & CONFIG1")
public class MyComponent {}

스프링 4.x5.0.x:

  • 접근 1: @Mithun이 답변함, 그것은 당신이 스프링 빈에 그의 것으로도 주석을 달 때마다 당신의 프로필 주석에서 OR을 AND로 변환하는 당신의 사례를 완벽하게 다룹니다.Condition 저는 하지 않은 .하지만 저는 아무도 제안하지 않았던 찬성과 반대의 의견을 가진 또 다른 접근법을 제안하고 싶습니다.

  • 그냥 하기 2: 사용하기@Conditional.Condition필요한 조합으로서의 구현.조합만큼 많은 구현을 생성해야 한다는 단점이 있지만 조합이 많지 않다면 더 간결한 솔루션이며 더 많은 유연성과 더 복잡한 논리적 해결책을 구현할 수 있는 기회를 제공한다고 생각합니다.

접근법 2의 구현은 다음과 같습니다.

당신의 봄 콩:

@Component
@Conditional(value = { TestAndConfig1Profiles.class })
public class MyComponent {}

TestAndConfig1Profiles구현:

public class TestAndConfig1Profiles implements Condition {
    @Override
    public boolean matches(final ConditionContext context, final AnnotatedTypeMetadata metadata) {
        return context.getEnvironment().acceptsProfiles("TEST")
                    && context.getEnvironment().acceptsProfiles("CONFIG1");
    }
}

이 접근 방식을 사용하면 다음과 같은 보다 복잡한 논리적 상황을 쉽게 처리할 수 있습니다.

(TEST & CONFIG1) | (TEST & CONFIG3)

질문에 대한 최신 답변을 제공하고 다른 답변을 보완하고자 합니다.

스프링은 AND 기능을 즉시 제공하지 않기 때문입니다.저는 다음과 같은 전략을 참조하십시오.

현의재@Profile 주석이 .@Conditional(ProfileCondition.class)ProfileCondition.class프로파일을 반복하고 프로파일이 활성화되었는지 확인합니다.마찬가지로 자신만의 조건부 구현을 만들고 콩 등록을 제한할 수 있습니다.

public class MyProfileCondition implements Condition {

    @Override
    public boolean matches(final ConditionContext context,
            final AnnotatedTypeMetadata metadata) {
        if (context.getEnvironment() != null) {
            final MultiValueMap<String, Object> attrs = metadata.getAllAnnotationAttributes(Profile.class.getName());
            if (attrs != null) {
                for (final Object value : attrs.get("value")) {
                    final String activeProfiles = context.getEnvironment().getProperty("spring.profiles.active");

                    for (final String profile : (String[]) value) {
                        if (!activeProfiles.contains(profile)) {
                            return false;
                        }
                    }
                }
                return true;
            }
        }
        return true;
    }

}

강의실:

@Component
@Profile("dev")
@Conditional(value = { MyProfileCondition.class })
public class DevDatasourceConfig

참고: 모든 코너 케이스(null, 길이 확인 등)를 확인하지 않았습니다.하지만, 이 방향이 도움이 될 수 있습니다.

@Mithun의 약간 개선된 버전은 다음과 같습니다.

public class AndProfilesCondition implements Condition {

public static final String VALUE = "value";
public static final String DEFAULT_PROFILE = "default";

@Override
public boolean matches(final ConditionContext context, final AnnotatedTypeMetadata metadata) {
    if (context.getEnvironment() == null) {
        return true;
    }
    MultiValueMap<String, Object> attrs = metadata.getAllAnnotationAttributes(Profile.class.getName());
    if (attrs == null) {
        return true;
    }
    String[] activeProfiles = context.getEnvironment().getActiveProfiles();
    String[] definedProfiles = (String[]) attrs.getFirst(VALUE);
    Set<String> allowedProfiles = new HashSet<>(1);
    Set<String> restrictedProfiles = new HashSet<>(1);
    for (String nextDefinedProfile : definedProfiles) {
        if (!nextDefinedProfile.isEmpty() && nextDefinedProfile.charAt(0) == '!') {
            restrictedProfiles.add(nextDefinedProfile.substring(1, nextDefinedProfile.length()));
            continue;
        }
        allowedProfiles.add(nextDefinedProfile);
    }
    int activeAllowedCount = 0;
    for (String nextActiveProfile : activeProfiles) {
        // quick exit when default profile is active and allowed profiles is empty
        if (DEFAULT_PROFILE.equals(nextActiveProfile) && allowedProfiles.isEmpty()) {
            continue;
        }
        // quick exit when one of active profiles is restricted
        if (restrictedProfiles.contains(nextActiveProfile)) {
            return false;
        }
        // just go ahead when there is no allowed profiles (just need to check that there is no active restricted profiles)
        if (allowedProfiles.isEmpty()) {
            continue;
        }
        if (allowedProfiles.contains(nextActiveProfile)) {
            activeAllowedCount++;
        }
    }
    return activeAllowedCount == allowedProfiles.size();
}

}

댓글에 올리지 못했습니다.

그러나 다른 옵션은 클래스/메소드 레벨에서 재생할 수 있습니다.@Profile주석구현만큼 유연하지 않음MyProfileCondition당신의 경우에 적합하다면 신속하고 깨끗합니다.

예를 들어 FAST와 DEV가 모두 활성화된 경우에는 시작되지 않지만 DEV가 다음과 같은 경우에만 시작됩니다.

@Configuration
@Profile("!" + SPRING_PROFILE_FAST)
public class TomcatLogbackAccessConfiguration {

    @Bean
    @Profile({SPRING_PROFILE_DEVELOPMENT, SPRING_PROFILE_STAGING})
    public EmbeddedServletContainerCustomizer containerCustomizer() {

다른 종류의 트릭이지만 많은 시나리오에서 작동할 수 있는 것은 @Configuration에 @Profile 주석을 배치하고 다른 @Bean에 @Profile 주석을 배치하는 것입니다. 이는 Java 기반 스프링 구성에서 두 프로파일 사이에 논리적 AND를 생성합니다.

@Configuration
@Profile("Profile1")
public class TomcatLogbackAccessConfiguration {

   @Bean
   @Profile("Profile2")
   public EmbeddedServletContainerCustomizer containerCustomizer() {

@Profile 주석을 사용하여 구성 클래스 또는 been 메서드를 이미 표시한 경우에는 다음을 사용하여 추가 프로파일(예: AND 조건)을 쉽게 확인할 수 있습니다.Environment.acceptsProfiles()

@Autowired Environment env;

@Profile("profile1")
@Bean
public MyBean myBean() {
    if( env.acceptsProfiles("profile2") ) {
        return new MyBean();
    }
    else {
        return null;
    }
}

저는 @rozhoc의 답변이 @Profile을 사용할 때 어떤 프로파일도 'default'에 해당하지 않는다는 사실을 설명하지 않았기 때문에 @rozhoc의 답변을 개선했습니다.또한 제가 원했던 조건은!default && !a@rozhoc의 코드가 제대로 처리되지 않았습니다.마침내 나는 몇몇 Java8을 사용했고 오직 그것만을 보여주었습니다.matches간결한 방법

@Override
public boolean matches(final ConditionContext context, final AnnotatedTypeMetadata metadata) {
    if (context.getEnvironment() == null) {
        return true;
    }
    MultiValueMap<String, Object> attrs = metadata.getAllAnnotationAttributes(Profile.class.getName());
    if (attrs == null) {
        return true;
    }

    Set<String> activeProfilesSet = Arrays.stream(context.getEnvironment().getActiveProfiles()).collect(Collectors.toSet());
    String[] definedProfiles = (String[]) attrs.getFirst(VALUE);
    Set<String> allowedProfiles = new HashSet<>(1);
    Set<String> restrictedProfiles = new HashSet<>(1);
    if (activeProfilesSet.size() == 0) {
        activeProfilesSet.add(DEFAULT_PROFILE);  // no profile is equivalent in @Profile terms to "default"
    }
    for (String nextDefinedProfile : definedProfiles) {
        if (!nextDefinedProfile.isEmpty() && nextDefinedProfile.charAt(0) == '!') {
            restrictedProfiles.add(nextDefinedProfile.substring(1, nextDefinedProfile.length()));
            continue;
        }
        allowedProfiles.add(nextDefinedProfile);
    }
    boolean allowed = true;
    for (String allowedProfile : allowedProfiles) {
        allowed = allowed && activeProfilesSet.contains(allowedProfile);
    }
    boolean restricted = true;
    for (String restrictedProfile : restrictedProfiles) {
        restricted = restricted && !activeProfilesSet.contains(restrictedProfile);
    }
    return allowed && restricted;
}

혼란스러운 경우를 대비하여 실제로 사용하는 방법은 다음과 같습니다.

@Profile({"!default", "!a"})
@Conditional(value={AndProfilesCondition.class})

언급URL : https://stackoverflow.com/questions/27055072/spring-how-to-do-and-in-profiles

반응형