A summary of exception handling in the Java EE project (a must read article)

  • 2020-05-16 06:57:43
  • OfStack

Why talk about exception handling in the J2EE project? Many java beginners may want to say, "exception handling is try... . catch... finally? Anyone can do that!" . When I first learned java, I also thought so. How to define the corresponding exception class in a multi-tier j2ee project? How are exceptions handled at each level of the project? When is the exception thrown? When are exceptions logged? How are exceptions logged? When you need to convert checked Exception to unchecked Exception, when you need to convert unChecked Exception to checked Exception; Should the exception be rendered to the front page? How to design an exception framework? This paper will discuss these problems.

1. JAVA exception handling

In procedural programming languages, we can use return values to determine if a method is performing normally. For example, in a program written in c language, if the method is executed correctly, it returns 1. Error returns 0. In an application developed by vb or delphi, when an error occurs, we pop up a message box to the user.

We do not get the details of the error by the return value of the method. Perhaps because methods are written by different programmers, when the same type of error occurs in different methods, the results and error messages returned are not identical.

Therefore, java language adopts a unified exception handling mechanism.

What is an exception? Errors that can be caught and handled at run time.

In the java language, Exception is the parent of all exceptions. Any exception extends to the Exception class. Exception is equivalent to one error type. If you want to define a new error type, extend a new Exception subclass. The advantage of using exceptions is that you can pinpoint the source code that caused the program to fail and get detailed error information.

Java exception handling is implemented through five keywords, try,catch,throw,throws, finally. Specific exception handling structure by try... . catch... .finally block to implement. The try block holds java statements where exceptions may occur. catch is used to catch and handle exceptions that occur. The Finally block is used to clean up unreleased resources in the program. Instead of managing how the code for the try block is returned, the finally block is always executed.

1 typical exception handling code


public String getPassword(String userId)throws DataAccessException{
String sql =  " select password from userinfo where userid=' " +userId + " ' " ;
String password = null;
Connection con = null;
Statement s = null;
ResultSet rs = null;
try{
con = getConnection();// Get data connection 
s = con.createStatement();
rs = s.executeQuery(sql);
while(rs.next()){
password = rs.getString(1);
}
rs.close();
s.close();
}
Catch(SqlException ex){
throw new DataAccessException(ex);
}
finally{
try{
if(con != null){
con.close();
}
}
Catch(SQLException sqlEx){
throw new DataAccessException( "Closing the connection failed ! " ,sqlEx);
}
}
return password;
}

We can see the advantages of Java's exception handling mechanism:

The classification of errors is unified 1 by extending the Exception class or its subclasses. This avoids the possibility that the same error may have different error messages in different methods. When the same error occurs in different methods, all you need is the same exception object for throw.

Get more detailed error information. With exception classes, you can give exceptions more detailed error information that is useful to the user. So that users can trace and debug the program.

Separate the correct return result from the error message. Reduced the complexity of the program. The caller does not need to know more about the returned result.

Force the caller to handle exceptions to improve the quality of the program. When a method declaration needs to throw an exception, the caller must use try... The.catch block handles exceptions. Of course, the caller can also let the exception continue to be thrown one layer up.

2. Checked exception or unChecked exception?

Java exceptions fall into two categories :checked exceptions and unChecked exceptions. All exceptions that inherit from java.lang.Exception are checked exceptions. All exceptions that inherit from java.lang.RuntimeException are unChecked exceptions.

When a method calls a method that might throw an checked exception, it must pass through try... The catch block catches exceptions for processing or rethrowing.
Let's look at the declaration of the createStatement() method of the Connection interface.


public Statement createStatement() throws SQLException;
SQLException is checked The exception. When calling createStatement Method, java Forces the caller to be right SQLException Do capture processing. 
public String getPassword(String userId){
try{
 ... 
Statement s = con.createStatement();
 ... 
Catch(SQLException sqlEx){
 ... 
}
 ... 
}

or


public String getPassword(String userId)throws SQLException{
Statement s = con.createStatement();
}

(of course, resources like Connection and Satement need to be closed in a timely manner, just to show that checked exceptions must be forced to be caught or continue to be thrown)

unChecked exceptions are also known as run-time exceptions. Typically, RuntimeException represents exceptions that the user cannot recover from, such as not being able to obtain a database connection, not being able to open a file, etc. Users can also catch unChecked exceptions as they did with checked exception 1, though. But if the caller does not catch the unChecked exception, the compiler does not force you to do so.

For example, a code to convert a character to an integer value is as follows:


String str =  " 123 " ;
int value = Integer.parseInt(str);

The method signature of parseInt is:


public staticint parseInt(String s)throws NumberFormatException

NumberFormatException is thrown when the incoming parameter cannot be converted to the corresponding integer. Because NumberFormatException extends to RuntimeException, it is an unChecked exception. So there is no need for try when calling the parseInt method... catch

Because java does not force the caller to catch or throw an unChecked exception. So programmers always like to throw unChecked exceptions. Or when a new exception class is needed, it is customary to extend from RuntimeException. When you call some of its methods, the compiler will always let you pass without an catch block, and you won't need to know what exception the method will throw. This may seem like a good idea, but it's a step away from the true intent of java's exception handling. And it's misleading to the programmer who calls your class, because the caller doesn't know what to expect to handle the exception. The checked exception explicitly tells the caller what exceptions to handle when calling the class. If the caller does not handle it, the compiler will prompt and fail to compile. Of course it's up to the caller to decide what to do with it.

So Java recommends that people use checked exceptions in their application code. As we mentioned in the previous section, the good thing about using exceptions is that you can force the caller to handle the exception that will be generated. java official documents, including java Tutorial, include checked exceptions as standard usage.

Using checked exceptions should mean that there is a lot of try... catch is in your code. As more and more try are written and processed... After the catch block, many people finally began to doubt whether checked abnormally inverted should be used as a standard.

Even the famous author of thinking in java, Bruce Eckel, changed his mind. Bruce Eckel even advocates the standard use of unChecked exceptions. And published an article to test whether the checked exception should be removed from java. Bruce Eckel: "when there is a small amount of code, checked exceptions are definitely 10 points of elegant design and help avoid many potential errors. But experience has shown that the opposite is true for a lot of code."

A detailed discussion of the checked exception and unChecked exception is available

The java Tutorial http: / / java sun. com docs/books/tutorial/essential/exceptions/runtime html

Using the checked exception can cause a number of problems.

The checked exception caused too much try... catch code

There may be many checked exceptions that developers cannot reasonably handle, such as SQLException. Developers have to do try... catch. When a developer fails to properly handle an checked exception, it is usually a matter of simply printing it out or doing nothing at all. Especially for the novice, too many checked exceptions make him feel at a loss.


try{
 ... 
Statement s = con.createStatement();
 ... 
Catch(SQLException sqlEx){
sqlEx.PrintStackTrace();
}

or


try{
 ... 
Statement s = con.createStatement();
 ... 
Catch(SQLException sqlEx){
// Do nothing 
}

The checked exception resulted in a lot of incomprehensible code

When a developer has to catch an checked exception that he or she cannot handle correctly, it is common to repackage it into a new exception and throw it. This does not do the program any good. It makes the code hard to understand later.

Just like we used the JDBC code, we need to deal with a lot of try... catch. The really useful code is included in try... catch within. Makes it difficult to understand the method

The checked exception causes the exception to be repeatedly encapsulated into another class exception and then thrown


public void methodA()throws ExceptionA{
 ... ..
throw new ExceptionA();
}
public void methodB()throws ExceptionB{
try{
methodA();
 ... 
}catch(ExceptionA ex){
throw new ExceptionB(ex);
}
}
Public void methodC()throws ExceptinC{
try{
methodB();
 ... 
}
catch(ExceptionB ex){
throw new ExceptionC(ex);
}
}

We see an endless layer of exceptions being wrapped and rethrown.

An checked exception causes the interface method to be broken

One method on one interface has been used by multiple classes, and when an checked exception is added to this method, all the code that calls this method needs to be modified.
It can be seen that these problems are caused by callers who cannot handle checked exceptions properly and are forced to catch and handle them, encapsulate them and rethrow them. It's inconvenient and doesn't do any good. unChecked exceptions are usually used in this case.

The chekced exception is not an exception without a 1, and it is much better than the error return value of traditional programming. It is much better to use the compiler to ensure that exceptions are handled correctly than to use the return value.

If an abnormality is fatal, it cannot be recovered. Or the caller can catch it without any benefit, using the unChecked exception.

If an exception is recoverable and can be handled correctly by the caller, use the checked exception.

When using an unChecked exception, you must specify in the method declaration the unChekced exception that the method might throw. It is up to the caller to decide whether or not to catch the unChecked exception

When to use an checked exception and when to use an unChecked exception? There is no absolute standard. But I can offer some Suggestions

When all callers must handle this exception, you can have the caller retry the operation. Or the exception corresponds to the second return value of the method. Use the checked exception.
This exception is only handled by a few more advanced callers, and 1-like callers are not handled correctly. Use unchecked exceptions. Callers with the ability to process can do advanced processing, and callers with the ability to do no processing at all.

This exception is a very serious error, such as database connection error, file could not be opened. Or the exceptions are related to the external environment. It can't be solved by retrying. Use the unchecked exception. Because this exception occurs once, the caller simply cannot handle it.

If you are unsure, use the unchecked exception. And details the exceptions that might be thrown to let the caller decide whether to handle them.

3. Design a new exception class

When designing a new exception class, first see if you really need the exception class. 1. Try not to design new exception classes in general, but to use existing exception classes in java.

Such as


IllegalArgumentException, UnsupportedOperationException 

Whether the new exception is an chekced exception or an unChecked exception. We must all consider the nesting of exceptions.


public void methodA()throws ExceptionA{
 ... ..
throw new ExceptionA();
}

The method methodA declaration throws ExceptionA.

public void methodB()throws ExceptionB

The methodB declaration throws ExceptionB, and when methodA is called from the methodB method, ExceptionA cannot handle it, so ExceptionA should continue to throw it up. One way is to throw the methodB declaration ExceptionA. This has changed the MethodB method signature. When 1 is changed, all methods that call methodB are changed.

Another option is to wrap ExceptionA into ExceptionB and then throw it. If we don't wrap ExceptionA in ExceptionB, we lose the root exception information, making it impossible to trace the original origin of the exception.


public Statement createStatement() throws SQLException;
SQLException is checked The exception. When calling createStatement Method, java Forces the caller to be right SQLException Do capture processing. 
public String getPassword(String userId){
try{
 ... 
Statement s = con.createStatement();
 ... 
Catch(SQLException sqlEx){
 ... 
}
 ... 
}
0

In the code above, ExceptionB nested an ExceptionA. Let's call ExceptionA a "cause exception" because ExceptionA caused ExceptionB. This way the exception information is not lost.

So when we define a new exception class, we must provide a constructor that can contain nested exceptions. There is also a private member to hold the "cause exception".


public Statement createStatement() throws SQLException;
SQLException is checked The exception. When calling createStatement Method, java Forces the caller to be right SQLException Do capture processing. 
public String getPassword(String userId){
try{
 ... 
Statement s = con.createStatement();
 ... 
Catch(SQLException sqlEx){
 ... 
}
 ... 
}
1

Of course, when we call the printStackTrace method, we need to print out all the "cause exception" information at the same time. So we need to override the printStackTrace method to display the full exception stack trace. Stack traces that include nested exceptions.


public Statement createStatement() throws SQLException;
SQLException is checked The exception. When calling createStatement Method, java Forces the caller to be right SQLException Do capture processing. 
public String getPassword(String userId){
try{
 ... 
Statement s = con.createStatement();
 ... 
Catch(SQLException sqlEx){
 ... 
}
 ... 
}
2

A complete support for nested checked exception class source code is shown below. Let's call it NestedException for the moment


public NestedException extends Exception{
private Throwable cause;
public NestedException (String msg){
super(msg);
}
public NestedException(String msg, Throwable ex){
super(msg);
This.cause = ex;
}
public Throwable getCause(){
return (this.cause ==null ?this :this.cause);
}
public getMessage(){
String message = super.getMessage();
Throwable cause = getCause();
if(cause != null){
message = message +  " ;nested Exception is  "  + cause;
}
return message;
}
public void printStackTrace(PrintStream ps){
if(getCause == null){
super.printStackTrace(ps);
}else{
ps.println(this);
getCause().printStackTrace(ps);
}
}
public void printStackTrace(PrintWrite pw){
if(getCause() == null){
super.printStackTrace(pw);
}
else{
pw.println(this);
getCause().printStackTrace(pw);
}
}
public void printStackTrace(){
printStackTrace(System.error);
}
}

Also design an unChecked exception class as above. You just need to inherit RuntimeException.

4. How to log exceptions

As a large application system need to use log files to record the operation of the system, in order to track and record the operation of the system. Exceptions to the system naturally need to be logged in the logging system.


public Statement createStatement() throws SQLException;
SQLException is checked The exception. When calling createStatement Method, java Forces the caller to be right SQLException Do capture processing. 
public String getPassword(String userId){
try{
 ... 
Statement s = con.createStatement();
 ... 
Catch(SQLException sqlEx){
 ... 
}
 ... 
}
4

We noticed that one error was recorded twice. At the origin of the error we only recorded at the info level. In the sendUserPassword method, we also log the entire exception.

I have seen a lot of projects that record exceptions like this. No matter 3721, they only record the whole exception when they encounter an exception. If an exception is thrown multiple times by constant encapsulation, it is logged multiple times. So where should the anomaly be recorded?

Exceptions should be logged at the location where they were originally generated!

If you must catch an exception that cannot be handled correctly, simply wrap it up as another exception to be thrown up. You do not have to record an exception that has already been logged again.

If an exception is caught, it can be handled. No exceptions need to be logged


public Statement createStatement() throws SQLException;
SQLException is checked The exception. When calling createStatement Method, java Forces the caller to be right SQLException Do capture processing. 
public String getPassword(String userId){
try{
 ... 
Statement s = con.createStatement();
 ... 
Catch(SQLException sqlEx){
 ... 
}
 ... 
}
5

When an unrecorded exception or external system exception is caught, the details of the exception should be recorded


public Statement createStatement() throws SQLException;
SQLException is checked The exception. When calling createStatement Method, java Forces the caller to be right SQLException Do capture processing. 
public String getPassword(String userId){
try{
 ... 
Statement s = con.createStatement();
 ... 
Catch(SQLException sqlEx){
 ... 
}
 ... 
}
6

Exactly where and how to record the abnormal information may be a matter of opinion. There are even systems that let exception class 1 record exceptions. When a new exception object is generated, the exception information is automatically logged.


public Statement createStatement() throws SQLException;
SQLException is checked The exception. When calling createStatement Method, java Forces the caller to be right SQLException Do capture processing. 
public String getPassword(String userId){
try{
 ... 
Statement s = con.createStatement();
 ... 
Catch(SQLException sqlEx){
 ... 
}
 ... 
}
7

This may seem like a nice 10, but it must have resulted in the anomaly being repeated. It also violates the "class responsibility assignment principle", which is a bad design. Logging exceptions that are not of the exception class should be done by a specialized logging system. And the abnormal record information is constantly changing. We are recording exceptions and should give more information. So that we can find the source of the problem according to the abnormal information, so as to solve the problem.

Although we've talked a lot about logging exceptions, and put too much emphasis on them to confuse developers, a good way to do this is to provide the system with an exception handling framework. It is up to the framework to decide whether and how to log exceptions. It's not up to the average programmer to decide. But it's good to know.

5. Exception handling in the J2EE project

Currently, project 1 of J2ee will be logically divided into several layers. There are three classic layers: presentation layer, business layer, and integration layer (including database access and external system access).

The J2ee project has its complexity, and the exception handling of the J2ee project needs special attention.

In distributed applications, we encounter many checked exceptions. All RMI calls (including EJB remote interface calls) throw java.rmi.RemoteException; At the same time, RemoteException is an checked exception. When we make remote calls in the business system, we all need to write a lot of code to handle these checked exceptions. The occurrence of RemoteException these checked anomalies is very serious to the system, and there is almost no possibility of retry. In other words, when there are RemoteException's terrible checked exceptions, we don't have any need to retry, but we have to write a lot of try... The catch code handles it. Generally, we make RMI calls at the lowest level. As long as there is one RMI call, all the upper level interfaces will ask to throw RemoteException exceptions. Because the way we're going to handle RemoteException is we're going to keep flipping it up. So 1 is breaking our business interface. RemoteException these J2EE system-level anomalies seriously affect our business interfaces. The goal of layering the system is to reduce dependencies between systems, so that technical changes in each layer do not affect the others.


//
public class UserSoaImplimplements UserSoa{
public UserInfo getUserInfo(String userId)throws RemoteException{
// ... 
 Remote method call .
// ... 
}
}
public interface UserManager{
public UserInfo getUserInfo(Stirng userId)throws RemoteException;
}

The same JDBC access throws an checked exception for SQLException.

To avoid the deep penetration of the business system by system-level checked exceptions, we can define one of the business system's own exceptions for the business method. For very serious exceptions like SQLException and RemoteException, we can define a new unChecked exception, and then wrap SQLException and RemoteException into unChecked exception and throw it.

If the system-level exception is to be handled by the caller at the previous level, you can define a new checked business exception, and then store the system-level exception as a business-level exception before throwing it.

Like 1, we need to define an unChecked exception and have all the methods of the integration layer interface declare to throw this unChecked exception.


public Statement createStatement() throws SQLException;
SQLException is checked The exception. When calling createStatement Method, java Forces the caller to be right SQLException Do capture processing. 
public String getPassword(String userId){
try{
 ... 
Statement s = con.createStatement();
 ... 
Catch(SQLException sqlEx){
 ... 
}
 ... 
}
9

Define a business exception for checked and have all the methods of the interface at the business layer declare to throw an Checked exception.


public class BusinessExceptionextends Exception{
 ... ..
}
public interface UserManager{
public Userinfo copyUserInfo(Userinfo user)throws BusinessException{
Userinfo newUser = null;
try{
newUser = (Userinfo)user.clone();
}catch(CloneNotSupportedException ex){
throw new BusinessException( "Don't support clone methods : " +Userinfo.class.getName(),ex);
}
}
}

The presentation layer of J2ee should be a very thin layer. The main functions are: get page request, assemble the parameters of the page into POJO object, call the corresponding business method, and then forward the page, presenting the corresponding business data to the page. The presentation layer needs to pay attention to one problem. The presentation layer needs to verify the validity of the data, such as some input fields can't be empty, character length verification, etc.

J2ee all parameters passed from the page to the background are character type. If you want to input a parameter of value or date type, you must convert the character value to the corresponding value or date value.

If the code validation parameter of the presentation layer is not valid, you should return to the original page and ask the user to re-enter the data and prompt the relevant error message.
It is common to convert a parameter from a page to a value, and you can see this code


ModeAndView handleRequest(HttpServletRequest request,HttpServletResponse response)throws Exception{
String ageStr = request.getParameter( " age " );
int age = Integer.parse(ageStr);
 ..................... 
String birthDayStr = request.getParameter( " birthDay " );
SimpleDateFormat format = new SimpleDateFormat( " MM/dd/yyyy " );
Date birthDay = format.parse(birthDayStr);
}

The code above should be seen often, but when the user enters a character from the page that cannot be converted to an integer or an incorrect date value.

The Integer.parse () method is thrown with an unChecked exception for NumberFormatException. But this exception is definitely not a fatal exception,1 when the user in the page entry field input value illegal, we should prompt the user to re-enter. But once an unchecked exception is thrown, there is no chance to try again. Code like this causes a lot of exception information to be displayed on the page. It makes our system look very fragile.

Similarly, the SimpleDateFormat.parse () method throws an unChecked exception for ParseException.

In this case we should catch these unChecked exceptions and prompt the user to re-enter them.


ModeAndView handleRequest(HttpServletRequest request,HttpServletResponse response)throws Exception{
String ageStr = request.getParameter( " age " );
String birthDayStr = request.getParameter( " birthDay " );
int age = 0;
Date birthDay = null;
try{
age=Integer.parse(ageStr);
}catch(NumberFormatException ex){
error.reject( " age " , "Not a valid integer value." );
}
 ..................... 
try{
SimpleDateFormat format = new SimpleDateFormat( " MM/dd/yyyy " );
birthDay = format.parse(birthDayStr);
}catch(ParseException ex){
error.reject( " birthDay " , "Not a valid date, please enter 'MM/dd/yyy' Format of the date" );
}
}

In presentation layer 1, it is important to find out if the calling method will throw unChecked exceptions, under what circumstances they will be thrown, and handle them properly.

The system's business methods are called at the presentation layer, and there is no need to catch exceptions in the 1 case. If the invoked business method throws an exception equivalent to the second return value, in this case it needs to be caught.


Related articles: