Spring 2.5: Autowire für Beans außerhalb des Contextes mit AutowireCapableBeanFactory

DarthShader

Erfahrenes Mitglied
Hallo,

ich möchte gerne Beans in ein POJO von Spring injizieren lassen. Diese POJOs sind nicht im Application Context, sondern werden ganz normal via "new" instanziiert.

Die Klassen haben jedoch z.b. folgende Properties:

Java:
// Oder mit @Autowired
@Resource
private MyService myService;

Es gibt natürlich die Bean mit Namen "myService", welche von Spring erstellt wird.

Ich versuche nun mit folgendem Code, den "myService" zu injizieren:

Java:
public class Test implements ApplicationContextAware {
    private ApplicationContext		applicationContext;

    public Foo createFoo() {
        Foo foo = new Foo();

        applicationContext.getAutowireCapableBeanFactory().autowireBeanProperties( 
				foo, AutowireCapableBeanFactory.AUTOWIRE_AUTODETECT, true );

        return foo;
    }

    @Override
    public void setApplicationContext( ApplicationContext applicationContext ) throws BeansException {
        this.applicationContext = applicationContext;
    }

}

In dem Beispiel wird "Foo" zwar erstellt (durch den Aufruf von Test.createFoo()), aber das Autowiring funktioniert nicht. Das heißt, das "myService" property von "Foo" bleibt null, Spring macht also anscheinend gar nichts ("myService" gibt es aber, und kann auch via "@Resource" injiziert werden, wenn ich den POJO ebenfalls als Bean deklariere, also nur um mal ganz generelle Fehler auszuschließen).


Hat jemand eine Idee, woran das liegen könnte?


Vielen Dank für Eure Hilfe!
 
Hallo,

schau mal hier:
Java:
package de.tutorials;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.FileSystemXmlApplicationContext;

public class SpringInjectionExample {

	/**
	 * @param args
	 */
	public static void main(String[] args) {
		ApplicationContext context = new FileSystemXmlApplicationContext("conf/context.xml");
		
		
		Service service = new Service();
		System.out.println(service);
		context.getAutowireCapableBeanFactory().configureBean(service, service.getClass().getSimpleName());
		System.out.println(service);
	}

}

Config:
XML:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
		xmlns:p="http://www.springframework.org/schema/p" xmlns:aop="http://www.springframework.org/schema/aop"
		xmlns:context="http://www.springframework.org/schema/context" xmlns:jee="http://www.springframework.org/schema/jee"
		xmlns:tx="http://www.springframework.org/schema/tx"
		xsi:schemaLocation="
			http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd
			http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
			http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd
			http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee-2.5.xsd
			http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.5.xsd">

	<bean id="Service" class="de.tutorials.Service" p:intProperty="4711" p:stringProperty="bubu"/>
	<bean id="SimpleService" class="de.tutorials.SimpleService"/>
	
	<context:annotation-config/>
</beans>


Java:
package de.tutorials;

import javax.annotation.Resource;

public class Service {
	String stringProperty;
	int intProperty;
	
	@Resource
	SimpleService simpleService;
	

	public String getStringProperty() {
		return stringProperty;
	}

	public void setStringProperty(String stringProperty) {
		this.stringProperty = stringProperty;
	}

	public int getIntProperty() {
		return intProperty;
	}

	public void setIntProperty(int intProperty) {
		this.intProperty = intProperty;
	}
	

	@Override
	public String toString() {
		return "Service [stringProperty=" + stringProperty + ", intProperty="
				+ intProperty + ", simpleService=" + simpleService + "]";
	}		
}

Java:
package de.tutorials;

public class SimpleService {

}

Ausgabe:
Code:
16.11.2009 23:11:07 org.springframework.context.support.AbstractApplicationContext prepareRefresh
INFO: Refreshing org.springframework.context.support.FileSystemXmlApplicationContext@4d20a47e: display name [org.springframework.context.support.FileSystemXmlApplicationContext@4d20a47e]; startup date [Mon Nov 16 23:11:07 CET 2009]; root of context hierarchy
16.11.2009 23:11:07 org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions
INFO: Loading XML bean definitions from file [/home/tom/Desktop/workspaces/3.6M3SDK/de.tutorials.training/conf/context.xml]
16.11.2009 23:11:07 org.springframework.context.support.AbstractApplicationContext obtainFreshBeanFactory
INFO: Bean factory for application context [org.springframework.context.support.FileSystemXmlApplicationContext@4d20a47e]: org.springframework.beans.factory.support.DefaultListableBeanFactory@5d3ad33d
16.11.2009 23:11:07 org.springframework.beans.factory.support.DefaultListableBeanFactory preInstantiateSingletons
INFO: Pre-instantiating singletons in org.springframework.beans.factory.support.DefaultListableBeanFactory@5d3ad33d: defining beans [Service,SimpleService,org.springframework.context.annotation.internalCommonAnnotationProcessor,org.springframework.context.annotation.internalAutowiredAnnotationProcessor,org.springframework.context.annotation.internalRequiredAnnotationProcessor]; root of factory hierarchy
Service [stringProperty=null, intProperty=0, simpleService=null]
Service [stringProperty=bubu, intProperty=4711, simpleService=de.tutorials.SimpleService@4dd36dfe]

Gruß Tom
 
Zuletzt bearbeitet von einem Moderator:
Hallo Thomas,

vielen Dank für Deine Antwort. Die entscheidende Zeile ist ja

Java:
context.getAutowireCapableBeanFactory().configureBean(service, service.getClass().getSimpleName());

nur benötigt diese Vorgehensweise eine Bean-Definition in der XML Datei, wenn ich das richtig verstanden habe. Das würde ich gerne vermeiden, weil es in meinem Fall einfach unnötig wäre, denn die Abhängigkeiten möchte ich mit "@Autowired" oder "@Resource" injizieren lassen.

Aus diesem Grund wollte ich

Java:
 applicationContext.getAutowireCapableBeanFactory().autowireBeanProperties( 
                foo, AutowireCapableBeanFactory.AUTOWIRE_AUTODETECT, true );

verwenden, was - wenn ich nicht völlig daneben liege - keine Bean Definition benötigt. Nur leider funktioniert es so nicht, und ich kann nicht erkennen, woran dies liegen mag.

Über weitere Hilfe würde ich mich sehr freuen

Danke!
 
Also, wenn du aus dem Beispiel von Tom einfach den Service bean rausnimmst, dann sind wir bei Deinem Szenario, richtig?
Danach geht natürlich das #configureBean() schief, denn das will unbedingt, dass für den bean Namen ein bean tatsächlich definiert ist.

Danach geht allerdings der #autowireBeanProperties() durch, da der anscheinend die bean Definition nicht braucht.

Du könntest Dir sogar noch das Instanziieren sparen:
Java:
Service s2 = (Service) context.getAutowireCapableBeanFactory().autowire(
	Service.class,
	AutowireCapableBeanFactory.AUTOWIRE_AUTODETECT,
	true);

Ich verwende Spring in der Version 2.5.6-SEC01.
 
Zuletzt bearbeitet von einem Moderator:
Darf ich fragen, was es grundsätzlich notwendig macht, eine Klasse selbst zu instantiieren und dann doch wieder Autowiren zu lassen? Nicht dass die geschilderten Ansätze falsch wären, aber in den meisten Fällen ist so ein Ansatz ein Workaround, der in vielen Fällen sauberer gelöst werden kann.

Gruß
Ollie
 
Also in meinem speziellen Fall ist es so, dass ich eine (bzw. sehr viele verschiedene) Dialog-Klasse habe, die viele Abhängigkeiten von Service-Beans hat. Den Dialog instanziiere ich neu, wenn ich ihn brauche. Beim schließen wird ein "dispose()" aufgerufen.

Ich könnte diese ganzen Abhängigkeiten nun z.B. via Konstruktor übergeben, das sieht aber irgendwie unschön aus. Da finde ich es besser die "@Resource" Annotation zu verwenden, und den Wiring-Mechanismus von Spring auszunutzen. Das macht die Weiterentwicklung auch konsequent, weil ich bei neuen Abhängigkeiten einfach nur ein "@Resource" hinzufüge und es quasi ohne Mehrarbeit zur Verfügung habe.

Zugegeben, es ist grenzwertig, so viel Mehraufwand ist ein normaler Weg natürlich nicht, ich finde die Lösung aber elegant, weil ich auch überall anders @Resource verwende.
 
Hast du dir mal Method-Injection (http://static.springsource.org/spri...l/ch03s04.html#beans-factory-method-injection) angeschaut? Damit ist es möglich, die Instantiierung einer Springbean hinter einer abstrakten Methode zu verstecken, so dass du bei jedem Aufruf eine frische Instanz von Spring bekommst.

Habe es mir angesehen, danke für den Hinweis. Allerdings kann ich damit keine Bean erstellen, die einen Parameter im Konstruktor erwartet, wenn ich das richtig gelesen habe (wie der JDialog, der das Parent Window als ctor-Parameter erwartet). Zugegeben, das ist dann natürlich auch keine richtige Bean mehr.
 

Neue Beiträge

Zurück