有 Java 编程相关的问题?

你可以在下面搜索框中键入要查询的问题!

java将外部化值注入Spring注释

我一直在考虑在编译时评估注释值的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及其相应的长值

现在,这段代码有明显的编译错误,因为我们试图给final变量赋值,但这是必须的,因为我们不能把变量赋值给注释值,除非它是static final

有没有办法绕过这件事?我一直在考虑Spring的定制注释,但根本问题仍然存在——如何将外部化的值分配给注释

任何想法都欢迎

编辑:一个小的更新——对于这个例子来说,Quartz集成有点过头了。我们只需要一个亚分钟分辨率的周期性执行,仅此而已


共 (4) 个答案

  1. # 1 楼答案

    一些spring注释支持SpEL

    首先:

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

    然后,例如:

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

    不过,我不确定@Scheduled是否支持SPeL,但总的来说,这是一种方法

    关于日程安排,请勾选此post of minethis related question

  2. # 2 楼答案

    SpringV3中的@Scheduled注释。2.2在原来的3个长参数的基础上增加了字符串参数来处理这个问题fixedDelayStringfixedRateStringinitialDelayString现在也可以使用:

     @Scheduled(fixedDelayString = "${my.delay.property}")
     public void someMethod(){
            // perform something
     }
    
  3. # 3 楼答案

    更好的方法是使用任务名称空间在xml中定义调度

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

    如果出于某种原因希望对注释执行此操作,则需要创建一个注释,该注释具有另一个可选属性,您可以在其中指定属性名称,或者更好地指定属性占位符表达式或Spel表达式

    @MyScheduled(fixedDelayString="${delay}")
    
  4. # 4 楼答案

    谢谢你们的回答,你们提供了有价值的信息,让我找到了这个解决方案,所以我对两个答案都投了赞成票

    我选择了定制bean后处理器和定制@Scheduled注释

    代码很简单(本质上是对现有Spring代码的一个微不足道的修改),我真的很想知道为什么他们一开始就不这么做BeanPostProcessor的代码计数实际上翻了一番,因为我选择处理旧注释和新注释

    如果你对如何改进这段代码有任何建议,我很乐意听听

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

    春天的背景。xml配置文件

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