33

コンパイル時にアノテーション値を評価するJava機能について考えていましたが、アノテーション値の外部化が非常に困難になっているようです。

しかし、それが実際に不可能かどうかはわかりませんので、これについての提案や決定的な答えをいただければ幸いです。

さらに重要なことに、Springでスケジュールされたメソッド呼び出し間の遅延を制御するアノテーション値を外部化しようとしています。例:

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

それscheduling.propertiesがクラスパス上delayValueにあり、対応するlong値とともにプロパティキーが含まれているとします。

変数に値を割り当てようとしているため、このコードには明らかなコンパイルエラーがありますfinalが、変数をアノテーション値に割り当てることができないため、これは必須ですstatic final

これを回避する方法はありますか?Springのカスタムアノテーションについて考えていましたが、根本的な問題は残っています-外部化された値をアノテーションに割り当てる方法は?

どんなアイデアでも大歓迎です。

編集:小さな更新-この例では、Quartzの統合はやり過ぎです。1分未満の解像度で定期的に実行する必要があります。それだけです。

4

5 に答える 5

88

Spring v3.2.2の@Scheduledアノテーションは、これを処理するために元の3つの長いパラメーターに文字列パラメーターを追加しました。fixedDelayStringfixedRateStringおよびinitialDelayString現在も利用可能です:

 @Scheduled(fixedDelayString = "${my.delay.property}")
 public void someMethod(){
        // perform something
 }
于 2013-05-09T14:14:29.867 に答える
4

両方の回答に感謝します。あなたは私をこの解決策に導いた貴重な情報を提供してくれたので、私は両方の回答に賛成しました。

カスタムBeanポストプロセッサとカスタム@Scheduledアノテーションを作成することを選択しました。

コードは単純で(基本的には既存のSpringコードを簡単に適応させたものです)、なぜ彼らが最初からこのようにしないのか疑問に思います。BeanPostProcessor古いアノテーションと新しいアノテーションを処理することを選択したため、のコード数は実質的に2倍になります。

このコードを改善する方法について何か提案があれば、私はそれを聞いてうれしいです。

CustomScheduledクラス(アノテーション)

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

    String cron() default "";

    String fixedDelay() default "";

    String fixedRate() default "";
}

CustomScheduledAnnotationBeanPostProcessorクラス

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構成ファイル

<beans...>
    <!-- Enables the use of a @CustomScheduled annotation-->
    <bean class="org.package.CustomScheduledAnnotationBeanPostProcessor" />
</beans>
于 2012-07-27T10:39:41.443 に答える
3

一部のSpringアノテーションはSpELをサポートしています。

初め:

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

そして、例えば:

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

@ScheduledSPeLをサポートしているかどうかはわかりませんが、一般的にはそれがアプローチです。

スケジュールに関しては、私のこの投稿この関連する質問を確認してください

于 2012-07-23T08:08:42.120 に答える
3

これを行うためのより良い方法は、タスクの名前空間を使用してxmlでスケジューリングを定義することです。

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

何らかの理由でアノテーションを使用したい場合は、別のオプションの属性を持つアノテーションを作成する必要があります。この場合、プロパティ名を指定するか、プロパティプレースホルダー式またはSpel式を指定することをお勧めします。

@MyScheduled(fixedDelayString="${delay}")
于 2012-07-23T08:08:45.883 に答える
0

Bean構成xmlではなくアノテーションを使用してこれを機能させる場合は、次のアノテーションを使用できます:@ Component、@PropertySourceとPropertySourcesPlaceholderConfigurerBean自体、次のようになります。

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

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

    @Bean
    public static PropertySourcesPlaceholderConfigurer placeHolderConfigurer() {
        return new PropertySourcesPlaceholderConfigurer();
    }   
}
于 2018-11-02T08:31:23.543 に答える