Monday October 29, 2007

Guice and Spring 2.5

Filed Under: Java — Solomon @ Oct 29 2007, 01:22:59 PM EDT

The Spring team announced the slick new configuration options and I couldn't resist playing with it. Specifically, I wanted to get a guice-style application working... I stated with that goal and ended with getting a Spring application working with Guice Annotations.

Warning: This post isn't really for the faint of heart. There are a lot of magical IoC incantations... but you'll probably learn a thing or two about Spring 2.0 and Guice.

Resources

It took quite a few resources before I felt comfortable with my task:

Neologisms

  • Autowire = Inject (at least in the new sense of the word autowire) = give me a value
  • Meta-annotation = annotation that annotates another annotation... almost like an annotation super class
  • Resolution Annotation (my neologism) = Binding Annotations (Guice's neologism) = "Guice-like annotations"/Qualifier Annotation (Spring neologism) = annotations that differentiate between implementations of the same type. These annotations should have specific application business or technical significance. For example, "@European food" and "@MiddleEastern food" or "@Staging deployer" and "@Production deployer."

Design

Just for kicks, I stated out with the classes from my previous entry on Spring 2.5 performance. I had to do the following in order to convince the Spring framework to accept the Guice annotations:

  1. I extracted all of the inner classes and interfaces into top level objects. I had to switch all int constants to Integers (a current Spring limitation).

  2. XML: I dug into the Spring code to find the right places to configure Guice's @Inject instead of Spring's @Autowire by configuring an instance AutowiredAnnotationBeanPostProcessor.

  3. XML: I used class and annotation "include-filters" to find the appropriate objects that needed to be configured

  4. XML: I configured my BeanFactory with a different AutowireCandidateResolver to use Guice's @BindingAnnotation in addition to Spring counterpart @Qualifier. Those annotations are used as meta-annotations which describe which annotations should be used at runtime to configure "resolution annotations". For example, the following annotations are "resolution annotations":

     public void setOrderServices(
       @Emea OrderService emea,
       @Apac OrderService apac)

    Emea and Apac have to be marked with other annotations that mark then to be "resolution annotations." Here's how Guice does it:

      @Retention(RetentionPolicy.RUNTIME)
      @BindingAnnotation
      @interface Emea {}

    "Vanilla" Spring annotations are configured as follows

      @Retention(RetentionPolicy.RUNTIME)
      @Qualifier
      @interface Emea {}

    Spring's implementation is very close in nature to Guice. Spring allows you to switch out Spring's default "Qualifier" meta-annotation with any annotation you'd like...

  5. XML + Code: I hacked a way to change configuration for beans to be singletons or prototypes based on runtime configuration... This was just to see how fast annotated beans are compared to their "normal" cousins.

Implementation

Here's what I had to do in 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:context="http://www.springframework.org/schema/context"
    xsi:schemaLocation="
        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">

    <!-- This tells Spring to autowire/inject based on annotations.
         * All of my beans are in com.solomon.*
         * I don't want any of Spring's out-of-the-box default Component annotations; I want to define my own
         * I want to hack my own scopes for testing purposes.  
           Sometimes I want beans to be prototypes, and sometimes I want them to be singletons.  
           I switch between the two scopes via a static variable
     -->
    <context:component-scan base-package="com.solomon" use-default-filters="false"
        scope-resolver="com.solomon.StaticScopeResolver">
        <!-- The "Semi Useless Performance Test" has one Guice annotation -->
        <context:include-filter type="annotation" expression="com.google.inject.Singleton" />

        <!-- The "Semi Useless Performance Test" classes are provider based...
            They don't have Guice Annotations; rather, they are configured via providers.
            This is Spring's way of "providing"
        -->
        <context:include-filter type="regex" expression="com.solomon.(BarImpl|Foo)" />
    </context:component-scan>

    <!-- WOOHOO!!! Guice's @Inject can be used to "autowire" Spring beans!!!! -->
    <bean
        class="org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor"
        name="org.springframework.context.annotation.internalAutowiredAnnotationProcessor">
        <property name="autowiredAnnotationType" value="com.google.inject.Inject" />
    </bean>

    <!-- WOOHOO!!! Guice's @BindingAnnotation meta-annotation can be used in 
         addition to Spring's @Qualifier meta-annotation -->
    <bean class="org.springframework.beans.factory.annotation.CustomAutowireConfigurer">
        <property name="customQualifierTypes">
            <set>
                <value>com.google.inject.BindingAnnotation</value>
            </set>
        </property>
    </bean>

    <!-- this is similar to, but a lot less sexy than, Guice's
            bindConstant().annotatedWith(S.class).to("test");  -->
    <bean class="java.lang.String" name="S">
        <qualifier type="S" />
        <constructor-arg value="test" />
    </bean>


    <!--  bindConstant().annotatedWith(I.class).to(5);
         Unfortunately, Spring's annotation processor doesn't handle primities yet. -->
    <bean class="java.lang.Integer" name="I">
        <qualifier type="I" />
        <constructor-arg value="5" />
    </bean>

</beans>

My Analysis

I thought about the successes and failures I ran across during the Semi Useless ™ implementation I wrote. I thought about those failures and how to correct them to make the Spring implementation more than just a Guice clone. Spring's Guice-like annotation implementation should take the advantages of the differences inherent to the Spring framework to make Spring users more comfortable with the new style of development.

The Great

  1. Spring 2.5's Guice-like annotation implementation is upto the usual Spring high standards. The annotations implementation is now extendable and robust.

  2. The fact that Spring adopted Guice's approach to annotated IoC (more or less) is a great testament to Guice's approach as well as the Spring team's continuing commitment to its user base.and to great ideas even if the Spring team is philosophically against the approach to some extent. Kudos to both the Spring and the Guice teams on this release.

  3. I've changed my mind about the invasiveness of Guice's annotations. I no longer think that they are as invasive as I once thought they were. However, Spring's configurable annotation resolver approach seems to fit better.

Room For Improvement

Spring 2.5 is a great advancement, but luckily there is plenty of opportinites for further improvement. New paradigms such as binding annotations bring on new challenges. The biggest one that I see relates to separating application construction configuration from external configuration.

By application construction configuration I mean: the information an IoC container would need for gluing your application together. Application construction configuration would include the fact that an OrderController needs an OrderService which in turn needs an OrderDao which in turn needs the Order JDBCConnection.

By external configuration I mean: the kind of stuff you want externalized in an XML file or a property file, such as connection information, file names, counts and Controller view names. Most of the time, those kinds of values are primitives.

The new Spring annotations are a fantastic new way to perform a application construction, but currently comes at the expense of reduced external configuration capabilities.

On my short list on how to improve this (of which I have some good ideas how to implement):

  1. Easier method of creating "primitive" values in XML. For example, here's some XML that would be :

    <primitive qualifier="I" type="int" value="5" />
    <primitive qualifier="S" type="java.util.String" value="test" />

    and/or:

    <primitive name="I" type="int" value="5" />
    <primitive name="S" type="java.util.String" value="test" />

    A property file oriented solution would also be a fantastic inclusion. For exmple:

    I=5
    S=test
  2. Primitive support for annotations. I should be able to annotate int's and booleans in Spring like I can in Guice, but for no it's not possible. For exmple, the following should work, but doesn't:

    @Autowire public void setI(@I int i)
  3. An @Value annotation with property resolution... For example:

    @Autowire  public void setI(@Value("${i}") int i)

    With a property file entry:

    i=5

    or simply the following for sensible defaults that can be overridable in XML:

    @Autowire public void setI(@Value("5") int i); 
  4. A slicker programmatic construction API... For example, Guice's "provider" API would be a great starting point.

  5. First class citizenship of Prototype beans. I'll expound on this point later.

  6. Performance improvements... Annotated Prototypes have fraction-of-millisecond resolution times on my laptop. Guice Prototypes have microsecond resolution times. Milliseconds add up really quickly for complex applications. I have some ideas about how to do this, but that's a discussion for another time.

  7. Guice has a Spring integration... Spring should have a Guice integration.

Parting thoughts

I'm pretty excited about Spring 2.5. The annotation implementation borrows a lot of ideas from Guice. You can now configure a good portion of your application without some of aches and pains required by traditional Spring configuration. The Spring annotation processing process is extremely configurable as to which Spring should use annotation and how those annotations are processed. I'm duly impressed overall, although there are some kinks and some room for improvements.

Spring 2.5 brings some pretty exciting new features beyond annotation. Check them out for yourself

Comments:

"I've changed my mind about the invasiveness of Guice's annotations. I no longer think that they are as invasive as I once thought they were."

Can you tell me what has changed your mind? Using annotations of a framework (Guice or Spring, doesn't matter) ties your code to that framework, so I like frameworks which provide 'application construction configuration' outside of my code.

Posted by Peter Bona on October 30, 2007 at 08:37 AM EDT #

Solomon,

Instead of setting the AutowireCandidateResolver directly on the factory there, you can provide any custom annotation types via a CustomAutowireConfigurer:

<bean class="org.springframework.beans.factory.annotation.CustomAutowireConfigurer">
<property name="customQualifierTypes">
<set>
<value>mypackage.MyQualifier</value>
</set>
</property>
</bean>

Posted by Mark Fisher on October 30, 2007 at 08:54 AM EDT #

Hi Solomon

Wow! I respect your code-fu skills (skillz?) but meh, my faint-heart was just about kaput come the end of your post.

As an exercise in playing around I like it: perhaps you could call the code something like "Spruice (pronounced 'banjo')", or "Guing (pronounced 'Schwing!')".

As something usable I hate it... ugh. I know it wasn't your intent (to provide something in any way usable), but gee whiz, the combination of XML configuration and custom annotations sprayed across the entire codebase just blow the whole idea of a central, structured place for one's application configuration out of the water. My brain would melt if I went to a client and had to figure out how everything hanged together if it used the approach from the post. Shudder :) Enough folks have tore a strip out of the Spring XML configuration (unfairly I think)... I'd be interested in knowing whether you really see the XML+annotations approach being 'better' as regards readability and maintainability?

Cheers
Rick

Posted by Rick on October 30, 2007 at 09:30 AM EDT #

@Mark Thanks for the info. I've update my post!

Posted by Solomon on October 30, 2007 at 10:19 AM EDT #

@Peter Bona

"Can you tell me what has changed your mind? Using annotations of a framework (Guice or Spring, doesn't matter) ties your code to that framework, so I like frameworks which provide 'application construction configuration' outside of my code."

I'll clarify this in my long post, but basically, there are two parts to the invasiveness and framework tie in

1) Runtime/Container requirements
2) Class/Jar Inclusion/Usage

I've proven to myself that Annotations don't cause Runtime invasiveness (#2). I can use Guice annotations + the Spring runtime.

I'm honestly not as concerned about invasiveness #1... Including a jar and implementing a few interfaces or adding an annotation here or there doesn't scare me as nearly as much as runtime constraints.

With that said, the exercise I went through showed that I could pretty easily convert a Guice app into a Spring app without having to switch too much of MY application code… The only thing I had to do was switch ints to Integers, which was a Spring constraint and not a Guice constraint.

I would probably use my own/standard annotations instead of Guice’s @Inject and @BindingAnnotation. I believe that Guice will allow that kind of flexibility in the future. I believe that the exercise I went through proves that Spring provides that now. In the near future, you’ll be able to switch between a Guice runtime, a Spring runtime and probably other IoC runtimes without too much difficulty.

Posted by Solomon on October 30, 2007 at 10:57 AM EDT #

@Rick

Taking a step back, your real question is why did the Spring team introduce the concept of @Annotation-driven configuration.

I'd break down of the stuff that belongs in XML/property files and what belongs in your application with annotations or other programmatic constructs as follows

1) external application configuration (for example, jsp page locations, database/ftp/resource connection definitions) belongs in a location external to your code

2) internal application construction meta-data (my FooService needs a FooDao) belongs as close to the code as possible

Spring "classic" takes the external application configuration to an extreme by throwing both internal and external construction meta-data into xml and property files. Spring "classic" does this for a variety of great reasons.

The Guice/Annotation model moves internal application configuration into the code. Guice does this for a variety of great reasons.

An ideal scenario has robust solutions for keeping the guts inside and the external inputs/outputs outside.

We're not at an ideal scenario yet, but the IoC community is moving forward toward an ideal.

Posted by Solomon on October 30, 2007 at 11:33 AM EDT #

For external application configuration, we use Commons Configuration's JNDIConfiguration, along with a static method I wrote to convert the Configuration object to a Properties object.

The following is the Spring xml for creating the JNDI properties object.

<bean id="jndiProperties" class="com.mycompany.ConfigurationConverterHelper" factory-method="getProperties">
<constructor-arg>
<bean class="org.apache.commons.configuration.JNDIConfiguration">
<constructor-arg value="java:comp/env/"/>
</bean>
</constructor-arg>
</bean>

We then use the spring PropertyPlaceholderConfigurer object to inject the JNDI properties into the Spring configuration. This configuration allows defaults to be set in mail.properties, jdbc.properties, or any other properties file you want to define. The JNDI properties will then override any values set in any of the properties files. This makes it very easy to have a default configuration that can then be overriden for a particular environment via JNDI. There's no longer a need to modify the spring xmls for a given environment configuration.

Here's the place holder configuration. The localOverride config gives us the behavior we want.

<!-- Configurer that replaces ${...} placeholders with values from a properties file -->
<!-- (in this case, JDBC-related settings for the dataSource definition below) -->
<bean id="propertyConfigurer" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="properties" ref="jndiProperties" />
<property name="locations">
<list>
<value>classpath:mail.properties</value>
<value>classpath:jdbc.properties</value>
</list>
</property>
<!-- allows the jndiProperties to trump the values in property files defined in locations -->
<property name="localOverride" value="true"/>
</bean>

The one caveat with jndi configurations. Slashes '/'in jndi, eg. mail/host, get converted to '.' when used in properties.

Here's an example of the mailSender config.

In JNDI we define
mail/host = xx.xx.xx.xx
mail/username = mailuser
mail/password = mailpwd

and the following configures the mailSender in Spring

<bean id="mailSender" class="org.springframework.mail.javamail.JavaMailSenderImpl">
<property name="host" value="${mail.host}"/>
<property name="username" value="${mail.username}"/>
<property name="password" value="${mail.password}"/>
</bean>




Here's the method to convert the configuration to a Properties object in ConfigurationConverterHelper. This method is necessary to use or convert the JNDI configuration objects into Strings.

public static Properties getProperties(Configuration config, boolean useToStringOnNonStringObjects) {
Properties props = new Properties();

Iterator keys = config.getKeys();

while (keys.hasNext())
{
String key = (String) keys.next();
List list = null;

try {
// this only works if the value for the key is a string, a list, or null, otherwise the
// conversion exception is thrown.
list = config.getList(key);
} catch (ConversionException e) {
logger.trace("key " + key + " could not be converted to a list.", e);
if (useToStringOnNonStringObjects) {
Object object = config.getProperty(key);
list = new ArrayList();
list.add(String.valueOf(object));
} else {
continue;
}
}

// turn lists into a string
StringBuffer property = new StringBuffer();
Iterator it = list.iterator();
while (it.hasNext())
{
property.append(String.valueOf(it.next()));
if (it.hasNext())
{
property.append(", ");
}
}

props.setProperty(key, property.toString());

}

return props;
}

Posted by Skip on November 07, 2007 at 10:11 AM EST #

Great article. I added http://jira.springframework.org/browse/SPR-4447 in your honor :)

Posted by btiernay on February 11, 2008 at 09:43 AM EST #

Post a Comment:

Name:
E-Mail:
URL:

Your Comment:

HTML Syntax: NOT allowed