Sto costruendo un'applicazione aziendale multilivello utilizzando Spring. Ho diversi livelli: Controller, Business e Provider. All'interno dell'applicazione ho creato un mini-framework di gestione degli errori personalizzato basato su una singola RuntimeException che ha un codice di errore per discriminare diversi tipi di errori.
I codici di errore sono enumerazioni che implementano questa interfaccia:
public interface ErrorCode {
public int getNumber();
public String getDeveloperMessage();
public String getHelpURL();
public boolean isSystemError();
}
Quindi, ad esempio, ho:
public enum SystemErrorCode implements ErrorCode{
E_UNKNOWN_ERROR(10000, "Unkwown internal error", true),
E_MISSING_ARGUMENT(10001, "Missing argument", true),
E_INVALID_ARGUMENT(10002, "Invalid argument", true),
E_CONTEXT_NO_TENANT(10003, "No tenant in context", true),
E_CONTEXT_TENANT_CHANGE(10004, "Tenant change attempt", true),
E_CONTEXT_ALREADY_INITIALIZED(10005, "Tenant already initialized", true),
E_NOT_IMPLEMENTED(10005, "Feature not implemented", true),
...
}
La classe di eccezioni stessa assomiglia a questa (getter, costruttori e metodi di utilità sono eliminati per brevità):
public class EngineRuntimeException extends RuntimeException {
private static final long serialVersionUID = 1L;
private final String incidentReportId;
private final ErrorCode code;
private final Map<String, Object> additionalInfo;
...
public static EngineRuntimeException wrap(Throwable exception, ErrorCode errorCode) {
if(exception instanceof EngineRuntimeException) {
EngineRuntimeException se = (EngineRuntimeException)exception;
if(errorCode != null && errorCode != se.getCode()) {
return new EngineRuntimeException(exception.getMessage(), exception, errorCode);
}
return se;
} else {
return new EngineRuntimeException(exception.getMessage(), exception, errorCode);
}
}
public static EngineRuntimeException wrap(Throwable exception) {
return wrap(exception, SystemErrorCode.E_UNKNOWN_ERROR);
}
Usando il metodo di utilità wrap posso incapsulare facilmente le eccezioni nei diversi livelli senza perdere lo stack originale e senza sporcarlo ripubblicando o incapsulando eccezioni più di una volta.
try {
if(a == null) {
throw new EngineRuntimeException(SystemErrorCode.E_UNKNOWN_ERROR, "blah, blah, blah");
}
// Some other code that can throw exceptions (checked or unchecked),
// these exceptions will be wrapped inside EngineRuntimeException if they
// are not already of that type.
} catch (Exception ex) {
throw EngineRuntimeException.wrap(ex);
}
Per ricapitolare, ogni livello ha un try-catch di limite di sicurezza che avvolge le eccezioni in EngineRuntimeException, solo rilanciando ciò che è già un EngineRuntimeException.
La domanda è: dove devo registrare l'eccezione? Voglio registrare l'eccezione solo una volta, quindi stavo pensando di fare le cose di logging all'interno del costruttore della classe EngineRuntimeException stessa. È una cattiva idea? È meglio fare il materiale di logging solo nei blocchi catch dei limiti del layer?
Inoltre non voglio semplicemente loggarmi usando un log4j ma voglio usare un servizio (@Service) iniettato da spring in modo che possa decidere di fare qualcosa di più che semplicemente loggare sul file system. Come posso farlo visto che non posso fare iniezioni di primavera in una classe che ho creato con l'istruzione "nuova".