英文:
Factory Method return Spring service
问题
我想要一个工厂类,它返回一个服务,我可以使用该服务进行一些验证。我实现了这个类。
public class EventUpdateValidatorFactory {
    public EventUpdateValidatorStrategy getValidator(EEventStatus eventStatus) {
        if (SECOND_APPROVAL.equals(eventStatus)) {
            return new EventSecondApprovalValidator();
        } else if (APPROVED.equals(eventStatus)) {
            return new EventApprovedValidator();
        } else if (ACCOUNTING_HQ.equals(eventStatus)) {
            return new EventAccountingHqValidator();
        }
        throw new IllegalArgumentException("Unknown status");
    }
}
接口 EventUpdateValidatorStrategy 如下:
public interface EventUpdateValidatorStrategy {
    default <T extends EventUpdateValidatorStrategy> void validate(User user, EventMasterData masterData, Event event, List<EventExternalSystemExpenseSave> expenses,
        List<EventExternalSystemSpeakerSave> speakers, long eventId) {
        this.validateMasterData(masterData, event);
        this.validateSpeakers(speakers, eventId);
        this.validateExpenses(expenses, eventId);
        this.doUpdate(user, masterData, expenses, speakers, eventId);
    }
    void validateMasterData(EventMasterData masterData, Event event);
    void validateExpenses(List<EventExternalSystemExpenseSave> expenses, long eventId);
    void validateSpeakers(List<EventExternalSystemSpeakerSave> speakers, long eventId);
    void doUpdate(User user, EventMasterData masterData, List<EventExternalSystemExpenseSave> expenses, List<EventExternalSystemSpeakerSave> speakers, long eventId);
}
EventSecondApprovalValidator 的实现如下:
@Service
@Transactional
public class EventSecondApprovalValidator implements EventUpdateValidatorStrategy {
    @Autowired
    private EventService eventService;
    @Autowired
    private ContextDateService contextDateService;
    @Autowired
    private EventExpenseService eventExpenseService;
    @Autowired
    private EventExternalSystemDAO eventExternalSystemDAO;
    @Override
    public void validateMasterData(LocalEventMasterData masterData, Event event) {
        // 一些逻辑
    }
    @Override
    public void validateExpenses(List<EventExternalSystemExpenseSave> expenses, long eventId) {
        // 一些逻辑
    }
    @Override
    public void validateSpeakers(List<EventExternalSystemSpeakerSave> speakers, long eventId) {
        // 一些逻辑
    }
    @Override
    public void doUpdate(User user, EventMasterData masterData, List<EventExternalSystemExpenseSave> expenses, List<EventExternalSystemSpeakerSave> speakers, long eventId) {
        ofNullable(expenses).ifPresent(expensesToSave -> expensesToSave.forEach(expense -> this.eventExternalSystemDAO.updateExpense(user, expense)));
        this.eventExternalSystemDAO.updateEvent(user, masterData, eventId);
    }
}
其他的 EventApprovedValidator 和 EventAccountingHqValidator 实现类似。
从主代码中,我进行以下调用:
final EventUpdateValidatorStrategy validator = EventUpdateValidatorFactory.getValidator(event.getStatus());
validator.validate(user, eventSave.getMasterData(), event, eventSave.getExpenses(), eventSave.getSpeakers(), eventID);
当我进入 EventSecondApprovalValidator 内部时,所有自动装配的服务都为 null,显然,我在第一次使用其中一个服务时会收到 NPE 错误。
我应该如何正确使用工厂根据 EEventStatus 返回所需的服务?
英文:
I want a factory class that return a service that I can use to do some validations. I implemented this class
public class EventUpdateValidatorFactory {
    public EventUpdateValidatorStrategy getValidator(EEventStatus eventStatus) {
	    if (SECOND_APPROVAL.equals(eventStatus)) {
		    return new EventSecondApprovalValidator();
	    } else if (APPROVED.equals(eventStatus)) {
		    return new EventApprovedValidator();
	    } else if (ACCOUNTING_HQ.equals(eventStatus)) {
		    return new EventAccountingHqValidator();
	    }
	    throw new IllegalArgumentException("Unknown status");
    }
}
The interface EventUpdateValidatorStrategy is this
public interface EventUpdateValidatorStrategy {
	default <T extends EventUpdateValidatorStrategy> void validate(User user, EventMasterData masterData, Event event, List<EventExternalSystemExpenseSave> expenses,
			List<EventExternalSystemSpeakerSave> speakers, long eventId) {
		this.validateMasterData(masterData, event);
		this.validateSpeakers(speakers, eventId);
		this.validateExpenses(expenses, eventId);
		this.doUpdate(user, masterData, expenses, speakers, eventId);
	}
	void validateMasterData(EventMasterData masterData, Event event);
	void validateExpenses(List<EventExternalSystemExpenseSave> expenses, long eventId);
	void validateSpeakers(List<EventExternalSystemSpeakerSave> speakers, long eventId);
	void doUpdate(User user, EventMasterData masterData, List<EventExternalSystemExpenseSave> expenses, List<EventExternalSystemSpeakerSave> speakers, long eventId);
}
The EventSecondApprovalValidator is this
@Service
@Transactional
public class EventSecondApprovalValidator implements EventUpdateValidatorStrategy {
	@Autowired
	private EventService eventService;
	@Autowired
	private ContextDateService contextDateService;
	@Autowired
	private EventExpenseService eventExpenseService;
	@Autowired
	private EventExternalSystemDAO eventExternalSystemDAO;
	@Override
	public void validateMasterData(LocalEventMasterData masterData, Event event) {
		// some logic
	}
	@Override
	public void validateExpenses(List<EventExternalSystemExpenseSave> expenses, long eventId) {
		// some logic
	}
	@Override
	public void validateSpeakers(List<EventExternalSystemSpeakerSave> speakers, long eventId) {
		// some logic
	}
	@Override
	public void doUpdate(User user, EventMasterData masterData, List<EventExternalSystemExpenseSave> expenses, List<EventExternalSystemSpeakerSave> speakers, long eventId) {
		ofNullable(expenses).ifPresent(expensesToSave -> expensesToSave.forEach(expense -> this.eventExternalSystemDAO.updateExpense(user, expense)));
		this.eventExternalSystemDAO.updateEvent(user, masterData, eventId);
	}
}
The other EventApprovedValidator and EventAccountingHqValidator implementations are similar.
From main code I do this call
final EventUpdateValidatorStrategy validator = EventUpdateValidatorFactory.getValidator(event.getStatus());
		validator.validate(user, eventSave.getMasterData(), event, eventSave.getExpenses(), eventSave.getSpeakers(), eventID);
and the result is that when I enter inside a EventSecondApprovalValidator all the autowired services are null and, obviously, I receive a NPE the first time that I use one of that service.
How I correctly use the factory to return the service that I need based on EEventStatus?
答案1
得分: 2
在`EventUpdateValidatorFactory.getValidator(EEventStatus)`方法中,你需要从上下文中返回`EventSecondApprovalValidator` bean,而不是使用`new`关键字创建新的实例。
`EventSecondApprovalValidator`类被注解为`@Service`(假设只有一个这样的类型),这种类型的实例将由Spring添加到`ApplicationContext`中,并且会注入所有依赖关系。因此,只需从上下文中获取并使用它。
一种快速的方法如下:
```java
public EventUpdateValidatorStrategy getValidator(ApplicationContext context,
        EEventStatus eventStatus) {
    if (SECOND_APPROVAL.equals(eventStatus)) {
        return context.getBean(EventSecondApprovalValidator.class);
    } else if (APPROVED.equals(eventStatus)) {
        return context.getBean(EventApprovedValidator.class);
    } else if (ACCOUNTING_HQ.equals(eventStatus)) {
        return context.getBean(EventAccountingHqValidator.class);
    }
    throw new IllegalArgumentException("Unknown status");
}
你还可以在EventUpdateValidatorFactory中使用@Autowire来自动注入所有验证器,并返回@Autowired的实例。这将保持getValidator方法的签名不变,但你需要将EventUpdateValidatorFactory定义为类似于@Component的类。
@Component
public class EventUpdateValidatorFactory {
    @Autowired
    EventSecondApprovalValidator a;
    @Autowired
    EventApprovedValidator b;
    @Autowired
    EventAccountingHqValidator c;
    public EventUpdateValidatorStrategy getValidator(EEventStatus eventStatus) {
        if (SECOND_APPROVAL.equals(eventStatus)) {
            return a;
        } else if (APPROVED.equals(eventStatus)) {
            return b;
        } else if (ACCOUNTING_HQ.equals(eventStatus)) {
            return c;
        }
        throw new IllegalArgumentException("Unknown status");
    }
}
<details>
<summary>英文:</summary>
In `EventUpdateValidatorFactory.getValidator(EEventStatus)` method, you need to return the `EventSecondApprovalValidator` bean from context, instead of creating a new instance using `new` keyword.
The class `EventSecondApprovalValidator` is `@Service` annotated (and assuming there is only one of this type), an instance of this type will be added to `ApplicationContext` by Spring with all dependencies injected. So, just fetch it from context and use it.
One quick way to do this is as follows:
    public EventUpdateValidatorStrategy getValidator(ApplicationContext context, 
            EEventStatus eventStatus) {
        if (SECOND_APPROVAL.equals(eventStatus)) {
            return context.getBean(EventSecondApprovalValidator.class);
        } else if (APPROVED.equals(eventStatus)) {
            return context.getBean(EventApprovedValidator.class);
        } else if (ACCOUNTING_HQ.equals(eventStatus)) {
            return context.getBean(EventAccountingHqValidator.class);
        }
        throw new IllegalArgumentException("Unknown status");
    }
You can also `@Autowire` all validators in `EventUpdateValidatorFactory` and return the `@Autowired` instances. This will keep the `getValidator` method's signature same, but you'll have to make `EventUpdateValidatorFactory` a `@Component`-esque class.
    @Component
    public class EventUpdateValidatorFactory {
        @Autowired
        EventSecondApprovalValidator a;
        @Autowired
        EventApprovedValidator b;
        @Autowired
        EventAccountingHqValidator c;
        public EventUpdateValidatorStrategy getValidator(EEventStatus eventStatus) {
            if (SECOND_APPROVAL.equals(eventStatus)) {
                return a;
            } else if (APPROVED.equals(eventStatus)) {
                return b;
            } else if (ACCOUNTING_HQ.equals(eventStatus)) {
                return c;
            }
            throw new IllegalArgumentException("Unknown status");
        }
</details>
# 答案2
**得分**: -2
```java
创建一个对象需要手动操作,这样就不会让 Spring 执行自动装配。考虑同时由 Spring 管理你的服务。
@Component
public class MyServiceAdapter implements MyService {
    @Autowired
    private MyServiceOne myServiceOne;
    @Autowired
    private MyServiceTwo myServiceTwo;
    @Autowired
    private MyServiceThree myServiceThree;
    @Autowired
    private MyServiceDefault myServiceDefault;
    public boolean checkStatus(String service) {
        service = service.toLowerCase();
        if (service.equals("one")) {
            return myServiceOne.checkStatus();
        } else if (service.equals("two")) {
            return myServiceTwo.checkStatus();
        } else if (service.equals("three")) {
            return myServiceThree.checkStatus();
        } else {
            return myServiceDefault.checkStatus();
        }
    }
}
英文:
Creating an object manually you are not letting Spring perform autowiring. Consider managing your services by Spring as well.
 @Component
public class MyServiceAdapter implements MyService {
    @Autowired
    private MyServiceOne myServiceOne;
    @Autowired
    private MyServiceTwo myServiceTwo;
    @Autowired
    private MyServiceThree myServiceThree;
    @Autowired
    private MyServiceDefault myServiceDefault;
    public boolean checkStatus(String service) {
        service = service.toLowerCase();
        if (service.equals("one")) {
            return myServiceOne.checkStatus();
        } else if (service.equals("two")) {
            return myServiceTwo.checkStatus();
        } else if (service.equals("three")) {
            return myServiceThree.checkStatus();
        } else {
            return myServiceDefault.checkStatus();
        }
    }
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。


评论