Spring Cleaning How to do more with less XML

Post on 06-Jan-2016

29 views 0 download

description

Spring Cleaning How to do more with less XML. Craig Walls Gateway Software Symposium 2007 craig@habuma.com 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? - PowerPoint PPT Presentation

Transcript of Spring Cleaning How to do more with less XML

Spring CleaningHow to do more with less XML

Craig WallsGateway Software Symposium 2007

craig@habuma.com

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

Q & ADon’t forget to turn in evals!!!

http://www.springinaction.com

craig@habuma.com