一、概述
今天来学习一下Spring的核心配置文件applicationContext.xml,主要来看一下其中经常使用的标签,当然由于Spring早就支持注解的方式,所以我们以后的侧重点将也是注解方式,但对于applicationContext.xml中的各项配置,其实与注解是很像的,等我们理解了xml配置之后,很容易就理解注解配置了。
注:学习的Spring版本是4.3.14。
二、各种标签
1. beans标签
Spring配置文件的根元素是beans
节点,在该节点内,我们可以配置Spring内置的各种命名空间以及bean默认的几项配置,通过配置各种命名空间,然后使用各命名空间的元素来完成对Spring的配置。
<?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.xsd">
</beans>
前面我们说过命名空间,这里就不多说了。Spring内置了大约10种左右的命名空间供我们选择配置:
命名空间 | 用途 |
---|---|
aop | 为声明切面及将@AspectJ注解的类代理为Spring切面提供了配置元素 |
beans | 声明及配置Bean,是Spring最核心也是最原始的命名空间 |
context | 为配置Spring应用上下文提供了配置元素,包括自动检测,自动装配bean等 |
jee | 提供了于Java EE API的集成,比如JNDI与EJB |
jms | Java消息相关的配置 |
lang | 支持配置由Groovy、JRuby或BeanShell等脚本实现的Bean |
mvc | 提供Spring MVC相关的配置,比如控制器,拦截器,视图管理器等相关 |
oxm | 支持Spring的对象到XML的映射配置 |
tx | 声明式的事务配置 |
util | 提供各种各样的工具类元素配置,包括把集合配置为Bean,支持属性占位符元素 |
当然,Spring的命名空间不止如此,只是展示了我们常用的而已,其他的等我们用到的时候再了解不迟。而我们如果需要某个命名空间下的元素时,引入相应的命名空间即可。比如说我们使用到了context命名空间:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd">
<context:property-placeholder ignore-resource-not-found="false" ignore-unresolvable="false"/>
</beans>
接下来,我们来看下beans标签中除了命名空间外的其他默认元素。
1.1 default-init-method
元素
如果在上下文中定义的很多bean都有相同名字的初始化方法,我们没有必要为每一个bean声明init-method
属性,这时候我们就可以使用beans元素的default-init-method
属性。该属性为应用上下文中所有的bean设置了共同的初始化方法。
1.2 default-destory-method
元素
这个元素于上述default-init-method
类似,该属性是用于方法的销毁。
1.3 default-lazy-init
元素
用于延迟加载,我们都知道,一般情况下,Spring在启动的时候会初始化所有相关的bean,这样就会出现启动的时候会特别慢的问题。这时候可以配置该属性设置为延迟加载,用到的时候再去加载。当然如果该配置文件配置了bean标签或者引入了其他的配置文件,以具体的bean中的配置或其他配置文件的配置优先;
- true,设置为true,则所有相关的bean的初始化被延迟加载;
- false,默认为false,即不延迟加载;
- 假如设置为延迟加载后,记得考虑比如定时任务等相关的操作,所以是否使用该属性,按需选择;
1.4 default-autowire
元素
Spring中的bean默认注入的方式。一般情况下,当我们需要往一个bean里注入另一个bean的时候,如果没有配置该属性,就需要手动通过ref标签引入。我们以一个简单的例子来看下:
(1). 两个对象:Student,Score
public class Student {
private String id;
private String name;
private Score score;
}
public class Score {
private Integer math;
}
(2). bean配置:
<bean id="student" class="entity.Student">
<property name="id" value="id"/>
<property name="name" value="name"/>
</bean>
<bean id="score" class="entity.Score">
<property name="math" value="100"></property>
</bean>
(3). main方法
public static void main(String[] args) {
ApplicationContext ac=new ClassPathXmlApplicationContext("/resource/applicationContext.xml");
Student student= ac.getBean("student", Student.class);
System.out.println(student.getScore().getMath());
}
首先,我们配置default-autowire
属性,也就是默认不自动注入,这时候,我们如果直接获取的话,就获取不到Score对象,提示空指针异常:
Exception in thread "main" java.lang.NullPointerException
at test.Main.main(Main.java:15)
我们配置default-autowire
属性为byName
之后,再看下结果:
100
这时候打印成功,也就是说,如果我们不配置该属性的话,需要手动使用ref标签来引用:
<bean id="student" class="entity.Student">
<property name="id" value="id"/>
<property name="name" value="name"/>
<property name="score" ref="score"/>
</bean>
当然,如果我们的对象是单个对象的话,那无论配置是否配置,都没什么影响。
下面我们来看下该属性的几个可选值:
- no,不使用自动注入,所以该Bean对象中其他Bean的引用必须通过ref标签来引用;
- byName,把与beanA的属性具有相同名字的beanB自动注入到beanA的对应属性中,如果没有,则不注入。这里的相同指的是beanB配置的id或name其中一个相同即可;
- byType,与byName相对,这里指的是相同类型,也就是相同的class,这种情况下,由于根据类型来注入,所以beanB只能有一个存在,否则会编译不通过;
- constructor,与byType类似,通过构造方法的参数来匹配,如果没有构造方法,或没有与构造方法参数类型一致的bean,则会提示异常;
这里判断是否存在类型一致先通过byType判断,如果只有一个类型的bean存在,则成功;如果有多个类型一致的,再根据byName,如果只有一个name相同的,则成功,否则失败,提示异常;
-
default,默认配置,由上级标签<beans>的
default-autowire
属性确定使用哪种装配方式,该属性一般配置于具体的bean标签中,而配置在beans标签中和配置为no
类似。
注意:在以前版本中,
default-autowire
还支持一个可选值:autodetect
,不过在Spring4之后已经废弃掉。
还有两点简单说下:
- 和
default-lazy-init
类似,如果bean中配置了autowire
属性,则具体bean中的注入类型的优先级将高于beans里的配置;- 当然,配置了该属性后可能会导致我们对其中的细节不够了解,也会弱化对象间的关系,出现问题的时候可能不太好定位,所以我们可以根据需要选择配置或不配置
default-autowire
;
1.5 default-autowire-candidates
元素
自动注入bean的候选者,比如说,default-autowire
中我们使用byType
的时候通常只能有一个类型为ClassA的bean。但是我们想在配置文件中有多个类型为ClassA的bean,就可以通过配置default-autowire-candidates
来作为自动注入bean的候选者;
<beans ...
default-autowire-candidates="*score" default-autowire="byType">
<bean id="student" class="entity.Student">
<property name="id" value="id"/>
<property name="name" value="name"/>
</bean>
<bean id="score2" class="entity.Score" >
<property name="math" value="100"></property>
</bean>
<bean id="score" class="entity.Score" >
<property name="math" value="200"></property>
</bean>
</beans>
在上面这个小例子中,我们通过配置该属性为*socre
,让容器在进行自动注入的时候选择name或id以score
结尾的bean来进行注入。
default-autowire-candidates
的值可以是某个bean的id,也可以是匹配字符串,比如*score
,则会将以score结尾的作为候选注入的bean,也可以指定多个字符串,通过逗号进行分割;- A default bean name pattern for identifying autowire candidates:
e.g. "*Service", "data*", "*Service*", "data*Service".
Also accepts a comma-separated list of patterns: e.g. "*Service,*Dao".- 有关候选bean的细节,我们可以查看官方文档中
bean
的属性autowire-candidate
的文档来更深一步学习。
1.6 default-merge
元素
这个属性用的可能不太多,这是用于集合的继承相关的操作。比如说,如果父类Bean包含list,map,set等一些集合元素,那么继承父类的子类将自动继承父类Bean的集合元素;当然,子类Bean可以选择合并父类的集合元素,也可以选择替换父类的的集合元素,Spring通过beans标签的default-merge
和bean标签内的list标签的merge
来支持这种操作;
我们通过简单的例子来看一下:定义对象Tutorial:
public class Tutorial {
private String name;
private List topicsList = new ArrayList<>();
}
配置两个bean,先不配置default-merge
属性:
<bean id="parent" class="entity.Tutorial">
<property name="name" value="Java parent"/>
<property name="topicsList">
<list>
<value>Java Core</value>
<value>Java Concurrency</value>
</list>
</property>
</bean>
<bean id="children" parent="parent">
<property name="name" value="Java children"/>
<property name="topicsList">
<list>
<value>EJB</value>
<value>Servlet</value>
</list>
</property>
</bean>
指定parent
的Bean集合数据为[Java Code, Java Concurrency]
,而children
中的集合数据为[EJB, Servlet]
,这时候我们测试下chidren是否合并了父类的集合元素:
public static void main(String[] args) {
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("/resource/applicationContext.xml");
try {
Tutorial parent = context.getBean("parent", Tutorial.class);
Tutorial children = context.getBean("children", Tutorial.class);
// 获取子类数组
List childrenList = children.getTopicsList();
// 获取父类数组
List parentList = parent.getTopicsList();
System.out.println(children.getName() + " contain " + parent.getName() + "? " +
childrenList.containsAll(parentList));
System.out.println(children.getName() + ": " + childrenList);
} finally {
context.close();
}
}
打印结果为:
Java children contain Java parent? false
Java children: [EJB, Servlet]
可以看到,children并没有合并父类的集合元素,只包含本身的元素。我们设置default-merge=true
,再来看一下结果:
Java children contain Java parent? true
Java children: [Java Core, Java Concurrency, EJB, Servlet]
此时,我们可以看到,子类已经合并了父类的集合元素了。
从上面这个简单的小例子我们大概了解了该标签的用处,该标签有
true
和false
两个可选值,默认情况下是false
,就是不合并父类的集合元素。同理,如果 list 标签配置了merge属性,则会覆盖掉beans标签里的该项配置。如果要查看更多实例,可以参考:Spring Collection Merging Example
2. bean标签
bean元素是Spring中最基本的配置单元,用来管理对象。通过该元素,Spring将创建一个对象,并在容器加载的时候实例化该对象。我们来看一下它的一些属性:
2.1 id
属性
bean的唯一标识,命名格式必须符合XML中ID属性的命名规范,不能有特殊字符;
在Spring3.1版本之前,该id属性被定义为
xsd:ID
,通过XML的id规范限制了它的唯一性,而在Spring3.1中,该id属性被定义为xsd:string
类型,尽管不再使用XML解析器,但id的唯一性仍由容器强制执行。
2.1 name
属性
bean的名称,命名可以比较随意,可以有特殊字符,并且一个bean可以有多个名称:name="name1,name2,name3"
,用逗号或分号或空格隔开(当然,在同一个name配置中,只能有一种符号分割,不能同时有多种)。name可以算作是 id 的别名,我们在获取bean的时候,可以通过id,也可以通过name来获取。在没有id的情况下,则name的第一个名称默认为id;
- 同一个Spring配置文件中,id,name是不能重复的;但如果一个Spring从多个配置文件中加载,则多个配置文件中是允许id或者name重复的,但后面加载的bean会覆盖掉前面加载的bean;
- 如果一个bean标签未指定id,name属性,则Spring会给一个默认的id,值为类的全名,比如类的路径是
com.company.Score
,则默认的id值是com.company.Score
;- 如果多个同类型的bean未指定id,name属性,则Spring会按照其出现的次序,分别给其指定 id 为
类全名#1
,类全名#2
;
举个小例子看一下:
<bean class="entity.Score">
<property name="math" value="100"/>
</bean>
<bean class="entity.Score">
<property name="math" value="200"/>
</bean>
<bean class="entity.Score">
<property name="math" value="300"/>
</bean>
获取bean的方式:
Score score = ac.getBean("entity.Score", Score.class);
Score score1 = ac.getBean("entity.Score#1", Score.class);
Score score2 = ac.getBean("entity.Score#2", Score.class);
System.out.println(score.getMath());
System.out.println(score1.getMath());
System.out.println(score2.getMath());
打印结果:
100
200
300
id和name这段解释可以参考官网:https://docs.spring.io/spring/docs/4.3.14.RELEASE/spring-framework-reference/html/beans.html#beans-beanname
2.3 class
属性
顾名思义,该属性就是当前对象所引用的类的全路径。
<bean name="student2,student3,student4" class="entity.Student"/>
2.4 autowire
属性
在上面beans标签的学习中,我们已经了解过default-autowire
属性,这两个属性是一样的,用于注入配置,只不过一个是全局默认的,一个是针对某一个bean的。在bean中配置完autowire
属性后,该bean的注入方式会覆盖掉beans标签所配置的默认注入方式。该属性的配置可选值和default-autowire
一样,就不多说了。
2.5 autowire-candidate
属性
同样,该属性和beans标签中的default-autowire-candidates
元素类似,意思就注入bean的候选者。再多说一句,前面我们说到配置有autowire属性的bean,Spring在实例化某个bean的时候会在容器中查找匹配的bean,然后通过autowire的配置对bean进行属性注入,这些被查找的bean我们称为候选bean;
该属性的配置有三个值:true
,false
,default
。配置为false
的时候意味着将该bean从候选bean中排除掉,也就是注入的时候不注入该bean;
另外再说两点:
- 该属性只影响到基于类型的注入,而不会影响到名称的引用。也就是如果是基于name的autowire,该属性将不会有影响。
- 同样,bean标签中的
autowire-candidate
属性的值将优先于beans标签的default-autowire-candidates
配置。我们以一个例子来说一下:
<beans ...
default-autowire="byType" default-autowire-candidates="*score1">
<bean id="score" class="entity.Score" autowire-candidate="true">
<property name="math" value="100"/>
</bean>
<bean class="entity.Score" >
<property name="math" value="200"/>
</bean>
<bean id="student" class="entity.Student" >
<property name="id" value="id"/>
<property name="name" value="name"/>
</bean>
</beans>
通过beans标签的default-autowire-candidates
属性设置同类型的候选bean是id以socre1
结尾的bean,而我们的bean的id是score
,但我们配置了该bean的autowire-candidate
为true,由于bean的优先级高,所以能注入成功。
2.6 destroy-method
属性和init-method
属性
同样,在上文beans中,我们已经介绍过default-init-method
了,这两个方法用于该bean的初始化方法和销毁方法。也就是,当Spring容器加载该bean的时候,会调用init-method
执行初始化操作,然后当Spring容器销毁该bean的时候(比如调用close方法),会调用destroy-method
来执行销毁操作。
public static void main(String[] args) {
ClassPathXmlApplicationContext ac=new ClassPathXmlApplicationContext("/resource/applicationContext.xml");
Student student= ac.getBean("student", Student.class);
System.out.println(student.getScore().getMath());
ac.close();
}
这里还需要说下,
destroy-method
只有在bean的scope
配置为singleton
,也就是单例模式下才会生效,原因等我们接下来学习到scope
的时候再细说。
2.7 lazy-init
元素
前文beans里说过,为了避免Spring容器启动的时候加载所有的bean,导致启动很慢的情况,所以beans标签提供了default-lazy-init
标签来设置是否开启懒加载模式。
同样,bean标签里的lazy-init
元素也是用于对单个bean判断,是否需要开启懒加载机制,这样如果开启的话,那该bean的加载将不再是容器启动的时候进行调用,而是在第一次进行getBean的时候才进行加载。
并且,该元素也是有
true
和false
可选,默认情况下是false
,即不开启懒加载机制。而且bean里lazy-init
的优先级也是高于最外层beans标签中default-lazy-init
元素的。
我们还是用上文的Student对象,添加一个默认构造方法:
public class Student {
private String id;
private String name;
private Score score;
public Student() {
System.out.println("test lazy init by construct");
}
}
然后先不设置lazy-init
元素,即采用默认值:
<bean id="student" class="entity.Student" >
<property name="id" value="id"/>
<property name="name" value="name"/>
</bean>
然后测试下:
ClassPathXmlApplicationContext ac=new ClassPathXmlApplicationContext("/resource/applicationContext.xml");
可以看到,我们的构造方法被调用了:
test lazy init by construct
也就是说,我们只是启动了容器,并没有去直接获取bean,但构造方法还是被执行了。
接下来,我们来配置下该属性为true,然后再看下结果:
<bean id="student" class="entity.Student" lazy-init="true">
<property name="id" value="id"/>
<property name="name" value="name"/>
</bean>
再看下打印结果,可以看到,程序并没有执行我们的构造方法。然后我们手动获取下bean:
ClassPathXmlApplicationContext ac=new ClassPathXmlApplicationContext("/resource/applicationContext.xml");
Student student= ac.getBean("student", Student.class);
然后再看下打印结果:
test lazy init by construct
这时候又执行了我们的构造方法。可以看到,程序在懒加载的时候,会延迟到第一次调用该bean的时候才进行加载该bean,这样对于程序启动的压力就会少许多。但或许会有额外的问题,比如说实例化的bean配置有问题,而这个问题也会延迟到运行的时候才会发觉。
不过这里再多说一点:
- 如果一个bean1设置了立即加载,而该bean引用了一个延迟加载的bean2,那么bean1在容器启动的时候被实例化,而bean2由于也被bean1引用,所以也会立即实例化,而这种情况也符合延迟加载的bean在第一次调用时才被实例化的规则;
- 对于定时任务类似的这种东西,一般是不设置为延迟加载的;
2.8 abstract
和parent
标签
很多时候,我们为了避免bean中属性的重复,会用到另外两个元素abstract
和parent
。使用标签abstract
修饰的bean被称为抽象的bean,这种bean不是用于实例化,而是用于继承的情况,也就是用于具体的子类继承的父类bean。该元素有true
和false
两个值,默认情况下该值是false,当指定为true的时候,就是抽象bean,无法实例化。
该值一般配合bean的另一个元素parent
来使用,parent
用于bean中的继承关系,使用该元素,可以继承父类的属性。这种子类bean的出现,是为了对父类bean中公共属性的封装,避免XML中属性的重复。
- Spring中的抽象bean和Java中的抽象类有相同点,但也有不同点。比如说都不能直接实例化,只能通过子类实例化的时候,先实例化父类,再实例化子类,相当于间接的实例化父类;
- 另一点是Spring中的抽象bean甚至不需要映射到某个类就可以被子类所引用,比如:
<bean id="test1" abstract="true">
<property name="name" value="name"/>
<property name="id" value="id"/>
</bean>
<bean id="children" class="entity.Children" parent="test1">
<property name="test" value="test/>
</bean>
这种情况就是将公共的属性进行统一注入,避免XML中各种属性配置的重复,但这种情况下,就需要子类必须要有相应的属性。对例子而言,也就是Children这个类中必须要有name
和id
属性。
我们分两部分来看一个例子,一是抽象bean无法初始化,二是子类bean可以继承父类bean的属性:
首先定义Parent对象:
public class Parent {
private String name;
public Parent() {
System.out.println("Parent初始化");
}
}
配置bean:
<bean id="parent" class="entity.Parent" abstract="true">
<property name="name" value="name"/>
</bean>
测试下:
ClassPathXmlApplicationContext ac=new ClassPathXmlApplicationContext("/resource/applicationContext.xml");
可以看到,程序并未打印我们在Parent中设置的构造方法,也就是说我们没有配置懒加载,但Parent对象并没有实例化。
然后我们再来看下属性的继承,再定义子类Children:
public class Children extends Parent{
private String id;
public Children() {
System.out.println("Children初始化");
}
}
配置子类的bean:
<bean id="children" class="entity.Children" parent="parent">
<property name="id" value="id"/>
</bean>
看一下最终打印结果:
ClassPathXmlApplicationContext ac=new ClassPathXmlApplicationContext("/resource/applicationContext.xml");
Children children= ac.getBean("children", Children.class);
System.out.println(children.getName() + "," + children.getId());
ac.close();
output:
Parent初始化
Children初始化
name,id
可以看到,先初始化了父类,再初始化子类,然后子类Children继承了Parent中的name属性。如果我们把parent
这项配置给去掉,我这里为了方便不上传这部分代码,直接看下打印结果:
Parent初始化
Children初始化
null,id
很显然,父类Parent中的name属性并没有被继承过来。
最后,再简单说下这里:
- 如果父类bean没有定义class属性,则子类bean必须定义class属性,并且子类中必须拥有父bean中所有属性;
- 还有一种情况,如果父bean有class属性,而子bean没有,那么子类的bean就和父bean是完全一样的bean,当然,子类的bean也就不能注入任何新的属性。
2.9 depends-on
标签
如果一个bean是另一个bean的依赖项,通常将一个bean设置为另一个bean的属性,然后在XML中可以通过ref
标签来引用。而有时,bean之间的关系并没有那么直接,比如实例化DAO之前先实例化DataBase这种情况,而这时候就可以使用depends-on
标签。
该标签用于指定bean初始化时的顺序,也就是说一个beanA初始化之前必须先初始化另一个beanB。对于depends-on
而言,并不需要beanA持有beanB的一个引用,只是一种强制性的在beanA初始化之前对beanB进行初始化。
<bean id="beanOne" class="ExampleBean" depends-on="manager"/>
<bean id="manager" class="ManagerBean" />
- 很多情况下,
depends-on
标签可以使用bean的构造器注入或setter注入代替;还有一点,该bean的类型应该是单例的,因为单例的bean才会被Spring进行管理;- 若要对多个bean进行依赖,
depends-on
提供了一个bean名称的列表,使用分号,逗号或空格来作为有效的分隔符;
<bean id="beanOne" class="ExampleBean" depends-on="manager,accountDao">
<property name="manager" ref="manager" />
</bean>
<bean id="manager" class="ManagerBean" />
<bean id="accountDao" class="x.y.jdbc.JdbcAccountDao" />
depends-on
可以指定依赖bean在具体bean初始化之前先初始化,也可以指定依赖bean在具体bean销毁之前先销毁,同样,这时候bean的类型也只能是单例的。
参考官网文件:https://docs.spring.io/spring/docs/4.3.14.RELEASE/spring/beans-factory-dependson
2.10 primary
标签
我们在前面学习类型的自动注入的时候,可能会出现多个候选者,然后我们可以通过autowire-candidate
注解可以设置某个或多个bean在候选bean中被排除掉,或者成为候选bean。而现在,primary
提供了另一种方式。
primary
元素是说在类型注入的时候,如果多个bean被自动列入候选者之中,那么可以通过使用primary
元素或@primary
注解让该bean成为优先选择者,如果候选者之中只有一个primary
修饰的bean,那么这个bean将会是自动注入的bean。
该属性同样有
true
和false
两个值,默认情况下是false;
还拿上面的例子来说:
<bean id="score" class="entity.Score" >
<property name="math" value="100"/>
</bean>
<bean id="score2" class="entity.Score" primary="true">
<property name="math" value="200"/>
</bean>
<bean id="student" class="entity.Student">
<property name="id" value="id"/>
</bean>
测试代码就不再写了,获取Student中的引用Score的math值:
200
可以看到,由于score2
的优先级高,Student类型中自动注入的就是score2类型。
和
@primary
注解实现类似功能的还有@Qualifier
注解,等我们接下来学习到的时候再来学习。
2.11 factory-bean
和factory-methd
标签
在前面的学习中,我们都是根据配置中的class全名通过反射进行实例化bean,而factory-bean
和factory-method
标签可以让我们通过工厂模式的方法来实例化bean。而工厂模式又大致分为两种,一种是静态工厂方法,另一种是实例工厂方法。
- 静态工厂,是指Factory本身并不需要实例化,而这个Factory类中提供了一个静态方法来生成bean对象;
- 而实例工厂是说Factory类中的方法不是静态的,这也就要求我们先实例一个工厂对象,然后通过这个工厂对象去获取我们所需要的bean对象。
我们先看下静态工厂的简单例子:
public class Student {
private String id;
private String name;
}
public class StudentFactory {
public StudentFactory() {
System.out.println("执行StudentFactory的构造方法进行初始化操作");
}
private static Student getInstance() {
return new Student();
}
}
我们定义了Student和静态工厂StudentFactory,再简单看下配置:
<bean id="student" class="entity.StudentFactory" factory-method="getInstance">
<property name="name" value="name"/>
</bean>
同样,我们再测试下:
Student student = ac.getBean("student", Student.class);
System.out.println(student.getName());
成功打印:
name
可以看到,程序并没有执行工厂类的默认构造方法,也就是没有初始化工厂类,而是通过工厂类的静态方法完成了我们bean对象的初始化工作。
接下来我们再简单看下实例工厂:
首先,将StudentFactory中的static去掉,变为实例方法,然后再看下配置:
<bean id="studentFactory" class="entity.StudentFactory"/>
<bean id="student" factory-bean="studentFactory" factory-method="getInstance">
<property name="name" value="name"/>
</bean>
我们先实例化工厂类,再依靠工厂类去获取我们的bean,然后再看下执行结果:
执行StudentFactory的构造方法进行初始化操作
name
可以,看到,程序先初始化了工厂类,然后再通过实例方法实例化了我们的bean对象。
工厂模式还是挺方便的,我们在实例化对象的时候,可以尝试使用工厂模式来执行我们的初始化操作。
2.12 scope
标签
scope标签表示的bean的作用域,默认情况下我们所创建的bean都是单例的。Spring支持以下几种作用域:
- singleton,默认作用域,在一个Spring容器中,一个bean只会有一个实例,这意味着所有的bean都是共享的,很适合无状态的bean定义;
- prototype,该作用域与
singleton
相反,每次请求都会返回新的实例,从某种意义上类似于Java的new
操作符,适合有状态的bean定义。但需要注意一点就是Spring对于该模式的bean,创建完实例后,就交给对象本身管理自己的生命周期,不会自动做一些销毁的操作。所以由于该模式下生命周期并不是由Spring进行管理,所以这种情况下配置的如destory-method
便不会生效。所以如果可以的话,我们可以在合适的适合调用Spring的销毁方法,让Spring来销毁对象释放资源。- request,该模式是说每次HTTP请求中,每个Bean都会生成一个实例,也就是说每次HTTP请求都将会产生不同的实例。和
prototype
类似,但该作用域仅在Web请求中才有效;- session,该模式是说每次HTTP Session中,每个bean都会生成一个实例,同样,作用域仅在Web请求中有效;
- globalSession,每个全局的HTTP Session,每个bean都会生成一个实例,该作用域仅在Portlet 上下文中有效,使用较少;Portlet规范是类似于Servlet的一种规范,有兴趣的童鞋可自行了解下。
- application,该模式和单例模式有点像,但
application
在ServletContext
中是单例的,而singleton
的应用范围是ApplicationContext
,同样,仅在Web请求中有效;简单看下注解及XML配置:
@Component
@Scope("application")
public class BeanClass {
}
//or
@Component
@ApplicationScope
public class BeanClass {
}
<bean id="beanId" class="com.howtodoinjava.BeanClass" scope="application" />
- WebSocket,该模式用于HTTP中远程主机间进行双向通信,同样,也是仅在Web请求下有效。通常 WebSocket范围内的bean通常是单例的,并且比任何单独的WebSocket会话要活的更长。注解和XML如下:
@Component
@Scope("websocket")
public class BeanClass {
}
<bean id="beanId" class="com.howtodoinjava.BeanClass" scope="websocket" />
- 还可以自定义线程的scope,这里就不多说了,想查看更多有关scope的内容,可参考官方文档,上面讲的非常清楚:
https://docs.spring.io/spring/docs/4.3.14.RELEASE/spring-framework-reference/html/beans.html#beans-factory-scopes
还可以参考:Spring Bean Scopes
需要注意下,因为singleton适合无状态的情况,所以在多线程的时候,如果bean被定义为单例模式,可能会出现线程安全问题,这里需要注意下。
总结下:
- 在Spring中,我们使用最多的就是singleton和prototype模式,singleton是一种无状态的bean类型,当项目中不存在多线程共享对象的时候可以选择singleton模式,这样可以稍微节约资源。使用prototype的时候记得注意对象的销毁操作。
- 在Spring配置文件XML中直接配置application,websocket,会报错,还没搞清楚为什么。
3. alias
标签
这个标签用于为bean提供别名。根据官方文档解释:
- 在bean本身定义中,我们可以通过id和name属性来为bean提供多个名称,这些名称等价于相同的bean,在某种情况下是很有用的,比如应用程序中的每个组件,使用特定于该组件本身的bean名称来引用公共的依赖关系。
- 但有的时候这种方式定义的别名并不总是能满足我们的所有需求,这时候就可以通过标签
alias
来实现另一种别名的定义。比如说在大型系统中,每个子系统都有自己的一套配置,组件A通过subsystemA-dataSource
名称定义了一个数据源bean,而组件B想通过subsystemB-dataSource
引用这一个数据源,而主系统也想通过myApp-dataSource
来引用该数据源,这种情况下就可以使用alias
标签来完成这种操作。- 这样一来,每个组件及主程序就可以通过一个唯一的名字来引用同一个数据源,而相互间不会受到干扰。
- 官方文档地址:https://docs.spring.io/spring/docs/4.3.14.RELEASE/spring-framework-reference/html/beans.html#beans-beanname-alias
使用方式很简单:
<alias name="subsystemA-dataSource" alias="subsystemB-dataSource"/>
<alias name="subsystemA-dataSource" alias="myApp-dataSource" />
不过需要保证alias
唯一,不能重复。
4. description
标签
这个标签就不多说了,就是对该配置文件的描述信息,不过配置的时候注意下顺序,一般用于配置的最顶层。
<description>Spring MVC Test</description>
5. import
标签
在我们的Spring配置中,随着我们项目的越来越大,配置文件的越来越复杂,模块化则是我们需要考虑的问题了,而import
标签则是充当了这样的角色,我们可以通过import
标签将多个Spring配置文件引入到我们的applicationContext文件中。
比如我们针对数据库配置,缓存配置,MQ配置,日志配置等模块,为每个模块配置相应的文件:
applicationContext-cache.xml
applicationContext-db.xml
applicationContext-mp.xml
applicationContext-log.xml
然后在applicationContext配置文件中使用import
进行引入:
<?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.xsd"
default-autowire="byType">
<import resource="classpath*:spring/applicationContext-cache.xml"/>
<import resource="classpath*:spring/applicationContext-db.xml"/>
<import resource="classpath*:spring/applicationContext-log.xml"/>
<import resource="classpath*:spring/applicationContext-mq.xml"/>
</beans>
这样,当我们需要新加配置的时候,只需要添加对应的配置文件,然后再使用import
标签进行引入即可。这里再简单说下,resource的几种选择:
<import resource="applicationContext-test.xml"/>
,默认情况下,Spring会根据相对路径来加载对应的配置文件;<import resource="classpath:"/>
,配置为classpath
的情况下,是去class路径及相应的jar包下加载。而classpath:
为精确配置,只能匹配一个,而classpath*:
是一种通配符的形式,可以加载多个;<import resource="file:"/>
,file是加载一个或多个文件系统的resource,比如file:D:/*.txt
将加载D盘下所有的txt文件;<import resource="http:"/>
,http就是从网络下加载相应的resource;
针对classpath,后续我们会再仔细讨论这个问题,现在先大致了解下即可。
参考资料:
Spring中的destroy-method方法详解
https://docs.spring.io/spring/docs/4.3.14/spring-framework-reference/beans.html