Spring-基于xml配置bean
文章目录
一. Bean实例化的4种方式
前提:要求Spring管理的类都要提供 公开的无参的构造。
通过Bean标签告诉Spring该用什么方式获得类的对象
1.1 无参数构造方法(开发最常用)
首先写一个pojo类
public class User {
private Integer id;
private String name;
public User(){
System.out.println("调用默认构造");
}
@Override
public String toString() {
return "User{" +
"id=" + id +
", name='" + name + '\'' +
'}';
}
}
然后告诉Spring如何管理
<!-- 让Spring管理当前的User类-->
<bean id = "user" class="org.buaa.pojo.User">
</bean>
最后使用它
public void testUser(){
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
User user = (User) context.getBean("user");
System.out.println(user);
/* 结果
调用默认构造
User{id=null, name='null'}
*/
}
1.2 静态工厂方法
静态工厂方法: 在一个类中书写静态方法,再这个方法中返回某个 Bean 的对象。
首先写一个基本的工厂
public class UserFactory {
public static User getUserInstance(){
return new User();
}
}
告诉Spring如何配置
<!--演示通过静态工厂获取某个类的对象-->
<!--通过factory-method标签告知,我们使用的是静态工厂-->
<bean id = "user" class = "org.buaa.factory.UserFactory" factory-method="getUserInstance"></bean>
实际上Spring管理的不是UserFactory类,而是User类
编写测试
public void testStaticFactory(){
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
// 这个方法最终得到的是一个User对象,所以应该接收一个User对象
// 通过静态工厂方法得到User
User user = (User) context.getBean("user");
System.out.println(user);
/*
调用默认构造
调用默认构造
User{id=null, name='null'}
*/
}
可以看到构造方法执行了两次。原因: 在加载核心配置文件时,Spring会加载每一个标签,而我们没有删除 #1.1 中的标签,所以目前核心配置文件如下。可以看到Spring管理了两个User,都会初始化一次,所以会调用两次构造。
<!-- 让Spring管理当前的User类-->
<bean id = "user" class="org.buaa.pojo.User"></bean>
<!--
演示通过静态工厂获取某个类的对象
id:当前静态工厂返回的Bean的唯一标识
class:静态工厂的class
factory-method:配置的静态工厂的静态方法
-->
<bean id = "userFactory" class = "org.buaa.factory.UserFactory" factory-method="getUserInstance"></bean>
实际上,我们仅书写加载核心配置文件的代码,也会出现调用两次构造的情况。
所以,核心配置文件只应加载一次。但是一个Spring的核心容器中可以管理多个相同的class而new出来的对象
1.3 实例工厂方法
实例工厂方法:和静态工厂类似,但工厂本身是一个实例。
在类中提供非静态的方法,在这个方法中返回需要获取的Bean对象
实例工厂
public class InstanceFactory {
public User getUserInstance(){
return new User();
}
}
配置
<!-- 配置实例工厂自己的Bean -->
<bean id = "instanceFactory" class = "org.buaa.factory.InstanceFactory"></bean>
<!--
实例工厂本身已经被Spring管理,而实例工厂返回另一个Bean对象,
在配置时需要通过factory-bean属性来引用已经存在的工厂Bean
需要通过factory-method 来指定实例工厂bean中返回别的bean的方法
-->
<bean id = "user2" factory-method="getUserInstance" factory-bean="instanceFactory"></bean>
测试
public void testInstanceFactory(){
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
User user = (User) context.getBean("user2");
System.out.println(user);
}
1.4 FactoryBean方式(Spring底层使用较多)
经常面试到
是一种实例化Bean的方式。
首先编写自定义类,实现 FactoryBean接口
/**
* 使用 Spring 底层提供的 FactoryBean接口 来实例化某个类的对象
* 自定义一个类,实现 FactoryBean接口
*/
public class UserInstance implements FactoryBean<User> {
// getObject方法中创建出需要管理的Bean对象
@Override
public User getObject() throws Exception {
return new User();
}
@Override
public Class<?> getObjectType() {
return User.class;
}
}
配置
<!--
配置通过FactoryBean接口获取User实例
目前配置Bean的虽然不是User,但它实现了FactoryBean接口,且泛型书写的是User,
Spring在实例化的时候,会发现它实现了FactoryBean接口,那么就可以从复写的getObject方法中获得对象
-->
<bean id = "user3" class = "org.buaa.factory.UserInstance"></bean>
测试
1.5 FactoryBean 与 BeanFactory区别
FactoryBean:是用来创建指定的一个Bean实例的接口,用来告诉Spring如何实例化一个具体的类的对象
BeanFactory:是管理Bean的,它是Spring中最的顶层接口之一(ApplicationContext 是它的子接口的子接口),代表的是Spring容器加载spring核心配置文件的对象
二. Bean相关知识
2.1 Bean的作用域
在配置 bean 的时候,有一个标签为 scope,代表它的作用域
<bean id = "user4" class = "org.buaa.pojo.User" scope="singleton"></bean>`
<bean id = "user5" class = "org.buaa.pojo.User" scope="prototype"></bean>`
- 默认:singleton
- singleton: 单例,当前的Bean标签关联的对象永远是唯一的,(常用)
- prototype: 多例,将某个Bean配置为多例后,只要通过Spring核心对象获取Bean,都会重新创建Bean对象
singleton下每次getBean,获取的结果都是同一个。
prototype下每次getBean,都会新建一个对象。
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
User user1 = (User) context.getBean("user4");
User user2 = (User) context.getBean("user4");
user1 == user2;// true
User user3 = (User) context.getBean("user5");
User user4 = (User) context.getBean("user5");
user3 == user4;// false
2.2 Bean的生命周期
Bean在创建/销毁时,让它执行一些方法。
pojo如下:
public class User {
...
public void init(){
System.out.println("初始化时可以执行的方法");
}
public void destory(){
System.out.println("销毁时可以执行的方法");
}
...
}
配置
<!-- 配置Bean被Spring管理时,可以指定自己的一些初始化、销毁方法 -->
<bean id = "user5" class = "org.buaa.pojo.User" init-method="init" destroy-method="destory"></bean>
测试
public void testUserInitAndDestory(){
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
User user = (User) context.getBean("user5");
System.out.println(user);
/*
调用默认构造
初始化时可以执行的方法
User{id=null, name='null'}
*/
((ClassPathXmlApplicationContext)context).close();
/* 销毁时可以执行的方法 */
}
注意 并没有执行销毁的方法,想要执行它,需要手动关闭Spring容器,而ApplicationContext接口没有提供close方法,所以必须强制幻化为ClassPathXmlApplicationContext对象,然后调用close。
注意 如果Bean设置为prototype多例,那么即使手动关闭也不会执行。
2.3 Bean的属性注入
2.3.1 构造方法
缺点:在xml文件中写死了传入什么值。当然也可以改为"${}"通过引配置文件解决,但是很麻烦
pojo
public class User {
...
public User(Integer id, String name){
this.id = id;
this.name = name;
}
...
}
配置
<!--
演示通过构造方法给Bean的属性注入数据
使用constructor-arg标签配置构造方法需要的参数
name:变量名
value:值
index:第index个参数
ref:可以传一个复杂对象
-->
<bean id = "car" class = "org.buaa.pojo.Car"/>
<bean id="user6" class = "org.buaa.pojo.User">
<constructor-arg name = "name" value = "jackl"/>
<constructor-arg index = "1" value ="1"/>
<constructor-arg name = "car" ref = "car"/>
</bean>
2.3.2 setter方法注入
首先要在pojo类中书写set方法,然后配置文件
pojo
public class Car {
...
private String color;
private Integer price;
private String type;
public void setColor(String color) {
this.color = color;
}
public void setPrice(Integer price) {
this.price = price;
}
public void setType(String type) {
this.type = type;
}
...
}
配置
<!--
演示通过set方法给Bean的属性注入数据
使用property标签配置构造方法需要的参数
name:set后的XXX名称,如setColor方法,对应 name = "Color"
value:值
ref:可以传一个复杂对象
-->
<bean id = "car" class = "org.buaa.pojo.Car">
<property name = "Color" value = "黄色"/>
<property name = "Price" value = "100"/>
<property name = "Type" value = "黄色"/>
</bean>
2.3.3 集合属性注入
其实本质还是前两种注入方式,只是想注入集合有些不太一样
<!--
演示通过set方法给Bean的集合注入属性,要求pojo类中存在一个 setLists的方法。
-->
<bean id = "student" class = "org.buaa.pojo.Student">
<property name = "lists">
<list>
<value>爬山</value>
<value>下海</value>
</list>
</property>
<!--演示下map如何注入-->
<property name = "maps">
<map>
<entry key = "addr" value = "北京"/>
</map>
</property>
</bean>
<!--
演示通过set方法给Bean的集合注入属性
-->
<bean id = "student" class = "org.buaa.pojo.Student">
<constructor-arg name = "set"/>
<set>
<value>爬山</value>
<value>下海</value>
</set>
</constructor-arg>
</bean>
xml配置中的标签及属性
bean标签中的属性
- class: 本质上是为反射提供服务,告诉框架这个类是什么类,举例: class=“com.service.UserDao”
- id:唯一标识
- factory-method:配置的静态工厂的静态方法
- factory-bean: 引用已经存在的工厂bean
- scope:用于声明Bean的作用域,singleton: 单例;prototype: 多例
- init-method: 声明创建Bean时执行某个方法
- destroy-method: 声明销毁Bean时执行某个方法,要求Bean为单例且需要手动关闭Spring时才执行
constructor-arg标签
是bean的子标签,用于使用构造方法传入数据
- name:构造方法上的参数名
- value:传入的值
- index:构造方法上第index个参数
- ref:可以传一个复杂对象如自定义、包装对象
property标签set
是bean的子标签,用于使用set方法传入数据
- name:set后的XXX名称,如setColor方法,对应 name = “Color”
- value:传入的值
- ref:可以传一个复杂对象如自定义、包装对象
集合标签
如果注入的数据是集合,那么要使用特定的集合标签
<list>
<value>值</value>
</list>
<set>
<value>值</value>
</set>
<map>
<entry key = "k", value = "v"/>
</map>
<array>
<value>11</value>
<value>12</value>
<value>13</value>
</array>
<props>
<!--Properties类,类似map,用于持久的保存属性如环境变量-->
<prop key = "sex">男</prop>
<prop key = "height">2.0</prop>
</props>