强制子类在Java Spring中的超级抽象类中的方法上添加@Schedule。

huangapple go评论94阅读模式
英文:

Enforce subclass to add @Schedule to a method in super abstract class in Java Spring

问题

超级抽象类

public abstract class SuperClass {
       public abstract void x();
       public void y() { 
              // Some implementation
       } 

       // 要被调度的方法。 
       public void scheduledMethod() {
              x();
              y();
       }
}

子类

public class Subclass extends SuperClass {
       @Override
       public void x() { 
              // Some implementation
       }

       // 如何强制开发人员添加此内容? 
       @Scheduled(cron = "0 0 0 * * ?")
       @Override
       public void scheduledMethod(){
              super.scheduledMethod();
       } 
}
英文:

I have a super abstract class that has some common implemented methods and other abstract methods to be implemented by a subclass. One of the common implemented methods is a method to be annotated as @Scheduled, but I want the subclass to define how this schedule should be defined (fixed delay, fixed rate or cron .. etc). How to implement such behaviour ?

One approach I thought of is to override the method to be scheduled in the subclass such that it just call its corresponding method in the super class and add the @Scheduled on it with the desired definition, but I don't know how to enforce the subclass to do so as this method is not abstract.

Super Abstract Class

public abstract class SuperClass {
       public abstract void x();
       public void y() { 
              // Some implementation
       } 
    
       // Method to be scheduled. 
       public void scheduledMethod() {
              x();
              y();
       }
}

Subclass

public class Subclass extends SuperClass {
       @Override
       public void x() { 
              // Some implementation
       }

       // How to enforce the developer to add this ? 
       @Scheduled(cron = "0 0 0 * * ?")
       public void scheduledMethod(){
              super.scheduledMethod();
       } 
}

答案1

得分: 2

我无法理解如何使用@Scheduled,但我有一个替代方案:

  1. 在您的抽象类中,要求子类实现一个方法来返回计划:
public String getCronString();
  1. 使用调度器(Scheduler)以编程方式安排任务,使用在子类中实现的方法getCronString()来返回cron计划。以下是使用Spring Boot以编程方式安排任务的几个示例:

基本上,如果您的子类没有实现public String getCronString();,您的代码将无法编译。

英文:

I couldn't get my head around how you could use @Scheduled but, I've an alternative:

  1. In your abstract class, require a method to be implemented by subclasses to return the schedule:
public String getCronString();
  1. Programmatically schedule the task using Scheduler using the method getCronString() that's implemented in your subclasses, to return the cron schedule. Few examples on how to programmatically schedule tasks with Spring boot:

Basically, if your subclasses are not implementing public String getCronString(); your code won't compile.

答案2

得分: 1

一个选项是在bean创建时检查`@Scheduled`注解是否存在[元存在](https://github.com/spring-projects/spring-framework/wiki/Spring-Annotation-Programming-Model#annotation-presence))。这可以通过在超类的[`@PostConstruct`](https://docs.oracle.com/javaee/7/api/javax/annotation/PostConstruct.html)方法中使用反射轻松实现:

```java
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.util.ClassUtils;

public abstract class SuperClass {
    ...

    @PostConstruct
    public void enforceScheduling() {
        boolean annotationPresent = AnnotationUtils.getAnnotation(ClassUtils.getMethod(getClass(), "scheduledMethod"), Scheduled.class) != null;
        if (!annotationPresent) {
            throw new IllegalStateException("@Scheduled annotation missing from scheduledMethod");
        }
    }
}

这将导致在应用程序启动时抛出异常,当Spring尝试创建bean时,会快速失败,并明确指出缺少什么。


<details>
<summary>英文:</summary>

One option would be to check that the `@Scheduled` annotation is present (or [meta-present](https://github.com/spring-projects/spring-framework/wiki/Spring-Annotation-Programming-Model#annotation-presence)) at bean creation time.  This can be easily achieved using reflection in a [`@PostConstruct`](https://docs.oracle.com/javaee/7/api/javax/annotation/PostConstruct.html) method on the superclass:

```java
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.util.ClassUtils;

public abstract class SuperClass {
    ...

    @PostConstruct
    public void enforceScheduling() {
        boolean annotationPresent = AnnotationUtils.getAnnotation(ClassUtils.getMethod(getClass(), &quot;scheduledMethod&quot;), Scheduled.class) != null;
        if (!annotationPresent) {
            throw new IllegalStateException(&quot;@Scheduled annotation missing from scheduledMethod&quot;);
        }
    }
}

This will cause an exception to be thrown at application start-up time when Spring attempts to create the bean, failing fast and making it very obvious what is missing.

答案3

得分: 0

过去在具有对类层次结构约束的项目中,我曾经做过一件事情,那就是编写一个单元测试,对无法在编译时或运行时轻松检查的类层次结构进行迭代,以检查它是否满足约束条件。

由于这里使用了Spring,我们可能更关心该类型的每个Bean是否与约束条件匹配,而不仅仅是类路径上的每个子类是否匹配。

在这种情况下,约束是在子类中的给定方法上存在@Scheduled注解(或元注解)。通过给定子类的Class对象,可以很容易地使用反射来判断注解的存在。

将这些内容结合起来,我们可以编写一个利用这种技术的单元测试:

import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.util.ClassUtils;

@SpringBootTest
public class SuperClassTest {
    @Autowired
    private List<SuperClass> beansOfThisType;

    @Test
    public void allBeansMustBeScheduled() {
        for (SuperClass bean : beansOfThisType) {
            boolean annotationPresent = AnnotationUtils.getAnnotation(ClassUtils.getMethod(bean.getClass(), "scheduledMethod"), Scheduled.class) != null;
            assertTrue(annotationPresent, "@Scheduled annotation missing from scheduledMethod for " + bean.getClass());
        }
    }
}

与仅针对Spring Bean而不是类路径上的每个对象进行检查的方法非常类似;区别在于获取要检查的Class对象列表的机制。从类路径中获取匹配的对象的方式并不简单,超出了本回答的范围。https://stackoverflow.com/questions/492184/how-do-you-find-all-subclasses-of-a-given-class-in-java列出了许多实现它的方法。

英文:

One thing I've done in the past in projects with constraints on a class hierarchy that I could not easily check at compile or runtime was to write a unit test that iterated through every concrete (non-abstract) instance of the class and checked whether it met that constraint.

Since this is using Spring, we probably care more that each bean of this type matches the constraint than whether each subclass on the classpath does.

The constraint in this case is that the @Scheduled annotation is present (or meta-present) on the given method in the subclass. The presence of the annotation can be easily achieved using reflection given the Class object of the subclass.

Putting this together, we can write a unit test utilizing this technique:

import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.util.ClassUtils;

@SpringBootTest
public class SuperClassTest {
    @Autowired
    private List&lt;SuperClass&gt; beansOfThisType;

    @Test
    public void allBeansMustBeScheduled() {
        for (SuperClass bean : beansOfThisType) {
            boolean annotationPresent = AnnotationUtils.getAnnotation(ClassUtils.getMethod(bean.getClass(), &quot;scheduledMethod&quot;), Scheduled.class) != null;
            assertTrue(annotationPresent, &quot;@Scheduled annotation missing from scheduledMethod for &quot; + bean.getClass());
        }
    }
}

Checking every object of the type on the classpath rather than just the Spring beans would be a very similar approach; the difference would be the mechanism to get the list of Class objects to check. Getting the matching objects from the classpath is non-straightforward enough that it's outside the scope of this answer. https://stackoverflow.com/questions/492184/how-do-you-find-all-subclasses-of-a-given-class-in-java lists a number of ways to accomplish it.

huangapple
  • 本文由 发表于 2020年8月16日 19:49:28
  • 转载请务必保留本文链接:https://go.coder-hub.com/63436514.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定