Spring kann Bean über Konstruktor nicht erstellen

Raphalon

Aktives Mitglied
Über eine xhtml-Seite werden Werte aus einer CDI Bean in einer (zunächst nur lesbaren) Tabelle angezeigt. Die Werte kommen aus einer Datenbank. Sie sollen nur _einmalig_ in eine List ausgelesen werden. Der User kann dann vor jeder Zeile ein Häkchen setzen, wenn die Zeile editierbar werden soll. Nach den vorgenommenen Änderungen sollen die Werte wieder in die DB gespeichert werden.

1. Zunächst habe ich versucht, die List zunächst über den Konstruktor einmalig zu laden.
Code:
@Inject
private UserService userService;     // über Spring injected, greift via DAO auf DB zu
private List<User> userList;
private List<Rowcontent> entryList;  // wird dem Anwender angezeigt

public UserEditAllBean() {
	setEntryList();               // einmalige Initialisierung über Konstruktor
}

public void setEntryList() {
	entryList = new ArrayList<Rowcontent>();
	userList = userService.findAll();                // hol die User-Daten aus der DB
	Iterator<User> iterator = userList.iterator();
	while (iterator.hasNext()) {
		entryList.add(new Rowcontent((User)iterator.next()));    // erstelle eine Liste (Rowcontent enthält zusätzliche Eigenschaften zu "User")
	}
}
Wenn ich Tomcat starte, schlägt jedoch die Initialisierung fehl, weil der userService null ist (Exception siehe unten). Dieser wird über Spring injected und greift via einem DAO auf die DB zu.

2. Zweiter Versuch war, einen leeren Konstruktor zu verwenden und die Liste mit einem preRenderView-Event zu initialisieren:

Code:
@Inject
private UserService userService;
private List<User> userList;
private List<Rowcontent> entryList;

public UserEditAllBean() {}

public void setEntryList(ComponentSystemEvent event) {
	entryList = new ArrayList<Rowcontent>();
	userList = userService.findAll();
	Iterator<User> iterator = userList.iterator();
	while (iterator.hasNext()) {
		entryList.add(new Rowcontent((User)iterator.next()));
	}
}
Dann aber wird jedesmal, wenn der Anwender den Haken zum Editieren setzt, die Liste wegen des preRenderView-Events die userList neu initialisiert, was vorhergehende Änderungen überschreibt (der Haken schickt ein onclick="submit()").

Als Alternative habe ich es mit Ajax-Requests versucht. Aber auch hier wird jedes mal der preRenderView-Event ausgeführt, und damit die Liste userList neu erzeugt.

Was wäre eine mögliche Lösung? Soll ich mehr Code posten?

Grüße,

Raphalon
Code:
[01.02.2012 10:50:01] ERROR ContextLoader - Context initialization failed
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'userEditAll' defined in file [C:\Programme\xampp\xampp\tomcat\webapps\jatpresent\WEB-INF\classes\de\tvu\gui\page\UserEditAllBean.class]: Instantiation of bean failed; nested exception is org.springframework.beans.BeanInstantiationException: Could not instantiate bean class [de.tvu.gui.page.UserEditAllBean]: Constructor threw exception; nested exception is java.lang.NullPointerException
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateBean(AbstractAutowireCapableBeanFactory.java:955)
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:901)
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:485)
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:456)
	at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:291)
	at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:222)
	at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:288)
	at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:190)
	at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:563)
	at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:872)
	at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:423)
	at org.springframework.web.context.ContextLoader.createWebApplicationContext(ContextLoader.java:276)
	at org.springframework.web.context.ContextLoader.initWebApplicationContext(ContextLoader.java:197)
	at org.springframework.web.context.ContextLoaderListener.contextInitialized(ContextLoaderListener.java:47)
	at org.apache.catalina.core.StandardContext.listenerStart(StandardContext.java:4723)
	at org.apache.catalina.core.StandardContext$1.call(StandardContext.java:5226)
	at org.apache.catalina.core.StandardContext$1.call(StandardContext.java:5221)
	at java.util.concurrent.FutureTask$Sync.innerRun(FutureTask.java:303)
	at java.util.concurrent.FutureTask.run(FutureTask.java:138)
	at java.util.concurrent.ThreadPoolExecutor$Worker.runTask(ThreadPoolExecutor.java:886)
	at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:908)
	at java.lang.Thread.run(Thread.java:662)
Caused by: org.springframework.beans.BeanInstantiationException: Could not instantiate bean class [de.tvu.gui.page.UserEditAllBean]: Constructor threw exception; nested exception is java.lang.NullPointerException
	at org.springframework.beans.BeanUtils.instantiateClass(BeanUtils.java:141)
	at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:72)
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateBean(AbstractAutowireCapableBeanFactory.java:948)
	... 21 more
Caused by: java.lang.NullPointerException
	at de.tvu.gui.page.UserEditAllBean.setEntryList(UserEditAllBean.java:37)
	at de.tvu.gui.page.UserEditAllBean.<init>(UserEditAllBean.java:30)
	at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
	at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:39)
	at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:27)
	at java.lang.reflect.Constructor.newInstance(Constructor.java:513)
	at org.springframework.beans.BeanUtils.instantiateClass(BeanUtils.java:126)
	... 23 more
 
Was steht denn so im UserService Konstruktor, also der Code, der die NPE verursacht hat...
 
Es ist der leere Konstruktor. Hier die gesamte Klasse

Code:
package de.tvu.service.impl;

import java.util.List;

import javax.inject.Inject;
import javax.inject.Named;
import javax.inject.Singleton;
import org.springframework.transaction.annotation.Transactional;
import de.tvu.dao.UserDao;
import de.tvu.domain.User;
import de.tvu.service.UserService;

@Named("userService")
@Singleton
public class UserServiceImpl implements UserService {
	
	@Inject
	private UserDao userDao;

	public User createNew() {
		return userDao.createNew();
	}

	@Transactional
	public void save(User user) {
		userDao.persist(user);
	}

	@Transactional
	public void delete(User user) {
		userDao.delete(user);
	}

	@Transactional(readOnly = true)
	public List<User> findAll() {
		return userDao.findAll();
	}

	@Transactional(readOnly = true)
	public User findById(long id) {
		return userDao.findById(id);
	}

	@Transactional(readOnly = true)
	public User findByLogin(String loginname) {
		return userDao.findByLogin(loginname);
	}
	
	@Transactional
	public User merge(User user) {
		return userDao.merge(user);
	}
	
}
 
Naja, dass hier:

Java:
@Inject
private UserService userService;     // über Spring injected, greift via DAO auf DB zu
private List<User> userList;
private List<Rowcontent> entryList;  // wird dem Anwender angezeigt

public UserEditAllBean() {
	setEntryList();               // einmalige Initialisierung über Konstruktor
}

public void setEntryList() {
	entryList = new ArrayList<Rowcontent>();
	userList = userService.findAll();                // hol die User-Daten aus der DB
	Iterator<User> iterator = userList.iterator();
	while (iterator.hasNext()) {
		entryList.add(new Rowcontent((User)iterator.next()));    // erstelle eine Liste (Rowcontent enthält zusätzliche Eigenschaften zu "User")
	}
}

Eine NullPointerException fliegt, ist doch klar. Spring distanziert erst die Klasse --> Konstruktor Aufruf --> setEntryList --> NullPointerException, weil der UserService eben noch nicht injected ist. Was spricht dagegen, den UserService als Konstruktorparameter zu übergeben?
 
Ich kann nicht folgen. Wenn ich den UserService als Konstruktor übergebe, etwa so
Code:
private UserService userService;
private List<User> userList;
private List<Rowcontent> entryList;

public UserEditAllBean(UserService userService) {
	this.userService = userService;
	setEntryList();
}
schlägt die Instantiierung auch fehl, weil
Code:
02.02.2012 08:03:13] ERROR ContextLoader - Context initialization failed
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'userEditAll' defined in file [C:\Programme\xampp\xampp\tomcat\webapps\jatpresent\WEB-INF\classes\de\tvu\gui\page\UserEditAllBean.class]: Instantiation of bean failed; nested exception is org.springframework.beans.BeanInstantiationException: Could not instantiate bean class [de.tvu.gui.page.UserEditAllBean]: No default constructor found; nested exception is java.lang.NoSuchMethodException: de.tvu.gui.page.UserEditAllBean.<init>()
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateBean(AbstractAutowireCapableBeanFactory.java:955)
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:901)
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:485)
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:456)
	at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:291)
	at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:222)
	at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:288)
	at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:190)
	at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:563)
	at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:872)
	at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:423)
	at org.springframework.web.context.ContextLoader.createWebApplicationContext(ContextLoader.java:276)
	at org.springframework.web.context.ContextLoader.initWebApplicationContext(ContextLoader.java:197)
	at org.springframework.web.context.ContextLoaderListener.contextInitialized(ContextLoaderListener.java:47)
	at org.apache.catalina.core.StandardContext.listenerStart(StandardContext.java:4723)
	at org.apache.catalina.core.StandardContext$1.call(StandardContext.java:5226)
	at org.apache.catalina.core.StandardContext$1.call(StandardContext.java:5221)
	at java.util.concurrent.FutureTask$Sync.innerRun(FutureTask.java:303)
	at java.util.concurrent.FutureTask.run(FutureTask.java:138)
	at java.util.concurrent.ThreadPoolExecutor$Worker.runTask(ThreadPoolExecutor.java:886)
	at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:908)
	at java.lang.Thread.run(Thread.java:662)
Caused by: org.springframework.beans.BeanInstantiationException: Could not instantiate bean class [de.tvu.gui.page.UserEditAllBean]: No default constructor found; nested exception is java.lang.NoSuchMethodException: de.tvu.gui.page.UserEditAllBean.<init>()
	at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:69)
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateBean(AbstractAutowireCapableBeanFactory.java:948)
	... 21 more
Caused by: java.lang.NoSuchMethodException: de.tvu.gui.page.UserEditAllBean.<init>()
	at java.lang.Class.getConstructor0(Class.java:2706)
	at java.lang.Class.getDeclaredConstructor(Class.java:1985)
	at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:64)
	... 22 more
 
Erzeuge erstmal den UserService per Hand im UserEditAllBean anstatt ihn zu injecten.
Wenn das geht, weisst du, dass etwas beim Injecten schief lief.


Wie mvitz schon richtig angemerkt hat, ist der UserService null.

Gibts noch irgendwelche Konfigurations-dateien?
 
ok, Du/Ihr habt wohl das gemeint (Spring-Config-Datei):
Code:
<bean id="userService" class="de.tvu.service.impl.UserServiceImpl"/>
    
<bean id="userEditAll" class="de.tvu.gui.page.UserEditAllBean">
    <constructor-arg ref="userService"/>
</bean>
Damit läuft die DI über den Konstruktor. Bin noch nicht so firm mit Spring, daher brauchte ich etwas länger.

An dieser Stelle die Frage: nun ist die Bean UserEditAllBean im Spring application context als "userEditAll" bekannt. Zugleich ist die Bean aber eine CDI-Bean und im WebContainer als "userEditAll" bekannt. Wie kann ich das differenzieren / verstehen? Dann gäbe es ja zwei Instanzen derselben Bean!?

Änderung (weil sich Beitrag von DerFeivel und meiner zeitlich überschnitten haben):
@DerFeivel: beim injecten ging nichts schief. Spring instantiiert aber erst, bevor injected wird - konnte in obigem Fall aber nicht instantiieren, weil der userService noch nicht da war (HenneOderEi). Der wäre dann nach dem Ausführen des Konstruktors injected worden.

Die von Dir vorgeschlagene Lösung könnte auch funktionieren, allerdings müßte ich dann die ganze Kette an verwendeten Objekten manuell instantiieren (z.B. außer dem userService die DAO-Klasse und die Entität), ansonsten wirft nicht userEditAll die NPE, sondern der userService. Das soll Spring für mich machen.
 
Zuletzt bearbeitet:
Da erwischst Du mich jetzt kalt. Ich habe eine JSF-Anwendung basierend auf Hibernate und Spring gebaut, basierend auf einer Vorlage (damit ich mich diese recht massigen Themen erst mal einarbeiten kann). Mit Spring kann ich auch Hibernate konfigurieren. Geht das mit CDI auch oder braucht man für CDI gar keine Konfig? Falls doch, würde man für CDI auch eine separate Konfigurationsdatei erstellen?
 
Tut mir leid, habe bisher mit JSF und CDI quasi nichts gemacht, außer ganz minimale Beispiele, die dann allerdings ohne Spring.
 

Neue Themen


Zurück
Oben