it-swarm.com.de

Einfügen eines externen Wertes in die Spring-Anmerkung

Ich habe über die Java-Funktion nachgedacht, die Anmerkungswerte in der Kompilierzeit auswertet, und es scheint wirklich schwierig zu sein, Anmerkungswerte zu externalisieren.

Ich bin mir jedoch nicht sicher, ob dies tatsächlich unmöglich ist, daher würde ich mich über Vorschläge oder endgültige Antworten zu diesem Thema freuen.

Genauer gesagt versuche ich, einen Anmerkungswert zu externalisieren, der Verzögerungen zwischen geplanten Methodenaufrufen in Spring steuert, z. B .:

public class SomeClass {

    private Properties props;
    private static final long delay = 0;

    @PostConstruct
    public void initializeBean() {
        Resource resource = new ClassPathResource("scheduling.properties");
        props = PropertiesLoaderUtils.loadProperties(resource);
        delay = props.getProperties("delayValue");
    }

    @Scheduled(fixedDelay = delay)
    public void someMethod(){
        // perform something
    }
}

Angenommen, scheduling.properties befindet sich im Klassenpfad und enthält den Eigenschaftsschlüssel delayValue zusammen mit dem entsprechenden langen Wert.

Dieser Code weist offensichtliche Kompilierungsfehler auf, da versucht wird, der Variablen final einen Wert zuzuweisen. Dies ist jedoch obligatorisch, da wir die Variable nicht dem Anmerkungswert zuordnen können, es sei denn, es ist static final.

Gibt es eine Möglichkeit, dies zu umgehen? Ich habe über die benutzerdefinierten Anmerkungen von Spring nachgedacht, aber das Hauptproblem bleibt - wie kann man den externalisierten Wert der Anmerkung zuweisen?

Jede Idee ist willkommen.

BEARBEITEN: Ein kleines Update - Quarzintegration ist in diesem Beispiel zu viel. Wir brauchen nur eine periodische Ausführung mit einer Auflösung von weniger als einer Minute und das ist alles.

23
quantum

Die @Scheduled-Annotation in Spring v3.2.2 hat den ursprünglichen 3 langen Parametern String-Parameter hinzugefügt, um dies zu behandeln. fixedDelayString, fixedRateString und initialDelayString sind jetzt auch verfügbar:

 @Scheduled(fixedDelayString = "${my.delay.property}")
 public void someMethod(){
        // perform something
 }
63
Mark-A

Eine bessere Möglichkeit, dies zu tun, besteht darin, die Planung in xml unter Verwendung des Namensraums der Aufgabe zu definieren

<context:property-placeholder location="scheduling.properties"/>
<task:scheduled ref="someBean" method="someMethod" fixed-delay="${delayValue}"/>

Wenn Sie es aus irgendeinem Grund mit Annotationen tun möchten, müssen Sie eine Annotation erstellen, die über ein anderes optionales Attribut verfügt, in dem Sie den Namen der Eigenschaft oder besser noch einen Eigenschaftenplatzhalterausdruck oder einen Spel-Ausdruck angeben können.

@MyScheduled(fixedDelayString="${delay}")
3
gkamal

Vielen Dank an beide für Ihre Antworten. Sie haben wertvolle Informationen zur Verfügung gestellt, die mich zu dieser Lösung geführt haben. Daher habe ich beide Antworten bestätigt.

Ich habe mich entschieden, einen benutzerdefinierten Bean-Post-Prozessor und eine benutzerdefinierte @Scheduled-Annotation zu erstellen.

Der Code ist einfach (im Wesentlichen eine triviale Anpassung des vorhandenen Spring-Codes) und ich wundere mich wirklich, warum sie es nicht von Anfang an so gemacht haben. Die Anzahl der Variablen von BeanPostProcessor ist effektiv verdoppelt, da ich mich für die alte und die neue Annotation entschieden habe.

Wenn Sie Vorschläge zur Verbesserung dieses Codes haben, bin ich froh, es herauszufinden.

CustomScheduled class (Annotation)

@Target({ElementType.METHOD, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface CustomScheduled {

    String cron() default "";

    String fixedDelay() default "";

    String fixedRate() default "";
}

CustomScheduledAnnotationBeanPostProcessor class

public class CustomScheduledAnnotationBeanPostProcessor implements BeanPostProcessor, Ordered, EmbeddedValueResolverAware, ApplicationContextAware, ApplicationListener<ContextRefreshedEvent>, DisposableBean 
{
    private static final Logger LOG = LoggerFactory.getLogger(CustomScheduledAnnotationBeanPostProcessor.class);

    // omitted code is the same as in ScheduledAnnotationBeanPostProcessor......

    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        return bean;
    }

    // processes both @Scheduled and @CustomScheduled annotations
    public Object postProcessAfterInitialization(final Object bean, String beanName) throws BeansException {
        final Class<?> targetClass = AopUtils.getTargetClass(bean);
        ReflectionUtils.doWithMethods(targetClass, new MethodCallback() {
            public void doWith(Method method) throws IllegalArgumentException, IllegalAccessException {

                Scheduled oldScheduledAnnotation = AnnotationUtils.getAnnotation(method, Scheduled.class);
                if (oldScheduledAnnotation != null) {
                    LOG.info("@Scheduled found at method {}", method.getName());
                    Assert.isTrue(void.class.equals(method.getReturnType()), "Only void-returning methods may be annotated with @Scheduled.");
                    Assert.isTrue(method.getParameterTypes().length == 0, "Only no-arg methods may be annotated with @Scheduled.");
                    if (AopUtils.isJdkDynamicProxy(bean)) {
                        try {
                            // found a @Scheduled method on the target class for this JDK proxy -> is it
                            // also present on the proxy itself?
                            method = bean.getClass().getMethod(method.getName(), method.getParameterTypes());
                        } catch (SecurityException ex) {
                            ReflectionUtils.handleReflectionException(ex);
                        } catch (NoSuchMethodException ex) {
                            throw new IllegalStateException(String.format(
                                    "@Scheduled method '%s' found on bean target class '%s', " +
                                    "but not found in any interface(s) for bean JDK proxy. Either " +
                                    "pull the method up to an interface or switch to subclass (CGLIB) " +
                                    "proxies by setting proxy-target-class/proxyTargetClass " +
                                    "attribute to 'true'", method.getName(), targetClass.getSimpleName()));
                        }
                    }
                    Runnable runnable = new ScheduledMethodRunnable(bean, method);
                    boolean processedSchedule = false;
                    String errorMessage = "Exactly one of 'cron', 'fixedDelay', or 'fixedRate' is required.";
                    String cron = oldScheduledAnnotation.cron();
                    if (!"".equals(cron)) {
                        processedSchedule = true;
                        if (embeddedValueResolver != null) {
                            cron = embeddedValueResolver.resolveStringValue(cron);
                        }
                        cronTasks.put(runnable, cron);
                    }
                    long fixedDelay = oldScheduledAnnotation.fixedDelay();
                    if (fixedDelay >= 0) {
                        Assert.isTrue(!processedSchedule, errorMessage);
                        processedSchedule = true;
                        fixedDelayTasks.put(runnable, fixedDelay);
                    }
                    long fixedRate = oldScheduledAnnotation.fixedRate();
                    if (fixedRate >= 0) {
                        Assert.isTrue(!processedSchedule, errorMessage);
                        processedSchedule = true;
                        fixedRateTasks.put(runnable, fixedRate);
                    }
                    Assert.isTrue(processedSchedule, errorMessage);
                }

                CustomScheduled newScheduledAnnotation = AnnotationUtils.getAnnotation(method, CustomScheduled.class);
                if (newScheduledAnnotation != null) {
                    LOG.info("@CustomScheduled found at method {}", method.getName());
                    Assert.isTrue(void.class.equals(method.getReturnType()), "Only void-returning methods may be annotated with @CustomScheduled.");
                    Assert.isTrue(method.getParameterTypes().length == 0, "Only no-arg methods may be annotated with @CustomScheduled.");
                    if (AopUtils.isJdkDynamicProxy(bean)) {
                        try {
                            // found a @CustomScheduled method on the target class for this JDK proxy -> is it
                            // also present on the proxy itself?
                            method = bean.getClass().getMethod(method.getName(), method.getParameterTypes());
                        } catch (SecurityException ex) {
                            ReflectionUtils.handleReflectionException(ex);
                        } catch (NoSuchMethodException ex) {
                            throw new IllegalStateException(String.format("@CustomScheduled method '%s' found on bean target class '%s', "
                                    + "but not found in any interface(s) for bean JDK proxy. Either "
                                    + "pull the method up to an interface or switch to subclass (CGLIB) "
                                    + "proxies by setting proxy-target-class/proxyTargetClass " + "attribute to 'true'", method.getName(),
                                    targetClass.getSimpleName()));
                        }
                    }

                    Runnable runnable = new ScheduledMethodRunnable(bean, method);
                    boolean processedSchedule = false;
                    String errorMessage = "Exactly one of 'cron', 'fixedDelay', or 'fixedRate' is required.";

                    boolean numberFormatException = false;
                    String numberFormatErrorMessage = "Delay value is not a number!";

                    String cron = newScheduledAnnotation.cron();
                    if (!"".equals(cron)) {
                        processedSchedule = true;
                        if (embeddedValueResolver != null) {
                            cron = embeddedValueResolver.resolveStringValue(cron);
                        }
                        cronTasks.put(runnable, cron);
                        LOG.info("Put cron in tasks map with value {}", cron);
                    }

                    // fixedDelay value resolving
                    Long fixedDelay = null;
                    String resolverDelayCandidate = newScheduledAnnotation.fixedDelay();
                    if (!"".equals(resolverDelayCandidate)) {
                        try {
                            if (embeddedValueResolver != null) {
                                resolverDelayCandidate = embeddedValueResolver.resolveStringValue(resolverDelayCandidate);
                                fixedDelay = Long.valueOf(resolverDelayCandidate);
                            } else {
                                fixedDelay = Long.valueOf(newScheduledAnnotation.fixedDelay());
                            }
                        } catch (NumberFormatException e) {
                            numberFormatException = true;
                        }
                    }

                    Assert.isTrue(!numberFormatException, numberFormatErrorMessage);

                    if (fixedDelay != null && fixedDelay >= 0) {
                        Assert.isTrue(!processedSchedule, errorMessage);
                        processedSchedule = true;
                        fixedDelayTasks.put(runnable, fixedDelay);
                        LOG.info("Put fixedDelay in tasks map with value {}", fixedDelay);
                    }

                    // fixedRate value resolving
                    Long fixedRate = null;
                    String resolverRateCandidate = newScheduledAnnotation.fixedRate();
                    if (!"".equals(resolverRateCandidate)) {
                        try {
                            if (embeddedValueResolver != null) {
                                fixedRate = Long.valueOf(embeddedValueResolver.resolveStringValue(resolverRateCandidate));
                            } else {
                                fixedRate = Long.valueOf(newScheduledAnnotation.fixedRate());
                            }
                        } catch (NumberFormatException e) {
                            numberFormatException = true;
                        }
                    }

                    Assert.isTrue(!numberFormatException, numberFormatErrorMessage);

                    if (fixedRate != null && fixedRate >= 0) {
                        Assert.isTrue(!processedSchedule, errorMessage);
                        processedSchedule = true;
                        fixedRateTasks.put(runnable, fixedRate);
                        LOG.info("Put fixedRate in tasks map with value {}", fixedRate);
                    }
                    Assert.isTrue(processedSchedule, errorMessage);
                }
            }
        });
        return bean;
    }
}

spring-context.xml Konfigurationsdatei

<beans...>
    <!-- Enables the use of a @CustomScheduled annotation-->
    <bean class="org.package.CustomScheduledAnnotationBeanPostProcessor" />
</beans>
3
quantum

Einige Frühlingskommentare unterstützen SpEL.

Zuerst:

<context:property-placeholder
    location="file:${external.config.location}/application.properties" />

Und dann zum Beispiel:

@Value("${delayValue}")
private int delayValue;

Ich bin nicht sicher, ob @Scheduled SPeL unterstützt, aber im Allgemeinen ist dies der Ansatz.

Im Hinblick auf die Terminplanung diese post von mir und diese verwandte Frage

2
Bozho

Wenn Sie dies mit Annotation anstelle von Bean-Konfigurations-XML tun möchten, können Sie die folgenden Annotationen verwenden: @Component, @PropertySource mit PropertySourcesPlaceholderConfigurer Bean selbst wie folgt:

@Component
@PropertySource({ "classpath:scheduling.properties" })
public class SomeClass {

    @Scheduled(fixedDelay = "${delay}")
    public void someMethod(){
        // perform something
    }

    @Bean
    public static PropertySourcesPlaceholderConfigurer placeHolderConfigurer() {
        return new PropertySourcesPlaceholderConfigurer();
    }   
}
0
selins sofa