1

我正在研究一个我们有任务概念的框架。希望很多人会编写任务,为此他们只需要实现 Task 接口。任务需要通过它们的名称来触发,这意味着某处需要有一个任务名称->任务类的中央注册表。目前我正在使用 Enum 来执行此操作,但这需要任务实现者为他们实现的每个任务将代码添加到 Enum 中,这很糟糕。

我想出的另一个选择是通过反射搜索所有实现 Task 接口的类并从那里注册它们。这也让我感到恶心,我觉得必须有更好的方法。

如果静态初始化程序不需要在运行之前至少引用一次类,那么它会是完美的。

注释是另一种选择,但它们需要更多的反射工作,或者如果我们要进行编译时代码生成来创建调度程序,则需要更改构建过程。

有更简单的想法吗?


下面是我现在正在做的一个简单版本,以了解我想要的控制流(为简单起见,将其拆分为一个类):

class Disp {
    interface Task {
        public String doTask();
    }

    static class HelloTask implements Task {
        public String doTask() {
            return "Hello";
        }
    }

    static class ByeTask implements Task {
        public String doTask() {
            return "Bye";
        }
    }

    static class TaskDispatcher {
        static enum Tasks {
            Hello {
                Task getTask() {
                    return new HelloTask();
                }
            },
            Bye {
                Task getTask() {
                    return new ByeTask();
                }
            };
            abstract Task getTask();
        };

        public static Task getTask(String taskName) {
            return Tasks.valueOf(taskName).getTask();
        }
    }

    public static void main(String[] args) {
        System.out.println("Hello task says: "+TaskDispatcher.getTask("Hello").doTask());
        System.out.println("Bye task says: "+TaskDispatcher.getTask("Bye").doTask());
    }

}

如果注册(比如说)HelloTask 的代码只能存在于 HelloTask 中,而不必泄漏到 TaskDispatcher 中(在实际项目中,这些是多个不同的文件),那就更好了

4

4 回答 4

1

不要害怕使用框架!使用 Spring 只需几行代码。每个Task都必须进行注释(它不必@Service来自 Spring),并且您在调度程序中所要做的一切(在引导上下文并启用组件扫描之后)是:

class TaskDispatcher {
    @Autowired
    List<Task> tasks;

    //...
}

Spring 会自动找到TaskCLASSPATH 上的所有 s 并为你注入它们。您可以使用 bean/类名称作为标识符或任何您想要的。

于 2012-10-28T14:23:47.240 回答
0

您可以查看服务提供者机制,例如在这个 SO post中。使用它,您可以创建一个接口,通过该接口Tasks 的实现者通知您的中央代码他们实现的所有任务。

我假设每个实现者都会贡献他们自己的 JAR,这种技术假设了这样的场景。

于 2012-10-28T14:24:11.777 回答
0

我过去通过简单地拥有一个包含从名称到类名的映射的配置文件来实现这种事情:

Task1=com.acme.tasks.MyTask1
Task2=com.acme.tasks.more.MyTask2

这用于填充注册表,然后您可以class.forName()对类名进行操作,并创建特定任务类的新实例。

然后添加一个新任务只是意味着将新任务类添加到类路径中,并在配置文件中添加一行 - 不需要重新编译现有代码。

于 2012-10-28T14:21:36.447 回答
0

我最近遇到了同样的问题,并提出了以下解决方案。每个Task(在我的情况下,我称之为SomethingHandler)都用自定义注释进行了注释@RequestHandlerBean

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target({ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER, ElementType.TYPE, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Qualifier
public @interface RequestHandlerBean {

    String taskIdentifier();

}

下面是一个使用这个注解的实现类:

@RequestHandlerBean(taskIdentifier = "task1")
public class HecAckIndexPlanRequestHandler implements  RequestHandler {

    public void handleRequest(Request request) { 
        // ... 
    }

}

在我的调度程序中,我现在使用了 Spring 的getBeansWithAnnotation方法ApplicationContext

Map<String, Object> annotatedHandlers = 
    context.getBeansWithAnnotation(RequestHandlerBean.class);

您现在可以Map<String, RequestHandler>使用以下代码创建地图:

Map<String, Object> annotatedHandlers = context.getBeansWithAnnotation(RequestHandlerBean.class);
Map<String, RequestHandler> handlers  = new HashMap<>();
for (Map.Entry<String, Object> entry : annotatedHandlers.entrySet()) {
    ServiceInstanceRequestHandler handler = (RequestHandler) entry.getValue();
    Annotation a = AnnotationUtils.findAnnotation(handler.getClass(), RequestHandlerBean.class);
    String taskIdentifier = AnnotationUtils.getValue(a, "taskIdentifier")
    handlers.put(taskIdentifier, handler);
 }

您现在可以通过调用任务

handlers.get(taskIdentifier).handleRequest(...)

希望这可以帮助。

于 2018-09-19T18:51:28.220 回答