처음 봤을 땐 뭐가 잘못된 건지 감도 오지 않았던 오류이다.
오류의 내용부터 살펴보자
근본 원인 (root cause)
java.lang.NullPointerException
com.bigbell.noticeboard.dao.BoardDAOImpl.getBoards(BoardDAOImpl.java:23)
com.bigbell.noticeboard.service.BoardServiceImpl.getBoards(BoardServiceImpl.java:22)
sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
java.lang.reflect.Method.invoke(Method.java:498)
org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:344)
org.springframework.aop.framework.ReflectiveMethodInvocation.invokeJoinpoint(ReflectiveMethodInvocation.java:198)
org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:163)
org.springframework.transaction.interceptor.TransactionInterceptor$1.proceedWithInvocation(TransactionInterceptor.java:123)
org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:388)
org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:119)
org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)
org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:215)
com.sun.proxy.$Proxy19.getBoards(Unknown Source)
com.bigbell.noticeboard.controller.NoticeBoardController.listBoard(NoticeBoardController.java:24)
sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
java.lang.reflect.Method.invoke(Method.java:498)
org.springframework.web.method.support.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:197)
org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:141)
org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:106)
org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:895)
org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:808)
org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:87)
org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1064)
org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:963)
org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1006)
org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:898)
javax.servlet.http.HttpServlet.service(HttpServlet.java:655)
org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:883)
javax.servlet.http.HttpServlet.service(HttpServlet.java:764)
org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:53)
java.lang.NullPointerException 이게 주요 원인이다
웹개발이 아닌 그냥 자바에서는 문제해결이 단순하다.
위 오류의 원인은 객체를 정의한 뒤, 생성자를 통해 인스턴스를 생성하지 않고 객체를 사용하려고 했기 때문에 생기는 에러이다.
스프링과 하이버네이트를 사용하다가 이런 오류가 발생하면
인스턴스를 생성하지 않고 메소드를 사용한 적도 없는데 왜 이런 오류가 발생할까?라는 생각도 들 것이다.
문제는 의존성 주입과정에서 생긴다.
web.xml이나 servlet.xml 또는 클래스를 통해 인터페이스와의 의존관계를 사용해놓고
실제로 주입하지 않으면(어노테이션을 사용하지 않으면) 위와 같은 오류가 발생한다.
결국 따지고 보면 객체만 정의해놓고 인스턴스를 생성하지 않아 발생하는 문제가 맞다는 것이다.
사례를 통해 살펴보자.
- servlet.xml 파일 중 일부내용
...
<bean id="sessionFactory"
class="org.springframework.orm.hibernate5.LocalSessionFactoryBean">
<property name="dataSource" ref="myDataSource" />
<property name="packagesToScan" value="com.bigbell.springdemo.entity" />
<property name="hibernateProperties">
<props>
<prop key="hibernate.dialect">org.hibernate.dialect.MySQLDialect</prop>
<prop key="hibernate.show_sql">true</prop>
</props>
</property>
</bean>
...
- sessionFactory를 재사용하기 위해 servlet.xml에 sessionFactory를 따로 생성해놓은 상태이다.
- BoardDAOImpl 클래스 (오류발생)
@Repository
public class BoardDAOImpl implements BoardDAO {
// inject the sessionFactory
// Autowired나 Modifier를 적지 않은 경우
private SessionFactory sessionFactory;
@Override
public List<Board> getBoards() {
// 오류 발생
Session currentSession = sessionFactory.getCurrentSession();
...
}
...
}
- @Autowired 또는 @Modifier를 통해 인스턴스를 생성해야하지만 하지 않은 상태
- SessionFactory의 객체만 정의된 상태에서 사용하려고 했기때문에 NullPointerException 발생
문제 해결
@Repository
public class BoardDAOImpl implements BoardDAO {
// inject the sessionFactory
@Autowired
private SessionFactory sessionFactory;
@Override
public List<Board> getBoards() {
// 오류 발생
Session currentSession = sessionFactory.getCurrentSession();
...
}
...
}
sessionFactory 위에 @Autowired만 적어주면 된다.
****
sessionFactory 말고도 다른 사례들도 마찬가지이다.
의존성 관계를 만들기 위해 클래스를 다 구성해놓고 어노테이션을 사용하지 않으면 오류가 발생한다.
java.lang.NullPointerException 오류가 발생한다면 의존관계를 확인해보자.