Rick has posted 25 posts at DZone. You can read more from them at their website. View Full User Profile

Spring XML Hell? Escape XML Hell

02.28.2008
| 28537 views |
  • submit to reddit

There are some who do not like to configure dependency injection (DI) in XML. They call it "XML Hell"! There are some who even complain about Spring's DI XML configuration with its lack of type safety.

However compared to J2EE XML configuration (and many other frameworks), Spring XML configuration is a breeze and quite powerful. In addition with Spring 2.5 annotations and the Spring Java Config module, you don't have to use XML to do DI.

 

For example, JSF provides XML DI support. However, JSF configuration in its current version can be very verbose. Spring can simplify doing DI with JSF quite a bit. Let's explore this as an JSF example, then show how to escape "XML Hell" using Spring XML and later the Spring Java Config module.

 

Let's say in JSF that I have a managed bean that is acting as a controller called ContactController, and it has three dependencies that you need to inject called ContactRepository, GroupRepository and TagRepository. In addition the ContactValidators is a utility class that we use for validation. To configure all of these in standard JSF XML we would do these verbose entries:

<managed-bean>
<managed-bean-name>contactValidators</managed-bean-name>
<managed-bean-class>com.arcmind.contact.validators.ContactValidators</managed-bean-class>
<managed-bean-scope>application</managed-bean-scope>
</managed-bean>

<managed-bean>
<managed-bean-name>contactController</managed-bean-name>
<managed-bean-class>
com.arcmind.contact.controller.ContactController
</managed-bean-class>
<managed-bean-scope>session</managed-bean-scope>
<managed-property>
<property-name>contactRepository</property-name>
<property-class>
com.arcmind.contact.model.ContactRepository
</property-class>
<value>#{contactRepository}</value>
</managed-property>
<managed-property>
<property-name>groupRepository</property-name>
<property-class>
com.arcmind.contact.model.GroupRepository
</property-class>
<value>#{groupRepository}</value>
</managed-property>
<managed-property>
<property-name>tagRepository</property-name>
<property-class>
com.arcmind.contact.model.TagRepository</property-class>
<value>#{tagRepository}</value>
</managed-property>
</managed-bean>

<managed-bean>
<managed-bean-name>contactRepository</managed-bean-name>
<managed-bean-class>
com.arcmind.contact.model.ContactRepository
</managed-bean-class>
<managed-bean-scope>application</managed-bean-scope>
</managed-bean>

<managed-bean>
<managed-bean-name>groupRepository</managed-bean-name>
<managed-bean-class>
com.arcmind.contact.model.GroupRepository
</managed-bean-class>
<managed-bean-scope>application</managed-bean-scope>
</managed-bean>

<managed-bean>
<managed-bean-name>tagRepository</managed-bean-name>
<managed-bean-class>
com.arcmind.contact.model.TagRepository
</managed-bean-class>
<managed-bean-scope>application</managed-bean-scope>
</managed-bean>

 

GAK! The JSF version of the configuration takes close to 60 lines. Granted there are many who only configure JSF through IDE tools so they might not care how long it is. However, if you prefer editing files to filling out IDE dialog boxes like me, then Spring can simplify the XML DI configuration a lot. Spring XML is much more developer friendly. This alone might make you interested in Spring if you are doing JSFdevelopment.


(You can download the complete example from this JSF tutorial at IBM developerWorks.)


Let's look at the terse Spring XML configuration version of the above.

 

<bean id="contactValidators" class="com.arcmind.contact.validators.ContactValidators"/>

<bean id="contactController" class="com.arcmind.contact.controller.ContactController" scope="session">
<property name="tagRepository" ref="tagRepository"/>
<property name="groupRepository" ref="groupRepository"/>
<property name="contactRepository" ref="contactRepository"/>
</bean>

<bean id="contactRepository" class="com.arcmind.contact.model.ContactRepository"/>

<bean id="groupRepository" class="com.arcmind.contact.model.GroupRepository"/>

<bean id="tagRepository" class="com.arcmind.contact.model.TagRepository"/>

Now the XML configuration is only 11 lines long instead of near 60. This is a nice improvement, but still there are some who do not like working with XML even if it is only 1/6th the size of the JSF XML version. (Note you can make the Spring XML DI configuration more terse by using auto-wiring and other techniques, however, the above is a good trade-off for readability.)


(You can find the above example at Architecture Proofs of Concept: contacts 4 example.)


Spring 2.5 provides many options for configuring DI without using much XML. Spring 2.5 provides annotation support that is similar to the support that Seam and Guice provide so that you can do the DI without writing a lot of XML. A less touted project from the Spring portfolios of projects is the Spring Java Configuration project. Here is an example of Spring Java Config as follows:

 

@Bean
public ContactValidators contactValidators() {
return new ContactValidators();
}

@Bean(scope=DefaultScopes.SESSION)
public ContactController contactController() {
ContactController contactController = new ContactController();
contactController.setContactRepository(contactRepository());
contactController.setTagRepository(tagRepository());
contactController.setGroupRepository(groupRepository());
return contactController;
}

@Bean
public ContactRepository contactRepository() {
return new ContactRepository();
}

@Bean
public GroupRepository groupRepository() {
return new GroupRepository();
}

@Bean
public TagRepository tagRepository() {
return new TagRepository();
}

The above is functionally equivalent to the last two XML versions of the configuration. Notice that the contactController is mapped into SESSION scope.


The nice thing about this configuration is that you can do whatever you want in Java to construct the object as the methods are like factory methods. Also since it is Java, you can subclass the configuration if you have common configuration. Thus if you have a class of application like say a CRUD web application then you can create a common base configuration and then subclass it per application. (Note again you could make the above more terse by using auto-wiring and using constructors instead of setter methods, etc.).


(You can find the above example using Spring's Java Config module at Architecture Proofs of Concept: contacts 5 example.)

 

There are many advantages to using Spring with JSF so look forward to seeing more posts on this topic. This post merely scratches the surface of the power of Spring. In addition, Tom Cellucci, Paul Hixson and I are writing a series of articles for IBM developerWorks on combining JSF, JPA and Spring effectively. The first article is all on Spring DI and JSF and will have step by step instructions for combining Spring and JSF.

The JSF/Spring article series will include:

  • Configuring Spring to work with JSF,
  • DI for objects whose life-cycle is controlled by JSF (e.g., PhaseListeners, Converters, etc.),
  • Solving common problems with JPA in a JSF web application,
  • Using Spring scope proxies to easily write efficient yet coherent JSF managed beans,
  • and more

The examples in this Spring/JSF series will extend the examples in this JSF 1.2 tutorial series.

What do you think of Spring Java Config?

What do you think of Spring's IoC support versus JSF's IoC support?



About the author

Rick Hightower is CTO of Mammatus and is an expert on Java and Cloud Computing. Rick is invovled in Java CDI advocacy and Java EE. CDI Implementations - Resin Candi - Seam Weld - Apache OpenWebBeans

Published at DZone with permission of its author, Rick Hightower.

(Note: Opinions expressed in this article and its replies are the opinions of their respective authors and not those of DZone, Inc.)

Comments

Jakob Jenkov replied on Thu, 2008/02/28 - 4:41pm

Rick, two quick comments:

 1) Sometimes an external config language is appropriate. But Spring's XML could be more concise. This is why I developed Butterfly Container Script. It does the same in a lot less characters. Even less characters than plain Java.

2) There is a minor (perhaps major?) problem with Springs Java config. Perhaps I just missed something, but look at this method:

  1. @Bean(scope=DefaultScopes.SESSION)  
  2. public ContactController contactController() {  
  3. ContactController contactController = new ContactController();  
  4. contactController.setContactRepository(contactRepository());  
  5. contactController.setTagRepository(tagRepository());  
  6. contactController.setGroupRepository(groupRepository());  
  7. return contactController;  
  8. }  

Notice how this contactController() factory method calls three other factory methods directly:

contactRepository()
tagRepository()
groupRepository()

Now what happens when I want to mock up the contactRepository() factory for a unit test? How do I do that? By extending the whole class containing the factory methods and overriding that one method? That could probably work, although it would require a full rebuild of the Spring ApplicationContext for each unit test (one for each test that needs to subclass and override/mock up a factory method, that is).

But how would you replace a factory at runtime though? If a factory method A calls another factory method B directly, you cannot really replace that second factory B later at runtime. Not without also replacing A.

In addition, these factory methods are called using reflection. If you really really need speed for some reason (in a tight loop), this can be annoying. Why can't you plugin a plain Java factory? 

So, here is a question: Spring has about 5 different configuration mechanisms. Does any of them really rock?

Personally I find that each of them has as many minor annoying limitations as solutions.

Walter Bogaardt replied on Thu, 2008/02/28 - 5:18pm

With mixing in of annotated configurations and spring xml life can get confusing quickly, in comes IntelliJ which seems to plug in nicely with the annotations and allows the developer to navigate to the xml delcaration. This works with the @ExternalBean annotation. In the case of JSF and spring this is nice feature especially when we get into request and session scopes. One quandary is having annotations to declare your DI seems like why do DI anymore?

Jim Hazen replied on Thu, 2008/02/28 - 5:45pm in response to: Jakob Jenkov

I haven't used Spring 2.5, but I assume that it has this feature...

Annotation driven frameworks usually provide a way to override the injected object via adding configuration. So in Rick's example this is the default (possibly production) configuration. The annotations wire by type and without any additional configuration work like the Spring config file shown.

If you wanted to inject stubs for testing I'm sure Spring provides a way of configuring it via a small xml file that defines your overrides. Possibly simply with the following contents.

<bean id="contactRepository" class="com.arcmind.contact.model.ContactRepositoryStub"/>
<bean id="groupRepository" class="com.arcmind.contact.model.GroupRepositoryStub"/>
<bean id="tagRepository" class="com.arcmind.contact.model.TagRepositoryStub"/>

The annotated code doesn't change. Due to the additional config, Spring would inject the Stubs instead of using the annotated method for these id's or id types.

This is useful for more than just testing as well. Configuration of Dev vs QA vs UAT vs Prod environments are also common uses. It's not that hard to avoid XML hell and still be able to test or describe a multi environment configuration. Sure you don't completely eliminate XML, you just eliminate the hell.

Jon Chase replied on Thu, 2008/02/28 - 11:21pm

Don't for get the Spring Bean Builder for Groovy/Grails users.

Jakob Jenkov replied on Fri, 2008/02/29 - 3:21am

Look at the method again:

  1. @Bean(scope=DefaultScopes.SESSION)  
  2. public ContactController contactController() { 
  3. ContactController contactController = new ContactController();  
  4. contactController.setContactRepository(contactRepository());  
  5. contactController.setTagRepository(tagRepository());  
  6. contactController.setGroupRepository(groupRepository());  
  7. return contactController;  

To be able to do what Jim Hazen suggests, Spring would have to use a subclass of the class containing these methods, and not the class itself. Furthermore, a new subclass for each time you want to replace something, and thereby a completely new container configuration, top to bottom. For a big application this will be somewhat time consuming during testing.

Yes, you may possibly be able to override calls *from outside* the container to each of these factory methods without using a completely new subclass. But, the factory methods internal calls directly to each other is a different story. It's a hardwiring directly to a certain factory method.

Jim Hazen replied on Fri, 2008/02/29 - 11:40am in response to: Jakob Jenkov

Hardwiring to a factory method with a changing implementation still isn't a problem. If Spring works like I describe, the calls to the tagRepository() (which are just getters) will be overridden by Spring and through the wonders of AOP will return the testing Stub class instead of the default implementation. This assumes that you're testing from within a Spring managed environment.

Solomon Duskis replied on Fri, 2008/02/29 - 1:44pm

I've been working on Spring JavaConfig (SJC) for a few months now with the Spring guys, so I'm pretty familiar with both the usages of SJC and the internal SJC code.

@Jim, SJC doesn't have a specific XML override mechanism or SJC, but Spring proper does have a strategy for bean replacements. Traditionally, if you want to override a bean definition in the XML world, you'd essentially configure the same bean name in 2 xml files, and the second definition "wins." I'm pretty sure that the same holds true for SJC @Configuration classes; if you have the same method names in two different @Configuration classes, the second method is used to create the the bean even if you're calling the first class's method.

I'll be testing that scenario in the next couple of hours.

Solomon Duskis replied on Fri, 2008/02/29 - 4:46pm

I confirmed it. I wrote a JUnit test that confirms the override behavior. Overa SJC works very much the same way that its XML countepart works. One thing to point out at the unit test below is the way that the "hacky" bean is created; it's a java method that calls another beans.

I wrote a blog entry about how you'd do "method-lookup" in SJC. SJC allows you to create an anonymous inner class that overrides the method in the standard Java manner instead of embedding a method name in XML and using a CGLib generated class.

I'm expecting big things from SJC.

 

public class OverrideTest{

@Test
public void testReadSimplePackage() {
JavaConfigApplicationContext context = new JavaConfigApplicationContext(new Class[] { Config1.class,
Config2.class });
assertEquals("here", context.getBean("hacky"));
}

@Configuration
public static class Config1 {
@Bean
public String hacky() {
return overridden().getName();
}

@Bean
public TestBean overridden() {
return new TestBean("not here");
}
}

@Configuration
public static class Config2 {
@Bean
public TestBean overridden() {
return new TestBean("here");
}
}
}
 
 

Jakob Jenkov replied on Fri, 2008/02/29 - 5:46pm

Well, sounds like you've got it figured out then. But how exactly can spring override the overridden() method in Config1 with the method in Config2, when Config2 does not subclass Config1? Byte code modification? Runtime class generation? Does Spring really add a subclass of Config1 as configuration, so they can intercept method calls?

... And, how do I remove the Config2.overridden() again, once it is overriden? By writing a third class and adding it to the config?? I guess that could work.

Will all this be reflection-free?

... funny thing is, that now Spring will actually violate type safety, by changing Config1 type (or really use a different type than you add)... but it doesn't matter much though, as long as it is just in the config code.

Solomon Duskis replied on Sat, 2008/03/01 - 10:17pm

@Jakob, SJC uses CGLib under the covers.  All calls from an @Bean annotated method to other @Bean annotated method are intercepted and transformed into context.getBean() calls. 

I think that you're right about the type safety.  I did change the test a bit, and everything failed when the context was crated.  I don't think there's a really good way to completely get around that problem.

Jakob Jenkov replied on Sun, 2008/03/02 - 8:22am in response to: Solomon Duskis

Hmm... but still, as long as it is not the types used inside your application that are changed, I don't think it is such a big problem. It is only the config classes passed to Spring that are altered, and that is a pretty controlled and limited part of your application, so I think most projects will be able to handle that.

John Denver replied on Sun, 2008/03/02 - 9:57am

Nice article!, Thanks to Spring now I like better JSF. Without Spring, JSF with own DI plain sucks.

vinod bala replied on Fri, 2008/11/14 - 2:31am

Hi
I'm doing project in Icefaces with spring and hibernate. For that i m writing junit test. i wrote test cases for DAO as well as services classes.

I have Bean Class which calls Service locator that have been actually mapped with faces-managed-beans.xml as

<managed-bean> <description> Backing bean that contains user informaiton. </description> <managed-bean-name>userBean</managed-bean-name> <managed-bean-class> com.....view.bean.UserBean </managed-bean-class> <managed-bean-scope>request</managed-bean-scope> <managed-property> <property-name>serviceLocator</property-name> <value>#{serviceLocatorBean}</value> </managed-property> </managed-bean>

My problem is when i wrote the Junit test case for that bean class and run it , it is not calling Service locator

protected ServiceLocator serviceLocator; public void method(){ User user = this.serviceLocator.getUserService().login(this.username.trim(), this.password.trim()); }

i'm getting null pointer exception on user
Actually in the case of Service class test cases i mapped it as :

protected void setUp() throws Exception { super.setUp(); org.springframework.context.ApplicationContext ctxService = new FileSystemXmlApplicationContext("D:/...../.../Web Content/WEB-INF/applicationContext.xml"); this.UserService = (UserService ctxService.getBean("userService"); }

In this set up method i have mapped the service class using application context.xml. But i cant map the bean class (Service locator ) plz give me a solution... Thanks..

jiji530 (not verified) replied on Mon, 2009/06/29 - 9:46pm

thanks for your post.perhaps you will like abercrombie ed hardy mortgage rates tiffanys ed hardy Is not it?

Comment viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.