spring cleaning how to do more with less xml craig walls [email protected] blog: wiki:
TRANSCRIPT
Spring CleaningHow to do more with less XML
Craig Walls
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
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
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="oldKnight" class="com.habuma.expectations.beans.KnightOfTheRoundTable">
<constructor-arg>
<value>Robin</value>
</constructor-arg>
<property name="quest">
<ref bean="rescueDamselQuest" />
</property>
</bean>
• Spring 1.2+:<bean id="newKnight"
class="com.habuma.expectations.beans.KnightOfTheRoundTable">
<constructor-arg value="Bedivere" />
<property name="quest" ref="rescueDamselQuest" />
</bean>
Shorthand XML: Tradeoffs
• Pros– More terse
• Cons– Can’t be used when specifying values in
collections (well…maybe)
Bean inheritence
• Available in Spring since 1.?
• 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• StringArrayPropertyEditor• 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.
• Spring 2.5 adds even more Spring MVC goodies that reduce XML (we’ll get to that in a moment)
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.5:– 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 (IntelliJ IDEA does, however).
– 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" />
Annotations and Spring
• Use @AspectJ for aspects
• Use @Transactional for transactions
• Spring JavaConfig
• Spring 2.5 injection annotations
• Spring 2.5 Web MVC 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 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, @Controller, @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.5.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
Spring 2.5 annotated MVC
• Controllers can be Annotated with @Controller– So that <context:component-scan> can find and register them
• Also annotated with @RequestMapping– To map requests to handler methods
• Optionally annotated with @SessionAttributes– Transparently stores model attributes in session
• Parameters can be mapped with @RequestParam– To automatically map request parameters to handler method
parameters.
• Parameters can also be mapped with @ModelAttribute– Maps a model attribute to a method parameter
• @ModelAttribute can also be applied to methods– To populate the model with the output from the method
Minimal XML needed for MVC
<context:component-scan
base-package="com.habuma.spitter.mvc" />
<bean
class="org.springframework.web.servlet.
mvc.annotation.
AnnotationMethodHandlerAdapter" />
Auto-register all classes incom.habuma.spitter.mvc package
Map all @RequestMapping-annotated beans
Annotated controller example@Controller
@RequestMapping("/editPet.do")
@SessionAttributes("pet")
public class EditPetForm {
@ModelAttribute("types")
public Collection<PetType> populatePetTypes() { return this.clinic.getPetTypes(); }
@RequestMapping(type = "POST")
public String processSubmit(@ModelAttribute("pet") Pet pet, BindingResult result, SessionStatus status) {
new PetValidator().validate(pet, result);
if (result.hasErrors()) { return "petForm"; }
else {
this.clinic.storePet(pet);
status.setComplete();
return "redirect:owner.do?ownerId=" + pet.getOwner().getId();
}
}
}
Springy
• Provides a Ruby DSL for configuring a Spring application context– http://code.trampolinesystems.com/springy– Current version is 0.3– Apache licensed
Loading a Springy context
• Programatically:
ApplicationContext ctx = new SpringyContext( 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 <beans> <bean id="dragonQuest" class="com.sia.knight.SlayDragonQuest" /> </beans>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
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