Автопроводка в конкретных классах абстрактного класса, чем реализует интерфейс - PullRequest
0 голосов
/ 19 декабря 2018

Я использую Spring boot, и у меня есть следующая модель задач

public class Task {
    private String name;
    private TaskType type; // ManualTask, AutomatedTask
    private boolean completed;
    //....other fields

    //getters and setters
}

Контроллер

@Controller
@RequestMapping("/api/task")
public class TaskController {

    @Autowired
    private TaskService taskService;

    @GetMapping("/{taskId}/handle")
    public String handle(Model model, @PathVariable("taskId") Long taskId) throws ClassNotFoundException, InstantiationException, IllegalAccessException {
        try {
            Task task = taskService.handle(taskId);
            model.addAttribute("task", task);
        } catch (Exception e) {
            return "errorpage";
        }
        return "successpage";
    }
}

У меня есть интерфейс

public interface TaskService {

    Task findById(Long taskId);

    Task handleTask(Long taskId) throws ClassNotFoundException, InstantiationException, IllegalAccessException;

}

Аннотациякласс реализует интерфейс:

@Service
public abstract class TaskServiceImpl implements TaskService {

    @Autowired
    private TaskRepository taskRepository;

    private static final String PATH_OF_CLASS = "com.task.service.impl";

    protected abstract Task doTypeSpecificTask(Long taskId);

    @Override
    public Task findById(Long taskId) {
        return taskRepository.findById(taskId).get();
    }

    @Override
    public Task handleTask(Long taskId) throws ClassNotFoundException, InstantiationException, IllegalAccessException {
        Task task = findById(taskId);
        TaskServiceImpl service = getHandlerService(task);
        return service.doTypeSpecificTask(taskId);
    }

    private TaskServiceImpl getHandlerService(Task task) throws ClassNotFoundException, InstantiationException, IllegalAccessException {
        String serviceClassName = PATH_OF_CLASS.concat(".").concat(task.getTaskType().getName()).concat("Service");
        Class<?> serviceClass = Class.forName(serviceClassName);
        if (!TaskServiceImpl.class.isAssignableFrom(serviceClass)) {
            throw new RuntimeException("Service class " + serviceClassName + " did not implements " + TaskServiceImpl.class.getName());
        }
        Object serviceObject = serviceClass.newInstance();
        TaskServiceImpl service = (TaskServiceImpl) serviceObject;
        return service;
    }


}

И конкретные службы, расширяющие абстрактный класс

@Service
@Primary
public class ManualTaskService extends TaskServiceImpl {

    @Autowired
    private TaskRepository taskRepository;

    @Autowired
    private ManualTaskHandlerService manualTaskHandlerService;

    @Override
    protected Task doTypeSpecificTask(Long taskId) {
        Task task = findById(taskId);
        manualTaskHandlerService.handleManualTask(task);
        task.setCompleted(true);
        return taskRepository.save(task);
    }
}

@Service
public class AutomatedTaskService extends TaskServiceImpl {

    @Autowired
    private TaskRepository taskRepository;

    @Autowired
    private AutomatedTaskHandlerService automatedTaskHandlerService;

    @Override
    protected Task doTypeSpecificTask(Long taskId) {
        Task task = findById(taskId);
        automatedTaskHandlerService.handleAutomatedTask(task);
        task.setCompleted(true);
        return taskRepository.save(task);
    }
}

public interface TaskRepository extends JpaRepository<Task, Long> {

}

ManualTaskService или AutomatedTaskService выбирается динамически в зависимости от типа задачи во время выполнения.

Теперь, без @Primary, я получаю следующую ошибку:

Field taskService in com.test.controller.TaskController required a single bean, but 2 were found:
- manualTaskService
- automatedTaskService

Action:

Consider marking one of the beans as @Primary, updating the consumer to accept multiple beans, or using @Qualifier to identify the bean that should be consumed

С @Primary, установленным в ManualTaskService, doTypeSpecificTask в ManualTaskService работает, но в AutomatedTaskService это происходит сбой, потому чтоautomatedTaskHandlerService.handleAutomatedTask(task).Также вызовы taskRepository из AutomatedTaskService завершаются неудачно.

Я пытался использовать @Qualifier, а также определить все @Autowired в абстрактном классе как защищенный, но ничего не работает.Что я делаю не так?

Ответы [ 2 ]

0 голосов
/ 20 декабря 2018

Я решил проблему с помощью фабричного шаблона, как указано в этой ссылке (спасибо @ user7294900 за предоставленную ссылку)

Я полностью удалил абстрактный класс TaskServiceImpl.Вместо этого я создал два новых интерфейса ManualTaskService и AutomatedTaskService, оба расширяющих TaskService интерфейс

public interface ManualTaskService extends TaskService {
}

public interface AutomatedTaskService extends TaskService {     
}

Затем я создал TaskServiceFactory

@Component
public class TaskServiceFactory {

    @Autowired
    private ManualTaskService manualTaskService;

    @Autowired
    private AutomatedTaskService automatedTaskService;

    public TaskService getService(TaskType type) throws Exception {
        switch (type) {
        case MANUAL_TASK:
            return manualTaskService;
        case AUTOMATED_TASK:
            return automatedTaskService;
        default:
            throw new Exception("Unrecognized task type");
        }
    }
}

Затем я создал реализации для обоихManualTaskService и AutomatedTaskService

@Service
public class ManualTaskServiceImpl implements ManualTaskService {

    @Autowired
    private TaskRepository taskRepository;

    @Autowired
    private ManualTaskHandlerService manualTaskHandlerService;

    @Override
    public Task findById(Long taskId) {
        return taskRepository.findById(taskId).get();
    }

    @Override
    public Task handleTask(Long taskId) throws Exception {
        Task task = findById(taskId);
        manualTaskHandlerService.handleManualTask(task);
        task.setCompleted(true);
        return taskRepository.save(task);
    }
}


@Service
public class AutomatedTaskServiceImpl implements AutomatedTaskService {

    @Autowired
    private TaskRepository taskRepository;

    @Autowired
    private AutomatedTaskHandlerService automatedTaskHandlerService;

    @Override
    public Task findById(Long taskId) {
        return taskRepository.findById(taskId).get();
    }

    @Override
    public Task handleTask(Long taskId) throws Exception {
        Task task = findById(taskId);
        automatedTaskHandlerService.handleAutomatedTask(task);
        task.setCompleted(true);
        return taskRepository.save(task);
    }

}

Наконец я обновил контроллер, чтобы получить тип задачи от пользователя, а затем с помощью TaskServiceFactory получил правильный экземпляр службы на основе типа

@Controller
@RequestMapping("/api/task")
public class TaskController {

    @Autowired
    private TaskServiceFactory taskServiceFactory;

    @PostMapping("/{taskId}/handle")
    public String handle(Model model, @PathVariable("taskId") Long taskId, HttpServletRequest request) throws Exception {
        try {
            TaskType type = TaskType.valueOf(request.getParameter("type"));
            Task task = taskServiceFactory.getService(type).handleTask(taskId, request);
            model.addAttribute("task", task);
        } catch (Exception e) {
            return "errorpage";
        }
        return "successpage";
    }
}
0 голосов
/ 19 декабря 2018

Вы должны иметь разные имена для каждого квалификатора:

@Autowired
@Qualifier("manualTaskService")
private TaskServiceImpl manualTaskService;

@Autowired
@Qualifier("automatedTaskService")
private TaskServiceImpl automatedTaskService;

Что определяется в услугах:

@Service("manualTaskService")
public class ManualTaskService extends TaskServiceImpl {

@Service("automatedTaskService")
public class AutomatedTaskService extends TaskServiceImpl {
...