在Spring这个基于容器的框架中,一般很少采用new的方式来创建类的对象,一般是在XML文件中,对类进行实例化。
Spring3.0除了传统的XML文件中进行装配bean,还可以进行基于java注解的方式来配置。这里主要介绍下XML的形式。
a、简单装配
1、简单的bean声明
首先定义一个简单的学生类,只有一个构造函数
package com.springTest.school;
public class Student{
public Student(){}
}
针对上面的学生类,在XML文件中进行bean的声明
<bean id="xiaoming" class="com.springTest.school.Student"></bean>
可以看出,我们声明了一个小明的实例。
2、通过构造器注入
2.1、基本类型
上面的学生类型什么都没有,只有单一构造函数,学生应当具有姓名、年龄等属性。<pre name="code" class="java">package com.springTest.school;
public class Student{
private int age;
private String name;
public Student(){}
public Student(String name,int age){
this.name=name;
this.age=age;
}
}
这时候,我们可以简单的声明bean,也可以让小明具有一个初始化的年龄和姓名。如下,xiaoming是一个名字为mingming的15岁骚年。
<bean id="xiaoming" class="com.springTest.school.Student">
<constructor-arg value="mingming"/>
<constructor-arg value="15"/>
</bean>
2.2、对象引用
Student类中的学生,可以会跳舞,会游泳,会打篮球、会吟诗等各项特长(先假设每一个学生都只有一个特长,后面会让他们多学点)。这时候,学生类又要改变下。
package com.springTest.school;
public class Student{
private int age;
private String name;
<span style="font-family: Arial, Helvetica, sans-serif;">private SpecialTalent specialTalent;//特长</span>
public Student(){}
public Student(String name,int age,SpecialTalent specialTalent){
this.name=name;
this.age=age;
this.specialTalent=specialTalent;
}
public void play(){specialTalent.show();}//展示特长,为了区别好理解,用play来表示
}
其中特长是一个接口
package com.springTest.school;
public interface SpecialTalent{
void show();
}
各种特长都实现该接口
package com.springTest.school;
//游泳
public class Swim{
public void show(){
System.out.println("I can swim");
}
}
package com.springTest.school;
//跳舞
public class Dance{
public void show(){
System.out.println("I can dance");
}
}
package com.springTest.school;
//打篮球
public class Basketball{
public void show(){
System.out.println("I can play basketball");
}
}
OK,现在我们再也不能向之前一样,通过构造函数来初始化一个学生的属性,因为学生的属性中,不在是只有基本类型,而是添加了特长,这个时候初始化构造函数,还是有其他的方法可以做到的。
首先,我们把各种特长的bean声明出来。
<bean id="dance" class="com.springTest.school.Dance"></bean>
<bean id="swim" class="com.springTest.school.Swim"></bean>
<bean id="basketball" class="com.springTest.school.BasketBall"></bean>
然后,我们声明学生的bean的时候,通过构造函数来初始化属性。
<bean id="xiaoming" class="com.springTest.school.Student">
<constructor-arg value="mingming"/>
<constructor-arg value="15"/>
<constructor-arg ref="swim">
</bean>
<bean id="xiaokong" class="com.springTest.school.Student">
<constructor-arg value="kongkong"/>
<constructor-arg value="15"/>
<constructor-arg ref="dance">
</bean>
这个时候,两个学生,都有某些属性。
3、注入bean属性(通过property)
3.1、属性为单个值的注入
我们上面看到了通过构造函数注入,现在把构造函数干掉,这个时候,如何在XML文件中配置值呢?使用property来注入属性,这个时候也是两类,一类是基础类型的属性,一类是扩展类型的属性。
这时候,Student的类如下:
package com.springTest.school;
public class Student{
private int age;
private String name;
private SpecialTalent specialTalent;//特长
public Student(){}
public void setName(String name){this.name=name;}
public String getName(){return name;}
public void setAge(int age){this.age=age;}
public int getAge(){return age;}
public void setSpecialTalent(SpecialTalent specialTalent){this.specialTalent=specialTalent;}
public SpecialTalent getSpecialTalent(){return specialTalent;}
public void play(){specialTalent.show();}
}
可以发现,我们不但干掉了带有参数的构造函数,还添加了每一个属性的set、get函数。
这个时候,在XML文件中注入,还是可以为类的属性赋值。Spring能够借助属性的set方法来配置属性的值,从而实现注入。
至于说上面的类属性中,除了基本类型外,还有引用类型,根据之前我们通过构造函数注入的方法,也可以类似的对引用类型进行XML的bean注入。
<bean id="xiaoma" class="com.springTest.school.Student">
<property name="name" value="xiaomage" />
<property name="age" value="17" />
<property name="specialTalent" ref="swim">
</bean>
通过上面的配置可以看出,小马哥是一个擅长游泳的17岁骚年。
这里的property是通过类属性的setXX()方法来进行注入的,如年龄age,property会调用setAge()方法将age的值设置为17。这里age是int类型,可以看出,spring在赋值时会自动转换的。
要注意的一点:<property>的作用不仅仅是依赖注入装配硬编码的值,而是将不同的对象组织在一起,如上面的xiaoma,swim。
3.2、属性为集合的注入
3.2.1、List、Set类型
private List<SpecialTalent> specialTalents;
public void setSpecialTalent(List<SpecialTalent> specialTalents){this.specialTalents=specialTalents;}
这个时候如何装配呢?经过下面的装配,小马哥瞬间会游泳、跳舞、打篮球。
<bean id="xiaoma" class="com.springTest.school.Student">
<property name="name" value="xiaomage" />
<property name="age" value="17" />
<property name="specialTalents">
<list>
<ref bean="swim"/>
<ref bean="basketball"/>
<ref bean="dance"/>
</list>
</property>
</bean>
我们这里用的是List,集合Set也是类似的。Map、Properties有些不同,下面简单介绍下。
3.2.2、Map类型
特长用Map定义
private Map<String,SpecialTalent> specialTalents;
public void setSpecialTalent(Map<String,SpecialTalent> specialTalents){this.specialTalents=specialTalents;}
装配如下:
<bean id="xiaoma" class="com.springTest.school.Student">
<property name="name" value="xiaomage" />
<property name="age" value="17" />
<property name="specialTalents">
<map>
<entry key="SWIM" value-ref="swim"/>
<entry key="DANCE" value-ref="dance"/>
</map>
</property>
</bean>
这里提到几个,一般都是key-value的模式,如果是引用就加上-ref,所以,共有key、key-ref、value、value-ref四种。
3.2.3、Properties类型
特长用Properties定义
private Properties specialTalents;
public void setSpecialTalent(Properties specialTalents){this.specialTalents=specialTalents;}
XML装配如下:
<bean id="xiaoma" class="com.springTest.school.Student">
<property name="name" value="xiaomage" />
<property name="age" value="17" />
<property name="specialTalents">
<props>
<prop key="SWIM">ssss</prop>
<prop key="DANCE">hhhhh</prop>
</props>
</property>
</bean>
这里需要注意的是,properties中,适用于key、value都是String类型的情况。
3.3、注入内部Bean
如果游泳、跳舞等都没有自己注入bean,或者我们想自己单独使用一个,这时候可以用到内部bean<bean id="xiaoma" class="com.springTest.school.Student">
<property name="name" value="xiaomage" />
<property name="age" value="17" />
<property name="specialTalent">
<bean class="com.springTest.Swim"/>
</property>
</bean>
内部bean是很直接的将需要引用的类内部引用,所以没有ID属性。内部bean适用于一次注入,不能够被复用。
3.4、命名空间p装配属性
这是一种完全等价于property的方法,需要在XML的beans属性中加上:xmlns:p="http://www.springframework.org/schema/p"然后写XML时候看起来会很简洁。
<bean id="xiaoma" class="com.springTest.school.Student"
p:name=xiaoming
p:age="19"
p:specialTalent-ref="swim" />
3.5、装配空值
<property name="someNonNullProperty"><null/></property>
4、Bean的作用域
Spring Bean默认都是单例,容器分配一个Bean时,总会返回Bean的同一个实例。<bean id="xiaoming" class="com.springTest.school.Student" scope="prototype" />
这样每一次请求xiaoming这个bean,总会产生一个新的学生,注意了,并不是所有学生都叫xiaoming,这里的xiaoming只是一个ID。
Singleton:在每一个Spring容器中,一个bean定义只有一个对象实例(默认就是这样)。
prototype:允许bean的定义可以被实例化任意次(每次调用产生一个实例对象)。
request:在一次http请求中,每一个bean定义对应一个实例。该作用域仅在基于web的Spring上下文(例如springmvc)中才有效。
session:在一个httpsession中,每一个bean定义对应一个实例。该作用域仅在基于web的Spring上下文(例如springmvc)中才有效。
global-session:在一个全局httpsession中,每一个bean定义对应一个实例。该作用域仅在portlet上下文中才有效。
5、bean的初始化、销毁时的处理
这里简单介绍下,XML文件中,还可以让实例在创建、销毁时,执行特定的方法。多说无益,看码。
<bean id="xiaoming" class="com.springTest.school.Student"
init-method="enterSchool"
destroy-method="leaveSchool" />
学生在实例化之前,需要进入学校,销毁学生身份之前,需要离开学校,这才是完整的周期啊。其中enterSchool、leaveSchool是两个方法。
接下来问题来了,一个人如果不到学校工作,那就不是老师,不离开学校,那就不能说他不是老师。所以,老师这个类实例化之前,也需要enterSchool,辞职前,也需要leaveSchool。好吧,那老师类实例化也需要加上两个必须执行的方法。那么,校长呢,教务主任呢,校工呢,都需要,总不能每一个都写一下吧,有办法解决的。
在XML文件的beans配置中,添加默认执行的属性:
default-init-method="enterSchool"
default-destroy-method="leaveSchool"
b、使用表达式装配
Spring3引入了Spring表达式语言(SpEL),SpEL是比较简洁的装配bean的方式,通过运行期执行的表达式将值装配到bean的属性或者构造器参数中。
SpEL的特性:
1、使用bean的ID来引用bean;
2、调用方法和访问对象的属性;
3、对值进行算术、关系、逻辑运算;
4、正则表达式匹配;
5、集合操作。
1、字面值表达式
这个其实使用的不多,也就是一个字面值,然后就获取到字面值。
憋说话,看码。
<pre name="code" class="html"><property name="name" value="#{'xiaoming'}" />
<property name="age" value="#{17}" />
<pre name="code" class="html"><property name="manOrNot" value="#{false}" />
<property name="name" value='#{"xiaoming"}' />
第一个就是表达式里面的xiaoming,第二个17岁,第三个不是爷们,第四个是为了区分第一个,看单引号、双引号,这俩其实都一样。
2、引用bean、properties和方法
上面我们说过一个学生要有特长,小明很牛,有小马的所有特长,这时候,可以用bean的表达式。
<bean id="xiaoming" class="com.springTest.school">
<property name="specialTalents" value="#{xiaoma.specialTalents}" />
</bean>
看吧,小明不需要列举自己会游泳、唱歌、跳舞,只需要说我跟小马的特长一样,cool。
上面其实等价于:
Student xiaoming=new Student();
xiaoming.setSpecialTalents(xiaoma.getSpecialTalents());
如果小明要表演一个特长,那么他可以表演人意一个,这时候,可以搞一个随机的函数,让他表演,这时候,给student类添加show属性。
<property name="show" value="#{RandomShow.selectShow()}" />
这时候,上面返回的如果是String,我们可以再来一个大小写的转变
<property name="show" value="#{RandomShow.selectShow()?.toUpperCase()}" />
上面为啥用了个问号?,这个是有原因的,因为可能selectshow返回null,这时候,如果不用问号,就会爆出空指针异常,加了就不会啦,这个不错。
3、操作类
<property name="randomNumber" value="#{T(java.lang.Math).random()}" />
T()主要价值在于可以访问指定类的静态方法和常量。