Spring3-装配Bean
程序员文章站
2022-03-03 08:19:29
...
本章类容来源于Spring in Action3第二章。
内容:
- 声明bean
- 构造器注入和setter方法注入
- 装配bean
- 控制bean的创建和销毁
观众朋友们好,这里是上海电视台,欢迎收看中国达人秀节目。
首先我们抽象出表演者角色。示例代码如下:
public interface Performer { public void perform(); }所有参与中国达人秀的同学都要实现该接口。
接下来,让我们来创建一个XML配置文件(Spring3支持注解方式,以后会讲到),告诉Spirng容器需要加载哪些Bean以及如何装配
Spring Xml配置文件的根元素来源于Spring beans命名空间的<beans>元素,一个典型的XML配置文件如下
<?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="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd" > </beans>
beans命名空间并不是唯一的Spring命名空间,Spring核心只带了10个命名空间配置,如aop,context,mvc,util,tx等。以后会带来其他命名空间的更多信息。
现在,在开始选秀之前,节目组会讲报名的选秀节目分类,以便评委对相同类型的节目做比较。好了,第一种类型定义为杂技师( juggler),示例代码如下
public class Juggler implements Performer { private int beanBags = 3; public Juggler() { } public Juggler(int beanBags) { this.beanBags = beanBags; } public void perform() { System.out.println("juggler " + beanBags + " bags"); } }
那么,有请我们的第一位杂技师表演者duke来到舞台,在Spring xml配置文件中声明duke的Bean
<bean id="duke" class="in.action3.chapter2.Juggler"></bean>好了,我们使用下面的代码,给duke一个排练的机会
public static void main(String[] args) { ApplicationContext ctx = new ClassPathXmlApplicationContext("spring/inject-service.xml"); Performer performer = (Performer)ctx.getBean("duke"); performer.perform(); }输出juggler 3 bags。
抛3个袋子不难,想要获得认可, 必须同时抛更多的袋子,让我们配置duke具有冠军潜质的杂技师。
构造器注入
为了让评委心动,我们配置让duke同时抛15个袋子。
<bean id="duke" class="in.action3.chapter2.Juggler"> <constructor-arg value="15"></constructor-arg> </bean>如果不配置<constructor-arg/>,Spring使用默认的构造方法,配置了<constructor-arg/>,Spring将调用Juggler的另一个构造方法。
通过构造器注入引用
因为duke不仅是一位杂技师,还是一个会朗诵诗歌的杂技师。我们要定义一个新的类型PoeticJuggler,才能展现duke的才华。
public class PoeticJuggler extends Juggler { private Poem poem ; public PoeticJuggler(Poem poem) { super(); this.poem = poem; } public PoeticJuggler(int bags, Poem poem) { super(bags); this.poem = poem; } @Override public void perform() { super.perform(); System.out.println("while reciting ...."); poem.recite(); } }PoeticJuggler持有Poem接口的引用,Poem接口定义:
public interface Poem { public void recite(); }我们定义一首诗Sonnet29,实现Poem接口:
public class Sonnet29 implements Poem { public void recite() { System.out.println("every body, now, I will recite Sonnet29, when ......"); } }在XML配置中,将Sonnet29声明为一个bean:
<bean id="sonnet29" class="in.action3.chapter2.Sonnet29"></bean>现在我们来声明一个PoeticJuggler的bean,讲sonnet29注入给它:
<bean id="poeticDuke" class="in.action3.chapter2.PoeticJuggler"> <constructor-arg value="15"></constructor-arg> <constructor-arg ref="sonnet29"></constructor-arg> </bean>poeticDuke排练下:
public static void main(String[] args) { ApplicationContext ctx = new ClassPathXmlApplicationContext("spring/inject-service.xml"); Performer performer = (Performer)ctx.getBean("poeticDuke"); performer.perform(); }
通过工厂方法创建bean
有时候静态工厂方法是实例化对象的唯一方法, Spring通过factory-method属性来实例化bean
现在我们来实现一个单例的舞台Stage:
public class Stage { private Stage(){}; private static class StageSingletonHolder{ static Stage instance = new Stage(); } public static Stage getInstance(){ return StageSingletonHolder.instance; } }题外话:
并发环境下延迟加载单例实例的解决方案Initialization on Demand Holder
即把instance的初始化投入到一个内部类的初始过程中,就可以兼顾正确性和性能。
1. 内部类的初始化是延迟的,外部类初始化时不会初始化内部类。
2. 内部类的初始化是线程安全的,所以不用担心两个instance或半残instance的问题。
3. 第一次获取instance需要等它初始化,以后再获取就不必了,而且也不需要锁。所以在性能上也是妥妥的。
1. 内部类的初始化是延迟的,外部类初始化时不会初始化内部类。
2. 内部类的初始化是线程安全的,所以不用担心两个instance或半残instance的问题。
3. 第一次获取instance需要等它初始化,以后再获取就不必了,而且也不需要锁。所以在性能上也是妥妥的。
配置Stage Bean:
<bean id="stage" class="in.action3.chapter2.Stage" factory-method="getInstance"></bean>
Bean的作用域
所有Spring Bean默认都是单例。如果每次请求时都要获得唯一的Bean实例,如何覆盖Spring默认的单例配置呢?
为此,我们为每个观众给予不同的门票,配置:
<bean id="ticket" class="in.action3.chapter2.Ticket" scope="prototype"></bean>通过scope属性来指定bean的作用域。
除了singleton, prototype之外,还有request,session, global-session作用域,这几个作用域用于web和portlet的上下文spring容器。
初始化和销毁Bean
为了定义Bean的初始化和销毁操作,只需要使用init-method和destory-method参数配置bean元素。
我们在舞台类加上turnOnLights()和turnOffLights()两个方法,表示舞台初始化是打开灯,舞台销毁是关闭灯。配置xml如下:
<bean id="stage" class="in.action3.chapter2.Stage" factory-method="getInstance" init-method="turnOnLights" destroy-method="turnOffLights"></bean>如果上下文中有很多Bean拥有相同名字的初始化和销毁方法,可以使用 beans元素的default-init-method和default-destory-method属性。
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd" default-init-method="turnOnLights" default-destroy-method="turnOffLights" >
为bean定义初始化和销毁操作的另一种方法是,bean实现Spring的InitializingBean和DisposableBean接口。分别实现其afterPropertiesSet()和destroy()两个方法。
注入Bean属性
通常javaBean的属性是私有的,同时拥有一组存取器方法,以setXXX()和getXXX()形式存在,Spring可以借助属性的set方法来配置属性的值, 实现setter方式的注入。
为了演示setter注入,我们欢迎下一位表演者,表演乐器演奏。
public class Instrumentalist implements Performer { private Instrument instrument; private String song; public Instrument getInstrument() { return instrument; } public void setInstrument(Instrument instrument) { this.instrument = instrument; } public String getSong() { return song; } public void setSong(String song) { this.song = song; } public void perform() { System.out.println("表演的歌曲名字是:"+song); instrument.play(); } }定义乐器接口:
public interface Instrument { public void play(); }定义萨克斯乐器:
public class Saxophone implements Instrument { public void play() { System.out.println("TOOT TOOT TOOT"); } }Spring 配置:
<bean id="saxophone" class="in.action3.chapter2.Saxophone"></bean> <bean id="kenney" class="in.action3.chapter2.Instrumentalist"> <!-- setter注入简单的值 --> <property name="song" value="Jinle bells"></property> <!-- setter注入bean --> <property name="instrument" ref="saxophone"></property> </bean>执行代码:
public static void main(String[] args) { ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("spring/inject-service.xml"); Performer performer = (Performer)ctx.getBean("kenney"); performer.perform(); ctx.close(); }输出:
表演的歌曲名字是:Jinle bells TOOT TOOT TOOT
注入内部Bean
<bean id="kenney" class="in.action3.chapter2.Instrumentalist"> <!-- setter注入简单的值 --> <property name="song" value="Jinle bells"></property> <!-- setter注入bean --> <property name="instrument" > <!-- 注入内部Bean,该bean是kenney私有的 --> <bean class="in.action3.chapter2.Saxophone"></bean> </property> </bean>
内部bean不能复用,且会影响配置文件的可读性。
使用Spring的命名空间p装配属性
使用命名空间p,需要在spring xml配置中添加声明,如下:
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:p="http://www.springframework.org/schema/p" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd" >重新定义kenney bean配置
<bean id="saxophone" class="in.action3.chapter2.Saxophone"></bean> <bean id="kenney" class="in.action3.chapter2.Instrumentalist" p:song="Jinle bells" p:instrument-ref="saxophone"> </bean>使用<porperty>还是p取决于你, 他们是等价的,p的主要优点是简洁。
装配集合List Set
装配集合类型的属性时,Spring提供了4种类型的集合装配元素。<list>,<set>,<map>,<props>
我们有请下一位达人,他也是乐器表演, 但他可以同时演奏多个乐器。
一个人乐队OneManBand
public class OneManBand implements Performer { private Collection<Instrument> instruments; public Collection<Instrument> getInstruments() { return instruments; } public void setInstruments(Collection<Instrument> instruments) { this.instruments = instruments; } public void perform() { for (Instrument instrument : instruments) { instrument.play(); } } }
配置hank为一个人乐队
<bean id="hank" class="in.action3.chapter2.OneManBand"> <property name="instruments"> <list> <ref bean="saxophone"/> <ref bean="guitar"/> <ref bean="harmonica"/> </list> </property> </bean>
装配Map属性
hank在演奏的时候,希望打印出乐器的名字,我们修改下OneManBand
public class OneManBand implements Performer { private Map<String, Instrument> instruments; public Map<String, Instrument> getInstruments() { return instruments; } public void setInstruments(Map<String, Instrument> instruments) { this.instruments = instruments; } public void perform() { for (Entry<String, Instrument> entry : instruments.entrySet()) { System.out.println("乐器:"+entry.getKey()+" 演奏:"); entry.getValue().play(); } } }配置:
<bean id="hank" class="in.action3.chapter2.OneManBand"> <property name="instruments"> <map> <entry key="saxophone" value-ref="saxophone"></entry> <entry key="guitar" value-ref="guitar"></entry> <entry key="harmonica" value-ref="harmonica"></entry> </map> </property> </bean>
装配propertis集合
如果map的键值都为String,可以使用java.util.Properties代替map。
为了演示,我们修改下OneManBand
public class OneManBand implements Performer { private Properties instruments; public Properties getInstruments() { return instruments; } public void setInstruments(Properties instruments) { this.instruments = instruments; } public void perform() { for (Entry<Object, Object> entry : instruments.entrySet()) { System.out.println("乐器:"+entry.getKey()+" 演奏:"+ entry.getValue()); } } }配置:
<bean id="hank" class="in.action3.chapter2.OneManBand"> <property name="instruments"> <props> <prop key="saxophone">saxophone</prop> <prop key="guitar">guitar</prop> <prop key="harmonica">harmonica</prop> </props> </property> </bean>
装配空值
显式的为属性装配null值,使用<null/>
例如:
<property name="someNullProperty"><null/></property>
上一篇: 获取实时汇率代码片段