掌握 Spring IoC 两种常规操作,提高工作效率

前言

上一讲介绍了 Spring 的 IoC,即控制反转,程序中由 Spring 来管理对象,当需要使用某个对象时,直接通过 IoC 容器来获取对象,并通过 DI 来完成对象之间的注入关系。下面继续来学习 IoC 的相关知识。

Spring 中的 bean

bean 是根据 scope 来生成的,表示 bean 的作用域,scope 有 4 种类型:

  • singleton,单例,表示通过 Spring 容器获取的该对象是唯一的;
  • prototype,原型,表示通过 Spring 容器获取的对象都是不同的;
  • request,请求,表示在一次 HTTP 请求内有效;
  • session,会话,表示在一个用户会话内有效。

后两种只适用于 Web 项目,大多数情况下,我们只会使用 singleton 和 prototype 两种 scope,并且 scope 的默认值是 singleton。

我们通过一个例子来学习这两种配置的区别。

(1)创建 User 实体类

public class User {
    private int id;
    private String name;
    private int age;
    public User() {
         System.out.println("创建了User对象");
    }   
}

(2)在 spring.xml 配置 User 的实例化 bean

<bean id="user" class="com.southwind.entity.User">
   <property name="id" value="1"></property>
   <property name="name" value="张三"></property>
   <property name="age" value="23"></property>
</bean>

(3)测试类中通过 Spring 容器获取两个 User 实例化对象 user1 和 user2,并且通过 == 方法判断是否为同一个对象

ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring.xml");
User user = (User) applicationContext.getBean("user");
User user2 = (User) applicationContext.getBean("user");
System.out.println(user == user2);

运行结果如下图所示。

<img src="https://images.gitbook.cn/446b73d0-96e5-11e8-9e0c-8bfb55c56242" width = "70%" />

看到结果打印 true,并且 User 的构造函数只执行了一次,表示 user1 和 user2 是同一个对象,因此 scope 默认值为 singleton,即默认通过 Spring 容器创建的对象都是单例模式。

修改 spring.xml 中的配置,将 scope 改为 prototype。

<bean id="user" class="com.southwind.entity.User" scope="prototype">
   <property name="id" value="1"></property>
   <property name="name" value="张三"></property>
   <property name="age" value="23"></property>
</bean>

执行代码,结果如下图所示。

<img src="https://images.gitbook.cn/50b449f0-96e5-11e8-a80f-e9b555a946c4" width = "70%" />

可以看到,调用了两次构造函数,并且 == 的结果为 false,表示现在的 user1 和 user2 是两个对象。

点击这里了解《Spring 全家桶》

Spring 的继承

Spring 的继承与 Java 的继承不一样,但思想很相似,子 bean 可以继承父 bean 中的属性。看到这里,你可能会有这样的疑问:子 bean 可以继承父 bean 中的方法吗?

其实这里不存在方法的继承,Spring 的继承是在对象层面进行操作的,即两个 bean 来自同一个类,因此方法是一样的,不存在继承关系,具体使用如下所示。

(1)在 spring.xml 中配置两个 User bean,并建立继承关系。

<bean id="user" class="com.southwind.entity.User">
   <property name="id" value="1"></property>
   <property name="name" value="张三"></property>
   <property name="age" value="23"></property>
</bean>
<bean id="user2" class="com.southwind.entity.User" parent="user"></bean>

(2)运行代码,结果如下。

User user2 = (User) applicationContext.getBean("user2");
System.out.println(user2);

<img src="https://images.gitbook.cn/9e5ecbd0-96e5-11e8-9e0c-8bfb55c56242" width = "70%" />

可以看到,创建了两个 User 对象 user1 和 user2,并且 user2 继承了 user1 的所有属性。user2 在继承 user1 所有属性的基础之上,还可以对属性进行覆盖,直接在 spring.xml 中添加 property 即可。

<bean id="user" class="com.southwind.entity.User">
   <property name="id" value="1"></property>
   <property name="name" value="张三"></property>
   <property name="age" value="23"></property>
</bean>
<bean id="user2" class="com.southwind.entity.User" parent="user">
   <!-- 覆盖 name 属性 -->
   <property name="name" value="李四"></property>
</bean>

再次运行代码,结果如下图所示。

<img src="https://images.gitbook.cn/b10d34b0-96e5-11e8-9f54-b3cc9167c22b" width = "70%" />

name 属性已经被修改为李四,完成覆盖。有读者可能会问,Spring 中的 bean 能不能在不同类之间继承?

答案是可以的,但是需要这两个类的属性列表完全一致,否则会报错,实际开发中并不会使用到这种方式。

Spring 的依赖

与继承类似,依赖也是 bean 和 bean 之间的一种关联方式,配置依赖关系后,被依赖的 bean 一定先创建,再创建依赖的 bean,我们还是通过代码来理解。

(1)创建 Car 实体类

public class Car {
    private int id;
    private String brand;
    public Car() {
        System.out.println("创建了Car对象");
    }
}

(2)在 spring.xml 中配置 User bean、Car bean

<bean id="user" class="com.southwind.entity.User">
   <property name="id" value="1"></property>
   <property name="name" value="张三"></property>
   <property name="age" value="23"></property>
</bean>
<bean id="car" class="com.southwind.entity.Car">
   <property name="id" value="1"></property>
   <property name="brand" value="宝马"></property>
</bean>

(3)测试类中获取两个 bean

ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring.xml");
User user = (User) applicationContext.getBean("user");
Car car = (Car) applicationContext.getBean("car");

结果如下图所示。

<img src="https://images.gitbook.cn/bb27b920-96e5-11e8-a80f-e9b555a946c4" width = "70%" />

看到结果,先创建 User,后创建 Car,这是由 spring.xml 中 bean 的配置顺序来决定的,先到先得,先配置 User bean,因此先创建了 User 对象。现在修改 spring.xml 配置,User 依赖 Car,设置 depends-on 属性,如下所示。

<bean id="user" class="com.southwind.entity.User" depends-on="car">
   <property name="id" value="1"></property>
   <property name="name" value="张三"></property>
   <property name="age" value="23"></property>
</bean>
<bean id="car" class="com.southwind.entity.Car">
   <property name="id" value="1"></property>
   <property name="brand" value="宝马"></property>
</bean>

再次运行代码,看到结果,先创建 Car,后创建 User,因为 User 依赖于 Car,所以必须先创建 Car 对象,User 对象才能完成依赖。

<img src="https://images.gitbook.cn/c54fab10-96e5-11e8-9e0c-8bfb55c56242" width = "70%" />

Spring 读取外部资源

在实际开发中,数据库配置一般会保存在 Properties 文件中方便维护,如果使用 Spring 容器来生成数据源对象,如何读取到 properties 配置文件中的内容?

(1)创建 jdbc.properties

driverName = com.mysql.jdbc.Driver
url = jdbc:mysql://localhost:3306/myTest?useUnicode=true&characterEncoding=UTF-8
user = root
pwd = root

(2)spring.xml 中配置 C3P0 数据源

<!-- 导入外部的资源文件 -->
<context:property-placeholder location="classpath:jdbc.properties"></context:property-placeholder>
<!-- 创建 C3P0 数据源 -->
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
   <property name="user" value="${user}"></property>
   <property name="password" value="${pwd}"></property>
   <property name="driverClass" value="${driverName}"></property>
   <property name="jdbcUrl" value="${url}"></property>
</bean>

第一步:导入外部资源文件。

使用 context:property-placeholder 标签,需要导入 context 命名空间。

第二步:通过 ${} 表达式取出外部资源文件的值。

(3)测试类中获取 Spring 创建的 dataSource 对象

ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring.xml");
DataSource ds = (DataSource) applicationContext.getBean("dataSource");
Connection conn = null;
try {
     conn = ds.getConnection();
} catch (SQLException e) {
     // TODO Auto-generated catch block
     e.printStackTrace();
}
System.out.println(conn);

(4)运行代码,看到结果,打印 dataSource 对象

<img src="https://images.gitbook.cn/d19573f0-96e5-11e8-a80f-e9b555a946c4" width = "70%" />

除了使用 <property> 元素为 Bean 的属性装配值和引用外,Spring 还提供了另外一种 bean 属性的装配方式:p 命名空间,该方式进一步简化配置代码。

p 命名空间

p 命名空间可以简化 bean 的各种配置,直接通过代码来学习。

(1)在 User 实体类中添加 Car 属性

public class User {
    private int id;
    private String name;
    private int age;
    private Car car;
    public User() {
        System.out.println("创建了User对象");
    }
    @Override
    public String toString() {
        return "User [id=" + id + ", name=" + name + ", age=" + age + ", car="
                + car + "]";
    }
}

(2)spring.xml 中创建 User bean 和 Car bean,并且通过 p 命名空间给属性赋值,同时完成依赖注入,注意需要引入 p 命名间

<bean id="user" class="com.southwind.entity.User" p:id="1" p:name="张三" p:age="23" p:car-ref="car"></bean>
<bean id="car" class="com.southwind.entity.Car" p:id="1" p:brand="宝马"></bean>

(3)测试类中获取 User 对象,并打印

ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring.xml");
User user = (User) applicationContext.getBean("user");
System.out.println(user);

运行结果如下图所示,创建了 User 对象和 Car 对象,并且完成属性赋值,以及级联关系。

<img src="https://images.gitbook.cn/dae77840-96e5-11e8-a80f-e9b555a946c4" width = "60%" />

总结

这一讲我们讲解了 Spring IoC 两种常规操作,分别是依赖注入与 p 命名空间,依赖注入是维护对象关联关系的机制。p 命名空间实际上是对配置文件的简化,以提高我们的开发效率。

点击下载源码

分享交流

我们为本课程付费读者创建了微信交流群,以方便更有针对性地讨论课程相关问题。入群方式请到第 1-4 课末尾添加小助手的微信号,并注明「全家桶」。

阅读文章过程中有任何疑问随时可以跟其他小伙伴讨论,或者直接向作者提问(作者看到后抽空回复)。你的分享不仅帮助他人,更会提升自己。

温馨提示:需购买才可入群哦,加小助手微信后需要截已购买的图来验证~

点击了解《案例上手 Spring 全家桶》

02-课程二级页头图.jpg
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,736评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,167评论 1 291
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,442评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,902评论 0 204
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,302评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,573评论 1 216
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,847评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,562评论 0 197
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,260评论 1 241
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,531评论 2 245
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,021评论 1 258
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,367评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,016评论 3 235
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,068评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,827评论 0 194
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,610评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,514评论 2 269

推荐阅读更多精彩内容