Why talk about exception handling in J2EE project? Maybe many Java beginners want to say, "Isn't exception handling just try....catch... finally? Everyone knows this!" The author also thinks this way when he is a beginner in Java. How to define the corresponding exception class in a multi-layer j2ee project? How to handle exceptions at each layer in the project? When is the exception thrown? When is the exception recorded? How to record exceptions? When should I convert checked Exception into unchecked Exception, and when should I convert unChecked Exception into checked Exception? Should the exception be presented to the front-end page? How to design an exception framework? These issues will be discussed in this article.
1. JAVA exception handling
In procedural programming languages, we can determine whether the method is executed normally by returning the value. For example, in a program written in C language, if the method is executed correctly, it will return 1. If the error is executed, it will return 0. In an application developed by vb or delphi, when an error occurs, we will pop up a message box to the user.
We cannot get the error details through the return value of the method. It may be because the method is written by different programmers, when the same type of error occurs in different methods, the returned results and error information are not consistent.
Therefore, the Java language adopts a unified exception handling mechanism.
What is an exception? Catchable and processable errors occurring at runtime.
In the java language, Exception is the parent class of all exceptions. Any exception is extended to the Exception class. Exception is equivalent to an error type. If you want to define a new error type, extend a new Exception subclass. The advantage of using exceptions is that it can accurately locate the source code location that causes the program error and obtain detailed error information.
Java exception handling is implemented through five keywords, try, catch, throw, throws, finally. The specific exception handling structure is implemented by the try….catch….finally block. The try block stores java statements that may have exceptions. The catch is used to catch the occurrence of exceptions and handle the exceptions. The final block is used to clear unfree resources in the program. The final block is always executed if the code that does not manage how the try block returns.
A 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 connections 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("Close connection failed!",sqlEx);}}return password;} It can be seen that the advantages of Java's exception handling mechanism:
The error is classified uniformly and implemented by extending the Exception class or its subclass. This avoids that the same error may have different error messages in different methods. When the same error occurs in different methods, you only need to throw the same exception object.
Get more detailed error information. Through exception classes, error information can be given to the exception that is more detailed and more useful to the user. Easy for users to track and debug programs.
Separate the correct return result from the error message. Reduces the complexity of the program. The caller does not need to know more about the return 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 the try....catch block to handle the exception. Of course, the caller can also allow the exception to continue to be thrown to the upper level.
2. Checked exception or unChecked exception?
Java exceptions are divided into two categories: checked exception and unChecked exception. All exceptions that inherit java.lang.Exception belong to checked exceptions. All exceptions that inherit java.lang.RuntimeException belong to unChecked exceptions.
When a method calls a method that may throw a checked exception, the exception must be captured and processed or re-throwed through the try...catch block.
Let's take a look at the declaration of the createStatement() method of the Connection interface.
public Statement createStatement() throws SQLException; SQLException is a checked exception. When the createStatement method is called, java forces the caller to capture the SQLException. 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 time. This is just to illustrate that the checked exception must force the caller to catch or continue to throw)
The unChecked exception is also called a runtime exception. Usually, the RuntimeException indicates an exception that the user cannot recover, such as the inability to obtain a database connection, the inability to open a file, etc. Although the user can also catch unChecked exceptions like handling checked exceptions. But if the caller does not catch the unChecked exception, the compiler will not force you to do that.
For example, a code that converts characters into integer values is as follows:
String str = "123"; int value = Integer.parseInt(str);
The parseInt method signature is:
public staticint parseInt(String s)throws NumberFormatException
When the passed parameters cannot be converted into the corresponding integer, a NumberFormatException will be thrown. Because NumberFormatException extends to RuntimeException, it is an unChecked exception. So there is no need to try...catch when calling the parseInt method
Because java does not force the caller to catch or throw the unChecked exception. So programmers always like to throw unChecked exceptions. Or when a new exception class is needed, it is always used to extending from RuntimeException. When you call these methods, if there is no corresponding catch block, the compiler will always let you pass, and you don't need to understand what exception will this method throw. It seems like this is a good idea, but doing so is away from the real intention of java exception handling. And it is misleading to the programmer who calls your class, because the caller has no idea under what circumstances it needs to handle exceptions. The checked exception can clearly tell the caller what exceptions need to be handled when calling this class. If the caller does not process it, the compiler will prompt and cannot compile and pass. Of course, how to deal with it is up to the caller to decide.
So Java recommends that people should use checked exceptions in their application code. Just like we mentioned in the previous section that the advantage of using exceptions is that it can force the caller to handle the exceptions that will be generated. Checked exceptions are used as standard in official java documents such as "java Tutorial".
Using checked exceptions should mean that there are many try...catches in your code. After writing and handling more and more try… catch blocks, many people finally began to wonder whether checked exceptions should be used as standard.
Even Bruce Eckel, the author of the famous "Thinking in Java", changed his former thoughts. Bruce Eckel even advocates using unChecked exceptions as standard usage. And publish an article to test whether the checked exception should be removed from java. Bruce Eckel: "When a small amount of code, checked exceptions are undoubtedly a very elegant concept and help avoid many potential errors. But experience shows that the result is exactly the opposite for a large amount of code."
For detailed discussions about checked exceptions and unChecked exceptions, please refer to
java Tutorial http://java.sun.com/docs/books/tutorial/essential/exceptions/runtime.html
Using checked exceptions can cause many problems.
Checked exception causes too much try...catch code
There may be many checked exceptions that cannot be handled reasonably by developers, such as SQLException. But the developers have to try...catch. When developers cannot handle a checked exception correctly, they usually simply print out the exception or do nothing. Especially for novices, 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} Checked exception causes many difficult-to-understand code generation
When developers have to catch a checked exception that they cannot handle correctly, they are usually repackaged into a new exception and then thrown. Doing so does not bring any benefits to the program. Instead, it makes the code difficult to understand later.
Just like we use JDBC code, there is a lot of try…catch. The really useful code is included in try…catch. Making it difficult to understand this method
Checked exception causes the exception to be constantly 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 that the exception is encapsulated and re-throwed layer by layer.
Checked exception causes interface method corruption
A method on an interface has been used by multiple classes. When an additional checked exception is added to this method, all codes calling this method need to be modified.
It can be seen that the above problems are forced to catch and process because the caller cannot handle the checked exception correctly, and are forced to encapsulate and then re-throw. This is very inconvenient and does not bring any benefits. In this case, unChecked exceptions are usually used.
The checked exception is not all. The checked exception is much easier to use than the error return value of traditional programming. It is much better to ensure that exceptions are handled correctly through the compiler than to judge by the return value.
If an exception is fatal, it is unrecoverable. Or the caller goes to catch it without any benefit, use the unChecked exception.
If an exception can be recovered and can be handled correctly by the caller, use the checked exception.
When using an unChecked exception, the unChekced exception that the method may throw must be described in detail in the method declaration. The caller decides whether to catch the unChecked exception
When to use checked exceptions, and when to use unChecked exceptions? There is no absolute standard. But I can give some suggestions
When all callers have to handle this exception, the caller can be allowed to retry the operation; or the exception is equivalent to the second return value of the method. Use checked exception.
This exception can only be handled by a few more advanced callers, and ordinary callers cannot handle it correctly. Use unchecked exception. Callers who have the ability to process can perform advanced processing, but generally callers do not handle it at all.
This exception is a very serious error, such as database connection errors, file failure to open, etc. Or these exceptions are related to the external environment. It is not possible to solve the problem of retrying. Use unchecked exception. Because once this exception occurs, the caller cannot handle it at all.
If it is not sure, use an unchecked exception. And describe in detail the exception that may be thrown to let the caller decide whether to handle it.
3. Design a new exception class
When designing a new exception class, first check whether this exception class is really needed. Generally speaking, try not to design new exception classes, but try to use exception classes that already exist in Java.
like
IllegalArgumentException, UnsupportedOperationException
Whether it is the new exception, a chekced exception or an unChecked exception. We all have to consider the nesting problem of exceptions.
public void methodA()throws ExceptionA{…..throw new ExceptionA();} Method methodA declaration throws ExceptionA.
public void methodB()throws ExceptionB
methodB declaration will throw ExceptionB. When methodA is called in the methodB method, ExceptionA cannot be processed, so ExceptionA should continue to throw up. One way is to make the methodB declaration throw ExceptionA. But this has changed the method signature of MethodB. Once changed, all methods that call methodB must be changed.
Another way is to encapsulate ExceptionA into ExceptionB and then throw it. If we do not encapsulate ExceptionA in ExceptionB, we will lose the root exception information, making it impossible to track the original source of the exception.
public void methodB()throws ExceptionB{try{methodA();...}catch(ExceptionA ex){throw new ExceptionB(ex);}} As in the above code, ExceptionB nests an ExceptionA. We will call ExceptionA "caused exception" for the time being, because ExceptionA causes ExceptionB to occur. This will prevent the exception information from being lost.
So when we define a new exception class, we must provide such a constructor that can contain nested exceptions. And there is a private member to save this "cause exception".
public Class ExceptionB extends Exception{private Throwable cause;public ExceptionB(String msg, Throwable ex){super(msg);this.cause = ex;}public ExceptionB(String msg){super(msg);}public ExceptionB(Throwable ex){this.cause = ex;}} Of course, when we call the printStackTrace method, we need to print out all the "caused exception" information at the same time. So we need to override the printStackTrace method to display all exception stack traces. Includes stack traces of nested exceptions.
public void printStackTrace(PrintStrean ps){if(cause == null){super.printStackTrace(ps);}else{ps.println(this);cause.printStackTrace(ps);}} A complete support for nested checked exception class source code is as follows. Let's call it NestedException here for the time being
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, you need to design an unChecked exception class as above. It just needs to inherit RuntimeException.
4. How to log exceptions
As a large application system, log files are required to record the operation of the system to facilitate tracking and recording the operation of the system. Exceptions that occur in the system naturally need to be recorded in the log system.
public String getPassword(String userId)throws NoSuchUserException{UserInfo user = userDao.queryUserById(userId);If(user == null){Logger.info("The user information cannot be found, userId="+userId);throw new NoSuchUserException("The user information cannot be found, userId="+userId);}else{return user.getPassword();}}public void sendUserPassword(String userId)throws Exception {UserInfo user = null;try{user = getPassword(userId);//…..sendMail();//}catch(NoSuchUserException ex)(logger.error("This user information cannot be found:"+userId+ex);throw new Exception(ex);} We noticed that an error was logged twice. At the origin of the error we only record at the info level. In the sendUserPassword method, we also record the entire exception information.
The author has seen many projects record exceptions in this way, regardless of 3721, and the entire exception will be recorded only when encountering an exception. If an exception is continuously encapsulated and thrown multiple times, it will be recorded multiple times. So where should the exception be recorded?
The exception should be recorded at the initial location!
If you have to catch an exception that cannot be handled correctly, just encapsulate it into another exception and throw it up. There is no need to record the recorded exception again.
If an exception is caught, this exception can be handled. No exceptions need to be recorded
public Date getDate(String str){Date applyDate = null;SimpleDateFormat format = new SimpleDateFormat("MM/dd/yyyy");try{applyDate = format.parse(applyDateStr);}catch(ParseException ex){//Why, when the format is wrong, return null}return applyDate;} When an unrecorded exception or external system exception is caught, the details of the exception should be recorded.
try{…String sql=”select * from userinfo”;Statement s = con.createStatement();…Catch(SQLException sqlEx){Logger.error("sql execution error"+sql+sqlEx);} Where to record exception information and how to record exception information may be a matter of opinion. Some systems even let exception class record exceptions. When a new exception object is generated, the exception information is automatically recorded.
public class BusinessException extends Exception {private void logTrace() {StringBuffer buffer=new StringBuffer();buffer.append("Business Error in Class: ");buffer.append(getClassName());buffer.append(",method: ");buffer.append(getMethodName());buffer.append(",message: ");buffer.append(this.getMessage());logger.error(buffer.toString());}public BusinessException(String s) {super(s);race();} This seems to be very wonderful, but it will inevitably lead to the exception being recorded repeatedly. At the same time, it violates the "principle of distribution of responsibilities of classes" and is a bad design. Recording exceptions does not belong to the exception class behavior, and recording exceptions should be done by a special logging system. And the abnormal recording information is constantly changing. We are recording exceptions that should give richer information. This helps us to find the root cause of the problem based on exception information to solve the problem.
Although we have discussed a lot about recording exceptions, too much emphasis on these makes developers more confused. One good way is to provide an exception handling framework for the system. The framework decides whether to record exceptions and how to record exceptions. It's not up to ordinary programmers to decide. But it is beneficial to know something.
5. Exception handling in J2EE project
At present, J2ee projects are generally divided into multiple layers logically. The classic ones are divided into three layers: presentation layer, business layer, and integration layer (including database access and external system access).
The J2ee project has its complexity, and several issues need to be paid special attention to when handling exceptions in the J2ee project.
When using distributed applications, we encounter many checked exceptions. All RMI calls (including EJB remote interface calls) will throw java.rmi.RemoteException; at the same time, RemoteException is a checked exception. When we make remote calls in the business system, we all need to write a large amount of code to handle these checked exceptions. Once RemoteException occurs, these checked exceptions are very serious to the system, and there is almost no possibility of retrying. That is to say, when these terrible checked exceptions of RemoteException appear, we do not have any need to retry, but we have to write a lot of try...catch code to handle it. Generally, we make RMI calls at the bottom level. As long as there is an RMI call, all upper-level interfaces will require a RemoteException to be thrown. Because the way we deal with RemoteException is to continue throwing it up. This will destroy our business interface. RemoteException These J2EE system-level exceptions seriously affect our business interfaces. The purpose of our layering of systems is to reduce dependence between systems, and the technological changes in each layer will not affect other layers.
//public class UserSoaImplimples UserSoa{public UserInfo getUserInfo(String userId)throws RemoteException{//…Remote method call.//……}}public interface UserManager{public UserInfo getUserInfo(String userId)throws RemoteException;} Similarly, JDBC access will throw a checked exception of SQLException.
In order to avoid the deep intrusion of system-level checked exceptions into the business system, we can define a business system's own exception for the business method. For very serious exceptions like SQLException and RemoteException, we can define a new unChecked exception, and then encapsulate SQLException and RemoteException into an unChecked exception and throw it.
If this system-level exception is to be handed over to the previous caller to handle, you can newly define a checked business exception, and then seal the system-level exception into a business-level exception and then throw it.
Generally, we need to define an unChecked exception, so that all methods of the integration layer interface declare to throw this unChecked exception.
public DataAccessExceptionextends RuntimeException{...}public interface UserDao{public String getPassword(String userId)throws DataAccessException;}public class UserDaoImplimples UserDAO{public String getPassword(String userId)throws DataAccessException{String sql = "select password from userInfo where userId= '"+userId+"'";try{…//JDBC calls s.executeQuery(sql);…}catch(SQLException ex){throw new DataAccessException("Database query failed"+sql,ex);}}} Define a checked business exception, so that all methods of the business layer interface declare to throw a 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("The clone method is not supported:"+Userinfo.class.getName(),ex);}}} The J2ee representation layer should be a very thin layer, and its main functions are: obtain page requests, assemble the page parameters into POJO objects, call the corresponding business methods, and then forward the page, present the corresponding business data to the page. The presentation layer needs to pay attention to one issue. The presentation layer needs to verify the legitimacy of the data, such as some input fields cannot be empty, character length verification, etc.
All parameters passed from J2ee to the background from the page are character type. If you require the input of numerical or date type parameters, the character value must be converted to the corresponding numerical or date value.
If the representation layer code verification parameters are not valid, it should be returned to the original page, allowing the user to re-enter the data, and prompt for relevant error information.
Usually, a parameter passed from the page is converted into a numeric value, we can see such a 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 above code should be seen often, but when the user enters a character that cannot be converted to an integer from the page or an incorrect date value.
The Integer.parse() method is thrown an unChecked exception with NumberFormatException. However, this exception is definitely not a fatal exception. Generally, when the value entered by the user in the page entry field is illegal, we should prompt the user to re-enter. But once an unchecked exception is thrown, there is no chance to try again. Codes like this cause a large amount of exception information to be displayed on the page. Make our system look very fragile.
Similarly, the SimpleDateFormat.parse() method will also throw an unChecked exception of ParseException.
In this case, we should catch these unChecked exceptions and prompt the user to re-enter.
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","is not a legal integer value");}………try{SimpleDateFormat format = new SimpleDateFormat("MM/dd/yyyy");birthDay = format.parse(birthDayStr);}catch(ParseException ex){error.reject("birthDay","is not a legal date, please enter the date in 'MM/dd/yyy' format");}} At the presentation layer, you must figure out whether the calling method will throw an unChecked exception, under what circumstances will these exceptions be thrown, and make the correct processing.
In the presentation layer, there is no need to catch exceptions in general. If the exception thrown by the called business method is equivalent to the second return value, in this case it is necessary to capture.
The above is all the content of this article. I hope it will be helpful to everyone's learning and I hope everyone will support Wulin.com more.