1. Java exception system
The Java exception architecture is designed to achieve large, robust, reliable programs using a small amount of code.
1.1. Exception handling
Exception handling is the only formal error reporting mechanism in Java. The biggest benefit of exception handling is to reduce the complexity of error code handling.
If you do not use exceptions, you must check for specific errors at the point of call and handle them at many points in the program; If an exception is used, there is no need to check at the method call because the exception mechanism guarantees that the error will be caught. So you only have to deal with errors in one place, which not only saves code, but also separates “describe what to do during proper execution” from “what to do if something goes wrong.”
An important rule of exception handling is to “catch exceptions only if you know how to handle them.” In fact, an important goal of exception handling is to separate the error-handling code from where the error occurred. This allows you to focus on what you need to do in one piece of code, and exception handling in the other. This way, the trunk code is not mixed up with the error-handling logic and is easier to understand and maintain. Centralize exception handling code in one place by allowing a single exception handler to handle multiple exceptions.
“Exception case” refers to a problem that prevents the current method or scope from continuing execution. One of the most important aspects of an exception is that if a problem occurs, it does not allow the program to continue on its normal path, but instead transfers control to the exception handler, forcing the problem to be handled and restoring a stable state.
When an exception is thrown, several things happen that are different from what the program normally does:
- First, an exception object is created on the heap using the new keyword;
- The current execution path is then terminated, throwing an exception object reference from the current environment;
- At this point, the exception handling mechanism takes over the program and looks for an appropriate exception handler to continue executing the program.
- If an appropriate exception handler is found, it is used to restore the exception. If no suitable exception handler is found, the exception is thrown up the call chain;
- If no suitable exception handler is found at the top level, the current thread exits abnormally.
Java exception handling:
Path Path = paths.get (paths.get (paths.get))"var"."error"); List<String> lines = Files.readAllLines(path, Charset.defaultCharset()); System.out.println(lines); } catch (IOException e) {// How to handle the exception. }finally {// clean up resources}Copy the code
1.1.1. Exception
Exception like any other object, exception objects are created on the heap using the new keyword, along with the allocation of storage space and the invocation of constructors.
A standard exception has several constructors:
- Default constructor with no arguments
- Accepts a constructor for a string
- Accepts a string and a Throwable constructor
public class Exception extends Throwable {
static final long serialVersionUID = -3387516993124229948L;
public Exception() { super(); } public Exception(String message) { super(message); } public Exception(String message, Throwable cause) { super(message, cause); } public Exception(Throwable cause) { super(cause); }}Copy the code
1.1.2. throw
Passing a reference to an exception object to a throw, which in effect “returns” from a method, can be used as a different return mechanism. The difference is that a return returns to the method call point and a throw returns to the exception handler.
private String throwException() {/ /return "Test";
throw new RuntimeException("Test Exception");
}
Copy the code
1.1.3. try
A “monitor area” is a piece of code that may generate exceptions, followed by code that handles those exceptions. The try block is the monitoring area.
If an exception is thrown inside a method (or an exception occurs when you call another method), the method ends at the point where it was thrown, and if you don’t want the method to end, you need to set a special block inside the method to catch the exception.
private String tryException(){try {// Monitor areareturnthrowException(); }catch (Exception e){// Exception handling area}return "";
}
Copy the code
1.1.4. catch
The exception thrown must be handled somewhere, and that point is the exception handler. Prepare handlers for each exception to be caught. The exception handler follows the try block, represented by the keyword catch.
Each catch clause looks like a method that accepts one and only one particular exception type. When an exception occurs, the exception handling mechanism searches for the first exception handler whose parameters match the exception type, and then enters the catch clause, at which point the exception is considered to have been handled. Once the catch clause ends, the handler’s search process ends.
When looking for an exception handler, it is not required that the exception thrown exactly matches the exception declared by the exception handler. An object of a derived class can also match the processor of the base class.
private String tryException(){try {// Monitor areareturnthrowException(); }catch (RuntimeException e){// Catch (Exception e){// Handle Exception}return "";
}
Copy the code
Order. The exception handling mechanism searches for the first matching exception handler, so the order of catch statements is critical, usually with the specific type first and the generic type second.
1.1.5. finally
For some code, you might want to execute an exception whether or not it is thrown in the try block. To do this, you can add a finally clause to the end of the exception handling.
Finally is important for languages that do not have mechanisms for garbage collection and automatic destructor calls. It allows the programmer to ensure that memory is always freed under any circumstances. However, with Java garbage collection, memory release is no longer an issue. The finally clause is used when restoring resources other than memory to their initial state. Common resources include network links, file handles, display locks, and so on.
private String tryException(){try {// Monitor areareturnthrowException(); }catch (RuntimeException e){// Handle RuntimeException} Catch (Exception e){// Handle Exception}finally {// pair Network links, file handles, locks and other resources for processing}return "";
}
Copy the code
1.2 Method Exception Description
Java encourages clients using a method to be informed of exceptions that the method may throw. This allows the caller to know that all exceptions are available in the code.
Exception Description The method declaration uses the additional keyword throws, followed by a list of all potential exception types, so the method signature becomes:
Private List<String>readFromFile(String filePath) throws IOException {
Path path = Paths.get(filePath);
return Files.readAllLines(path, Charset.defaultCharset());
}
Copy the code
The code must be kept consistent with the exception specification. If the code inside the method raises an exception that is not handled, the compiler will either handle the exception or add the exception type to the exception specification list.
Of course, you can declare exceptions in the method signature without actually throwing them. This saves a place for the exception that can be thrown later without changing the calling code.
Checked exceptions. Exceptions that are checked at compile time are called checked exceptions.
Note: You can obtain the list of checked exceptions through the reflection mechanism.
1.3 Restrictions on exceptions
When overriding a method, only exceptions listed in the exception list of the base class method can be thrown, which means that the program works just as well when the code used by the base class is applied to objects of its derived class.
1.3.1. Method rewrite
The exception specification is not part of the method type, which consists of the method name and parameter types, although it is enforced by the compiler during inheritance. Therefore, methods cannot be overloaded based on exception specifications.
The list of exceptions in a subclass method when a method is overridden is much looser.
- The subclass method exception list is exactly the same as the parent class
- The subclass method exception list is a subset of the superclass method exception list
- The subclass method does not throw an exception
- A subclass method throws a child exception of a parent method exception
The specific code is as follows:
Public interface FileReader {List<String>readFromFile(String filePath) throws IOException; } class FileReader1 implements FileReader @override public List<String>readFromFile(String filePath) throws IOException {
returnnull; }} class FileReader2 implements FileReader @override public List<String>readFromFile(String filePath) throws FileNotFoundException {
returnnull; }} Class FileReader3 implements FileReader @override public List<String>readFromFile(String filePath){
returnnull; }}Copy the code
1.3.2. Method overloading
Java method overloading involves only method names and argument lists. Method return values and exception lists are used as a basis for method overloading.
public List<String> readFromFile(String path) throws IOException{
returnnull; } public List<String>readFromFile(String path) throws FileNotFoundException{
returnnull; } * /Copy the code
1.3.3. Constructor
Exception restrictions do not apply to constructors, and subclass constructors can throw arbitrary exceptions regardless of the exceptions thrown by the base class constructor. However, because the base-class constructor must be called in some form, the exception specification of the derived constructor must contain the exception specification of the base-class constructor.
The constructor sets the object to a safe initialization state, and if there is other work to be done, such as opening a file, this action can only be cleaned up after the object is used and the user invokes a cleanup method. These cleanup actions do not work properly if an exception is thrown in a constructor, so be careful when writing constructors.
class Parent{ Parent() throws IOException{ } } class Child extends Parent{ Child() throws IOException { super(); /** Child() throws IOException {// super must be the first statement and cannot catch exceptions try {super(); }catch (Exception e){}} */}Copy the code
1.4 Abnormal check
Exceptions that are forced to be checked at compile time are called checked exceptions. That is, an exception declared in a method declaration.
Checked exceptions require that the method caller must handle the exception. In some ways, checked exceptions violate the original purpose of Java exception handling.
private List<String> readFromFile(String filePath) throws IOException {
Path path = Paths.get(filePath);
return Files.readAllLines(path, Charset.defaultCharset());
}
Copy the code
IOException handling cannot be ignored when the readFromFile method is called.
In general, when faced with checked exceptions, we usually deal with them as follows:
- Modify your own method signatures to add new exception declarations;
- Use try catch to wrap exception calls (most of the time, we don’t know how to recover);
- Turn checked exceptions into runtime exceptions;
private void printFile2(String filePath){
try {
List<String> lines = readFromFile(filePath); lines.forEach(System.out::println); }catch (IOException e){throw new RuntimeException(e) with an exception chain; }}Copy the code
1.4.1 Spring DAO Support
There are a lot of checked exceptions in the JDBC interface, and a lot of try catch boilerplate code occurs when operating on a database, burying the core logic in a sea of code.
To this end, Spring optimizes it, and the specific optimization measures are as follows:
- On the basis of run-time exception, a whole set of exception system (data access sexception and subclass) is established.
- Convert checked exceptions in JDBC to runtime exceptions;
- Use template methods to reduce redundant code.
The jdbcTempalte code snippet is as follows:
public <T> T execute(StatementCallback<T> action) throws DataAccessException {
Assert.notNull(action, "Callback object must not be null");
Connection con = DataSourceUtils.getConnection(obtainDataSource());
Statement stmt = null;
try {
stmt = con.createStatement();
applyStatementSettings(stmt);
T result = action.doInStatement(stmt);
handleWarnings(stmt);
return result;
}
catch (SQLException ex) {
// Release Connection early, to avoid potential connection pool deadlock
// in the case when the exception translator hasn't been initialized yet. String sql = getSql(action); JdbcUtils.closeStatement(stmt); stmt = null; DataSourceUtils.releaseConnection(con, getDataSource()); con = null; Throw translateException("StatementCallback", SQL, ex); } finally { JdbcUtils.closeStatement(stmt); DataSourceUtils.releaseConnection(con, getDataSource()); }}Copy the code
2. Customize exceptions
You are not limited to the exception types provided by Java. We can customize exception classes to represent specific problems that may be encountered in our programs.
To customize an exception class, you must inherit from an existing exception class. The best way to do this is to select an exception class that has a similar meaning.
// Service exception Class BizException extends RuntimeException{publicBizException() { super(); } public BizException(String message) { super(message); } public BizException(String message, Throwable cause) { super(message, cause); } public BizException(Throwable cause) { super(cause); }}Copy the code
Exceptions are usually named for problems that occur, and the name of the exception should be readable.
2.1. Abnormal inheritance system
Exceptions themselves are classes, and there is a complete inheritance system.
2.2. Throwable
Throwable is used to represent any class that can be thrown as an exception.
Throwable objects can be divided into two types (inherited from Throwable) :
- Error is used to indicate compile-time and system errors, and developers are not concerned except in special cases
- Exception represents an underlying type that can be thrown. Exceptions can be thrown in Java class libraries, user methods, and runtime failures. This is the exception that matters most to developers.
Throwable is used to maintain the exception stack. The core methods are as follows:
methods | meaning |
---|---|
printStackTrace | Print call stack information to system.error |
printStackTrace(PrintStream) | Specifies that Stream prints call stack information |
printStackTrace(PrintWriter) | Specify Print to Print call stack information |
getStackTrace() | Gets call stack sequence information |
fillInStackTrace() | Records the current state of the stack frame |
The exception stack records the sequence of method calls that “get you to the point where exceptions are thrown” and is one of the main pieces of information for troubleshooting.
public static void main(String... Arg){try {// Normal processing process, correct execution of the process to do what Path = paths.get ("var"."error"); List<String> lines = Files.readAllLines(path, Charset.defaultCharset()); System.out.println(lines); } catch (IOException e) {// How to handle the exception. }}Copy the code
Run the program and get the result, the exception stack is as follows:
java.nio.file.NoSuchFileException: var/error
at sun.nio.fs.UnixException.translateToIOException(UnixException.java:86)
at sun.nio.fs.UnixException.rethrowAsIOException(UnixException.java:102)
at sun.nio.fs.UnixException.rethrowAsIOException(UnixException.java:107)
at sun.nio.fs.UnixFileSystemProvider.newByteChannel(UnixFileSystemProvider.java:214)
at java.nio.file.Files.newByteChannel(Files.java:361)
at java.nio.file.Files.newByteChannel(Files.java:407)
at java.nio.file.spi.FileSystemProvider.newInputStream(FileSystemProvider.java:384)
at java.nio.file.Files.newInputStream(Files.java:152)
at java.nio.file.Files.newBufferedReader(Files.java:2781)
at java.nio.file.Files.readAllLines(Files.java:3199)
at com.geekhalo.exception.Demo.main(Demo.java:15)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:483)
at com.intellij.rt.execution.application.AppMain.main(AppMain.java:147)
Copy the code
2.3. The Exception
Exception is the base class for all Exception classes related to programming.
methods | meaning |
---|---|
getMessage | Get details |
getLocaliedMessage | Gets details about the local language representation |
2.4. RuntimeException
Exceptions derived from RuntimeException become “unchecked exceptions”. This exception is automatically thrown by the Java virtual machine, so it does not have to be listed in the exception specification of the method.
private void throwRuntimeException(){
throw new RuntimeException();
}
Copy the code
RuntimeException and its subclasses need not be declared in methods.
3. Common exception handling policies
Once you’ve finished customizing the exception, the next key is how to handle the exception.
3.1. Abnormal recovery
The purpose of an exception handler is to handle the exception that occurs. Therefore, the first exception handling strategy is to handle the exception and do exception recovery.
private void recoveryException(String filePath){
try {
List<String> lines = readFromFile(filePath); lines.forEach(System.out::println); }catch (IOException e){// Print a log to recover logger.error ("failed to read from file {}", filePath, e);
}
}
private List<String> readFromFile(String filePath) throws IOException {
Path path = Paths.get(filePath);
return Files.readAllLines(path, Charset.defaultCharset());
}
Copy the code
3.2. Rethrow the exception
When you don’t have enough information to recover from an exception. You can rethrow the exception you just caught. A reference to the current exception object has been obtained in the catch clause and can be thrown directly.
private void printFile(String filePath) throws IOException{
try {
List<String> lines = readFromFile(filePath); lines.forEach(System.out::println); }catch (IOException e){// throw e; }} private List<String>readFromFile(String filePath) throws IOException {
Path path = Paths.get(filePath);
return Files.readAllLines(path, Charset.defaultCharset());
}
Copy the code
A rethrow throws the exception to the next call, and any catch clause following the same try is ignored. If you just throw the current exception, the printStackTrace shows the call chain of the point where the exception was thrown, not the point where it was rethrown. If you want to update the call information, you can call the fillInStackTrace method, which returns another Throwable object that fills the current call stack information with the original exception object.
3.3. Exception chain
If you want to throw a new exception after catching one and want to preserve the original exception information, this is called exception concatenation.
Subclasses of Throwable can accept a cause object in the constructor that represents the original exception, passing the original exception to the new exception so that new exceptions created and thrown at the current location can be traced through the exception chain to where the exception originally occurred.
private void printFile2(String filePath){
try {
List<String> lines = readFromFile(filePath); lines.forEach(System.out::println); }catch (IOException e){// throw new BizException(e); }} private List<String>readFromFile(String filePath) throws IOException {
Path path = Paths.get(filePath);
return Files.readAllLines(path, Charset.defaultCharset());
}
Copy the code
In Throwable subclasses, only Error, Exception, and RuntimeException provide cause arguments in the constructor. If you want to chain other exceptions, you can use the initCause method.
4. [k? : n
Exceptions are an integral part of framework design.
Exception handling in the framework also follows a fixed operation flow:
- Customize exceptions according to requirements;
- Provide exception processor, unified processing of exceptions;
4.1. Spring MVC unified exception handling
Spring MVC is the most common Web framework, easy to get started and quick to develop.
Follow the separation policy of normal process and exception handling. The developer only needs to care about the normal logic, and the framework handles the abnormal flow uniformly. So how do you do that?
4.1.1. Define service exceptions
First, you need to define your own business exceptions.
Public abstract class BusinessException extends RuntimeException{/** * private final int code; /** * private final String MSG; private final String timestamp = String.valueOf(System.currentTimeMillis()); protected BusinessException(int code, String msg){ this.code = code; this.msg = msg; } protected BusinessException(int code, String msg, Exception e) { super(e); this.code = code; this.msg = msg; }}Copy the code
4.1.2. Exception handling
Exceptions can be customized using the HandlerExceptionResolver extension.
RestHandlerExceptionResolver service exceptions to Rest request for processing. The exception is uniformly converted to JSON and returned to the user.
@Component public class RestHandlerExceptionResolver implements HandlerExceptionResolver { private static final Logger LOGGER = LoggerFactory.getLogger(RestHandlerExceptionResolver.class); @Override public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object Handler, Exception ex) {// Is a Rest request and can be handledif(isRestRequest(handler) && isAcceptException(ex)){// Pass the exception to the RestResVo object RestResVo<Void> RestResVo = RestResVo.error((BusinessException)ex); Try {// write response.getwriter ().println(json.tojsonString (restResVo)); }catch (Exception e){ LOGGER.error("failed to write json {}", restResVo, e); } // empty ModelAndView indicates that it has been processedreturn new ModelAndView();
}
return null;
}
private boolean isRestRequest(Object handler) {
if (handler instanceof HandlerMethod){
HandlerMethod handlerMethod = (HandlerMethod) handler;
returnAnnotationUtils.findAnnotation(handlerMethod.getMethod(), ResponseBody.class) ! =null || AnnotationUtils.findAnnotation(handlerMethod.getBeanType(), ResponseBody.class) ! = null; }return false;
}
private boolean isAcceptException(Exception ex) {
returnex instanceof BusinessException; }}Copy the code
PageHandlerExceptionResolver exception to page request for processing. Uniformly forward exceptions to the Error view.
@Component public class PageHandlerExceptionResolver implements HandlerExceptionResolver { @Override public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {// is a page request and can handle the current Exceptionif(isPageRequest(handler) &&isAcceptException (ex)){// Return error view ModelAndView mv = new ModelAndView("error");
mv.addObject("error", ex);
return mv;
}
return null;
}
private boolean isPageRequest(Object handler) {
if (handler instanceof HandlerMethod){
HandlerMethod handlerMethod = (HandlerMethod) handler;
return AnnotationUtils.findAnnotation(handlerMethod.getMethod(), ResponseBody.class) == null
&& AnnotationUtils.findAnnotation(handlerMethod.getBeanType(), ResponseBody.class) == null;
}
return true;
}
private boolean isAcceptException(Exception ex) {
returnex instanceof BusinessException; }}Copy the code
4.2. Spring Cloud abnormal penetration
When using Spring Cloud for microservices, if an exception occurs on the Server side, the client receives a 5XX error that interrupts the current normal request logic. However, the business information contained in the exception is also lost. How to maintain the exception information to the maximum extent?
4.2.1. Define service exceptions
First, you still define your own business exception class.
@Data
public class CodeBasedException extends RuntimeException {
private Integer code;
private String msg;
private Object data;
public CodeBasedException(){ super(); } public CodeBasedException(String msg) { super(msg); this.msg = msg; } public CodeBasedException(Integer code, String msg, Object data) { this.code = code; this.msg = msg; this.data = data; } public CodeBasedException(String message, Integer code, String msg, Object data) { super(message); this.code = code; this.msg = msg; this.data = data; }}Copy the code
4.2.2. Server-side processing
On the Server side, the business exception is caught and the information is written back through the Header.
HandlerInterceptorBasedExceptionBinder after completion of the business process, capture CodeBasedException abnormalities, and exception information through the Response object back to the Header.
public class HandlerInterceptorBasedExceptionBinder implements HandlerInterceptor { private static final Logger LOGGER = LoggerFactory.getLogger(HandlerInterceptorBasedExceptionBinder.class); @Override public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {if (ex == null){
return;
}
if (ex instanceof CodeBasedException){
CodeBasedException codeBasedException = (CodeBasedException) ex;
response.addHeader(SoaConstants.HEADER_ERROR_CODE, String.valueOf(codeBasedException.getCode()));
response.addHeader(SoaConstants.HEADER_ERROR_MSG, encode(codeBasedException.getMsg()));
response.addHeader(SoaConstants.HEADER_ERROR_EXCEPTION_MSG, encode(codeBasedException.getMessage()));
return;
}
response.setHeader(SoaConstants.HEADER_ERROR_CODE, "500");
response.setHeader(SoaConstants.HEADER_ERROR_MSG, encode(ex.getMessage()));
response.setHeader(SoaConstants.HEADER_ERROR_EXCEPTION_MSG, encode(String.valueOf(ex.getStackTrace())));
LOGGER.error("failed to handle request.", ex); }}Copy the code
If it is Spring Boot project, we need to finish HandlerInterceptorBasedExceptionBinder registration.
@Configuration
public class SoaWebMvcConfigurer implements WebMvcConfigurer{
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new HandlerInterceptorBasedExceptionBinder()).addPathPatterns("/ * *"); }}Copy the code
4.2.3. Client processing
After the client gets the result of the request, it extracts the exception information from the Header, reassembles it and throws the exception.
FeignErrorDecoderBasedExceptionConverter exception information extracted from the Header, and reassembled and throw SoaRemoteCallException.
public class FeignErrorDecoderBasedExceptionConverter implements ErrorDecoder {
private static final Logger LOGGER = LoggerFactory.getLogger(FeignErrorDecoderBasedExceptionConverter.class);
public FeignErrorDecoderBasedExceptionConverter() {
}
@Override
public Exception decode(String methodKey, Response response) {
Map<String, Collection<String>> headers = response.headers();
report(methodKey, response);
return checkException(headers);
}
private void report(String methodKey, Response response) {
String message = format("status %s reading %s", response.status(), methodKey);
try {
if(response.body() ! = null) { String body = Util.toString(response.body().asReader()); message +="; content:\n" + body;
}
} catch (IOException ignored) { // NOPMD
}
LOGGER.error("status {}, message {}", response.status(), message);
}
private Exception checkException(Map<String, Collection<String>> headers) {
String code = getValue(headers, SoaConstants.HEADER_ERROR_CODE);
String msg = HeaderValueUtils.decode(getValue(headers, SoaConstants.HEADER_ERROR_MSG));
String exceptionMsg = HeaderValueUtils.decode(getValue(headers, SoaConstants.HEADER_ERROR_EXCEPTION_MSG));
Integer errorCode = NumberUtils.isNumber(code) ? Integer.valueOf(code) : -1;
return new SoaRemoteCallException(exceptionMsg, errorCode, msg, "");
}
private String getValue(Map<String, Collection<String>> headers, String key) {
Collection<String> values = headers.get(key);
if(values ! = null && values.size() == 1){return values.iterator().next();
}
LOGGER.debug("failed to find value of {} in header {}", key, headers);
returnnull; }}Copy the code
Finally, you need to complete FeignErrorDecoderBasedExceptionConverter registration.
@Bean
public FeignErrorDecoderBasedExceptionConverter exceptionCheckFeignDecoder() {return new FeignErrorDecoderBasedExceptionConverter();
}
Copy the code
5. Section
- The nature of Java exceptions is to separate normal handling logic from exception handling logic;
- In terms of exception usage, there are two steps required:
- Custom exception
- Custom exception handlers
- The Java exception mechanism does a great job of separating tasks, whether it’s routine development or framework extension.