示例:Chinese Idol 中国偶像选秀节目
下面是一个表演者的接口,其中有表演方法,当然,表演会有意外,需要抛出异常
package com.li.spring.chineseido;
public interface Performer {
void perform() throws PerformanceException;
}
然后我们定义一个类:Juggler,为杂技表演者,该类继承表演者接口,并实现表演方法。
package com.li.spring.chineseidol;
import java.io.File;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class Juggler implements Performer {
private int balls=3;
public int getBalls() {
return balls;
}
public void setBalls(int balls) {
this.balls = balls;
}
@Override
public void perform() throws PerformanceException {
System.out.println("JUGGLER "+balls+" BALLS");
}
public Juggler() {
super();
}
public Juggler(int balls) {
this.balls=balls;
}
public static void main(String[] args) {
ApplicationContext ctx=new ClassPathXmlApplicationContext("com"+File.separator+"li"+File.separator+"spring"+File.separator+"chineseido"+File.separator+"chinese-ido.xml");
Performer performer=(Performer) ctx.getBean("li");
try {
performer.perform();
} catch (PerformanceException e) {
e.printStackTrace();
}
}
}
在Spring中配置chinese-idol.xml中声明
<bean id="li" class="com.li.spring.chineseido.Juggler">
bean>
Bean的名字由id属性定义,即这个Bean被称为 “li”,其中class指出对应的类,使用类的全限定名。当Spring容器加载该Bean时,Spring将使用默认的构造器来实例化 “li” Bean:
即,new com.li.spring.chineseidol.Juggle();
代码加载上下文:
ApplicationContext ctx=new ClassPathXmlApplicationContext("com"+File.separator+"li"+File.separator+"spring"+File.separator+"chineseido"+File.separator+"chinese-ido.xml");
Performer performer=(Performer) ctx.getBean("li");
try {
performer.perform();
} catch (PerformanceException e) {
e.printStackTrace();
}
其中 File.separator 是文件路径分隔符,也可用双反斜杠替代。其中,getBean()方法来获取Bean实例,运行结果:
JUGGLER 3 BALLS
在Spring配置文件chiness-idol.xml中声明:
<bean id="li" class="com.li.spring.chineseidol.Juggler">
<constructor-arg value="10">constructor-arg>
bean>
其中constructor-arg 表示构造参数,value值为10,这样会自动去调用有参构造方法。在构造Bean时,可以使用
JUGGLER 10 BALLS
当然,一个类可能会有多个构造方法,那么怎么确定来调用哪个构造方法呢?实际上,对构造器来说,一旦传入的参数个数以及类型确定了,那么构造方法也就确定了。
我们先定义一个接口,该接口是骑行这一动作,里面包含一个骑行方法
package com.li.spring.chineseidol;
public interface Ridable {
void ride();
}
编写一个类,Bike,一个骑行的具体实现,实现了Ridable接口,用以装配Bean。
package com.li.spring.chineseidol;
public class Bike implements Ridable {
@Override
public void ride() {
System.out.println("ride a bike...");
}
}
创建一个骑行杂技表演者的类,该类继承Juggler类
package com.li.spring.chineseidol;
import java.io.File;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class RidableJuggler extends Juggler {
/*这是一个接口,面向接口编程*/
private Ridable ridable;
public RidableJuggler(){
super();
}
public RidableJuggler(int balls, Ridable ridable) {
super(balls);
this.ridable=ridable;
}
public RidableJuggler(Ridable ridable) {
super();
this.ridable=ridable;
}
@Override
public void perform() throws PerformanceException {
super.perform();
System.out.println("while riding...");
ridable.ride();
}
public static void main(String[] args) {
ApplicationContext ctx=new ClassPathXmlApplicationContext("com"+File.separator+"li"+File.separator+"spring"+File.separator+"chineseidol"+File.separator+"chinese-idol.xml");
Performer performer=(Performer) ctx.getBean("zhang");
try {
performer.perform();
} catch (PerformanceException e) {
e.printStackTrace();
}
}
}
通过构造器注入对象引用——XML配置
<bean id="bike" class="com.li.spring.chineseidol.Bike">
bean>
<bean id="zhang" class="com.li.spring.chineseidol.RidableJuggler">
<constructor-arg value="10">constructor-arg>
<constructor-arg ref="bike">constructor-arg>
bean>
该测试的执行逻辑是:
Ridable ridable=new Bike();
Performer performer=new RidableJuggler(10, ridable);
运行结果:
JUGGLER 10 BALLS
while riding...
ride a bike...
有时静态工厂方法是实例化对象的唯一方法,Spring支持通过
示例:考虑Idol选秀过程中参赛者展示才艺的唯一舞台——Stage类型对象,采用单例模式,单例只能通过静态工厂方法来获取调用。
采用 initialication on demand holder 技术,这是单例模式的一种写法,完全使用了Java虚拟机的机制进行同步保证,没有一个同步的关键字,做到了延迟加载和线程安全。
package com.li.spring.chineseidol;
import java.io.File;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class Stage {
private Stage() {}
/*采用 initialication on demand holder 技术*/
private static class SingletonHolder{
static Stage instance=new Stage();
}
/* 单例模式
* 采用这种方法的好处就是:
* 1. 延迟初始化
* 2. 线程安全
*/
public static Stage getInstance() {
return SingletonHolder.instance;
}
public void show() {
System.out.println("演出开始了");
}
public static void main(String[] args) {
ApplicationContext ctx=new ClassPathXmlApplicationContext("com"+File.separator+"li"+File.separator+"spring"+File.separator+"chineseido"+File.separator+"chinese-ido.xml");
Stage stage=(Stage) ctx.getBean("theStage");
stage.show();
}
}
Spring配置文件:
<bean id="theStage" class="com.li.spring.chineseidol.Stage"
factory-method="getInstance">
bean>
factory-method属性就是让Spring容器知道它并不是自己取调用构造方法去构造一个Bean的示例的,而是通过调用一个名为 “getInstance” 的静态工厂方法来获取实例的。
Spring Bean默认都是单例(singleton)
配置Bean的scope属性为prototype,允许Bean可以被实例化多次(每次调用创建一个新的实例)
Spring的Bean作用域允许用户配置所创建的Bean属于哪种作用域,无需在Bean的实现里硬编码。
Scope | what is does |
---|---|
singleton | Scopes the bean definition to a single instance per Spring container.(default) |
prototype | Allows a bean to be instantiated any number of times(once per use) |
request | Scopes a bean definition to an HTTP request. Only valid when used with a web-capable Spring context(such as with Spring MVC) |
session | Scopes a bean definition to an HTTP session. Only valid when used with a web-capable Spring context(such as with Spring MVC) |
global-session | Scopes a bean definition to a global HTTP session. Only valid when used in a portlet context |
当初始化一个Bean时,可执行一些初始化操作来保证Bean处于可用状态。当不再需要Bean而将其从容器中移除时,可按顺序执行一些清除操作。
Spring提供Bean生命周期的构造方法:
Idol选秀表演大厅Auditorium,在表演前要开灯,结束后要关灯。
package com.li.spring.chineseidol;
import java.io.File;
import org.springframework.context.support.AbstractApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class Auditorium {
public void turnOnLight() {
System.out.println("turn on the light!");
}
public void turnOffLight() {
System.out.println("turn off the light!");
}
public static void main(String[] args) {
/*上下文实例不一样了*/
AbstractApplicationContext ctx=new ClassPathXmlApplicationContext("com"+File.separator+"li"+File.separator+"spring"+File.separator+"chineseidol"+File.separator+"chinese-idol.xml");
/*获取Bean*/
Auditorium au=(Auditorium)ctx.getBean("auditorium");
/*手动的去移除这个Bean*/
ctx.registerShutdownHook();
}
}
Bean配置文件:
<bean id="auditorium" class="com.li.spring.chineseidol.Auditorium"
init-method="turnOnLight" destroy-method="turnOffLight">bean>
运行结果:
turn on the light!
turn off the light!
使用
获取Spring上下文使用的是 ApplicationContext 接口,一般来说我们可以通过其实现类来实例化它,其有两个实现类是值得我们关注的 ClassPathXmlApplicationContext 和 FileSystemXmlApplicationContext,当然这两个类也是很容易理解的,一个是从类路径下加载配置文件,一个是从计算机的文件系统上加载配置文件,一般来说使用 ClassPathXmlApplicationContext就可以了。
AbstractApplicationContext ctx=new ClassPathXmlApplicationContext("com"+File.separator+"li"+File.separator+"spring"+File.separator+"chineseidol"+File.separator+"chinese-idol.xml");
在上述代码中,我们在字符串中使用了File类的一个静态常量,这是一个分割符号,可以应对不同的系统。
如果我们在工程中写了多个配置文件呢?如果把它们统一加载到同一个Bean工厂中又是一个问题。此问题有两个解决方法。
第一种便是文件包含,即在一个配置文件中,引入其他所有配置文件,使用的是
<beans>
......
<import resource="classpath:com/li/student/student.xml"/>
......
beans>
如上,该标签的resource表示了引入资源的路径,值里面classpath表示是在类路径下,位于com.li.student包下的student.xml文件。
第二种方法是在加载配置文件时,就加入多个配置文件,因为ClassPathXmlApplicationContext类还有一个构造方法:
public ClassPathXmlApplicationContext(String... configLocations) throws BeansException {
......
}
由此可见,这个构造方法可以接受多个String类型的参数,这时就可以加入多个配置文件的路径。