2014-09-06 5 views
3

Я разрабатываю небольшое веб-приложение в Spring MVC. Я получаю исключение каждый раз, когда я пытаюсь получить свой пользовательский класс в любом контроллере, если этот класс использует другой пользовательский класс. Это проще показать на примере:Spring: java.lang.NoClassDefFoundError: Не удалось инициализировать класс

контроллер, где я пытаюсь получить объект пользовательского класса WireTest:

@Controller 
@RequestMapping("/offices") 
public class OfficesController { 

    @Autowired 
    private WireTest wt; 

    @RequestMapping("") 
    public String offices(Model model) { 
    model.addAttribute("test", wt.getString()); 
    return "offices"; 
    } 
} 

Проблема происходит всегда, правильно ли я создать объект непосредственно или использование @Autowired. В коде здесь я показываю случай @Autowired, но это не имеет значения - я мог бы написать private WireTest wt = new WireTest(), и исключение было бы одинаковым.

WireTest.java класс:

@Service 
public class WireTest { 
    public String getString() {return (DBhelper.getString());} 
} 

Часть DBhelper.java класса (Есть и другие статические члены, полный код ниже):

public class DBhelper { 
    public static String getString() {return "Hi!";} 
} 

И исключение:

HTTP Status 500 - Handler processing failed; nested exception is   
java.lang.NoClassDefFoundError: Could not initialize class org.sher.wtpractice.dao.DBhelper 

Я также могу использовать эти классы без каких-либо проблем в консольном приложении lication, поэтому он работает за пределами Spring.

Моя конфигурация Spring:

web.xml:

<?xml version="1.0" encoding="UTF-8"?> 
<web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee" 
     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
     xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"> 

    <!-- Processes application requests --> 
    <servlet> 
     <servlet-name>dispatcher</servlet-name> 
     <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> 
     <load-on-startup>1</load-on-startup> 
    </servlet> 

    <servlet-mapping> 
     <servlet-name>dispatcher</servlet-name> 
     <url-pattern>/</url-pattern> 
    </servlet-mapping> 

    <context-param> 
     <param-name>contextConfigLocation</param-name> 
     <param-value>/WEB-INF/dispatcher-servlet.xml</param-value> 
    </context-param> 

    <listener> 
     <listener-class> 
      org.springframework.web.context.ContextLoaderListener 
     </listener-class> 
    </listener> 
</web-app> 

dispatcher-servlet.xml:

<?xml version="1.0" encoding="UTF-8" ?> 
<beans xmlns="http://www.springframework.org/schema/beans" 
     xmlns:context="http://www.springframework.org/schema/context" 
     xmlns:mvc="http://www.springframework.org/schema/mvc" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
     xsi:schemaLocation=" 
    http://www.springframework.org/schema/beans 
    http://www.springframework.org/schema/beans/spring-beans.xsd 
    http://www.springframework.org/schema/context 
    http://www.springframework.org/schema/context/spring-context.xsd 
    http://www.springframework.org/schema/mvc 
    http://www.springframework.org/schema/mvc/spring-mvc.xsd"> 

<context:component-scan base-package="org.sher.wtpractice.spring, org.sher.wtpractice.dao" /> 
    <mvc:annotation-driven /> 
    <mvc:resources mapping="/css/**" location="/css/"/> 
    <mvc:resources mapping="/js/**" location="/js/"/> 
    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"> 
     <property name="prefix"> 
      <value>/WEB-INF/jsp/</value> 
     </property> 
     <property name="suffix"> 
      <value>.jsp</value> 
     </property> 
    </bean> 
</beans> 

Я использую Maven для создания и Tomcat 7 в качестве сервера, если это имеет значение. Spring версия 4.0.1.RELEASE

Обновление: Полный код DBhelper.java

import org.hibernate.Session; 
import org.hibernate.SessionFactory; 
import org.hibernate.Transaction; 
import org.hibernate.boot.registry.StandardServiceRegistryBuilder; 
import org.hibernate.cfg.Configuration; 
import org.hibernate.service.ServiceRegistry; 

import java.util.Calendar; 
import java.util.Date; 
public class DBhelper { 
    private static final SessionFactory sessionFactory = createSessionFactory(); 

    private static SessionFactory createSessionFactory() { 
     Configuration configuration = new Configuration(); 
     configuration.configure(); 
     ServiceRegistry serviceRegistry = new StandardServiceRegistryBuilder().applySettings(
       configuration.getProperties()).build(); 
     return configuration.buildSessionFactory(serviceRegistry); 
    } 

    public static Object queryWrapper(DBoperation op) { 
     Session session = sessionFactory.openSession(); 
     Transaction transaction = null; 
     Object result = null; 
     try { 
      transaction = session.beginTransaction(); 
      result = op.operation(session); 
      session.getTransaction().commit(); 
     } catch (Exception e) { 
      if (transaction != null) { 
       transaction.rollback(); 
      } 
//   throw e; 
     } finally { 
      if (session != null && session.isOpen()) 
       session.close(); 
     } 
     return result; 
    } 
    public static Date normalizeCal(Calendar cal) { 
     cal.set(Calendar.MILLISECOND, 0); 
     return cal.getTime(); 
    } 
    public static String getString() {return "Hi!";} 
} 

Любая помощь будет принята с благодарностью!

+0

Является ли ваш класс DBhelper частью любого файла jar? – zerocool

+0

№. Единственное отличие от этой точки зрения между классами DBhelper и WireTest заключается в том, что они расположены в разных пакетах - WireTest на один уровень глубже. – ars

+0

Можете ли вы разобрать файл войны и проверить, был ли этот класс упакован maven в рамках войны? – zerocool

ответ

4

Попробуйте выполнить ленивую загрузку фабрики сеансов, а не инициализировать ее статически, назначив поле final.

Например, создайте метод, например, следующий, который возвращает фабрику сеанса, при необходимости создав его. Всякий раз, когда вы хотите использовать сессионный завод, вызовите этот метод вместо ссылки непосредственно на поле:

private static SessionFactory getSessionFactory() { 
    if (sessionFactory == null) { 
     sessionFactory = createSessionFactory(); 
    } 
    return sessionFactory; 
} 

Лично мне не нравится статическая инициализация для чего нетривиального, так как вы из-под контроль, когда это произойдет.С помощью вышеуказанного метода вы управляете: фабрика сеансов создается в первый раз, когда вам нужно ее использовать.

Если вы хотите знать, в чем проблема, у меня есть два предложения. Во-первых, перезагрузите контейнер веб-приложения и убедитесь, что вы впервые получили сообщение об исключении. («Первый раз» очень важно здесь: Could not initialize class означает, что виртуальная машина уже опробованы и не удалось инициализировать класс.) Во-вторых, попробуйте оберточной содержимое метода createSessionFactory в try - блок catch, такие как:

try { 
    ... 
} catch (Throwable e) { 
    e.printStackTrace(); 
    throw new RuntimeException(e); 
} 

Однако я не могу гарантировать, что любой подход предложит вам много просветления.

EDIT: вместо того, чтобы просто спекулировать, я решил попробовать это и посмотреть, что произойдет. Поэтому я сбил Spring и Hibernate с небольшим веб-приложением и развернул его в Tomcat. При попытке получить его работу я столкнулся несколько проблемами при попытке чтения конфигурации Hibernate, вызванной мне сделать следующие ошибки:

  • Я не включил файл hibernate.cfg.xml в .war файла ,
  • Я не включил JAR-драйвер JDBC базы данных в .war-файл,
  • База данных, к которой я пытался подключиться, была недоступна.

В каждом случае мой первый подход работал. Первый запрос, сделанный после перезагрузки Tomcat, дал мне ExceptionInInitializerError с дальнейшими подробностями ниже. Второй и последующие запросы дали мне NoClassDefFoundError s, которые не очень рассказывали мне о проблеме.

Проблема заключается в том, что после того, как класс ExceptionInInitializerError был брошен для класса, черные списки JVM выполняют этот класс и в дальнейшем откажутся что-либо сделать с ним. Таким образом, вы получаете только один шанс увидеть, что не так в статическом коде инициализации. Если вы инициализировали Hibernate лениво, как я рекомендовал выше, вы получите подробное сообщение об исключении каждый раз. Рассмотрите эту еще одну причину, чтобы избежать статической инициализации.

+0

В последние часы я переписал весь Hibernate-код Spring-способом - с beans, autowiring и соединением с базой данных, объявленным в web.xml (честно говоря, я до сих пор не понимаю, почему Spring devs полагают, что я это сделаю). Это не помогло, но ошибка была другой: Tomcat просто не смог запустить приложение. Поэтому я просмотрел журналы и, наконец, понял, что Tomcat не смог найти драйвер JDBC. После некоторого googling я узнал, что Tomcat ищет драйвер JDBC в своей собственной папке lib, а не WAR, и проблема была решена. – ars

+0

Благодарим вас за рекомендацию. Я думаю, что если бы мой 'createSessionFactory' был написан в ленивом стиле, исключение было бы достаточно красноречивым, чтобы понять причину этого, поэтому проблема была бы решена мгновенно. Поэтому я отмечаю ваш ответ как принятый. – ars

0

Проверьте статическую инициализацию DBhelper.

"NoClassDefFoundError: Could not initialize class" error

В этом случае экземпляр sessionFactory как родниковая боба, и пусть весной контейнер собрать его DBhelper.

+0

Ну, я очень новичок в Весна, и не совсем понимаю, как это сделать. Должен ли я сделать отдельный класс со статическим конечным объектом SessionFactory, пометить его как bean (@Service, я полагаю) и передать его в DBhelper? Кроме того, я не понимаю, почему я должен изменить код, связанный с Hibernate из-за особенностей Spring. Есть ли более элегантный способ? Как обычно люди справляются с этим? – ars

+0

Ваша ссылка на вопрос с весной-спящим действительно полезна, я постараюсь сделать, как там. Но я до сих пор не понимаю суть проблемы. Почему Spring не может сделать то, что может сделать простое консольное приложение? – ars

+0

Поскольку спящий режим и такие вещи всегда нуждаются в конфигурации, а весна IoC извлекает конфигурацию из вашего бизнес-кода. Вы можете прочитать некоторые документы об этом. http://docs.spring.io/spring/docs/3.2.x/spring-framework-reference/html/beans.html – Loki

Смежные вопросы