Можем ли мы условно объявить весенний боб?

Есть ли способ, которым мы можем условно объявить компонент Spring:

<bean class="path.to.the.class.MyClass" if="${1+2=3}" />

Было бы полезно вместо использования профилей. У меня нет конкретного случая использования, но он пришел ко мне.

Ответ 1

Вы можете использовать @Conditional из Spring 4 или @ConditionalOnProperty из Spring Boot.

  • 1. Использование Spring4 (только),

    если вы НЕ используете весеннюю загрузку, это может быть излишним.

Во-первых, создайте класс Condition, в котором ConditionContext имеет доступ к среде:

public class MyCondition implements Condition {
    @Override
    public boolean matches(ConditionContext context, 
                           AnnotatedTypeMetadata metadata) {
        Environment env = context.getEnvironment();
        return null != env 
               && "true".equals(env.getProperty("server.host"));
    }
}

Затем аннотируйте свой bean-компонент:

@Bean
@Conditional(MyCondition.class)
public ObservationWebSocketClient observationWebSocketClient(){
    //return bean
}
  • 2.Использование Spring Boot:

@ConditionalOnProperty(name="server.host", havingValue="localhost")

И в вашем файле abcd.properties,

server.host=localhost

Ответ 2

У меня есть фрагмент для такой вещи. Он проверяет значение свойства, которое задано в аннотации, поэтому вы можете использовать такие вещи, как

@ConditionalOnProperty(value="usenew", on=false, propertiesBeanName="myprops")
@Service("service")
public class oldService implements ServiceFunction{
    // some old implementation of the service function.
}

Он даже позволяет вам определять разные бобы с тем же именем:

@ConditionalOnProperty(value="usenew", on=true, propertiesBeanName="myprops")
@Service("service")
public class newService implements ServiceFunction{
    // some new implementation of the service function.
}

Эти два могут быть объявлены в одно и то же время, позволяя вам иметь "service" именем bean с различными реализациями в зависимости от того, включено или выключено свойство...

Фрагмент для него:

/**
 * Components annotated with ConditionalOnProperty will be registered in the spring context depending on the value of a
 * property defined in the propertiesBeanName properties Bean.
 */

@Target({ ElementType.TYPE, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@Conditional(OnPropertyCondition.class)
public @interface ConditionalOnProperty {
    /**
     * The name of the property. If not found, it will evaluate to false.
     */
    String value();
    /**
     * if the properties value should be true (default) or false
     */
    boolean on() default true;
    /**
     * Name of the bean containing the properties.
     */
    String propertiesBeanName();
}

/**
 * Condition that matches on the value of a property.
 *
 * @see ConditionalOnProperty
 */
class OnPropertyCondition implements ConfigurationCondition {
    private static final Logger LOG = LoggerFactory.getLogger(OnPropertyCondition.class);

    @Override
    public boolean matches(final ConditionContext context, final AnnotatedTypeMetadata metadata) {
        final Map attributes = metadata.getAnnotationAttributes(ConditionalOnProperty.class.getName());
        final String propertyName = (String) attributes.get("value");
        final String propertiesBeanName = (String) attributes.get("propertiesBeanName");
        final boolean propertyDesiredValue = (boolean) attributes.get("on");

        // for some reason, we cannot use the environment here, hence we get the actual properties bean instead.
        Properties props = context.getBeanFactory().getBean(propertiesBeanName, Properties.class);
        final boolean propValue = parseBoolean(props.getProperty(propertyName, Boolean.toString(false)));
        LOG.info("Property '{}' resolved to {}, desired: {}", new Object[] { propertyName, propValue, "" + propertyDesiredValue });
        return propValue == propertyDesiredValue;
    }
    /**
     * Set the registration to REGISTER, else it is handled during  the parsing of the configuration file
     * and we have no guarantee that the properties bean is loaded/exposed yet
     */
    @Override
    public ConfigurationPhase getConfigurationPhase() {
        return ConfigurationPhase.REGISTER_BEAN;
    }
}