spring cleaning how to do more with less xml craig walls [email protected] blog: wiki:

86
Spring Cleaning How to do more with less XML Craig Walls [email protected] Blog: http://www.springinaction.com Wiki: http://wiki.habuma.com

Upload: eric-norris

Post on 31-Dec-2015

221 views

Category:

Documents


5 download

TRANSCRIPT

Spring CleaningHow to do more with less XML

Craig Walls

[email protected]

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="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" />

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.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 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, @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();

}

}

}

Annotated MVC pros/cons

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.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

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

[email protected]