820 likes | 998 Views
Spring Cleaning How to do more with less XML. Craig Walls Gateway Software Symposium (St. Louis) September 29, 2007 craig-sia@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?
E N D
Spring CleaningHow to do more with less XML Craig Walls Gateway Software Symposium (St. Louis) September 29, 2007 craig-sia@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?
About me • Agile developer with Semantra • Natural language data access • 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 • 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 and properties are inherited
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 properties are 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
ByteArrayPropertyEditor CharacterEditor CharArrayPropertyEditor ClassArrayEditor ClassEditor CustomBooleanEditor CustomCollectionEditor CustomDateEditor CustomMapEditor CustomNumberEditor FileEditor InputStreamEditor LocaleEditor PatternEditor PropertiesEditor ResourceBundleEditor StringArrayPropertyEditor StringTrimmerEditor URIEditor URLEditor Spring’s built-in property editors
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” is java.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 all properties 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 will be autowired “byType” Unless overridden on 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 @Aspect public 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.