1. Background
The article I wrote here is because a friend asked what the difference between checked exception and unchecked exception in java. My answer to it was: I only use RuntimeException when programming. Actually, I have a premise when I say something. I should say it exactly: I only use or pay attention to RuntimeException when writing business code under a mature development framework. Because, because frameworks often encapsulate exception handling uniformly, so that programmers can pay better attention to business code, and some business errors usually occur during the system's run, business exceptions are usually designed as subclasses of RuntimeException.
My answer obviously cannot satisfy my friends! Because, no matter who is a beginner in Java, we use a lot of try...catch... when we learn IO classes and JDBC programming, this kind of repeated try...catch will make us remember the exceptions of java! Beginners often don't know why Java exceptions are designed like this. They usually only process exceptions simply - simply print out exceptions in the catch block, and the most commonly used statement is:
e.printStackTrace().
We also have exceptions such as memory such as array crossing bounds:
java.lang.ArrayIndexOutOfBoundsException: 6
This will also make us remember it fresh, because it often appears when we debug the program! We will find that this type of exception does not need to use try...catch... to catch it in the code.
The above two examples are actually the checked exception and unchecked exception asked by a friend. The exception that requires try...catch... is the checked exception, and the unchecked exception is the unchecked exception. If I want to talk about their differences, I say that one of them wants to try...catch... and the other does not need it. Is this answer OK? I think this answer is pale. Some students will further say that try...catch is obviously a method caller that forces the exception to be thrown explicitly handled. Is e.printStackTrace() considered processing exceptions? I think that is just a simple and lazy way to deal with it! So what kind of handling method is considered clever? The Java language designer actually expects that after an exception occurs, the caller can restore the exception in the catch so that the program can continue to execute. However, "smart programmers are lazy." Hehe. In most cases, we choose to record logs and UI user prompts after the exception appears. Later, I will combine the jersey framework to talk about the unified exception handling. After reading this, some people will say that the difference between checked exception and unchecked exception is that one needs to be processed and the other does not need to be processed. Is this answer correct? I think it's wrong! My point is: we have to deal with it whether it is a checked exception or an unchecked exception!
In the previous paragraph, we still didn't seem to solve the difference between checked exception and unchecked exception. I don't think it's important to give the answer. What's important is how we handle these exceptions and how we use exceptions during development.
My point is (Web system development):
1. Encapsulate the checked exception at the framework level and convert it into an unchecked exception to avoid writing tedious try...catch code during development.
2. Business-level development, define different RuntimeExceptions according to program code responsibilities (it is an unchecked exception, generally defined as a subclass of RuntimeException)
3. Through the first two views, custom exceptions in the system will only have unchecked exceptions, and the system will only be at the upper layer of the client exchanging data, set up a unified exception handling mechanism, and convert some exceptions into information that users can understand and convey to users.
4. Others such as the business layer, data persistence layer, etc., are only responsible for throwing exceptions, but be careful not to lose the exception stack (this is a mistake that beginners are prone to making).
The background is long enough! Let's get to the point and see how the unified exception handler of the Jersey framework is used!
2. Unified exception handling mechanism of jersey framework
There are the following agreements:
1. The example uses jersey1.x version
2. Spring version is 2.5
3. For simplicity, the sample project does not use the Maven mechanism
Example business scenario description:
1. We read a properties configuration file, and the content of the configuration file is:
key1=hello key2=iteye.com
2. Start a GET request http://localhost:8888/a/resources/test?n=11, requiring n to be a number and must be less than 10. If n is wrong, an unchecked exception error will be generated.
3. In this example, the data access layer will read a file, and a checked exception error will occur if the file is read.
Sample project structure design
Code snippet description
1. Data storage file: test.properties
key1=hello key2=iteye.com
This is the file we want to read, and for simplicity it is a properties file.
2. Data access class: TestDao.java
package com.iteye.redhacker.jersey.dao;import java.io.IOException;import java.io.InputStream;import java.net.URL;import java.util.Properties;import org.springframework.stereotype.Component;import com.iteye.redhacker.jersey.exception.DaoException;import com.iteye.redhacker.jersey.exception.ExceptionCode;@Componentpublic class TestDao {public String saysHello() {ClassLoader classLoader = TestDao.class.getClassLoader();String iniFile = "com/iteye/redhacker/jersey/dao/test.properties";URL url = classLoader.getResource(iniFile);InputStream is;try {is = url.openStream();} catch (IOException e) {throw new DaoException(e, ExceptionCode.READ_FILE_FAILED);}Properties proper = null;try {if (proper == null) {proper = new Properties();}proper.load(url.openStream());} catch (IOException e) {throw new DaoException(e, ExceptionCode.READ_CONFIG_FAILED);} finally {if (is != null) {try {is.close();is = null;} catch (IOException e) {throw new DaoException(e, ExceptionCode.COLSE_FILE_FAILED);}}} return proper.getProperty("key1") + "," + proper.getProperty("key2");}}In this class, all checked exceptions are converted into unchecked exceptions (our custom exception). When calling the sayHello() method, try...catch...
3. Business implementation class: TestService.java
package com.iteye.redhacker.jersey.service;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.stereotype.Component;import com.iteye.redhacker.jersey.dao.TestDao;import com.iteye.redhacker.jersey.exception.ExceptionCode;import com.iteye.redhacker.jersey.exception.ServiceException;@Componentpublic class TestService {@Autowiredprivate TestDao testDao;public String saysHello(int n) {// Business stipulates that n cannot be greater than 10 if (n > 10) {throw new ServiceException(ExceptionCode.MUST_BE_LESS_THAN_10);}return testDao.sayHello();}/** * @param testDao the testDao to set */public void setTestDao(TestDao testDao) {this.testDao = testDao;}}In this class, we throw a business exception of our own, which is an unchecked exception.
Note: We injected the TestDao class using @Autowired, which is an annotation provided by Spring; we must provide a Set method to annotate the attributes, otherwise the annotation will fail.
4. Request access class: TestResources.java
package com.iteye.redhacker.jersey.delegate;import javax.ws.rs.GET;import javax.ws.rs.Path;import javax.ws.rs.Produces;import javax.ws.rs.QueryParam;import javax.ws.rs.core.MediaType;import com.iteye.redhacker.jersey.service.TestService;import com.sun.jersey.api.spring.Autowire;@Path("/test")@Autowirepublic class TestResources {private TestService testService;@GET@Produces(MediaType.TEXT_PLAIN)public String saysHello(@QueryParam("n") int n) {return testService.sayHello(n);}/** * @param testService the testService to set */public void setTestService(TestService testService) {this.testService = testService;}}Here is a resource defined by jersey. We can access this resource in this way: initiate a GET request, access the URI is /resources/test, and pass a query parameter n, for example: /resources/test?n=1
Note: We used @Autowire is not an annotation for Spring, it is an annotation for the jersey-srping integration package; we must provide a Set method to annotate the attributes, otherwise the annotation will fail.
5. Unified exception handler class: ExceptionMapperSupport.java
package com.iteye.redhacker.jersey.jaxrs;import javax.servlet.ServletContext;import javax.servlet.http.HttpServletRequest;import javax.ws.rs.core.Context;import javax.ws.rs.core.MediaType;import javax.ws.rs.core.Response;import javax.ws.rs.core.Response.Status;import javax.ws.rs.ext.ExceptionMapper;import javax.ws.rs.ext.Provider;import org.apache.log4j.Logger;import org.springframework.web.context.WebApplicationContext;import com.iteye.redhacker.jersey.exception.BaseException;import com.iteye.redhacker.jersey.exception.ExceptionCode;import com.sun.jersey.api.NotFoundException;/** * Unified Exception Handler*/@Providerpublic class ExceptionMapperSupport implements ExceptionMapper<Exception> {private static final Logger LOGGER = Logger.getLogger(ExceptionMapperSupport.class);private static final String CONTEXT_ATTRIBUTE = WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE;@Contextprivate HttpServletRequest request;@Contextprivate ServletContext servletContext;/** * Exception handling* * @param exception * @return Response object after exception handling*/public Response toResponse(Exception exception) {String message = ExceptionCode.INTERNAL_SERVER_ERROR;Status statusCode = Status.INTERNAL_SERVER_ERROR;WebApplicationContext context = (WebApplicationContext) servletContext.getAttribute(CONTEXT_ATTRIBUTE);// Handle unchecked exceptionif (exception instanceof BaseException) {BaseException baseException = (BaseException) exception;String code = baseException.getCode();Object[] args = baseException.getValues();message = context.getMessage(code, args, exception.getMessage(), request.getLocale());} else if (exception instanceof NotFoundException) {message = ExceptionCode.REQUEST_NOT_FOUND;statusCode = Status.NOT_FOUND;} // checked exception and unchecked Exceptions are recorded in the log LOGGER.error(message, exception); return Response.ok(message, MediaType.TEXT_PLAIN).status(statusCode).build();}}In this class, we handle the unchecked exception we defined, and also handle unknown exceptions of the system (including unknown unchecked exceptions and checked exceptions). Our processing method is: a. Record the exception log; b. Send a standard http standard error status code and error message to the client, and the client handles the error information by itself. It is worth noting that this processing method is advocated by REST, and it appropriately uses the HTTP standard status code;
In this class, we also use spring's international configuration component to internationalize the error key thrown by the system, which is conducive to the internationalization upgrade of our project.
6. Custom exception base class: BaseException.java
package com.iteye.redhacker.jersey.exception;/** * Exception base class, the runtime exceptions of each module are inherited from this class*/public class BaseException extends RuntimeException { /** * the serialVersionUID */ private static final long serialVersionUID = 1381325479896057076L; /** * message key */ private String code; /** * message params */ private Object[] values; /** * @return the code */ public String getCode() { return code; } /** * @param code the code to set */ public void setCode(String code) { this.code = code; } /** * @return the values */ public Object[] getValues() { return values; } /** * @param values the values to set */ public void setValues(Object[] values) { this.values = values; } public BaseException(String message, Throwable cause, String code, Object[] values) { super(message, cause); this.code = code; this.values = values; }}This class defines the basic template of the project exception class, and other exceptions inherit from it. It is worth noting that it cleverly utilizes some features of international configuration and can even throw an error message defined below, and reuse the error message by passing parameters:
{0}{1} parameter error
7. Other exceptions are basically the same, but the types are different. Let's take a look at DaoException.java
package com.iteye.redhacker.jersey.exception;public class DaoException extends BaseException {/** * Constructors * * @param code * Error code */public DaoException(String code) {super(code, null, code, null);}/** * Constructors * * @param cause * Exception interface* @param code * Error code */public DaoException(Throwable cause, String code) {super(code, cause, code, null);}/** * Constructors * * @param code * Error code * @param values * A set of exception information pending parameters */public DaoException(String code, Object[] values) {super(code, null, code, values);}/** * Constructors * * @param cause * Exception interface* @param code * Error code * @param values * A set of exception information pending parameters */public DaoException(Throwable cause, String code, Object[] values) {super(code, null, code, values);}private static final long serialVersionUID = -3711290613973933714L;}It inherits BaseException. When this exception is thrown, we directly make preliminary judgments from the exception name, and the error comes from the Dao layer.
8. errMsg.properties is used to define exception information. Let’s take a look:
read.file.failed=Read file failed read.config.failed=Read configuration item failed must.be.less.than.10=The parameter must be less than 10colse.file.failed=Reset file failed request.not.found=The corresponding service was not found internal.server.error=Server internal error
III. Deployment and testing
You can download the source code in the attachment of this article. After importing eclipse, check the source code.
The deployment is very simple, just add your tomcat/config/server.xml:
<Host>...<Context path="/a" reloadable="true" docBase="D:/workspace/test/JerseyExceptionMapperTest/web" /></Host>
Just start tomcat!
Do two tests:
1.
2.
For the first test, you can also see the following exception error in the log:
[2013-08-15 00:25:55] [ERROR] Parameter must be less than 10com.iteye.redhacker.jersey.exception.ServiceException: must.be.less.than.10at com.iteye.redhacker.jersey.service.TestService.sayHello(TestService.java:20) at com.iteye.redhacker.jersey.delegate.TestResources.sayHello(TestResources.java:21) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25) at java.lang.reflect.Method.invoke(Method.java:597) at com.sun.jersey.spi.container.JavaMethodInvokerFactory$1.invoke(JavaMethodInvokerFactory.java:60) at com.sun.jersey.spi.container.JavaMethodInvokerFactory$1.invoke(JavaMethodInvokerFactory.java:60) at com.sun.jersey.server.impl.model.method.dispatch.AbstractResourceMethodDispatchProvider$TypeOutInvoker._dispatch(AbstractResourceMethodDispatchProvider.java:185)at com.sun.jersey.server.impl.model.method.dispatch.ResourceJavaMethodDispatcher.dispatch(ResourceJavaMethodDispatcher.java:75)at com.sun.jersey.server.impl.uri.rules.HttpMethodRule.accept(HttpMethodRule.java:288) at com.sun.jersey.server.impl.uri.rules.ResourceClassRule.accept(ResourceClassRule.java:108) at com.sun.jersey.server.impl.uri.rules.RightHandPathRule.accept(RightHandPathRule.java:147) at com.sun.jersey.server.impl.uri.rules.RootResourceClassesRule.accept(RootResourceClassesRule.java:84) at com.sun.jersey.server.impl.application.WebApplicationImpl._handleRequest(WebApplicationImpl.java:1483) at com.sun.jersey.server.impl.application.WebApplicationImpl._handleRequest(WebApplicationImpl.java:1414) at com.sun.jersey.server.impl.application.WebApplicationImpl.handleRequest(WebApplicationImpl.java:1363) at com.sun.jersey.server.impl.application.WebApplicationImpl.handleRequest(WebApplicationImpl.java:1353)
For other tests, you can try it, such as deliberately deleting test.properties. When the file to be read cannot be found, how the checked exception is converted into a self-defined unchecked exception, and logs it, returning the standard http error status code and error information to the client.
4. Summary
1. It is not difficult to see through the jersey framework that in web project development, we have handled the checked exception and unchecked exception as uniformly as possible at the framework level so that we can pay more attention to the implementation of the business.
2. If it is a non-web project, I think the program architecture designer should also try to handle exceptions uniformly; if it is not uniformly handled, when a checked exception is encountered, we should handle it appropriately, instead of simply doing an e.printStackTrace(); if we cannot recover the exception, we should at least record the error information of the exception completely in the log file so that errors will be checked when subsequent programs fail.
Full text (end)