Spring XML Hell? Escape XML Hell

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?

 

--Rick Hightower -- Java Zone Leader

 

5
Average: 5 (2 votes)

(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 - 5: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 - 6: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 - 6:45pm in response to: jj83777

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 Fri, 2008/02/29 - 12:21am

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

Jakob Jenkov replied on Fri, 2008/02/29 - 4: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 - 12:40pm in response to: jj83777

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 replied on Fri, 2008/02/29 - 2: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 replied on Fri, 2008/02/29 - 5: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 - 6: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 replied on Sat, 2008/03/01 - 11: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 - 9:22am in response to: sduskis

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.

Sidewinder replied on Sun, 2008/03/02 - 10:57am

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

Comment viewing options

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