복붙노트

[SPRING] 외부화 된 값을 Spring annotation으로 주입

SPRING

외부화 된 값을 Spring annotation으로 주입

필자는 컴파일 시간에 주석 값을 평가하는 자바 기능을 생각해 왔으며, 실제로 외 부화 애노테이션 값을 어렵게 만드는 것으로 보입니다.

그러나 실제로 불가능한 것인지 확신 할 수 없으므로 이에 대한 제안이나 답변을 주시면 감사하겠습니다.

더 중요한 것은 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가 classpath에 있고 해당 long 값과 함께 속성 키 delayValue를 포함한다고 가정하십시오.

이제이 코드는 최종 변수에 값을 할당하려고하기 때문에 명백한 컴파일 오류가 있지만 고정 최종 변수가 아니라면 변수를 주석 값에 할당 할 수 없으므로 필수입니다.

이 문제를 해결할 방법이 있습니까? Spring 커스텀 애노테이션에 대해 생각해 왔지만 근본적인 문제는 남아 있습니다 - 외부화 된 값을 애노테이션에 할당하는 방법은 무엇입니까?

어떤 생각이라도 환영합니다.

편집 : 작은 업데이트 - 석영 통합이 예제에 대한 잔인한입니다. 서브 분 해상도로 주기적으로 실행하면됩니다.

해결법

  1. ==============================

    1.Spring v3.2.2의 @Scheduled 주석은 이것을 처리하기 위해 String 매개 변수를 원래의 3 개의 긴 매개 변수에 추가했습니다. fixedDelayString, fixedRateString 및 initialDelayString을 사용할 수 있습니다.

    Spring v3.2.2의 @Scheduled 주석은 이것을 처리하기 위해 String 매개 변수를 원래의 3 개의 긴 매개 변수에 추가했습니다. fixedDelayString, fixedRateString 및 initialDelayString을 사용할 수 있습니다.

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

    2.이렇게하는 더 좋은 방법은 작업 이름 공간을 사용하여 xml로 일정을 정의하는 것입니다.

    이렇게하는 더 좋은 방법은 작업 이름 공간을 사용하여 xml로 일정을 정의하는 것입니다.

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

    어떤 이유로 주석을 사용하여 작업하려는 경우 속성 이름을 지정하거나 더 나은 속성 자리 표시 자 식 또는 Spel 식을 지정할 수있는 다른 선택적 특성이있는 주석을 만들어야합니다.

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

    3.두 가지 모두 해답을 주셔서 감사합니다. 귀사는이 솔루션으로 안내하는 귀중한 정보를 제공해 주셨습니다. 그래서 나는 두 답을 모두지지했습니다.

    두 가지 모두 해답을 주셔서 감사합니다. 귀사는이 솔루션으로 안내하는 귀중한 정보를 제공해 주셨습니다. 그래서 나는 두 답을 모두지지했습니다.

    나는 사용자 정의 bean post processor와 custom @Scheduled annotation을 만들기로 선택했다.

    코드는 간단합니다 (본질적으로 기존 스프링 코드의 간단한 수정입니다). 그리고 나는 그들이 왜 이렇게하지 않았는지 궁금합니다. BeanPostProcessor의 코드 카운트는 이전 주석과 새로운 주석을 처리하기 때문에 두 배가됩니다.

    이 코드를 개선하는 방법에 대한 제안 사항이 있으시면 언제든지 알려 주시기 바랍니다.

    CustomScheduled 클래스 (주석)

    @Target({ElementType.METHOD, ElementType.ANNOTATION_TYPE})
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    public @interface CustomScheduled {
    
        String cron() default "";
    
        String fixedDelay() default "";
    
        String fixedRate() default "";
    }
    

    Custom ScheduledAnnotationBeanPostProcessor 클래스

    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>
    
  4. ==============================

    4.일부 스프링 주석은 SpEL을 지원합니다.

    일부 스프링 주석은 SpEL을 지원합니다.

    먼저:

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

    그리고 나서, 예를 들면 :

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

    @Scheduled가 SPeL을 지원하는지 잘 모르겠지만, 일반적으로 접근 방법입니다.

    일정 계획과 관련하여이 게시물과이 관련 질문을 확인하십시오.

  5. ==============================

    5.bean 구성 xml이 아닌 주석으로이 작업을 수행하려면 @Component, PropertySourcesPlaceholderConfigurer Bean과 함께 @PropertySource와 같이 다음과 같은 주석을 사용할 수 있습니다.

    bean 구성 xml이 아닌 주석으로이 작업을 수행하려면 @Component, PropertySourcesPlaceholderConfigurer Bean과 함께 @PropertySource와 같이 다음과 같은 주석을 사용할 수 있습니다.

    @Component
    @PropertySource({ "classpath:scheduling.properties" })
    public class SomeClass {
    
        @Scheduled(fixedDelay = "${delay}")
        public void someMethod(){
            // perform something
        }
    
        @Bean
        public static PropertySourcesPlaceholderConfigurer placeHolderConfigurer() {
            return new PropertySourcesPlaceholderConfigurer();
        }   
    }
    
  6. from https://stackoverflow.com/questions/11608531/injecting-externalized-value-into-spring-annotation by cc-by-sa and MIT license