跳到主要内容

Spring IoC (控制反转)之 xml 配置

1. 前言

本小节目的在于带领大家熟练 xml 文件配置, 应用 xml 文件配置 IoC。

在第二节中我们通过一个入门工程简单地体验了一把 Spring 的使用。在第三节中梳理了一下 Spring 的工作流程。

大家有了一个初步认知,Spring 框架的工作脱离不了核心配置文件 applicationContext.xml

在配置文件中我们目前只用到了一个 bean 标签,它的作用是用于描述 Java 的类,让框架启动加载配置文件实例化的。

疑问导出

那么我们知道描述一个类有几个要素,类名、属性、构造函数 set 和 get 方法对吧?而 bean 标签如何描述一个详细的类呢?

带着疑问… 开始本节内容。

2.bean 标签中的属性介绍

核心配置文件回顾

<bean id="user" class="com.wyan.entity.User" ></bean>  



在上面的代码中可以看到,在 bean 标签中有两个属性,一个是 id 一个是 class。那么在 bean 标签中都有哪些属性呢?

属性列表

学号姓名
id定义的唯一标识
name同 id 的意义一致
class
factory-bean工厂对象
factory-method工厂方法
init-method初始化执行的方法
destroy-method销毁执行的方法
scope对象的作用域
lazy-init懒加载
autowire依赖注入
depends-on依赖于某个实例

疑问导出

上述属性是配置 bean 标签中可以选择的属性,当然一般来讲,我们无需配置所有,可以根据自己的需求配置需要的属性信息,那么如何选择这些属性呢?

2.1 属性详细解释

2.1.1 id 和 name 标签的使用

我们目前已经知道所有被实例化后的对象都存在于 Spirng 的容器中,那么从容器中获取这些对象需要一个属性 id 对吧?那么 name 和 id 有什么关系呢?

查看官方文档得知 Spring 的容器会给初始化的每个 bean 都定义一个或多个标识符。这些标识符在容器内必须是唯一的。一个 bean 通常只有一个标识符。而 name 和 id 都可以起到标识符的作用。

所以在 XML 配置文件,我们一般使用 id 或者 name 属性,定义 bean 的唯一标识,

这样我们才能通过定义好的唯一标识,从 Spring 的容器中获取他们。

代码实例:

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.xsd">

<bean id="user" name="user2" class="com.wyan.entity.User" ></bean>

</beans>


测试代码如下:

    public static void main(String[] args) {
ApplicationContext context =
new ClassPathXmlApplicationContext("classpath:applicationContext.xml");
System.out.println(context.getBean("user"));
System.out.println(context.getBean("user2"));
}


结果如图所示:

图片描述

结论证明

我们通过 bean 标签中的 id 属性 user, 或者使用 bean 标签中的 name 属性 user2, 都可以得到 Spring 容器中的 user 对象的示例,而且打印的地址是同一个。我们之前说过一句,默认在容器中的实例都是单例的,在这里也得到了证明。

2.1.2 class 属性

bean 标签的定义实质上是创建一个或多个对象的方法。当 xml 文件被解析加载的时候,使用该 bean 定义封装的配置数据来创建(或获取)实际对象,而创建获取的对象是谁呢?就是通过 class 属性中定义的类的全路径来指定 。

一般来讲 class 中的类实例化有两种方式

​ 一种是反射 ,相当于我们使用的 new 关键字。这种也是我们常用的方式。当然不要忘记提供无参数的构造方法(类中默认有无参构造,但是如果自定义了有参构造,默认的无参不会提供)

​ 一种是工厂模式 ,需要借助于 factory-bean 和 factory-method 两个属性,这种方式不常用,我们可以了解下。

2.1.3 factorybean 和 factorymethod 属性

这两个属性主要用于工厂模式实例化 bean 的时候使用,不是很常见。工厂模式有两种,这里分别做个实例,帮助大家理解。

静态工厂模式实例:

<!--applicationContext的配置bean节点-->
<bean id="user" class="com.wyan.entity.User" factory-method="createUserInstance"/>


创建 bean 示例的 Java 工厂类:

public class User {

private static User user = new User();

private User() {}

public static User createInstance() {
return user;
}
}


解释:在定义使用静态工厂方法创建的 bean 时,class 属性指定的是被创建的类,包含静态的方法,并使用 factory-method 属性来指定工厂方法本身名称。

普通工厂模式

<!--spring实例化工厂对象 用于创建java实例 -->
<bean id="beanFactory" class="com.wyan.factory.BeanFactory"></bean>
<!-- 被工厂创建的对象实例 -->
<bean id="user1" factory-bean="beanFactory" factory-method="createUser1"/>


工厂类代码:

public class BeanFactory {

private static User1 user1 = new User1();

private static User2 user2 = new User2();

public User1 createUser1() {
return user1;
}

public User2 createUser2() {
return user2;
}
}


解释:先实例化先创建各个对象示例的工厂对象到容器中,自身的 bean 标签将 class 属性保留为空,并在 factory-bean 属性中指定当前容器中的工厂 Bean 名称,再使用 factory-method 属性设置创建示例的方法名称。

2.1.4 init-method 和 destroy-method 属性的使用

这两个属性比较好理解 init-method 就是 bean 被初始化后执行的方法,destory-method 就是 bean 被销毁执行的代码。

我们来个测试类

public class User {

public User(){
System.out.println("我被spring实例化了");
}

public void initMethod(){
System.out.println("user类实例化时候执行的代码");
}
public void destoryMethod(){
System.out.println("user类实例被销毁时候执行的代码");
}
}


配置文件

 <bean id="user" name="user2" class="com.wyan.entity.User" init-method="initMethod" destroy-method="destoryMethod" ></bean>**


测试代码:

    public static void main(String[] args) {
ApplicationContext context =
new ClassPathXmlApplicationContext("classpath:applicationContext.xml");

}


加载 Spring 的配置文件控制台打印如下:

图片描述

有个小疑问:销毁语句没打印呢?那是因为并没有调用容器的销毁方法。

改造测试代码如下:

    public static void main(String[] args) {
AbstractApplicationContext context =
new ClassPathXmlApplicationContext("classpath:applicationContext.xml");
context.close();
}


解释:ApplicationContext 没有 close 方法使用它的子类

运行结果:

图片描述

2.1.5 其余属性作用

scope :指定示例的作用范围,后续章节详细讲解;

lazy-init :表示是否为懒加载;

autowire :指定属性注入方式,后续章节详解;

depends-on: 表示是否有依赖的 bean 对象,后续依赖注入章节详细解释。

2.2 构造函数的使用

刚刚我们详细解释了 bean 标签内部的属性,经过几个小实例以后不禁也有个问题:

如果我们定义的类中有一些初始化的参数,并且定义好了有参数的构造,通过 xml 配置文件如何体现呢?

实现起来非常简单,跟我来进行一个小实例:

改造 User 类

这是一个普通的 Java 类对象,包含两个属性及其 get 和 set 方法,并且提供了空参构造和有参构造,为了测试方便再覆写一个 toString 方法。

public class User {

private Integer id;
private String name;

public User() {
}
public User(Integer id, String name) {
this.id = id;
this.name = name;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "User{" +
"id=" + id +
", name='" + name + '\'' +
'}';
}
}


xml 配置文件方式

    <bean id="user" class="com.wyan.entity.User" >
<constructor-arg name="id" value="1"></constructor-arg>
<constructor-arg name="name" value="zs"></constructor-arg>
</bean>


测试结果

图片描述

其实对于有参构造实例化对象而言,使用一个标签 constructor-arg 即可,表示构造的参数,如果有多个,可以继续添加,这里不多做演示。

疑问导出

可能有同学会想,那么如果以后我们的属性需要动态更改呢?或者我们的属性不是基本类型而是另外的对象呢? 后续在第三章依赖注入多种属性的小节给大家讲解 。

3. 小结

本章节带着大家详细解释了 bean 标签的使用,那么通过本章节我们收获了哪些呢?

  1. 容器内部命名唯一标识可以通过 id 也可以通过 name;
  2. 实例化对象有两种方式 反射模式和工厂模式;
  3. 如果是反射模式,那么必须配置 class 属性,因为需要用 class 属性中类的全路径来实例化 bean 对象;
  4. 如果需要在类实例化初始化参数,可以使用 init 方法也可以使用有参构造。

持之以恒的学习是成功的最快捷径… 切记眼高手低。