英文:
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
,但我有一个替代方案:
- 在您的抽象类中,要求子类实现一个方法来返回计划:
public String getCronString();
- 使用调度器(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:
- In your abstract class, require a method to be implemented by subclasses to return the schedule:
public String getCronString();
- 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(), "scheduledMethod"), Scheduled.class) != null;
if (!annotationPresent) {
throw new IllegalStateException("@Scheduled annotation missing from scheduledMethod");
}
}
}
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<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());
}
}
}
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.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论