Spring 2.0 und spätere Versionen bieten eine schemabasierte Konfiguration, die es uns ermöglicht, unsere Konfiguration aussagekräftiger und präziser zu spezifizieren. Die Konfiguration von Spring Security 1.0 war zum Beispiel recht umständlich, wurde aber mit Spring 2.0 wesentlich vereinfacht.
Ein Nachteil ist, dass die Dokumentation der Namespaces nicht so gut ist wie die für reguläre Beans. Für reguläre Beans kann ich einen Blick in die
Ein kleines Problem
Aber abgesehen davon bin ich vor kurzem auf ein sehr praktisches Problem bei der Verwendung der schemabasierten Konfiguration in Kombination mit einem PropertyPlaceholderConfigurer gestoßen.
Der Kontext meiner Anwendung enthielt einen PropertyPlaceholderConfigurer, damit er für verschiedene Umgebungen konfiguriert werden konnte. Natürlich funktionierte das für alle meine Beans wie am Schnürchen. Es funktionierte auch für meine Spring Security-Konfiguration, z.B. für die Einstellungen für den LDAP-Server:
<security:ldap-server id="adLdap" url="ldap://${ldap.host}:${ldap.port}/${ldap.base.dn}"
manager-dn="${ldap.connect.dn}" manager-password="${ldap.connect.password}" />
Aber es funktionierte nicht mehr, als ich auch die Rollen, die die Anwendung verwendet, so konfigurierbar machen wollte:
<security:intercept-url pattern="/remoting/**" access="${security.group.authenticated.name}" />
Jetzt habe ich diese Ausnahme vom Frühlingscontainer bekommen, als er meine Bohnen verkabelt hat:
java.lang.IllegalArgumentException: Unsupported configuration attributes: ${security.group.authenticated.name}
Offenbar wurde dieser Wert nicht ersetzt!
Analyse
Es stellte sich heraus, dass auch andere Leute dieses Problem hatten und dass sogar einige Bugs darüber berichtet wurden.
So verstehe ich das Problem. Wenn eine XML-Datei von einem XmlBeanDefinitionReader gelesen wird, werden alle XML-Elemente, die nicht zum Standard-Bean-Namespace gehören, an einen NamespaceHandler übergeben, der dann eine Bean-Definition erstellt, die diesen XML-Elementen entspricht.
Und danach werden alle Bean-Definitionen, die erstellt wurden (sowohl durch das Standard-Bean-Parsing als auch durch die NamespaceHandler), von allen vorhandenen BeanFactoryPostProcessors verarbeitet. Wie z.B. der PropertyPlaceholderConfigurer.
Aber nicht alle Namespace-Handler machen das so. In dem Fall, auf den ich gestoßen bin, stellte sich heraus, dass der SecurityNamespaceHandler
PropertyPlaceholderReplacingXmlBeanFactory
Am einfachsten ist die Verwendung dort, wo Sie normalerweise eine XmlBeanFactory verwenden würden. Stattdessen verwenden Sie jetzt die com.xebia.springframework.beans.factory.xml.PropertyPlaceholderReplacingXmlBeanFactory wie folgt:
Resource context1resource = new ClassPathResource("beans_with_property_placeholders.xml");
Resource props1resource = new ClassPathResource("beanvalues.properties");
Properties props1 = new Properties();
props1.load(props1resource.getInputStream());
BeanFactory factory = new PropertyPlaceholderReplacingXmlBeanFactory(context1resource, props1);
Dies hat den Effekt, dass die Datei beans_with_property_placeholders.xml geladen wird, wie es die reguläre XmlBeanFactory tun würde.
<xbeans:import-with-property-placeholder-replacement>
Meistens haben Sie jedoch weniger Kontrolle über die Factory, die zum Laden Ihrer Beans verwendet wird. Wenn Sie eine Webanwendung erstellen, verwenden Sie höchstwahrscheinlich den XmlWebApplicationContext oder etwas Ähnliches. Es ist zwar durchaus möglich, einen PropertyPlaceholderReplacingXmlWebApplicationContext zu erstellen, der den com.xebia.springframework.beans.factory.xml.PropertyPlaceholderReplacingXmlBeanDefinitionReader anstelle des Standardreaders verwendet (tun Sie das ruhig :-) ), aber ich dachte, dass eine wahrscheinlichere Anwendung darin besteht, dass ein generischer Kontext eine andere Kontextdatei importiert und die zu ersetzenden Werte beim Importieren angibt. Zu diesem Zweck habe ich das Tag import-with-property-placeholder-replacement geschrieben und in den neuen Namespace www.xebia.com/schema/xbeans eingefügt .
In meinem Fall würde der Standardkontext XML die Sicherheitseinstellungen von Spring importieren und dabei die Werte der Zugriffsattribute durch die richtigen Werte ersetzen:
<xbeans:import-with-property-placeholder-replacement
resource="security-context.xml"
properties="security.properties">
PropertyPlaceholderReplacingContextLoader
Und eine weitere Möglichkeit, dieses Material zu verwenden, ist ein JUnit 4.x Testfall. Die Annotation @RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(loader = PropertyPlaceholderReplacingContextLoader.class,
locations = {"/com/xebia/springframework/beans/factory/xml/beans_with_property_placeholders.xml"})
@ContextConfigurationProperties("/com/xebia/springframework/beans/factory/xml/beanvalues.properties")
@TestExecutionListeners(value = { DependencyInjectionTestExecutionListener.class })
Fazit
Nun, die Implementierung war ein interessanter Streifzug durch die Interna von Spring 2.x. Es war interessant herauszufinden, wie Spring die Beans-Dateien lädt und wie ich mich da einklinken kann. Und die Implementierung meines eigenen Namespace hat auch Spaß gemacht. Außerdem bin ich gespannt, ob und wie Spring 3.0 dieses Problem lösen wird.
Verfasst von
Vincent Partington
Contact



