60 seconds to Spring
Introduction
Spring is a layered Java/J2EE application framework, based on code published in Expert One-on-One J2EE Design and Development by Rod Johnson (Wrox, 2002).
This a series of very short tutorial giving you a jump start understanding of Spring Framework in 60 seconds (per tutorial). It does not go into why you should do it, read the Spring Reference Documentation for that.
Pre-requisites
Before running any of the tutorial on your machine, you need to get the below few thing done first.
-
Installed ANT 1.6.1 plus environment setting and some simple knowledge on using ANT.
Tutorials Source Code
60sec2Spring.zip (spring.jar included)
Tutorials
Bean Tutorial 1 - Hello World Bean
Bean Tutorial 2 - To Singleton or Not To Singleton
Bean Tutorial 3 - Setter-Based and Construction-Based Dependency Injection
第一个例子,就是用xml文件创建bean,再也不用自己写factory了。
第二个例子就更简单
singleton:就相当于static
第三个例子,第一个例子解决了对象创建的问题,现在就是依赖bean的调用问题,
依赖bean有两种方式调用,一种是构造函数,一种是set方法,
这没什么特殊的,我在swing中大量使用过。
但奇怪的是,在main方法中没有调用set或者构造方法。
这里同样是用xml创建了bean,并且还隐秘的创建了依赖bean(他已经自动找到了),不需要我们再调用(不要再找他),呵呵,不只是省力,更体现了IoC吧。
reference提到了三种Injection的形式,它们分别是Interface Injection、Setter Injection和Constructor Injection,对应着IoC中谈到的type 1、type 2、type 3。除了Interface Injection由于较强的侵略性,其它的两种形式,并没有什么不同。Spring和Pico都实现了这两种形式的Injection,只是Spring推崇Setter Injection,而Pico倾向于Constructor Injection。
当两个组件需要相互通信时,Setter Injection表现得明显要比Constructor Injection好。
60 sec to Spring [TOC]
Bean Tutorial 1 - Hello World Bean
This tutorial show you how to call a Hello World Bean using Spring IoC.
Step 1: Create a HelloBean.java in src
org/jarchitect/spring/tutorial1/HelloBean.java
package org.jarchitect.spring.tutorial1; public class HelloBean { public String sayHelloWorld() { return "Hello World"; } } |
Step 2: Specify the HelloBean class in the bean.xml
bean.xml
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd"> <beans> <description>jarchitect Spring Tutorial 1</description> <bean id="helloBean" class="org.jarchitect.spring.tutorial1.HelloBean"/> </beans> |
Step 3: Create a Main.java in src
org/jarchitect/spring/tutorial1/Main.java
package org.jarchitect.spring.tutorial1; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.InputStream; import org.springframework.beans.factory.xml.XmlBeanFactory; public class Main { public static void main(String[] args) { try { //Read the configuration file InputStream is = new FileInputStream("bean.xml"); XmlBeanFactory factory = new XmlBeanFactory(is); //Instantiate an object HelloBean helloBean = (HelloBean) factory.getBean("helloBean"); //Execute the public method of the HelloBean System.out.println(helloBean.sayHelloWorld()); } catch (FileNotFoundException e) { e.printStackTrace(); } } } |
Step 4: Use an ANT script to build and execute the Main class. Just run ant from the command prompt will do the trick.
Below are the output from ANT.
C:/60sec2Spring/SpringTutorial1>ant
Buildfile: build.xml
build:
[javac] Compiling 2 source files to C:/60sec2Spring/SpringTutorial1/bin
run:
[java] May 18, 2004 2:25:14 PM org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions
[java] INFO: Loading XML bean definitions from (no description)
[java] May 18, 2004 2:25:15 PM org.springframework.beans.factory.support.AbstractBeanFactory getBean
[java] INFO: Creating shared instance of singleton bean 'helloBean'
[java] Hello World BUILD SUCCESSFUL
Total time: 3 second |
Done.
[TOC]
Bean Tutorial 2 - To Singleton or Not To Singleton
Beans are defined to be deployed in one of the two modes: Singleton and Non-Singleton (Prototype). When a bean is a singleton, only one shared instance of the bean will be managed. The prototype mode of a bean deployment results in the creation of a new bean instance every time a request for that specific bean is done. Tutorial 2 will demostrate the difference behaviour when defining a bean as Singleton and Prototype.
Note: By default all beans are deployed in singleton mode, unless you specify otherwise.
Step 1: Create a CounterBean.java in src
org/jarchitect/spring/tutorial2/CounterBean.java
package org.jarchitect.spring.tutorial2;
public class CounterBean {
private int counter;
public CounterBean() {
System.out.println("Construct CounterBean......");
}
public int getCount() {
return counter++;
}
}
Step 2: Specify the CounterBean class in the bean.xml
Do notice that I have defined one CounterBean as singleton bean and the other as prototype bean.
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN"
"http://www.springframework.org/dtd/spring-beans.dtd">
<beans>
<description>jarchitect Spring Tutorial 2</description>
<bean id="singletonBean" class="org.jarchitect.spring.tutorial2.CounterBean" singleton="true"/>
<bean id="prototypeBean" class="org.jarchitect.spring.tutorial2.CounterBean" singleton="false"/>
</beans>
Step 3: Create a Main.java in src
org/jarchitect/spring/tutorial2/Main.java
package org.jarchitect.spring.tutorial2;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.InputStream;
import org.springframework.beans.factory.xml.XmlBeanFactory;
public class Main {
public static void main(String[] args) {
try {
//Read the configuration file
InputStream is = new FileInputStream("bean.xml");
XmlBeanFactory factory = new XmlBeanFactory(is);
//Instantiate an singleton object
System.out.println("---- Singleton Bean ----");
CounterBean singletonBean =
(CounterBean) factory.getBean("singletonBean");
System.out.println("First Call :- " + singletonBean.getCount());
singletonBean = (CounterBean) factory.getBean("singletonBean");
System.out.println("Second Call :- " + singletonBean.getCount());
System.out.println("");
//Instantiate an object
System.out.println("---- Prototype Bean ----");
CounterBean prototypeBean =
(CounterBean) factory.getBean("prototypeBean");
System.out.println("First Call :- " + prototypeBean.getCount());
prototypeBean = (CounterBean) factory.getBean("prototypeBean");
System.out.println("Second Call :- " + prototypeBean.getCount());
} catch (FileNotFoundException e) {
e.printStackTrace();
}
}
}
Step 4: Use an ANT script to build and execute the Main class. Just run ant from the command prompt will do the trick.
Below are the output from ANT.
C:/60sec2Spring/SpringTutorial2>ant
Buildfile: build.xml
build:
[javac] Compiling 2 source files to C:/60sec2Spring/SpringTutorial2/bin
run:
[java] Jun 1, 2004 12:16:42 PM org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions
[java] INFO: Loading XML bean definitions from (no description)
[java] Jun 1, 2004 12:16:42 PM org.springframework.beans.factory.support.AbstractBeanFactory getBean
[java] INFO: Creating shared instance of singleton bean 'singletonBean'
[java] ---- Singleton Bean ----
[java] Construct CounterBean......
[java] First Call :- 0
[java] Second Call :- 1
[java] ---- Prototype Bean ----
[java] Construct CounterBean......
[java] First Call :- 0
[java] Construct CounterBean......
[java] Second Call :- 0
BUILD SUCCESSFUL
Total time: 2 seconds
Done.
[TOC]
Note: By default all beans are deployed in singleton mode, unless you specify otherwise.
package org.jarchitect.spring.tutorial2; public class CounterBean { private int counter; public CounterBean() { System.out.println("Construct CounterBean......"); } public int getCount() { return counter++; } }
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd"> <beans> <description>jarchitect Spring Tutorial 2</description> <bean id="singletonBean" class="org.jarchitect.spring.tutorial2.CounterBean" singleton="true"/> <bean id="prototypeBean" class="org.jarchitect.spring.tutorial2.CounterBean" singleton="false"/> </beans>
package org.jarchitect.spring.tutorial2; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.InputStream; import org.springframework.beans.factory.xml.XmlBeanFactory; public class Main { public static void main(String[] args) { try { //Read the configuration file InputStream is = new FileInputStream("bean.xml"); XmlBeanFactory factory = new XmlBeanFactory(is); //Instantiate an singleton object System.out.println("---- Singleton Bean ----"); CounterBean singletonBean = (CounterBean) factory.getBean("singletonBean"); System.out.println("First Call :- " + singletonBean.getCount()); singletonBean = (CounterBean) factory.getBean("singletonBean"); System.out.println("Second Call :- " + singletonBean.getCount()); System.out.println(""); //Instantiate an object System.out.println("---- Prototype Bean ----"); CounterBean prototypeBean = (CounterBean) factory.getBean("prototypeBean"); System.out.println("First Call :- " + prototypeBean.getCount()); prototypeBean = (CounterBean) factory.getBean("prototypeBean"); System.out.println("Second Call :- " + prototypeBean.getCount()); } catch (FileNotFoundException e) { e.printStackTrace(); } } }
C:/60sec2Spring/SpringTutorial2>ant
Buildfile: build.xml
build:
[javac] Compiling 2 source files to C:/60sec2Spring/SpringTutorial2/bin
run:
[java] Jun 1, 2004 12:16:42 PM org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions
[java] INFO: Loading XML bean definitions from (no description)
[java] Jun 1, 2004 12:16:42 PM org.springframework.beans.factory.support.AbstractBeanFactory getBean
[java] INFO: Creating shared instance of singleton bean 'singletonBean'
[java] ---- Singleton Bean ----
[java] Construct CounterBean......
[java] First Call :- 0
[java] Second Call :- 1
[java] ---- Prototype Bean ----
[java] Construct CounterBean......
[java] First Call :- 0
[java] Construct CounterBean......
[java] Second Call :- 0
BUILD SUCCESSFUL
Total time: 2 seconds
Bean Tutorial 3 - Setter-Based and Construction-Based Dependency Injection
Inversion of Control/Dependency Injection exists in two major variants:
-
Setter-based dependency injection is realized by calling setters on your beans after invoking a no-argument constructor to instantiate your bean. Beans defined in the BeanFactory that use setter-based dependency injection are true JavaBeans. Spring generally advocates usage of setter-based dependency injection, since a large number of constructor arguments can get unwieldy, especially when some properties are optional.
-
Constructor-based dependency injection is realized by invoking a constructor with a number of arguments, each representing a collaborator or property.
Step 1: We will need two dummy classes for our tutorial, AnotherBean.java and YetAnotherBean.java.
org/jarchitect/spring/tutorial3/AnotherBean.java
package org.jarchitect.spring.tutorial3; public class AnotherBean { public AnotherBean() { System.out.println("Construct AnotherBean"); } } |
org/jarchitect/spring/tutorial3/YetAnotherBean.java
package org.jarchitect.spring.tutorial3; public class YetAnotherBean { public YetAnotherBean() { System.out.println("Construct YetAnotherBean"); } } |
Step 2: We will create a ConstructorExampleBean and a SetterExampleBean.
org/jarchitect/spring/tutorial3/ConstructorExampleBean.java
package org.jarchitect.spring.tutorial3; public class ConstructorExampleBean { private AnotherBean beanOne; private YetAnotherBean beanTwo; private int i; public ConstructorExampleBean( AnotherBean anotherExampleBean, YetAnotherBean yetAnotherBean, int i) { this.beanOne = anotherExampleBean; this.beanTwo = yetAnotherBean; this.i = i; System.out.println("Construction Done!!!"); } public ConstructorExampleBean() { System.out.println("Default Constructor : Should not be call!!!"); } } |
org/jarchitect/spring/tutorial3/SetterExampleBean.java
package org.jarchitect.spring.tutorial3; public class SetterExampleBean { private AnotherBean beanOne; private YetAnotherBean beanTwo; private int i; public SetterExampleBean() { System.out.println("Default Constructor"); } public void setBeanOne(AnotherBean bean) { this.beanOne = bean; System.out.println("Set beanOne"); } public void setBeanTwo(YetAnotherBean bean) { this.beanTwo = bean; System.out.println("Set beanTwo"); } public void setIntegerProperty(int i) { this.i = i; System.out.println("Set i"); } } |
Step 3: Specify the HelloBean class in the bean.xml
bean.xml
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd"> <beans> <description>jarchitect Spring Tutorial 3</description> <bean id="setterBean" class="org.jarchitect.spring.tutorial3.SetterExampleBean"> <description>Setter-based Dependency Injection.</description> <property name="beanOne"><ref bean="anotherExampleBean"/></property> <property name="beanTwo"><ref bean="yetAnotherBean"/></property> <property name="integerProperty"><value>1</value></property> </bean> <bean id="constructorBean" class="org.jarchitect.spring.tutorial3.ConstructorExampleBean"> <description>Constructor Dependency Injection</description> <constructor-arg><ref bean="yetAnotherBean"/></constructor-arg> <constructor-arg><ref bean="anotherExampleBean"/></constructor-arg> <constructor-arg index="2"><value>1</value></constructor-arg> </bean> <bean id="anotherExampleBean" class="org.jarchitect.spring.tutorial3.AnotherBean"/> <bean id="yetAnotherBean" class="org.jarchitect.spring.tutorial3.YetAnotherBean"/> </beans> |
Step 4: reate a Main.java in src
org/jarchitect/spring/tutorial3/Main.java
package org.jarchitect.spring.tutorial3; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.InputStream; import org.springframework.beans.factory.xml.XmlBeanFactory; public class Main { public static void main(String[] args) { try { //Read the configuration file InputStream is = new FileInputStream("bean.xml"); XmlBeanFactory factory = new XmlBeanFactory(is); //Instantiate SetterExampleBean SetterExampleBean setterBean = (SetterExampleBean) factory.getBean("setterBean"); //Instantiate ConstructorExampleBean ConstructorExampleBean constructorBean = (ConstructorExampleBean) factory.getBean("constructorBean"); } catch (FileNotFoundException e) { e.printStackTrace(); } } } |
Step 5: Use an ANT script to build and execute the Main class. Just run ant from the command prompt will do the trick.
Below are the output from ANT.
C:/60sec2Spring/SpringTutorial3>ant
Buildfile: build.xml
build:
[javac] Compiling 5 source files to C:/60sec2Spring/SpringTutorial3/bin
run:
[java] Jun 1, 2004 12:36:51 PM org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions
[java] INFO: Loading XML bean definitions from (no description)
[java] Jun 1, 2004 12:36:51 PM org.springframework.beans.factory.support.AbstractBeanFactory getBean
[java] INFO: Creating shared instance of singleton bean 'setterBean'
[java] Default Constructor
[java] Construct AnotherBean
[java] Jun 1, 2004 12:36:51 PM org.springframework.beans.factory.support.AbstractBeanFactory getBean
[java] INFO: Creating shared instance of singleton bean 'anotherExampleBean'
[java] Construct YetAnotherBean
[java] Set beanOne
[java] Set beanTwo
[java] Jun 1, 2004 12:36:51 PM org.springframework.beans.factory.support.AbstractBeanFactory getBean
[java] INFO: Creating shared instance of singleton bean 'yetAnotherBean'
[java] Set i
[java] Jun 1, 2004 12:36:51 PM org.springframework.beans.factory.support.AbstractBeanFactory getBean
[java] INFO: Creating shared instance of singleton bean 'constructorBean'
[java] Construction Done!!!
[java] Jun 1, 2004 12:36:51 PM org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory autowireConstructor
[java] INFO: Bean 'constructorBean' instantiated via constructor [public org.jarchitect.spring.tutorial3.
ConstructorExampleBean(org.jarchitect.spring.tutorial3.AnotherBean,org.jarchitect.spring.tutorial3.YetAnotherBean,int)]
BUILD SUCCESSFUL
Total time: 2 seconds |
Overtime
From bean.xml
...... <bean id="constructorBean" class="org.jarchitect.spring.tutorial3.ConstructorExampleBean"> <description>Constructor Dependency Injection</description> <constructor-arg><ref bean="yetAnotherBean"/></constructor-arg> <constructor-arg><ref bean="anotherExampleBean"/></constructor-arg> <constructor-arg index="2"><value>1</value></constructor-arg> </bean> ...... |
Notice the index="2", try removing it and run ANT again. You will have the following error.
....... [java] INFO: Creating shared instance of singleton bean 'constructorBean' [java] org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'constructorBean' defined in (no description): 3 constructor arguments specified but no matching constructor found in bean 'constructorBean'(hint: specify index arguments for simple parameters to avoid type ambiguities) [java] at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory. autowireConstructor(AbstractAutowireCapableBeanFactory.java:287) [java] at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory. createBean(AbstractAutowireCapableBeanFactory.java:181) [java] at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:159) [java] at org.jarchitect.spring.tutorial3.Main.main(Unknown Source) [java] Exception in thread "main" [java] Java Result: 1 BUILD SUCCESSFUL Total time: 1 second |
Reason: The argument is an int, but BeanFactory take it as a String, so the mapping isn't obvious to the matching algorithm. In case of primitives, it's recommended to specify the argument index explicitly.
More Reason: For example, constructors MyClass(bool flag, String toto, int moo, float cow). There is no way for the BeanFactory to tell which is which from the string representations of each.
Next, try swapping
<constructor-arg index="2"><value>1</value></constructor-arg> <constructor-arg><ref bean="anotherExampleBean"/></constructor-arg> <constructor-arg><ref bean="yetAnotherBean"/></constructor-arg>
and run ANT.
Done.