Wann müssen Sie eine Ausnahme auslösen
Zuerst müssen wir eine Frage verstehen: Wann müssen wir eine Ausnahme auslösen? Ausnahmen sollen für Entwickler bequem zu verwenden sein, dürfen aber nicht wahllos verwendet werden. Der Autor hat auch viele Freunde gefragt, wann Ausnahmen ausgelöst werden sollen, und es gibt tatsächlich nicht viele, die genaue Antworten geben können. Tatsächlich ist dieses Problem sehr einfach. Wenn Sie der Meinung sind, dass bestimmte „Probleme“ nicht gelöst werden können, können Sie eine Ausnahme auslösen. Wenn Sie beispielsweise einen Dienst schreiben und beim Schreiben eines bestimmten Codeabschnitts feststellen, dass möglicherweise ein Problem auftritt, lösen Sie bitte eine Ausnahme aus. Glauben Sie mir, dies ist der beste Zeitpunkt für Sie, eine Ausnahme auszulösen.
Welche Art von Ausnahme sollte ausgelöst werden?
Nachdem wir verstanden haben, wann wir eine Ausnahme auslösen müssen, denken wir über eine andere Frage nach: Welche Art von Ausnahme sollten wir wählen, wenn wir wirklich eine Ausnahme auslösen? Handelt es sich um eine geprüfte Ausnahme oder eine ungeprüfte Ausnahme (RuntimeException)? Lassen Sie mich dieses Problem anhand eines Beispiels veranschaulichen, beginnend mit überprüften Ausnahmen. Beispielsweise gibt es eine solche Geschäftslogik, die bestimmte Daten aus einer bestimmten Datei lesen muss, da dieser Lesevorgang möglicherweise aufgrund anderer Probleme wie dem Löschen einer Datei nicht möglich ist. Wenn aufgrund der Erfassung ein Lesefehler auftritt, müssen die Daten aus der Redis- oder MySQL-Datenbank abgerufen werden. Siehe den folgenden Code: getKey(Integer) ist das Eingabeprogramm.
public String getKey(Integer key){
String-Wert;
versuche es mit {
InputStream inputStream = getFiles("/file/nofile");
//Als nächstes den Wert des Schlüssels aus dem Stream lesen
value = ...;
} Catch (Ausnahme e) {
//Wenn eine Ausnahme ausgelöst wird, wird sie von MySQL oder Redis abgerufen
value = ...;
}
}
public InputStream getFiles(String path) löst eine Ausnahme aus {
Datei file = neue Datei (Pfad);
InputStream inputStream = null;
versuche es mit {
inputStream = new BufferedInputStream(new FileInputStream(file));
} Catch (FileNotFoundException e) {
throw new Exception("I/O read error",e.getCause());
}
return inputStream;
}
Ok, nachdem Sie den obigen Code gelesen haben, haben Sie möglicherweise einige Gedanken im Kopf. Es stellt sich heraus, dass geprüfte Ausnahmen die Verpflichtungslogik steuern können, aber denken Sie daran, sie nicht zu verwenden Es sollte vernünftig sein, Ausnahmen auszulösen, da die Funktion von Ausnahmen nur eine Ausrede ist, wenn Sie den Programmablauf nicht steuern können Auf diese Weise werden Ausnahmen verwendet. Die erweiterte Rolle führt zu einer erhöhten Codekomplexität, einer erhöhten Kopplung und einer verringerten Codelesbarkeit. Also dürfen wir solche Ausnahmen nicht nutzen? Tatsächlich nein, wenn wirklich ein Bedarf besteht, können wir es auf diese Weise nutzen, aber denken Sie daran, es nicht wirklich als Werkzeug oder Mittel zur Steuerung des Prozesses zu betrachten. Wann sollte eine solche Ausnahme ausgelöst werden? Es sollte berücksichtigt werden, dass der Anrufer die Möglichkeit haben muss, den Fehler zu behandeln, wenn er beim Aufruf einen Fehler macht. Nur wenn diese Anforderungen erfüllt sind, werden wir die Verwendung geprüfter Ausnahmen in Betracht ziehen.
Werfen wir als Nächstes einen Blick auf ungeprüfte Ausnahmen (RuntimeException). Wir sehen tatsächlich viele Ausnahmen wie „java.lang.NullPointerException/java.lang.IllegalArgumentException“ usw. Wann lösen wir diese Art von Ausnahme aus? Wenn wir eine bestimmte Methode schreiben, kann es sein, dass dieses Problem während der Laufzeit auftritt. Wenn kein solches Problem vorliegt, muss der Aufrufer diese Ausnahme nicht abfangen Zu diesem Zeitpunkt wird eine RuntimeException ausgelöst. Wenn beispielsweise ein Pfad übergeben wird, muss ein dem Pfad entsprechendes Dateiobjekt zurückgegeben werden:
public void test() {
myTest.getFiles("");
}
öffentliche Datei getFiles(String path) {
if(null == path || "".equals(path)){
throw new NullPointerException("Der Pfad darf nicht leer sein!");
}
Datei file = neue Datei (Pfad);
Rückgabedatei;
}
Das obige Beispiel zeigt, dass, wenn der Pfad leer ist, wenn der Aufrufer getFiles(String) aufruft, eine Nullzeigerausnahme (eine Unterklasse von RuntimeException) ausgelöst wird und der Aufrufer keinen expliziten Versuch ausführen muss ... Catch...-Vorgang für erzwungene Verarbeitung Dies erfordert, dass der Aufrufer beim Aufruf einer solchen Methode zuerst überprüft, um RuntimeException zu vermeiden:
Welche Ausnahme sollte verwendet werden
Anhand der obigen Beschreibung und Beispiele können wir eine Schlussfolgerung ziehen. Der Unterschied zwischen RuntimeException und geprüfter Ausnahme besteht darin, ob der Aufrufer gezwungen ist, diese Ausnahme zu behandeln, andernfalls wird die geprüfte Ausnahme verwendet Wählen Sie eine ungeprüfte Ausnahme (RuntimeException). Wenn keine besonderen Anforderungen bestehen, empfehlen wir im Allgemeinen die Verwendung von RuntimeException.
Szeneneinführung und Technologieauswahl
Architekturbeschreibung
Wie wir wissen, werden traditionelle Projekte auf der Grundlage des MVC-Frameworks entwickelt. In diesem Artikel wird hauptsächlich das Design einer Restful-Style-Schnittstelle verwendet, um die Eleganz der Ausnahmebehandlung zu erleben.
Wir konzentrieren uns auf die Restful-API-Schicht (ähnlich der Controller-Schicht im Web) und die Service-Schicht, untersuchen, wie Ausnahmen im Dienst ausgelöst werden und wie die API-Schicht die Ausnahmen erfasst und umwandelt.
Die verwendeten Technologien sind: Spring-Boot, JPA (Ruhezustand), MySQL. Wenn Sie mit diesen Technologien nicht vertraut sind, müssen die Leser die relevanten Materialien selbst lesen.
Beschreibung des Geschäftsszenarios
Wählen Sie ein relativ einfaches Geschäftsszenario am Beispiel der Lieferadressenverwaltung im E-Commerce. Wenn Benutzer Waren auf dem mobilen Endgerät kaufen, müssen sie die Lieferadresse verwalten. Im Projekt werden einige API-Schnittstellen für mobile Endgeräte bereitgestellt Zugriff, wie zum Beispiel: Lieferadresse hinzufügen, Lieferadresse löschen, Lieferadresse ändern, Standard-Lieferadresseneinstellung, Abfrage der Lieferadressenliste, Abfrage einzelner Lieferadressen und andere Schnittstellen.
Erstellen Sie Einschränkungen
Ok, das ist ein sehr einfaches Geschäftsszenario, das eingerichtet wurde. Unabhängig von der Art der API-Operation enthält es natürlich einige Regeln:
Lieferadresse hinzufügen:
Zutaten:
Benutzer-ID
Empfangen von Adressentitätsinformationen
Einschränkungen:
Die Benutzer-ID darf nicht leer sein und dieser Benutzer existiert
Die Pflichtfelder für die Lieferadresse dürfen nicht leer sein
Wenn der Benutzer noch keine Lieferadresse hat, wird diese Lieferadresse beim Erstellen als Standardversandadresse festgelegt –
Lieferadresse löschen:
Zutaten:
Benutzer-ID
Lieferadressen-ID
Einschränkungen:
Die Benutzer-ID darf nicht leer sein und dieser Benutzer existiert
Die Lieferadresse darf nicht leer sein und die Lieferadresse existiert
Bestimmen Sie, ob diese Lieferadresse die Lieferadresse des Benutzers ist
Stellen Sie fest, ob diese Lieferadresse die Standardlieferadresse ist. Wenn es sich um die Standardlieferadresse handelt, kann sie nicht gelöscht werden
Lieferadresse ändern:
Zutaten:
Benutzer-ID
Lieferadressen-ID
Einschränkungen:
Die Benutzer-ID darf nicht leer sein und dieser Benutzer existiert
Die Lieferadresse darf nicht leer sein und die Lieferadresse existiert
Bestimmen Sie, ob diese Lieferadresse die Lieferadresse des Benutzers ist
Standardadresseneinstellung:
Zutaten:
Benutzer-ID
Lieferadressen-ID
Einschränkungen:
Die Benutzer-ID darf nicht leer sein und dieser Benutzer existiert
Die Lieferadresse darf nicht leer sein und die Lieferadresse existiert
Bestimmen Sie, ob diese Lieferadresse die Lieferadresse des Benutzers ist
Abfrage der Lieferadressenliste:
Zutaten:
Benutzer-ID
Einschränkungen:
Die Benutzer-ID darf nicht leer sein und dieser Benutzer existiert
Einzelne Lieferadressenanfrage:
Zutaten:
Benutzer-ID
Lieferadressen-ID
Einschränkungen:
Die Benutzer-ID darf nicht leer sein und dieser Benutzer existiert
Die Lieferadresse darf nicht leer sein und die Lieferadresse existiert
Bestimmen Sie, ob diese Lieferadresse die Lieferadresse des Benutzers ist
Einschränkende Beurteilung und Technologieauswahl
Für die oben aufgeführten Einschränkungen und Funktionslisten habe ich mehrere typische Ausnahmebehandlungsszenarien zur Analyse ausgewählt: Hinzufügen einer Lieferadresse, Löschen einer Lieferadresse und Abrufen einer Liste von Lieferadressen.
Welche notwendigen Wissensreserven sollten wir also haben? Werfen wir einen Blick auf die Versandadressenfunktion:
Beim Hinzufügen einer Lieferadresse müssen die Benutzer-ID und die Entitätsinformationen zur Lieferadresse überprüft werden. Wie wählen wir also ein Tool zur nicht leeren Beurteilung aus? Das traditionelle Urteil lautet wie folgt:
/**
* Adresse hinzufügen
* @param uid
* @param-Adresse
* @return
*/
öffentliche Adresse addAddress(Integer uid,Address address){
if(null != uid){
//Prozess..
}
null zurückgeben;
}
Wenn Sie im obigen Beispiel nur beurteilen, dass die UID leer ist, ist es in Ordnung. Wenn Sie dann beurteilen, ob einige notwendige Attribute in der Adressentität leer sind, wird dies katastrophal sein, wenn viele Felder vorhanden sind.
Wie sollen wir also diese Urteile über die Eingabe von Parametern fällen? Lassen Sie mich Ihnen zwei Wissenspunkte vorstellen:
Die Preconditions-Klasse in Guava implementiert die Beurteilung vieler Parametereingabemethoden
JSR 303-Validierungsspezifikation (die aktuelle Implementierung ist relativ vollständig, Hibernate-Validator wird von Hibernate implementiert)
Wenn diese beiden Empfehlungstechnologien verwendet werden, wird die Beurteilung der Eingabe von Parametern viel einfacher. Es wird jedem empfohlen, diese ausgereiften Technologien und Jar-Toolkits zu verwenden, was eine Menge unnötiger Arbeitsbelastung reduzieren kann. Wir müssen uns nur auf die Geschäftslogik konzentrieren. Anstatt aufgrund dieser Eingabeurteile mehr Zeit zu verzögern.
So gestalten Sie Java-Ausnahmen elegant
Domain-Einführung
Je nach Projektszenario werden zwei Domänenmodelle benötigt, eines ist die Benutzerentität und das andere ist die Adressentität.
Die Adressdomäne lautet wie folgt:
@Entity
@Data
öffentliche Klassenadresse {
@Id
@GeneratedValue
private Ganzzahl-ID;
private String-Provinz;//Provinz
private String-Stadt;//Stadt
privater String County;//Bezirk
private Boolean isDefault;//Ist es die Standardadresse
@ManyToOne(cascade={CascadeType.ALL})
@JoinColumn(name="uid")
privater Benutzerbenutzer;
}
Die Benutzerdomäne lautet wie folgt:
@Entity
@Data
öffentlicher Klassenbenutzer {
@Id
@GeneratedValue
private Ganzzahl-ID;
privater String-Name;//name
@OneToMany(cascade= CascadeType.ALL,mappedBy="user",fetch = FetchType.LAZY)
private Set
-Adressen;}
Ok, das Obige ist eine Modellbeziehung, und die Beziehung zwischen Benutzer und Lieferadresse ist eine 1-n-Beziehung. Das obige @Data verwendet ein Tool namens Lombok, das automatisch Setter- und Getter-Methoden generiert, was für interessierte Leser sehr praktisch ist und sich selbst darüber informieren kann.
Dao-Einführung
Für die Datenverbindungsschicht verwenden wir das Spring-Data-JPA-Framework, das erfordert, dass wir nur die vom Framework bereitgestellte Schnittstelle erben und die Methoden gemäß der Konvention benennen, um die gewünschten Datenbankoperationen abzuschließen.
Der Benutzerdatenbankbetrieb ist wie folgt:
@Repository
Die öffentliche Schnittstelle IUserDao erweitert JpaRepository
Der Lieferadressenvorgang ist wie folgt:
@Repository
öffentliche Schnittstelle IAddressDao erweitert JpaRepository
}Wie die Leser sehen können, muss unser DAO nur JpaRepository erben, und es hat uns bereits dabei geholfen, grundlegende CURD- und andere Vorgänge abzuschließen. Wenn Sie mehr über das Spring-Data-Projekt erfahren möchten, lesen Sie bitte die offizielle Dokumentation von Spring Forschung zu Anomalien.
Service abnormales Design
Ok, endlich haben wir unseren Fokus erreicht. Wir müssen einige Servicevorgänge abschließen: Lieferadresse hinzufügen, Lieferadresse löschen und die Lieferadressenliste abrufen.
Schauen Sie sich zunächst meine Definition der Serviceschnittstelle an:
öffentliche Schnittstelle IAddressService {
/**
* Lieferadresse erstellen
* @param uid
* @param-Adresse
* @return
*/
Adresse createAddress(Integer uid,Address address);
/**
* Lieferadresse löschen
* @param uid
* @param Hilfe
*/
void deleteAddress(Integer uid,Integer hilf);
/**
* Alle Lieferadressen der Benutzer abfragen
* @param uid
* @return
*/
List
listAddresses(Integer uid);}
Konzentrieren wir uns auf die Umsetzung:
Lieferadresse hinzufügen
Werfen wir zunächst einen Blick auf die zuvor zusammengestellten Einschränkungen:
Zutaten:
Benutzer-ID
Empfangen von Adressentitätsinformationen
Einschränkungen:
Die Benutzer-ID darf nicht leer sein und dieser Benutzer existiert
Die Pflichtfelder für die Lieferadresse dürfen nicht leer sein
Wenn der Benutzer noch keine Lieferadresse hat, wird die Lieferadresse beim Erstellen als Standardversandadresse festgelegt
Schauen Sie sich zunächst die folgende Code-Implementierung an:
@Override
öffentliche Adresse createAddress(Ganzzahlige UID, Adressadresse) {
//============ Das Folgende sind Einschränkungen ==============
//1. Die Benutzer-ID darf nicht leer sein und dieser Benutzer existiert
Preconditions.checkNotNull(uid);
Benutzer user = userDao.findOne(uid);
if(null == Benutzer){
throw new RuntimeException("Aktueller Benutzer nicht gefunden!");
}
//2. Die notwendigen Felder der Lieferadresse dürfen nicht leer sein
BeanValidators.validateWithException(Validator, Adresse);
//3. Wenn der Benutzer noch keine Lieferadresse hat, wird die Lieferadresse beim Erstellen als Standardversandadresse festgelegt
if(ObjectUtils.isEmpty(user.getAddresses())){
address.setIsDefault(true);
}
//============ Das Folgende ist die normalerweise ausgeführte Geschäftslogik ==============
address.setUser(user);
Adressergebnis = addressDao.save(address);
Rückgabeergebnis;
}
Unter diesen wurden die drei oben beschriebenen Einschränkungen erfüllt. Wenn die drei Einschränkungen erfüllt sind, kann die normale Geschäftslogik ausgeführt werden. Andernfalls wird eine Ausnahme ausgelöst (es wird allgemein empfohlen, eine Laufzeitausnahme auszulösen – hier RuntimeException).
Wir stellen die folgenden Technologien vor, die ich oben verwendet habe:
Preconfitions.checkNotNull(T t) wird mithilfe von com.google.common.base.Preconditions in Guava beurteilt. Da im Dienst viele Überprüfungen verwendet werden, wird empfohlen, Preconfigions auf statischen Import zu ändern:
1import static com.google.common.base.Preconditions.checkNotNull;
Natürlich empfehlen auch die Anweisungen in Guavas Github, dass wir es auf diese Weise verwenden.
BeanValidators.validateWithException(Validator, Adresse);
Dies erfolgt mithilfe der von hibernate implementierten JSR 303-Spezifikation. Sie müssen einen Validator und eine Entität übergeben, die überprüft werden muss. So erhalten Sie den Validator wie folgt:
@Konfiguration
öffentliche Klasse BeanConfigs {
@Bean
public javax.validation.Validator getValidator(){
neues LocalValidatorFactoryBean();
zurückgeben }
}
Es erhält ein Validator-Objekt, und dann können wir es in den Dienst einfügen und verwenden:
@Autowired
privater Validator-Validator ;
Wie wird die BeanValidators-Klasse implementiert? Tatsächlich ist die Implementierung sehr einfach. Beurteilen Sie einfach die Anmerkung von jsr 303 und es wird in Ordnung sein.
Wo sind also die JSR 303-Anmerkungen geschrieben? Natürlich ist es in der Adressentitätsklasse geschrieben:
@Entity
@Setter
@Getter
öffentliche Klassenadresse {
@Id
@GeneratedValue
private Ganzzahl-ID;
@NotNull
private String-Provinz;//Provinz
@NotNull
private String-Stadt;//Stadt
@NotNull
privater String County;//Bezirk
private Boolean isDefault = false;//Ist es die Standardadresse
@ManyToOne(cascade={CascadeType.ALL})
@JoinColumn(name="uid")
privater Benutzerbenutzer;
}
Schreiben Sie die Einschränkungen auf, die Sie benötigen, um Urteile zu fällen. Wenn diese angemessen sind, können Sie Geschäftsvorgänge durchführen und die Datenbank betreiben.
Die Überprüfung dieses Teils ist notwendig, da durch eine solche Überprüfung das Einfügen schmutziger Daten vermieden werden kann. Wenn Leser über formale Online-Erfahrung verfügen, können sie so etwas verstehen. Jeder Codefehler kann toleriert und geändert werden, aber wenn ein Problem mit schmutzigen Daten auftritt, kann dies eine verheerende Katastrophe sein. Programmprobleme können behoben werden, das Auftreten fehlerhafter Daten kann jedoch möglicherweise nicht wiederhergestellt werden. Aus diesem Grund müssen Sie die Einschränkungen im Dienst ermitteln, bevor Sie Geschäftslogikoperationen durchführen.
Bei der Beurteilung handelt es sich hier um eine Beurteilung der Geschäftslogik, die aus geschäftlicher Sicht erfolgt. Darüber hinaus kann es in vielen Szenarien zu unterschiedlichen Geschäftsbedingungen kommen, und Sie müssen dies nur entsprechend den Anforderungen tun.
Eine Zusammenfassung der Einschränkungen lautet wie folgt:
Grundlegende Urteilsbeschränkungen (grundlegende Urteile wie Nullwert)
Einschränkungen für Entitätsattribute (erfüllen grundlegende Urteile wie jsr 303)
Einschränkungen der Geschäftsbedingungen (verschiedene geschäftliche Einschränkungen, die durch Anforderungen vorgeschlagen werden)
Wenn diese drei Punkte erfüllt sind, können Sie mit dem nächsten Schritt fortfahren
Ok, dies führt im Grunde dazu ein, wie man eine grundlegende Beurteilung trifft. Kehren wir zum Problem des Ausnahmedesigns zurück. Der obige Code hat klar beschrieben, wie man eine Ausnahme an der entsprechenden Stelle vernünftig auslöst.
Zählt das bloße Auslösen einer RuntimeException als ordnungsgemäßes Auslösen einer Ausnahme? Natürlich nicht. Was die in Diensten ausgelösten Ausnahmen betrifft, gibt es meiner Meinung nach ungefähr zwei Methoden zum Auslösen von Ausnahmen:
Löst eine Ausnahme mit dem Statuscode RumtimeException
aus Löst eine RuntimeException des angegebenen Typs aus
Im Vergleich zu diesen beiden Ausnahmen bedeutet die erste Ausnahme, dass alle meine Ausnahmen eine RuntimeException auslösen, sie muss jedoch einen Statuscode haben. Der Aufrufer kann anhand des Statuscodes abfragen, welche Art von Dienst sie ausgelöst hat.
Der zweite Ausnahmetyp bezieht sich auf das Anpassen eines bestimmten Ausnahmefehlers für jede im Dienst ausgelöste Ausnahme und das anschließende Auslösen der Ausnahme.
Wenn das System keine weiteren besonderen Anforderungen stellt, wird im Allgemeinen empfohlen, während der Entwicklung und des Entwurfs die zweite Methode zu verwenden. Aber zum Beispiel können grundlegende Urteilsausnahmen vollständig mit der von guava bereitgestellten Klassenbibliothek betrieben werden. JSR 303-Ausnahmen können auch mit ihren eigenen gekapselten Ausnahmebeurteilungsklassen betrieben werden, da es sich bei diesen beiden Ausnahmen um Grundbeurteilungen handelt und für sie keine speziellen Ausnahmen angegeben werden müssen. Für die Ausnahme, die durch die dritte Verpflichtungsbedingungsbeschränkungsbeurteilung ausgelöst wird, müssen Sie jedoch eine Ausnahme des angegebenen Typs auslösen.
Für
1throw new RuntimeException("Aktueller Benutzer nicht gefunden!");
Definieren Sie eine bestimmte Ausnahmeklasse, um diese Verpflichtungsausnahme zu beurteilen:
Die öffentliche Klasse NotFindUserException erweitert RuntimeException {
public NotFindUserException() {
super("Dieser Benutzer kann nicht gefunden werden");
}
public NotFindUserException(String message) {
super(Nachricht);
}
}
Dann ändern Sie dies in:
1throw new NotFindUserException("Aktueller Benutzer nicht gefunden!");
oder
1wirf eine neue NotFindUserException();
Ok, durch die oben genannten Änderungen an der Serviceschicht sind die Codeänderungen wie folgt:
@Override
öffentliche Adresse createAddress(Ganzzahlige UID, Adressadresse) {
//============ Das Folgende sind Einschränkungen ==============
//1. Die Benutzer-ID darf nicht leer sein und dieser Benutzer existiert
checkNotNull(uid);
Benutzer user = userDao.findOne(uid);
if(null == Benutzer){
throw new NotFindUserException("Aktueller Benutzer nicht gefunden!");
}
//2. Die notwendigen Felder der Lieferadresse dürfen nicht leer sein
BeanValidators.validateWithException(Validator, Adresse);
//3. Wenn der Benutzer noch keine Lieferadresse hat, wird die Lieferadresse beim Erstellen als Standardversandadresse festgelegt
if(ObjectUtils.isEmpty(user.getAddresses())){
address.setIsDefault(true);
}
//============ Das Folgende ist die normalerweise ausgeführte Geschäftslogik ==============
address.setUser(user);
Adressergebnis = addressDao.save(address);
Rückgabeergebnis;
}
Ein solcher Dienst scheint stabiler und verständlicher zu sein.
Lieferadresse löschen:
Zutaten:
Benutzer-ID
Lieferadressen-ID
Einschränkungen:
Die Benutzer-ID darf nicht leer sein und dieser Benutzer existiert
Die Lieferadresse darf nicht leer sein und diese Lieferadresse existiert
Bestimmen Sie, ob diese Lieferadresse die Lieferadresse des Benutzers ist
Stellen Sie fest, ob diese Lieferadresse die Standardlieferadresse ist. Wenn es sich um die Standardlieferadresse handelt, kann sie nicht gelöscht werden
Es ähnelt dem Hinzufügen der Lieferadresse oben, daher werde ich nicht auf Details eingehen. Das Servicedesign von delete ist wie folgt: @Override
public void deleteAddress(Ganzzahlige UID, Ganzzahlige Hilfe) {
//============ Das Folgende sind Einschränkungen ==============
//1. Die Benutzer-ID darf nicht leer sein und dieser Benutzer existiert
checkNotNull(uid);
Benutzer user = userDao.findOne(uid);
if(null == Benutzer){
wirf eine neue NotFindUserException();
}
//2. Die Lieferadresse darf nicht leer sein und die Lieferadresse existiert
checkNotNull(aid);
Adressadresse = addressDao.findOne(aid);
if(null == Adresse){
wirf eine neue NotFindAddressException();
}
//3. Bestimmen Sie, ob diese Lieferadresse die Lieferadresse des Benutzers ist
if(!address.getUser().equals(user)){
wirf eine neue NotMatchUserAddressException();
}
//4. Bestimmen Sie, ob es sich bei dieser Lieferadresse um die Standardlieferadresse handelt. Sie kann nicht gelöscht werden
if(address.getIsDefault()){
wirf eine neue DefaultAddressNotDeleteException();
}
//============ Das Folgende ist die normalerweise ausgeführte Geschäftslogik ==============
AdresseDao.delete(Adresse);
}
Es werden vier verwandte Ausnahmeklassen entwickelt: NotFindUserException, NotFindAddressException, NotMatchUserAddressException, DefaultAddressNotDeleteException. Je nach Geschäftsanforderungen werden unterschiedliche Ausnahmen ausgelöst.
Holen Sie sich die Lieferadressenliste:
Zutaten:
Benutzer-ID
Einschränkungen:
Die Benutzer-ID darf nicht leer sein und dieser Benutzer existiert
Der Code lautet wie folgt: @Override
public List
listAddresses(Integer uid) {//============ Das Folgende sind Einschränkungen ==============
//1. Die Benutzer-ID darf nicht leer sein und dieser Benutzer existiert
checkNotNull(uid);
Benutzer user = userDao.findOne(uid);
if(null == Benutzer){
wirf eine neue NotFindUserException();
}
//============ Das Folgende ist die normalerweise ausgeführte Geschäftslogik ==============
Benutzerergebnis = userDao.findOne(uid);
return result.getAddresses();
}
API anormales Design
Es gibt grob zwei Wurfmethoden:
Löst eine Ausnahme mit dem Statuscode RumtimeException
aus Löst eine RuntimeException des angegebenen Typs aus
Dies wurde beim Entwerfen von Service-Layer-Ausnahmen erwähnt. Beim Auslösen von Ausnahmen durch den Service-Layer haben wir zwei Möglichkeiten gewählt Auslösen: Sie müssen den Typ der API-Ausnahme und den entsprechenden Statuscode angeben, bevor Sie die Ausnahme auslösen. Der Kern dieses Ausnahmedesigns besteht darin, Benutzern, die die API aufrufen, ein klareres Verständnis der Ausnahme zu ermöglichen Außerdem muss eine entsprechende Tabelle erstellt werden, um dem Benutzer detaillierte Informationen zur Ausnahme anzuzeigen, die dem Statuscode und den möglichen Problemen entsprechen, die mit der Ausnahme auftreten können, um die Abfrage des Benutzers zu erleichtern. (z. B. die von Github bereitgestellte API-Dokumentation, die von WeChat bereitgestellte API-Dokumentation usw.) gibt es einen weiteren Vorteil: Wenn der Benutzer die Eingabeaufforderungsnachricht anpassen muss, kann die Eingabeaufforderung basierend auf dem zurückgegebenen Statuscode geändert werden.
API-Validierungseinschränkungen
Für den Entwurf der API muss zunächst ein DTO-Objekt vorhanden sein, das für die Kommunikation und Übertragung von Daten mit dem Aufrufer verantwortlich ist. Anschließend wird die DTO-Domäne zum Betrieb an den Dienst übergeben Zweitens muss darauf geachtet werden, dass die API-Schicht zusätzlich zu dem erwähnten Dienst, der eine grundlegende Beurteilung (Null-Beurteilung) und eine JSR-303-Überprüfung erfordert, auch eine entsprechende Überprüfung durchführen muss Informieren Sie den Aufruf darüber, dass er fehlgeschlagen ist und nicht. Wenn Sie mit illegalen Daten auf den Dienst zugreifen, sind die Leser möglicherweise etwas verwirrt. Wenn der Dienst nicht überprüft wurde, warum muss die API-Schicht trotzdem überprüft werden? Hier wird ein Konzept entworfen: Murphys Gesetz in der Programmierung. Wenn die Datenüberprüfung auf der API-Ebene vernachlässigt wird, können illegale Daten auf die Serviceebene übertragen und dann möglicherweise schmutzige Daten in der Datenbank gespeichert werden.
Der Kern einer strengen Programmierung lautet also: Glauben Sie niemals, dass die empfangenen Daten legal sind.
API anormales Design
Beim Entwerfen von API-Ebenenausnahmen müssen Sie, wie oben erwähnt, Fehlercodes und Fehlermeldungen bereitstellen. Anschließend können Sie eine allgemeine API-Superklassenausnahme bereitstellen:
Die öffentliche Klasse ApiException erweitert RuntimeException {
protected Langer Fehlercode ;
geschützte Objektdaten ;
public ApiException(Long errorCode,String message,Object data,Throwable e){
super(Nachricht,e);
this.errorCode = errorCode ;
this.data = Daten ;
}
public ApiException(Long errorCode,String message,Object data){
this(errorCode,message,data,null);
}
public ApiException(Long errorCode,String message){
this(errorCode,message,null,null);
}
public ApiException(String message,Throwable e){
this(null,message,null,e);
}
public ApiException(){
}
öffentliche ApiException(Throwable e){
super(e);
}
public Long getErrorCode() {
return errorCode;
}
public void setErrorCode(Long errorCode) {
this.errorCode = errorCode;
}
öffentliches Objekt getData() {
Rückgabedaten;
}
public void setData(Object data) {
this.data = Daten;
}
}
Es werden folgende API-Funktionen verwendet: ApiDefaultAddressNotDeleteException, ApiNotFindAddressException, ApiNotFindUserException, ApiNotMatchUserAddressException.
以默认地址不能删除为例:
Die öffentliche Klasse ApiDefaultAddressNotDeleteException erweitert ApiException {
public ApiDefaultAddressNotDeleteException(String message) {
super(AddressErrorCode.DefaultAddressNotDeleteErrorCode, message, null);
}
}
AddressErrorCode.DefaultAddressNotDeleteErrorCode ist eine der folgenden Adressen:
öffentliche abstrakte Klasse AddressErrorCode {
public static final Long DefaultAddressNotDeleteErrorCode = 10001L;//默认地址不能删除
public static final Long NotFindAddressErrorCode = 10002L;//找不到此收货地址
public static final Long NotFindUserErrorCode = 10003L;//找不到此用户
public static final Long NotMatchUserAddressErrorCode = 10004L;//用户与收货地址不匹配
}
ok, 那么api层的异常就已经设计完了, 在此多说一句, AddressErrorCode错误码类存放了可能出现的错误码, 更合理的做法是把他放到配置文件中进行管理.
api处理异常
api层会调用service层, 然后来处理service中出现的所有异常, 首先, 需要保证一点, 一定要让api层非常轻, 基本上做成一个转发的功能就好(接口参数,传递给service参数,返回给调用者数据,这三个基本功能),然后就要在传递给service参数的那个方法调用上进行异常处理。
此处仅以添加地址为例:
@Autowired
privater IAddressService addressService;
/**
* Lieferadresse hinzufügen
* @param-AdresseDTO
* @return
*/
@RequestMapping(method = RequestMethod.POST)
public AddressDTO add(@Valid @RequestBody AddressDTO addressDTO){
Adressadresse = neue Adresse();
BeanUtils.copyProperties(addressDTO,address);
Adressergebnis;
versuche es mit {
Ergebnis = addressService.createAddress(addressDTO.getUid(), Adresse);
}catch (NotFindUserException e){
throw new ApiNotFindUserException("找不到该用户");
}catch (Ausnahme e){//未知错误
wirf eine neue ApiException(e);
}
AddressDTO resultDTO = new AddressDTO();
BeanUtils.copyProperties(result,resultDTO);
resultDTO.setUid(result.getUser().getId());
RückgabeergebnisDTO;
}
然后将任何service异常都转化成api异常, 然后抛出api异常这是常用的一种异常转化方式.相似删除收货地址和获取收货地址也类似这样处理,在此,不在赘述.
API异常转化
已经讲解了如何抛出异常和何如将service异常转化为api异常,那么转化成api异常直接抛出是否就完成了异常处理呢? 答案是否定的, 当抛出api异常后, 我们需要把api异常返回的数据(json oder xml)让用户看懂,那么需要把api异常转化成dto对象(ErrorDTO),看如下代码:
@ControllerAdvice(annotations = RestController.class)
Klasse ApiExceptionHandlerAdvice {
/**
* Von Handlern ausgelöste Ausnahmen behandeln.
*/
@ExceptionHandler(value = Exception.class)
@ResponseBody
public ResponseEntity
ErrorDTO errorDTO = new ErrorDTO();
if(Exception-Instanz von ApiException){//api异常
ApiException apiException = (ApiException)Exception;
errorDTO.setErrorCode(apiException.getErrorCode());
}else{//未知异常
errorDTO.setErrorCode(0L);
}
errorDTO.setTip(Exception.getMessage());
ResponseEntity
return ResponseEntity;
}
@Setter
@Getter
Klasse ErrorDTO{
privater langer Fehlercode;
privater String-Tipp;
}
}
Ok, ich habe die API-Funktion übernommen und die DTO-Funktion übernommen殊的切面处理.
Wenn beim Aufruf der API-Schnittstelle eine Ausnahme auftritt, kann der Benutzer auch das normale Datenformat erhalten, wenn beispielsweise kein Benutzer vorhanden ist (UID ist 2), aber die Lieferadresse für diesen Benutzer hinzugefügt wird, Postbote (Google-Plugin wird verwendet). um http-Anfragen zu simulieren) Daten:
{
„errorCode“: 10003,
„Tipp“: „Der Benutzer kann nicht gefunden werden“
}
Das obige ist der detaillierte Inhalt vonWie entwerfe ich elegante Java-Ausnahmen?. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!