Coding With Fun
Home Docker Django Node.js Articles Python pip guide FAQ Policy

Whether the service layer exception in JavaWeb is handled directly or thrown into the Totalle layer for processing


Jun 01, 2021 Article blog


Table of contents


What happens to Service layer exceptions in JavaWeb This is a very enlightening question.

When a beginner learns coding and error handling, he or she first knows that the programming language has a form or convention for handling errors (such as Java is throwing exceptions) and then starts using these tools, but in turn ignores the nature of the problem:

Errors are handled in order to write the correct program

How on earth is "right"? i s determined by the problem to be solved. Different problems, solutions are different.

For example, a web接口 accepts a user's request, which requires the introduction of a parameter called "age", and perhaps the business requires that the field should be an integer between 0 and 150. I f the user enters a string or a negative number, it is certainly not accepted. G enerally in the back end of a place will do the legitimacy of the input check, check but throw exceptions. B ut in the final analysis, the "right" solution to this problem always prompts the user in some form. I t depends on whether the interface is app H5 + ajax or a server-generated interface like jsp which prompts the user to do some kind of front-end work. E ither way, you need to "design a bug fix" process based on your needs. F or example, a common process requires the back end to throw exceptions, and then go all the way to a centralized code that handles errors, converts them to an HTTP error (a specific business error code) and provides them to the front end, which then "tips." If a user enters an illegal request, the logical backend can't fix itself, which is the "right" strategy.

For example, if a user wants to upload an avatar, the back end sends the image to a cloud store, resulting in a cloud storage error of 500. W hat do we do? Y ou may have thought of retrying a few times, because perhaps the problem is just temporary network jitter, and retrying can work. H owever, if trying again more than once does not work. I f you designed some kind of hot-preparation scheme while working on the system, you might instead send it to another server. Retry and Dependency on Using Backup are both Process Now.

(Recommended tutorial: Java tutorial)

But if the retry doesn't work and all the backup services don't work, you might be able to throw the error to the front end, as you did above, prompting the user to "open the server." I t's easy to see from this scenario that you want to throw your mistakes because the catch place is the most convenient place to deal with problems. A solution to a problem may take several different error handling combinations to do.

In another example, your program throws an NPE T his is usually a programmer's bug -- or the programmer wants to express something "no" and forgets to tell if null it's null in subsequent processing; null I n either case, the wrong user will always see a very vague misinformation, which is not enough. T he "right" approach is for the programmer to discover it as quickly as possible and fix it as soon as possible. T o do this, you need to monitor the system to keep climbing log and alert the problem. Instead of waiting for the user to find customer service to spit.

Another example, such as your back-end program suddenly OOM hangs up. H anging programs can't recover themselves. T o be "right", you must consider this issue in a container other than the service. F or example, if your service runs on k8s they monitor the status of your program, then restart the new service instance to make up for the suspended service, and then adjust the traffic, cut off the traffic to the pending service, and replace it with a new instance. R ecovery here cannot be implemented with exceptions alone because it is cross-system, but the truth is the same. B ut is a reboot the "right" thing to do? I f the service is completely stateless, the problem is not great. B ut if it is stateful, some user data may be messed up by half of the requests executed. S o be careful to "recover data to a legal state" when restarting. T his goes back to what you need to know to be the "right" approach. Simple grammatical functions alone cannot solve this problem without brains.

  • We can promote that the "external container" of a worker thread is the "master" of the management thread. T he "external container" of a network request is a web server. T he "external container" of a user process is the operating system. Erlang incorporates this supervisor-worker mechanism into the design of the language.

(Recommended micro-class: Java micro-class)

There are three main reasons why Web programs are largely able to throw exceptions to the top:

  • The request comes from the front end, and the final probability can only be told to the user about a problem caused by an error in the user's request (data legitimacy, permissions, user context state). Therefore, it makes sense to throw exceptions into a place where errors are handled centrally and convert them to a business error code.
  • Back-end services are generally stateless. T his is also the general principle of Internet system design. A stateless state means you can restart at will. For the user's data because the next one generally does not go wrong.
  • The backend relies on DB transactions for data modification. So a changed half of the uncommitted transactions does not cause side effects.

But be clear that these three don't always hold true. T here will always be processing logic that is not completely stateless, and not all data modifications can be protected by a transaction. I n particular, pay attention to the call to microseries, the modification of memory state is not transactional protection, a lack of attention will appear to mess up the user data problems. Like what:

 try {
   int res1 = doStep1();
   this.statusVar1 += res1;
   int res2 = doStep2();
   this.statusVar2 += res2;
   int res3 = doStep3(); // throw an exception
   this.statusVar3 = statusVar1 + statusVar2 + res3;
} catch ( ...) { 
   // ...
}

this.statusVar1 start by assuming that some constant constraint (invariant) needs to be maintained between this.statusVar3 this.statusVar2 W hen you then execute this code, if you throw an exception under doStep3 the assignment to statusVar3 will not execute. F ailure to rollback changes to statusVar1 and statusVar2 can cause data violations. P rogrammers, on the other hand, generally find it difficult to directly discover that this data has been altered. B roken data can secretly lead to other code logic errors that depend on that data (such as giving credits, but not). And this kind of error is generally very difficult to investigate, and finding the wrong little one from a lot of data is quite difficult.

What's harder to figure out than the one above is this code:

// controller
void controllerMethod(/* some params*/) {
  try {
    return svc.doWorkAndGetResult(/* some params*/);
  } catch (Exception e) {
    return ErrorJsonObject.of(e);
  }
}


// class svc
void doWorkAndGetResult(/* some params*/) {
    int res1 = otherSvc1.doStep1(/* some params */);
    this.statusVar1 += res1;
    int res2 = otherSvc2.doStep2(/* some params */);
    this.statusVar2 += res2;
    int res3 = otherSvc3.doStep3(/* some params */);
    this.statusVar3 = statusVar1 + statusVar2 + res3;
    return SomeResult.of(this.statusVar1, this.statusVar2, this.statusVar3);
}

The scary thing about this code is that when you write it, you might think that something like doStep1~3 can be caught in Controller even if it's thrown out of the catch Y ou don't have to deal with any exceptions at the svc layer, so don't write try……catch is natural. B ut in fact, any one of the throw exceptions in doStep1 doStep3 can cause svc data state to be inconsistent. doStep2 E ven if you can start by documenting or other means of communication, doStep1 doStep2 doStep3 is bound to succeed in the first place and not get it wrong, so the code you write is right from the start. B ut you may not be able to control their implementation (for example, they are provided by a lib developed by another team), and their implementation may be reversed to make a mistake. Y our code may change from "no problem" to "possible problem" without knowing it at all... What's even scarier is that code like this doesn't work correctly:

void doWorkAndGetResult(/* some params*/) {
    try {
       int res1 = otherSvc1.doStep1(/* some params */);
       this.statusVar1 += res1;
       int res2 = otherSvc2.doStep2(/* some params */);
       this.statusVar2 += res2;
       int res3 = otherSvc3.doStep3(/* some params */);
       this.statusVar3 = statusVar1 + statusVar2 + res3;
       return SomeResult.of(this.statusVar1, this.statusVar2, this.statusVar3);
   } catch (Exception e) {
     // do rollback
   }
}

You might think that this would handle the data rollback and even you'd find the code very elegant. B ut actually doStep1~3 throws the wrong one everywhere, rollback code is different. You have to write this:

void doWorkAndGetResult(/* some params*/) {
    int res1, res2, res3;
    try {
       res1 = otherSvc1.doStep1(/* some params */);
       this.statusVar1 += res1;
    } catch (Exception e) {
       throw e;
    }


    try {
      res2 = otherSvc2.doStep2(/* some params */);
      this.statusVar2 += res2;
    } catch (Exception e) {
      // rollback statusVar1
      this.statusVar1 -= res1;
      throw e;
    }

  
    try {
      res3 = otherSvc3.doStep3(/* some params */);
      this.statusVar3 = statusVar1 + statusVar2 + res3;
    } catch (Exception e) {
      // rollback statusVar1 & statusVar2
      this.statusVar1 -= res1;
      this.statusVar2 -= res2;
      throw e;
   } 
}

This is the code that gets the right results -- errors anywhere maintain data consistency. E legant? I t looks ugly. T his is even uglier than go if err != nil B ut if I had to choose between correctness and elegance, I wouldn't hesitate to choose the former. A s a programmer who can't directly think that throwing exceptions can solve any problem, you have to learn to write programs with the right logic, even if it's hard and ugly. To achieve high correctness, you can't always focus most of your attention on "the OK process of everything" and think of mistakes as work that you can do at will, or simply believe that exception can do everything automatically.

To sum up, I want all programmers to have a minimum of awe of error handling. Java side of Java has to be avoided because of Checked Exception design issues (see Wide - Is it necessary for Java to design a checked exception?). ), and Uncaughted Exception is too weak to help programmers better.

So programmers have to hit their souls three times every time they make a mistake or mishandle it: Is it the right thing to do? W hat will users see? W ill it mess up the data? Don't think you've thrown an exception.

Also, write UT to protect the fragile correctness of your code when the compiler can't help too much.

That's whether the Service Layer in Java Web, shared by w3cschool, is abnormally thrown to the Controller layer for processing or direct processing. I hope it will help you all.