Nói một cách đơn giản, theo dõi ngăn xếp là một danh sách các lệnh gọi phương thức mà ứng dụng nằm ở giữa khi một Ngoại lệ được đưa ra.
Ví dụ đơn giản
Với ví dụ được đưa ra trong câu hỏi, chúng ta có thể xác định chính xác nơi ngoại lệ được ném trong ứng dụng. Chúng ta hãy xem dấu vết ngăn xếp:
Exception in thread "main" java.lang.NullPointerException
at com.example.myproject.Book.getTitle(Book.java:16)
at com.example.myproject.Author.getBookTitles(Author.java:25)
at com.example.myproject.Bootstrap.main(Bootstrap.java:14)
Đây là một dấu vết ngăn xếp rất đơn giản. Nếu chúng ta bắt đầu ở đầu danh sách "tại ...", chúng ta có thể biết lỗi xảy ra ở đâu. Những gì chúng ta đang tìm kiếm là trên cùng phương pháp gọi đó là một phần của ứng dụng của chúng tôi. Trong trường hợp này, đó là:
at com.example.myproject.Book.getTitle(Book.java:16)
Để gỡ lỗi này, chúng ta có thể mở ra Book.java
và xem xét dòng 16
, đó là:
15 public String getTitle() {
16 System.out.println(title.toString());
17 return title;
18 }
Điều này sẽ chỉ ra rằng một cái gì đó (có thể title
) nằm null
trong đoạn mã trên.
Ví dụ với một chuỗi các trường hợp ngoại lệ
Đôi khi các ứng dụng sẽ bắt một Ngoại lệ và ném lại nó là nguyên nhân của Ngoại lệ khác. Điều này thường trông giống như:
34 public void getBookIds(int id) {
35 try {
36 book.getId(id); // this method it throws a NullPointerException on line 22
37 } catch (NullPointerException e) {
38 throw new IllegalStateException("A book has a null property", e)
39 }
40 }
Điều này có thể cung cấp cho bạn một dấu vết ngăn xếp trông giống như:
Exception in thread "main" java.lang.IllegalStateException: A book has a null property
at com.example.myproject.Author.getBookIds(Author.java:38)
at com.example.myproject.Bootstrap.main(Bootstrap.java:14)
Caused by: java.lang.NullPointerException
at com.example.myproject.Book.getId(Book.java:22)
at com.example.myproject.Author.getBookIds(Author.java:36)
... 1 more
Điều khác biệt ở cái này là "Nguyên nhân bởi". Đôi khi các trường hợp ngoại lệ sẽ có nhiều phần "Nguyên nhân bởi". Đối với những điều này, bạn thường muốn tìm "nguyên nhân gốc", đây sẽ là một trong những phần "Nguyên nhân" thấp nhất trong theo dõi ngăn xếp. Trong trường hợp của chúng tôi, đó là:
Caused by: java.lang.NullPointerException <-- root cause
at com.example.myproject.Book.getId(Book.java:22) <-- important line
Một lần nữa, với ngoại lệ này chúng tôi muốn nhìn vào dòng 22
của Book.java
để xem những gì có thể gây ra NullPointerException
ở đây.
Ví dụ đáng ngại hơn với mã thư viện
Thông thường dấu vết ngăn xếp phức tạp hơn nhiều so với hai ví dụ trên. Đây là một ví dụ (đây là một ví dụ dài, nhưng thể hiện một số mức độ ngoại lệ bị xiềng xích):
javax.servlet.ServletException: Something bad happened
at com.example.myproject.OpenSessionInViewFilter.doFilter(OpenSessionInViewFilter.java:60)
at org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1157)
at com.example.myproject.ExceptionHandlerFilter.doFilter(ExceptionHandlerFilter.java:28)
at org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1157)
at com.example.myproject.OutputBufferFilter.doFilter(OutputBufferFilter.java:33)
at org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1157)
at org.mortbay.jetty.servlet.ServletHandler.handle(ServletHandler.java:388)
at org.mortbay.jetty.security.SecurityHandler.handle(SecurityHandler.java:216)
at org.mortbay.jetty.servlet.SessionHandler.handle(SessionHandler.java:182)
at org.mortbay.jetty.handler.ContextHandler.handle(ContextHandler.java:765)
at org.mortbay.jetty.webapp.WebAppContext.handle(WebAppContext.java:418)
at org.mortbay.jetty.handler.HandlerWrapper.handle(HandlerWrapper.java:152)
at org.mortbay.jetty.Server.handle(Server.java:326)
at org.mortbay.jetty.HttpConnection.handleRequest(HttpConnection.java:542)
at org.mortbay.jetty.HttpConnection$RequestHandler.content(HttpConnection.java:943)
at org.mortbay.jetty.HttpParser.parseNext(HttpParser.java:756)
at org.mortbay.jetty.HttpParser.parseAvailable(HttpParser.java:218)
at org.mortbay.jetty.HttpConnection.handle(HttpConnection.java:404)
at org.mortbay.jetty.bio.SocketConnector$Connection.run(SocketConnector.java:228)
at org.mortbay.thread.QueuedThreadPool$PoolThread.run(QueuedThreadPool.java:582)
Caused by: com.example.myproject.MyProjectServletException
at com.example.myproject.MyServlet.doPost(MyServlet.java:169)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:727)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:820)
at org.mortbay.jetty.servlet.ServletHolder.handle(ServletHolder.java:511)
at org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1166)
at com.example.myproject.OpenSessionInViewFilter.doFilter(OpenSessionInViewFilter.java:30)
... 27 more
Caused by: org.hibernate.exception.ConstraintViolationException: could not insert: [com.example.myproject.MyEntity]
at org.hibernate.exception.SQLStateConverter.convert(SQLStateConverter.java:96)
at org.hibernate.exception.JDBCExceptionHelper.convert(JDBCExceptionHelper.java:66)
at org.hibernate.id.insert.AbstractSelectingDelegate.performInsert(AbstractSelectingDelegate.java:64)
at org.hibernate.persister.entity.AbstractEntityPersister.insert(AbstractEntityPersister.java:2329)
at org.hibernate.persister.entity.AbstractEntityPersister.insert(AbstractEntityPersister.java:2822)
at org.hibernate.action.EntityIdentityInsertAction.execute(EntityIdentityInsertAction.java:71)
at org.hibernate.engine.ActionQueue.execute(ActionQueue.java:268)
at org.hibernate.event.def.AbstractSaveEventListener.performSaveOrReplicate(AbstractSaveEventListener.java:321)
at org.hibernate.event.def.AbstractSaveEventListener.performSave(AbstractSaveEventListener.java:204)
at org.hibernate.event.def.AbstractSaveEventListener.saveWithGeneratedId(AbstractSaveEventListener.java:130)
at org.hibernate.event.def.DefaultSaveOrUpdateEventListener.saveWithGeneratedOrRequestedId(DefaultSaveOrUpdateEventListener.java:210)
at org.hibernate.event.def.DefaultSaveEventListener.saveWithGeneratedOrRequestedId(DefaultSaveEventListener.java:56)
at org.hibernate.event.def.DefaultSaveOrUpdateEventListener.entityIsTransient(DefaultSaveOrUpdateEventListener.java:195)
at org.hibernate.event.def.DefaultSaveEventListener.performSaveOrUpdate(DefaultSaveEventListener.java:50)
at org.hibernate.event.def.DefaultSaveOrUpdateEventListener.onSaveOrUpdate(DefaultSaveOrUpdateEventListener.java:93)
at org.hibernate.impl.SessionImpl.fireSave(SessionImpl.java:705)
at org.hibernate.impl.SessionImpl.save(SessionImpl.java:693)
at org.hibernate.impl.SessionImpl.save(SessionImpl.java:689)
at sun.reflect.GeneratedMethodAccessor5.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:597)
at org.hibernate.context.ThreadLocalSessionContext$TransactionProtectionWrapper.invoke(ThreadLocalSessionContext.java:344)
at $Proxy19.save(Unknown Source)
at com.example.myproject.MyEntityService.save(MyEntityService.java:59) <-- relevant call (see notes below)
at com.example.myproject.MyServlet.doPost(MyServlet.java:164)
... 32 more
Caused by: java.sql.SQLException: Violation of unique constraint MY_ENTITY_UK_1: duplicate value(s) for column(s) MY_COLUMN in statement [...]
at org.hsqldb.jdbc.Util.throwError(Unknown Source)
at org.hsqldb.jdbc.jdbcPreparedStatement.executeUpdate(Unknown Source)
at com.mchange.v2.c3p0.impl.NewProxyPreparedStatement.executeUpdate(NewProxyPreparedStatement.java:105)
at org.hibernate.id.insert.AbstractSelectingDelegate.performInsert(AbstractSelectingDelegate.java:57)
... 54 more
Trong ví dụ này, có nhiều hơn nữa. Điều chúng tôi chủ yếu quan tâm là tìm kiếm các phương thức từ mã của chúng tôi , đó sẽ là bất cứ thứ gì trong com.example.myproject
gói. Từ ví dụ thứ hai (ở trên), trước tiên chúng tôi muốn xem xét nguyên nhân gốc, đó là:
Caused by: java.sql.SQLException
Tuy nhiên, tất cả các cuộc gọi phương thức theo đó là mã thư viện. Vì vậy, chúng tôi sẽ chuyển đến "Nguyên nhân bởi" phía trên nó và tìm kiếm cuộc gọi phương thức đầu tiên có nguồn gốc từ mã của chúng tôi, đó là:
at com.example.myproject.MyEntityService.save(MyEntityService.java:59)
Giống như trong các ví dụ trước, chúng ta nên xem xét MyEntityService.java
trực tuyến 59
, bởi vì đó là lỗi bắt nguồn (điều này hơi rõ ràng là đã xảy ra lỗi, vì SQLException nêu lỗi, nhưng quy trình gỡ lỗi là những gì chúng ta theo sau).