Skip to main content

Spring framework without XML... At all!

First, there was EJB 2.1 with countless XML files all over. It won't be such a big exaggeration to say that for every line of business code you had to create at least 10 lines of framework code and two pages of XML. Local and remote interfaces, manual JNDI lookup, triply nested try-catches, checked RemoteExceptions everywhere... this was enterprise. There were even tools to generate some of this boilerplate automatically.

Then couple of guys came and created Spring framework. After being forced to cast by obscure PortableRemoteObject.narrow() it was like taking a deep breath of fresh air, like writing poetry after working in coal mine. Time went by (BTW do you remember how many years ago was the last major JRE release?) and Sun learnt their lesson. EJB 3.0 was even simpler compared to Spring, XML-free, annotations, dependency injection. 3.1 was another great step toward simplicity, being more and more often compared to Spring. Logically current state of the art EJB specification might be considered as a subset of what Spring offers, I am actually surprised why there is no EJB spec. implementation in plain Spring (oh, wait...), considering its out-of-the-box support for JPA 1.0/2.0, JSR-250, JSR-330, JAX-WS/RS compatible solutions and others. But even though, Spring framework is nowadays perceived as a slow, heavyweight and hard to maintain, mainly due to reliance on XML descriptors. Once simple, now Spring is a whipping boy in the JEE framework battle.

I don't like politics, I won't defend my beloved framework writing lengthy essays. Instead I will take simple, but not trivial Spring application and quickly rewrite it so that it won't use XML. Not reduce the amount of XML, not leave only few untouchable lines. No XML - at all.

For the purposes of this article I created very simple Spring web application (base version under xml branch, final on master on my GitHub account) using JDBC, JMS and JMX, just not to make things trivial. Every change I made to the source code will be reflected in a separate commit to this repository. Step by step I will be removing XML configuration until there will be no Spring XML left. This is where we start:

<?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:tx="http://www.springframework.org/schema/tx"
       xmlns:amq="http://activemq.apache.org/schema/core"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
            http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.5.xsd
             http://activemq.apache.org/schema/core http://activemq.apache.org/schema/core/activemq-core-5.4.2.xsd
             http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">

    <context:mbean-export />

    <bean id="fooService" class="com.blogspot.nurkiewicz.FooService">
        <property name="jmsOperations" ref="jmsTemplate" />
    </bean>

    <bean id="fooRequestProcessor" class="com.blogspot.nurkiewicz.FooRequestProcessor">
        <property name="fooRepository" ref="fooRepository" />
    </bean>

    <bean id="fooRepository" class="com.blogspot.nurkiewicz.FooRepository" init-method="init">
        <property name="jdbcOperations" ref="jdbcTemplate" />
    </bean>


    <!-- JDBC -->
    <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
        <property name="driverClassName" value="org.h2.Driver" />
        <property name="url" value="jdbc:h2:~/workspace/h2/spring-noxmal;DB_CLOSE_ON_EXIT=FALSE;TRACE_LEVEL_FILE=4;AUTO_SERVER=TRUE" />
        <property name="username" value="sa" />
        <property name="password" value="" />
    </bean>

    <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
        <constructor-arg ref="dataSource" />
    </bean>

    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <constructor-arg ref="dataSource" />
    </bean>

    <tx:annotation-driven />


    <!-- JMS -->
    <bean id="jmsConnectionFactory" class="org.apache.activemq.pool.PooledConnectionFactory">
        <constructor-arg>
            <bean class="org.apache.activemq.ActiveMQConnectionFactory">
                <property name="brokerURL" value="tcp://localhost:61616" />
            </bean>
        </constructor-arg>
    </bean>

    <amq:queue id="requestsQueue" physicalName="requests" />

    <bean id="jmsTemplate" class="org.springframework.jms.core.JmsTemplate">
        <constructor-arg ref="jmsConnectionFactory" />
        <property name="defaultDestination" ref="requestsQueue" />
    </bean>

    <bean id="jmsContainer" class="org.springframework.jms.listener.DefaultMessageListenerContainer">
        <property name="connectionFactory" ref="jmsConnectionFactory" />
        <property name="destination" ref="requestsQueue" />
        <property name="sessionTransacted" value="true"/>
        <property name="concurrentConsumers" value="5"/>
        <property name="messageListener">
            <bean class="org.springframework.jms.listener.adapter.MessageListenerAdapter">
                <constructor-arg ref="fooRequestProcessor" />
                <property name="defaultListenerMethod" value="process"/>
            </bean>
        </property>
    </bean>

</beans>


Few user beans, JDBC including transaction support and utilizing JMS, both sending and receiving. The details of this application aren't that important: one of the beans is exposed via JMX, it sends JMS message, then that message is received and persisted in database.

The most commonly used and well established approach to reduce XML boilerplate in Spring is to use @Service and @Resource annotations together with introducing <context:component-scan/> for user beans (show changes):

<?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:tx="http://www.springframework.org/schema/tx"
       xmlns:amq="http://activemq.apache.org/schema/core"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
            http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.5.xsd
             http://activemq.apache.org/schema/core http://activemq.apache.org/schema/core/activemq-core-5.4.2.xsd
             http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">

    <context:mbean-export />

    <context:component-scan base-package="com.blogspot.nurkiewicz"/>

    <!-- JDBC -->
    <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
        <property name="driverClassName" value="org.h2.Driver" />
        <property name="url" value="jdbc:h2:~/workspace/h2/spring-noxmal;DB_CLOSE_ON_EXIT=FALSE;TRACE_LEVEL_FILE=4;AUTO_SERVER=TRUE" />
        <property name="username" value="sa" />
        <property name="password" value="" />
    </bean>

    <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
        <constructor-arg ref="dataSource" />
    </bean>

    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <constructor-arg ref="dataSource" />
    </bean>

    <tx:annotation-driven />


    <!-- JMS -->
    <bean id="jmsConnectionFactory" class="org.apache.activemq.pool.PooledConnectionFactory">
        <constructor-arg>
            <bean class="org.apache.activemq.ActiveMQConnectionFactory">
                <property name="brokerURL" value="tcp://localhost:61616" />
            </bean>
        </constructor-arg>
    </bean>

    <amq:queue id="requestsQueue" physicalName="requests" />

    <bean id="jmsTemplate" class="org.springframework.jms.core.JmsTemplate">
        <constructor-arg ref="jmsConnectionFactory" />
        <property name="defaultDestination" ref="requestsQueue" />
    </bean>

    <bean id="jmsContainer" class="org.springframework.jms.listener.DefaultMessageListenerContainer">
        <property name="connectionFactory" ref="jmsConnectionFactory" />
        <property name="destination" ref="requestsQueue" />
        <property name="sessionTransacted" value="true"/>
        <property name="concurrentConsumers" value="5"/>
        <property name="messageListener">
            <bean class="org.springframework.jms.listener.adapter.MessageListenerAdapter">
                <constructor-arg ref="fooRequestProcessor" />
                <property name="defaultListenerMethod" value="process"/>
            </bean>
        </property>
    </bean>

</beans>

10 lines less of XML, not very impressive... And what about user beans?

@Service
public class FooRepository {

    @Resource
    private JdbcOperations jdbcOperations;

    @PostConstruct
    public void init() {
        log.info("Database server time is: {}", jdbcOperations.queryForObject("SELECT CURRENT_TIMESTAMP", Date.class));
    }
    
    //...
    
}


Setters were replaced by annotations, init-method as well. Now what? Majority of annotation-enthusiasts stop here, but as you can see, there is plenty of XML left... The only problem is – how to annotate third-party classes like connection pools, Spring-provided support classes, etc.?

The real fun begins here. First we will get rid of the data source XML and replace it with... (show changes):


import javax.sql.DataSource;
import org.apache.commons.dbcp.BasicDataSource;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class ContextConfiguration {

    @Bean
    public DataSource dataSource() {
        final BasicDataSource ds = new BasicDataSource();
        ds.setDriverClassName("org.h2.Driver");
        ds.setUrl("jdbc:h2:~/workspace/h2/spring-noxmal;DB_CLOSE_ON_EXIT=FALSE;TRACE_LEVEL_FILE=4;AUTO_SERVER=TRUE");
        ds.setUsername("sa");
        return ds;
    }

}

@Configuration, @Bean, dataSource(), what the...?!? It works exactly the way you think: Spring finds the ContextConfiguration class and examines all methods annotated with @Bean. Each and every method like that is treated equally to <bean...> XML declaration (there are even @Scope, @DependsOn and @Lazy annotations), so we can remove the dataSource bean declaration from XML. Actually, we can get rid of JdbcTemplate and transaction manager as well (show changes):

@Bean
public JdbcOperations jdbcOperations() {
    return new JdbcTemplate(dataSource());
}

@Bean
public PlatformTransactionManager transactionManager() {
    return new DataSourceTransactionManager(dataSource());
}


Look closely how easily one can inject data source bean to other beans. You have a method that creates data source on one hand, on the other hand two methods require data source to be injected (JdbcTemplate and transaction manager). It can't be easier, this is probably the way your girlfriend would implement dependency injection (Guice, anyone?)

One thing should bother you though... If you call dataSource() twice, wouldn't this mean that you have just created two separate, independent DataSource instances? Clearly not what was intended... Well, it bothered me (see comments), but it seems that once again Spring is a one clever beast. Not finding @Scope annotation it assumes data source should be a singleton. So it applies some CGLIB-proxying-magic around dataSource() method transparently and protects it from being called more than once. Or, more precisely, you think you can call it many times, but all subsequent calls will return already factored bean, not even reaching the actual implementation. Nice!

All in all, this shortened our XML configuration to this:

<?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:tx="http://www.springframework.org/schema/tx"
       xmlns:amq="http://activemq.apache.org/schema/core"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
            http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.5.xsd
             http://activemq.apache.org/schema/core http://activemq.apache.org/schema/core/activemq-core-5.4.2.xsd
             http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">

    <context:mbean-export />

    <context:component-scan base-package="com.blogspot.nurkiewicz"/>

    <!-- JDBC -->
    <tx:annotation-driven />

    <!-- JMS -->
    <bean id="jmsConnectionFactory" class="org.apache.activemq.pool.PooledConnectionFactory">
        <constructor-arg>
            <bean class="org.apache.activemq.ActiveMQConnectionFactory">
                <property name="brokerURL" value="tcp://localhost:61616" />
            </bean>
        </constructor-arg>
    </bean>

    <amq:queue id="requestsQueue" physicalName="requests" />

    <bean id="jmsTemplate" class="org.springframework.jms.core.JmsTemplate">
        <constructor-arg ref="jmsConnectionFactory" />
        <property name="defaultDestination" ref="requestsQueue" />
    </bean>

    <bean id="jmsContainer" class="org.springframework.jms.listener.DefaultMessageListenerContainer">
        <property name="connectionFactory" ref="jmsConnectionFactory" />
        <property name="destination" ref="requestsQueue" />
        <property name="sessionTransacted" value="true"/>
        <property name="concurrentConsumers" value="5"/>
        <property name="messageListener">
            <bean class="org.springframework.jms.listener.adapter.MessageListenerAdapter">
                <constructor-arg ref="fooRequestProcessor" />
                <property name="defaultListenerMethod" value="process"/>
            </bean>
        </property>
    </bean>

</beans>

You my stop now and think how would you rewrite the remaining XML-defined beans into Java. Don't worry, there is no catch here – it as straightforward as it should be (see changes).

@Bean
public ConnectionFactory jmsConnectionFactory() {
    final ActiveMQConnectionFactory factory = new ActiveMQConnectionFactory();
    factory.setBrokerURL("tcp://localhost:61616");
    return new PooledConnectionFactory(factory);
}

@Bean
public Queue requestsQueue() {
    return new ActiveMQQueue("requests");
}

@Bean
public JmsOperations jmsOperations() {
    final JmsTemplate jmsTemplate = new JmsTemplate(jmsConnectionFactory());
    jmsTemplate.setDefaultDestination(requestsQueue());
    return jmsTemplate;
}


Declaration of DefaultMessageListenerContainer contains some anonymous inner bean, that is being used only once within parent bean. So private method is OK (see changes):

@Bean
public AbstractJmsListeningContainer jmsContainer() {
    final DefaultMessageListenerContainer container = new DefaultMessageListenerContainer();
    container.setConnectionFactory(jmsConnectionFactory());
    container.setDestination(requestsQueue());
    container.setSessionTransacted(true);
    container.setConcurrentConsumers(5);
    container.setMessageListener(messageListenerAdapter());
    return container;
}

private MessageListenerAdapter messageListenerAdapter() {
    final MessageListenerAdapter adapter = new MessageListenerAdapter(fooRequestProcessor);
    adapter.setDefaultListenerMethod("process");
    return adapter;
}


Not much to be said, mainly because Spring plain Java configuration is so trivial and straightforward – and the code may speak for itself. In case you've got lost, after so many transformations we are now here:

<?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:tx="http://www.springframework.org/schema/tx"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
            http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.5.xsd
             http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">

    <context:mbean-export />

    <context:component-scan base-package="com.blogspot.nurkiewicz"/>

    <tx:annotation-driven />

</beans>

UPDATE: Luckily Spring 3.1 and 3.2 greatly simplified Java configuration by introducing: WebApplicationInitializer, @EnableTransactionManagement and @EnableMBeanExport (requested by me). Thus most of the workaround below are no longer needed.

To be honest, it wasn't very hard, but the remaining few lines of XML were especially difficult to remove. Believe me, you don't want to go the same path I had to choose to replace these nice little namespace-powered declarations. But after several minutes, few unsuccessful experiments and lots of Spring code reviewed I finally removed JMX (see changes) and transaction (see changes) declarations. Looks innocent and I am glad you won't have to dig through Spring code base to reinvent it:

@Bean
public AnnotationMBeanExporter annotationMBeanExporter() {
    return new AnnotationMBeanExporter();
}

@Bean
public TransactionAttributeSource annotationTransactionAttributeSource() {
    return new AnnotationTransactionAttributeSource();
}

@Bean
public TransactionInterceptor transactionInterceptor() {
    return new TransactionInterceptor(transactionManager(), annotationTransactionAttributeSource());
}

This would be it. All we have left is this tiny XML bootstrap declaration that instructs Spring where to find all of the beans and web.xml snippet making web container to actually start the Spring application context:

<context:component-scan base-package="com.blogspot.nurkiewicz"/>

<web-app xmlns="http://java.sun.com/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
         version="3.0">

    <listener>
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>

</web-app>

There is no other way to start Spring properly in web-environment, after all you have to make the web-container aware of the Spring framework somehow. I know, I know, I promised no XML at all. So I lied, I'm sorry, OK? Big deal... Wait, wait, just kidding :-), we will get rid of this bootstrap XML completely in seconds. Well, depending how fast can you download the newest Tomcat 7 or other JSR 315 (among insiders known as Servlet 3.0) capable web container...



Web fragments is a technology allowing seamless integration of various web frameworks with servlet containers. If you worked with several frameworks, they all require registering specific servlet, filter or listener in web.xml. Most of the time this is the only servlet dependency and Spring is no exception. The idea behind web fragments tries to liberate the end developers from this requirement. Servlet 3.0 compatible container should scan all JARs within /WEB-INF/lib directory in WAR artifact and if any JAR contains web-fragment.xml file inside its /META-INF directory, it will be included in final web.xml.

You realize where I am going? What if we could create such a small web-fragment JAR only for XML-free Spring startup? This is the typical, far from complete WAR file structure:

.
|-- META-INF
`-- WEB-INF
    |-- classes
    |   |-- com
    |   |   `-- blogspot
    |   |       `-- nurkiewicz
    |   |           |-- ContextConfiguration.class
    |   |           |-- FooRepository.class
    |   |           |-- FooRequestProcessor.class
    |   |           |-- FooService$1.class
    |   |           `-- FooService.class
    |   `-- logback.xml
    |-- lib
    |   |-- spring-web-3.0.5.RELEASE.jar
    |   |-- spring-web-fragment-0.0.1-SNAPSHOT.jar
    |   |   `-- META-INF
    |   |       |-- MANIFEST.MF
    |   |       |-- web-fragment-context.xml
    |   |       `-- web-fragment.xml
    |   `-- spring-beans-3.0.5.RELEASE.jar
    `-- web.xml


The sole purpose of spring-web-fragment-*.jar is to provide web-fragment.xml file for the container, being the bootstrap between servlet environment and Spring framework:

<web-fragment xmlns="http://java.sun.com/xml/ns/javaee"
              xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
              xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-fragment_3_0.xsd"
              version="3.0">

    <listener>
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>

    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>classpath*:/META-INF/web-fragment-context.xml</param-value>
    </context-param>

</web-fragment>

One new element is the explicitly defined web-fragment-context.xml Spring context file. We cannot use the default location in the WAR file (/WEB-INF/applicationContext.xml), as this file no longer exists (!) But our tiny fragment JAR seems to be the best place for it:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="
     http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
     http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
       ">

    <context:component-scan base-package="." />

</beans>

Package declaration "." looks disturbing. This is very unfortunate but I tried to workaround the requirement of defining at least one package there. This requirement is probably for a reason (I guess scanning the full CLASSPATH takes some time), but I couldn't just put my own package, as I would have to change this declaration for every single project. But this would violate the biggest advantage of web-fragment approach – once you create this empty JAR with two tiny XML files, you can use it for all your projects. All you have to do is to add this JAR into your WAR's libraries and start annotating POJOs with @Service (and/or use @Configuration).

If such an utility artefact is going to be ever available out-of-the-box along other Spring dependencies (if you like the idea, vote), beginners might enjoy their Spring-journey right from the moment of adding Spring in pom.xml. In fact, pom.xml can now be written in different languages, as well as logback.xml. Look ma, no XML! Are you convinced? Do you prefer XML or Java? Or Groovy? Please, don't answer. Spring gives you the choice to be as lightweight and as simple as you want it to be. Without oversimplification and cutting off more advanced functionalities.

Update [25.01.2011]: Russian translation by Alexander Belotserkovsky is available.

Comments

  1. Thanks for the post, Tomek! The pain you experienced dealing with eliminating those last few lines of XML (context:component-scan and friends) is well-understood.

    If you have not done so already, please follow SPR-7194, which deals specifically with support for a @ComponentScan annotation; and SPR-7420, which serves as an umbrella for all such work providing annotations parallel to key namespace elements e.g., tx:annotation-driven, aop:aspectj-autoproxy, etc.

    Thanks also for your submission of SPR-7872. Providing XML-free support for bootstrapping Spring applications in a Servlet 3.0 environment is certainly something we're planning on; early support should be there even in Spring 3.1. Web fragments will likely play a role as too will Servlet 3.0's notion of ServletContainerInitializer classes.

    - Chris

    ReplyDelete
  2. Nice but hardcoding urls like tcp://localhost:61616 is big NONO! Does @Bean work PropertyPlaceholderConfigurer somehow? With @Value maybe?

    ReplyDelete
  3. And there is little known web.xml context-param named contextClass, allowing you to change default XmlWebApplicationContext to AnnotationConfigWebApplicationContext

    see http://stackoverflow.com/questions/3312489/spring-3-annotated-configuration-picks-up-configuration-and-component-but-not

    ReplyDelete
  4. Chris: thank you for the resources and your reply, I'm glad to hear that you are working to simplify Spring configuration even more!

    Anthavio: I'm so glad you asked! Actually I devoted whole article on hard-coding literals in Java code. As for your question: @Configuration annotated classes are Java classes like any other - so you can do whatever you want during Spring startup: open properties file, parse XML, load configuration via JNDI... "Classic" @Value approach also works (see the article I already mentioned).

    Only I can't get PropertyOverrideConfigurer to work with my example, I'll try again later.

    Thanks for the tip on AnnotationConfigWebApplicationContext!

    ReplyDelete
  5. If you are into reducing your (configuration) code further, take a look at spann ( http://code.google.com/p/spann/ ).

    Spann provides an api for advanced Bean configuration via custom annotations and is best fitted if your configuration code is too long and repeats itself or you want to use meta-programming style configuration (for example when making strong use of patterns).

    ReplyDelete
  6. Great article. I learned some new things about Spring.
    But... you're making some dubious claims. You're contrasting EJB to Spring, which makes little sense. It's not the EJB spec, but JEE 6 that is comparable to Spring.
    And, just for the record, I sincerely believe this same example would be much easily implemented in JEE 6... lot less providers, managers etc...

    ReplyDelete
  7. Ron Piterman: thanks for the link, I will certainly take a look at this library.

    veggen: you are right, but the only point I tried to prove is that you don't need XML when working with Spring. I'm also aware that the same example can be implemented in JEE 6 with a similar ease.

    I don't feel capable to compare Spring with JEE, but I guess now it's a matter of preference. Spring application is self-contained (JPA implementation, JMS, etc.), while JEE 6 provides this services out-of-the-box in AS. On the other hand Spring framework ships with support for a vast number of less-standard technologies like: batch processing, social networking, EIP, NoSQL, OSGi.

    As always, a matter of choosing the right tool.

    ReplyDelete
  8. Hi Tomek,

    Thanks a lot for your post !!

    Coud you tell us if you compared the starting time difference ? Is Spring+JavaConfig faster than Spring+XML at starting time?

    thanks in advance for you feedback

    best regards

    JM

    ReplyDelete
  9. sorry tomek i posted as anonymous due to fu....ing proxy !!

    here my email

    jmadridpro at gmail dot com

    ReplyDelete
  10. JM: I have no idea what is the performance impact of using @Configuration vs. XML, but as far as I can tell, reading and interpreting the configuration doesn't take that much time compared to the actual bean startup.

    Of course your mileage may vary, but I wouldn't assume @Configuration approach being much faster than XML parsing.

    ReplyDelete
  11. Thanks for the info. Very useful.
    I tried your approach while testing some AspectJ example code and found that by adding a factory method to the @Configuration bean, those sample aspects were discovered correctly. Haven't tested it on a larger application, though.
    Here's the code:


    import org.springframework.context.annotation.Configuration;

    @Bean
    public AnnotationAwareAspectJAutoProxyCreator aspectJAutoProxyCreator() {
    return new AnnotationAwareAspectJAutoProxyCreator();
    }

    -Óli

    ReplyDelete
  12. hmmm. Copied the wrong import statement. Here's the crucial class:

    import org.springframework.aop.aspectj.annotation.AnnotationAwareAspectJAutoProxyCreator;

    -Óli

    ReplyDelete
  13. Thanks for the post dude but its really bit longer may be the details required it but would have been better if its little shorter.

    Javin
    How classpath works in Java

    ReplyDelete
  14. Óli: thanks for the tip, I guess all of the XML constructs can be translated into methods in @Configuration bean. But I truly hope that Spring team will soon provide less complicated (annotations?) replacements (see first comment).

    Javin: I always find myself writing longer articles than necessary. And as you can see longer is not necessarily equal to better. I will keep your comment in memory!

    ReplyDelete
  15. Spring is not doing any periodical improvements, more less are inventions or enhancement. So better don't wait for Spring to come up with something, wrap spring in your ideas.
    @Tomek : The article is nice... The only point here is, you can do things without XML. But XML is always gives flexibility and clarity at code base when we compare with annotation. At the other side Annotation gives flexibility and clarity at development time.
    @Those who worried about performance : Both approach is loading and wiring things at startup. You have to think about performance of the application while its running.

    ReplyDelete
  16. great article Tomasz, no harm in explaining in detail either :)
    Keep up the good work.

    ReplyDelete
  17. Very interesting article in deed. I love the details, that's where the devil is.

    Thanks,

    Steinar

    ReplyDelete
  18. More Spring :)

    As I prefer using @annotations as much as I can there are things like applicationContext.xml , database and other configuration that seems more readble/convenient.

    ReplyDelete
  19. It's a big help in code refactoring. How about from a performance standpoint? is this faster than xml?

    ReplyDelete
  20. xml is a very interesting language to be used and contain the data object model or abbreviated DOM.tutorial very good and hopefully can help me in building a web application thanks

    ReplyDelete
  21. In Spring 3.1 you can use @ComponentScan annotation and combine this with Support for web applications what leaves you without any XML ;)

    ReplyDelete
  22. when replacing tx:annotation-driven by

    @Bean
    public TransactionAttributeSource annotationTransactionAttributeSource() {
    return new AnnotationTransactionAttributeSource();
    }
    @Bean
    public TransactionInterceptor transactionInterceptor() {
    return new TransactionInterceptor(transactionManager(), annotationTransactionAttributeSource());
    }

    It doesn't apply proxying for bean injections, and therefore does not apply TransactionInterceptor to method annotated as @Transactional.
    Have any ideas?

    ReplyDelete
    Replies
    1. Consider using @EnableTransactionManagement on @Configuration classes and get rid of custom beans you quote above. Note the this article is a bit outdated, I will write an update one day.

      Delete
  23. This post is gold. Thank you!

    ReplyDelete

Post a Comment