Spring Cleaning How to do more with less XML
description
Transcript of Spring Cleaning How to do more with less XML
Spring CleaningHow to do more with less XML
Craig WallsGateway Software Symposium 2007
Blog: http://www.springinaction.com
Wiki: http://wiki.habuma.com
About you…
• Java? .NET? Ruby/Rails? Erlang?– Java 6? Java 5? Java 1.4? Java 1.3? 1.2
or older?
• Who’s using Spring? How long?– Spring 1.2? Spring 2? Spring 2.1?
• Favorite session so far?
• What brings you to this session?
About me
• Agile developer with Semantra– Natural language business intelligence
• Developing software professionally for over 13 years– Telecom, Finance, Retail, Education– Java for most of that– Also some C/C++, C#/.NET, Ruby
• Spring fanatic
Spring sucks!
He’s not really trying to sell too many books, is he?
Spring sucks
• Spring is configured with XML
• XML is evil
• Evil sucks
• Therefore, Spring sucks
The so-called solutions to XML
• I don’t need no stinkin’ dependency injection!
• I’ll do it myself!
• Annotations
The truth about Spring and DI
• Spring != XML– Spring’s container is decoupled from its
configuration strategy
• Spring is more than just DI– Spring is a full application framework
But I’ll concede that…
• DI is at the core of everything you do in Spring
• Spring DI typically involves lots of XML
• XML can be verbose
• Let’s see how to do more Spring with less XML
Three plans of attack
• Smarter XML - Use Spring XML trickery to reduce verbosity
• Annotations - Use annotations to configure Spring
• Scripting - Use scripting to configure Spring
Disclaimer
• There is no one-size-fits-all fix– Apply an ounce of pragmatism
Spring XML done smartly
Honey, I shrunk the XML!
Smart Spring XML
• Shorthand XML
• Bean inheritence
• Property editors
• The “p” namespace
• Custom configuration
• Autowiring
• Arid POJOs (aka, extreme autowiring)
Shorthand XML
• Introduced in Spring 1.2
• Original <value> and <ref> elements replaced with value and ref attributes.
Shorthand XML in action
• Pre-Spring 1.2:<bean id="myBean" class="com.habuma.MyBeanImpl"> <property name="someProperty"> <value>This is a string value</value> </property> <property name="someReference"> <ref bean="someOtherBean" /> </property></bean>
• Spring 1.2+:<bean id="myBean" class="com.habuma.MyBeanImpl"> <property name="someProperty" value="This is a string" /> <property name="someReference" ref="someOtherBean" /></bean>
Shorthand XML: Tradeoffs
• Pros– More terse
• Cons– Can’t be used to when specifying values in
collections (well…maybe)
Bean inheritence
• Available in Spring since ???
• Declare common configuration details in a parent bean
• Create sub-beans that inherit from the parent
Bean inheritence example 1
<bean id="knightParent” class="com.springinaction.knight. KnightOfTheRoundTable" abstract="true">
<property name="quest" ref="quest" /> <property name="horse" ref="horse" /> <property name="sword" ref="sword" /> <property name="armor" ref="armor" /> <property name="shield" ref="shield" /></bean>
<bean id="knight" parent="knightParent"> <constructor-arg value="Bedivere" /> </bean>
Parent type andproperties areinherited
Bean inheritence example 2
<bean id="knightParent" abstract="true"> <property name="quest" ref="quest" /> <property name="horse" ref="horse" /> <property name="sword" ref="sword" /> <property name="armor" ref="armor" /> <property name="shield" ref="shield" /></bean>
<bean id="knight" class="com.springinaction.knight. KnightOfTheRoundTable" parent="knightParent"> <constructor-arg value="Bedivere" /> </bean>
Only propertiesare inherited
Bean inheritence tradeoffs
• Pros– Helps keep Spring configurations more
DRY
• Cons– A little tricky to navigate bean hierarchies…
especially without tool support
Property editors
• Supported in all versions of Spring– Actually part of the JavaBeans spec
• Express complex configurations as simpler strings– Property editors help Spring convert simple
strings to complex objects
Spring’s built-in property editors
• ByteArrayPropertyEditor• CharacterEditor• CharArrayPropertyEditor• ClassArrayEditor• ClassEditor• CustomBooleanEditor• CustomCollectionEditor• CustomDateEditor• CustomMapEditor• CustomNumberEditor
• FileEditor• InputStreamEditor• LocaleEditor• PatternEditor• PropertiesEditor• ResourceBundleEditor• StringArrayPropertyEdito
r• StringTrimmerEditor• URIEditor• URLEditor
Property editors in action
In Java…public class KnightOnCall implements Knight { ... private URL url; public void setUrl(URL url) { this.url = url; }
private PhoneNumber phoneNumber; public void setPhoneNumber(PhoneNumber phoneNumber) { this.phoneNumber = phoneNumber; }}
In the XML…<bean id="knight" class="com.springinaction.knight.KnightOnCall"> <property name="url" value="http://www.knightoncall.com" /> <property name="phoneNumber" value="940-555-1234" /></bean>
Registering a customer editor
<bean id="customEditorConfigurer" class= "org.springframework.beans.factory.config.CustomEditorConfigurer"> <property name="customEditors"> <map> <entry key="com.springinaction.knight.PhoneNumber"> <bean id="phoneEditor" class= "com.springinaction.springcleaning.PhoneNumberEditor" /> </entry> </map> </property></bean>
Spring MVC & property editors
• In Spring MVC, you might configure SimpleUrlHandlerMapping like this…
<bean id="urlMapping" class="org.springframework.web.servlet.handler.
SimpleUrlHandlerMapping"> <property name="mappings"> <props> <prop key="/home.htm">homeController</prop> <prop key="/login.htm">loginController</prop> <prop key="/addspittle.htm">addSpittleController</prop> <prop key="/addspitter.htm">addSpitterController</prop> </props> </property></bean>
“mappings” isjava.util.Properties
Spring MVC w/property editors
• But PropertiesEditor can make it simpler<bean id="urlMapping" class="org.springframework.web.servlet.handler.
SimpleUrlHandlerMapping"> <property name="mappings"> <value> /home.htm=homeController /login.htm=loginController /addSpittle.htm=addSpittleController /addSpitter.htm=addSpitterController </value> </property></bean>
Allow me to digress…
• Although not related to property mappings at all, Spring 2.0 introduces some handy XML-saving features…
<bean id="urlMapping” class="org.springframework.web.servlet.mvc.support. ControllerClassNameHandlerMapping" />
• ControllerClassNameHandlerMapping automatically maps controllers to URL patterns based on the controller’s class name.
Property editors tradeoffs
• Pros– Complex types that normally would require
lines of XML can be expressed as simple strings
• Cons– Not always apparent what type is being
created– Looks weird if you don’t know what’s going
on
The “p” namespace
• New in Spring 2.0
• Enables very terse injection of properties as attributes of the <bean> element
• Made available with…<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
“p” example
<bean id="knight" class="com.springinaction.knight.KnightOfTheRoundTable" p:quest-ref="quest" p:horse-ref="horse" p:sword-ref="sword" p:armor-ref="armor" p:shield-ref="shield"> <constructor-arg value="Bedivere" /> </bean>
“p” namespace tradeoffs
• Pros– Super terse
• Cons– May seem alien to developers not familiar
with it
Custom configuration elements
• Available since Spring 2.0• Encapsulate complex bean configuration
behind simpler XML elements.• Spring 2.0 comes with several out-of-the-box
namespaces– aop, jee, lang, tx, util
• More coming in Spring 2.1:– context, jms
• Other Spring projects include (or will include) custom elements:– Spring Security, Spring Modules, etc
“jee” namespace example
• Configure JNDI object using <bean>:<bean id="dataSource"
class="org.springframework.jndi.JndiObjectFactoryBean"> <property name="jndiName" value="/jdbc/RantzDatasource" /> <property name="resourceRef" value="true" /></bean>
• Using <jee:jndi-lookup>:<jee:jndi-lookup id="dataSource" jndi-name="/jdbc/RantzDatasource" resource-ref="true" />
How to build custom element
• Create namespace schema (XSD)
• Create namespace handler class
• Create element parser class
• Create META-INF/spring.schemas– Maps schemas to physical XSD file
• Create META-INF/spring.handlers– Maps schemas to namespace handlers
Custom element tradeoffs
• Pros– Simplifies XML configuration– Enables domain-specific configuration– More expressive
• Cons– Hides what is really being configured (that
may be a good thing, though)
Autowiring
• Spring’s so smart…let it figure out how to wire up your bean properties
• Autowiring comes in five flavors:– No - Do not autowire– byName - Inject beans into properties where the bean’s ID
matches the property’s name– byType - Inject beans into properties where the bean’s type
is assignable to a property– constructor - Choose a constructor where Spring can inject
beans (by type) into the constructor’s arguments– autoDetect - Try constructor first, then byType
Autowiring
• Autowiring strategy can be specified on a per-bean basis or a per-XML file basis:– Per bean: Set the autowire attribute on the
individual <bean> elements.• Available in all versions of Spring
– Per XML-file: Set the default-autowire attribute on the <beans> element.
• Available since Spring 2.0
Autowiring example (per bean)
<bean id="knight” class="com.springinaction.knight.KnightOfTheRoundTable" autowire="byType">
<constructor-arg value="Bedivere" />
</bean>
Injects allproperties
Still must inject constructor args
Autowiring example (per file)
<?xml version="1.0" encoding="UTF-8"?><beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="…" default-autowire="byType">
<bean id="knight" class="com.springinaction.knight.KnightOfTheRoundTable” autowire="no"> <constructor-arg value="Bedivere" /> </bean>
...
</beans>
All beans willbe autowired
“byType”
Unless overriddenon each bean
Autowiring tradeoffs
• Pros– Can dramatically reduce the amount of XML in a Spring
configuration
• Cons– Along with terseness comes lack of clarity. What was wired
where?– Visualization tools (Spring IDE, BeanDoc) won’t recognize
autowired beans as being wired.– byName autowiring couples configuration to implementation
details– byType and constructor can be problematic when there are
ambiguities
Arid POJOs
• Spring add-on by Chris Richardson (POJOs in Action)
• Available at http://code.google.com/p/aridpojos
• Turns auto-wiring up a notch– Automatically declare and autowire all beans in a
specified package (or packages)
• Based on notion that all beans are declared similarly– Also has an auto-DAO feature
Arid POJOs
• Add to Spring config with…<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:arid="http://chrisrichardson.net/schema/arid" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd http://chrisrichardson.net/schema/arid http://chrisrichardson.net/schema/arid.xsd">
Arid POJOs example 1
• Automatically declare all beans in a package and then autowire them “byType”…
<arid:define-beans package="com.habuma.dao" autowire="byType" />
Arid POJOs tradeoffs
• All the same pros and cons as autowiring…– Just more so
Annotating Spring
Dependency injection is where it’s @
Annotations and Spring
• Use @AspectJ for aspects
• Use @Transactional for transactions
• Spring JavaConfig
• Spring 2.1 annotations
Spring without @AspectJ
• Prior to Spring 2.0, AOP was a clumsy mess of XML:
<bean id="knight" class="org.springframework.aop.framework.ProxyFactoryBean"> <property name="target" ref="knightTarget" /> <property name="interceptorNames" value="pointcutAdvisor" /> <property name="proxyInterfaces" value="com.springinaction.knight.Knight" /></bean>
<bean id="knightTarget" class="com.springinaction.knight.KnightOfTheRoundTable">...</bean>
<bean id="pointcutAdvisor" class="org.springframework.aop.support.RegExpPointcutAdvisor"> <property name="pattern" value=".*embark.*" /> <property name="advice" value="minstrel" /></bean>
<bean id="minstrel" class="com.springinaction.knight.MinstrelAdvice" />
This is just weird
Spring without @AspectJ
• Spring 2.0’s “aop” namespace made things a little bit better…
<bean id="minstrel" class="com.springinaction.knight.Minstrel" />
<aop:config> <aop:aspect ref="minstrel"> <aop:after-returning method="sing" pointcut= "execution(* *.Knight.embarkOnQuest(..))" /> </aop:aspect></aop:config>
Spring with @AspectJ
• Spring 2.0 also introduced integration with @AspectJ– Now aspects require only minimal XML
• <aop:aspectj-autoproxy/>• One bean declaration for each aspect class
• Not true AspectJ aspect– Still Spring proxy– Just uses @AspectJ annotations
@AspectJ example
@Aspectpublic class Minstrel { @Pointcut("execution(* *.Knight.embarkOnQuest(..))") public void embark() {}
@AfterReturning("embark()") public void sing() { System.out.println("Fa la la!"); System.out.println("The brave knight is embarking on a quest!"); }}
@AspectJ example
• In the XML…
<aop:aspectj-autoproxy /><bean class="Minstrel" />
• Yep…that’s it.
@AspectJ tradeoffs
• Pros– Significantly less XML required for aspects
• Cons– Couples aspect classes to AspectJ– Not all AspectJ pointcuts available; still
proxy-based
@Transactional
• Prior to Spring 2.0, transactions were just as messy as other types of aspects– TransactionProxyFactoryBean instead of
ProxyFactoryBean– Bean inheritence helped a little
The “tx” namespace
• Spring 2.0 added the “tx” namespace
• Made things a bit simpler…
<tx:advice id="txAdvice"> <tx:attribute> <tx:method name="add*" propagation="required" /> <tx:method name="*" propagation="supports" read-only="true"/> </tx:attributes></tx:advice>
@Transactional
• Spring 2.0 also introduced the @Transactional annotation– Very appropriate use of annotations
• Transactions declared with minimal XML
@Transactional example
• In Java:@Transactional(propagation=Propagation.SUPPORTS)public class CustomerServiceImpl implements CustomerService { @Transactional(propagation=Propagation.REQUIRED) public void addNewCustomer(Customer customer) { ... }...}
• In XML:<tx:annotation-driven />
@Transactional tradeoffs
• Pros– Like @AspectJ, very very little XML
required
• Cons– Invasive--Spring annotations couple your
code to Spring
Spring JavaConfig
• Add-on for Spring– http://www.springframework.org/javaconfig
• Currently at version 1.0-M2a
• Recreates Spring XML configuration in Java using annotations
• Provides several annotations for Spring configuration:– @Configuration - Declares class as a configuration class– @Bean - Declares a method as a bean declaration– @ExternalBean - Declares an abstract method as a
reference to an externally defined bean– @AutoBean - Declares an abstract method to server as a
holder for automatically instantiated/wired bean– @ScopedProxy - Used to declare scoped proxy for a bean
(non-singleton/non-prototype)
Spring JavaConfig
• Two ways to use JavaConfig:– Use AnnotationApplicationContext
• Simple, no-XML approach• Hard to use with webapps• Can’t parameterize configuration instances
– Configure a ConfigurationPostProcessor (in XML)
• Easy to use with web apps (using minimal bootstrap XML)
• Configuration can be parameterized
Loading JavaConfig
• AnnotationApplicationContext:ApplicationContext ctx = new AnnotationApplicationContext( MyConfig.class.getName());
• ConfigurationPostProcessor:<bean class="com.habuma.samples.MyJavaConfig" /><bean class= "o.sf.config.java.process.ConfigurationPostProcessor" />
JavaConfig example@Configurationpublic abstract class KnightConfig { @Bean public Knight knight() { KnightofTheRoundTable knight = new KnightOfTheRoundTable("Bedivere"); knight.setQuest(quest()); return knight; }
@Bean private Quest quest() { return new HolyGrailQuest(); }
@ExternalBean private abstract Horse horse();}
JavaConfig tradeoffs
• Pros– Minimally invasive - annotations are confined to
configuration-specific classes– Dynamic - Use any Java constructs you like– Testable - Easily write unit tests against
configuration itself– Refactorable - No static identifiers– Offers bean visibility using Java constructs– Parameterizable if using bootstrap XML
• Cons– Non-intuitive - Structured like Spring XML, but
looks like Java
Spring 2.5 annotations
• Spring 2.5 will add a few new annotations– @Component - Indicates that a class is a
component that should be registered in Spring– @Autowired - Indicates that a property should be
autowired– @Scoped - Declares scoping on auto-detected
bean
• Works with new <context:component-scan /> configuration element
<context:component-scan>
• Scans a package and all of its subpackages• Auto-configures all beans annotated with
@Component, @Repository, or @Aspect• Autowires (byType) all properties and
methods that are annotated with @Autowired• Also supports some JSR-250 annotations
– @PostConstruct, @PreDestroy, @Resource, @Resources
Spring 2.5 annotation example@Component("knight")public class KnightOfTheRoundTable implements Knight { private String name; private Quest quest; private Horse horse;
... public KnightOfTheRoundTable(String name) { this.name = name; }
@Resource public void setQuest(Quest quest) { this.quest = quest; }
@Autowired private void myKingdomForAHorse(Horse horse) { this.horse = horse; }}
Spring 2.5 annotation example<?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.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.1.xsd">
<context:component-scan base-package="com.springinaction.knight" />
<bean id="knight" class="com.springinaction.knight.KnightOfTheRoundTable"> <constructor-arg value="Bedivere" /> </bean></beans>
Spring 2.5 annotation tradeoffs
• Pros– Moves configuration details closer to the
beans being configured (DRY)– Injection no longer limited to public setter
methods and constructors
• Cons– Moves configuration details closer to the
beans being configured (invasive)– Could be static identifiers
Scripting Spring
Cut XML and be buzzword compliant at the same time
Scripting Spring Configuration
• Springy (JRuby)
• Grails Spring Builder (Groovy)
Springy
• Provides a Ruby DSL for configuring a Spring application context– http://code.trampolinesystems.com/springy– Current version is 0.2– Apache license
Loading a Springy context
• Programatically:
ApplicationContext ctx = new JRubyApplicationContext( new ClassPathResource("com/habuma/samples/ctx.rb"));
• No obvious way to use with web applications…bummer…
Springy example
bean :knight, "com.springinaction.knight.KnightOfTheRoundTable" do |b| b.new "Bedivere” b.quest = :quest...end
bean :quest, "com.springinaction.knight.HolyGrailQuest" do |b| b.newend
Springy example 2
• Can you do this in Spring XML?
for num in (1..10) bean :"knight#{num}", "com.springinaction.knight.KnightOfTheRoundTable" do |b| b.new "Bedivere" b.quest = :quest endend
Springy and inline XML
• If you absolutely must use XML…
inline_xml do <<XML <bean id="dragonQuest" class="com.sia.knight.SlayDragonQuest" />XMLend
Springy: Serialize to XML
• Get Spring XML from a JRuby-defined context:
((JRubyApplicationContext) ctx).getContextAsXml();
Springy tradeoffs
• Pros– Completely XML free
• Unless you want to inline some XML
– All of JRuby available for defining a Spring context
• Cons– Serializes to XML then reloads it
• Performance implications
– No clear way to use in a web app
Grails Spring Bean Builder
• Provides a Groovy DSL to configure a Spring context
• Part of Grails– In grails-core-0.5.6.jar– http://www.grails.org/Spring+Bean+Builder
Bean Builder exampledef bb = new grails.spring.BeanBuilder()bb.beans { quest(HolyGrailQuest) {} horse(Horse) {} sword(Sword) {} shield(Shield) {} armor(Armor) {} knight(KnightOfTheRoundTable, "Bedivere") { delegate.quest = quest delegate.horse = horse delegate.sword = sword delegate.shield = shield delegate.armor = armor }}
ApplicationContext ctx = bb.createApplicationContext()def knight = ctx.getBean("knight")knight.embarkOnQuest()
Bean Builder tradeoffs
• Pros– Completely XML free– Can use all of Groovy’s goodness to configure
Spring
• Cons– Not clear how to use it outside of a Groovy script– Not clear how to use it in a web app (aside from
Grails)– (just a nit) Not separate from Grails
• Must include Grails in your application classpath
Recap
He made the XML shorter…too bad he couldn’t have done the
same thing with the presentation
What we have learned
• Spring XML sucks…– If you don’t take advantage of the tricks to cut the
clutter
• Spring and annotations : Not a zero sum game– Spring encourages proper use of annotations (and
tolerates improper use)
• Spring != XML– Spring is more than just a configuration
mechanism– JRuby, Groovy, and annotation configuration
alternatives
A few final Spring tips
• You don’t have to wire everything!– Use sensible defaults– Case in point: Spring MVC command controllers
commandName and commandClass properties
• Remember that there are two types of configuration…– Internal: Use Spring– External: Perhaps PropertyPlaceholderConfigurer
or PropertyOverrideConfigurer
• Don’t put all of your beans in one XML file– Break your Spring context down– Perhaps by application layer or functional divisions