Home > Net >  How to create custom @ConditionalOnProperty for simpler usage?
How to create custom @ConditionalOnProperty for simpler usage?

Time:10-14

In a Spring-Boot project, I use @ConditionalOnProperty to choose whether some Beans get loaded or not. It looks like the following:

@ConditionalOnProperty(
    prefix = "myservice",
    name = "implversion",
    havingValue = "a"
)
@Service
public class MyServiceImplA implements MyService {
   // ...
}

This allows me to choose with specific profiles which Bean should be loaded, for example different implementations of an interface, depending on the value of myservice.implversion being a or b or whatever other value.

I'd like to achieve the same effect with a user-friendlier annotation like such:

@OnMyServiceVersion(value = "a")
@Service
public class MyServiceImplA implements MyService {
   // ...
}

How can one do this?


I've tried annotating my custom annotation with @Conditional and implementing the Condition interface but I don't understand how to check properties that way. The Spring-Boot OnPropertyCondition extends SpringBootCondition is not public so I cannot start from there, and extending annotations isn't allowed, so I'm kind of stuck.

I've also tried the following with no success:

// INVALID CODE, DO NOT USE
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@ConditionalOnProperty(
    prefix = "myservice",
    name = "implversion",
    havingValue = OnMyServiceVersion.value()
)
public @interface OnMyServiceVersion {
    String value();
}

CodePudding user response:

You can annotate your @OnMyServiceVersion annotation with @ConditionalOnProperty and alias the value of your annotation to the havingValue attribute of @ConditionalOnProperty:

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.TYPE, ElementType.METHOD })
@ConditionalOnProperty(prefix = "myservice", name = "implversion")
public @interface OnMyServiceVersion {

    @AliasFor(annotation = ConditionalOnProperty.class, attribute = "havingValue")
    String value() default "";

}

Here's a complete example that shows this in action:

package com.example.demo;

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.core.annotation.AliasFor;
import org.springframework.stereotype.Service;

@SpringBootApplication
public class CustomPropertyConditionApplication {

    public static void main(String[] args) {
        SpringApplication.run(CustomPropertyConditionApplication.class, "--myservice.implversion=b");
    }
    
    @Service
    @OnMyServiceVersion("a")
    static class ServiceA {
        
        ServiceA() {
            System.out.println("Service A");
        }
        
    }
    
    @Service
    @OnMyServiceVersion("b")
    static class ServiceB {
        
        ServiceB() {
            System.out.println("Service B");
        }
        
    }
    
    @Documented
    @Retention(RetentionPolicy.RUNTIME)
    @Target({ ElementType.TYPE, ElementType.METHOD })
    @ConditionalOnProperty(prefix = "myservice", name = "implversion")
    static @interface OnMyServiceVersion {
        
        @AliasFor(annotation = ConditionalOnProperty.class, attribute = "havingValue")
        String value() default "";
        
    }

}

This will output Service B when run. If you change the arguments in the main method to --myservice.implversion=a it will output Service A. If you remove the argument, it won't output either.

CodePudding user response:

I think this will be helpful

Spring allows us to customize the behavior of the @Conditional annotation by creating our custom condition templates. To create one, we simply need to implement the Condition interface:

class Java8Condition implements Condition {
    @Override
    public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
        return JavaVersion.getJavaVersion().equals(JavaVersion.EIGHT);
    }
}

The matches method tells Spring whether the condition has passed or not. It has two arguments that give us respectively information about the context where the bean will initialize and the metadata of the used @Conditional annotation.

After that, we should place our new condition as an attribute in the @Conditional annotation:

@Service
@Conditional(Java8Condition.class)
public class Java8DependedService {
    // ...
}

CodePudding user response:

@Bean(name = "emailNotification")
@ConditionalOnProperty(prefix = "notification", name = "service")
public NotificationSender notificationSender() {
    return new EmailNotification();
}

for reference https://www.baeldung.com/spring-conditionalonproperty

  • Related