Spring 的基本应用——IoC 和 AOP

前言

对于任何一个 Java 开发人员,Spring 的大名一定如雷贯耳,在行业中可谓是无人不知、无人不晓,说它是 Java 领域第一框架毫不为过。

image

(图片来自 Spring 官网)

Spring 概念诞生于 2002 年,创始人 Rod Jahnson 在其著作《Expert One-on-One J2EE Design and Development》中第一次提出了 Spring 的核心思想,于 2003 年正式发布第一个版本 Spring Framework 0.9。

经过十几年的优化迭代,Spring Framework 已经从最初的取代 EJB 框架逐步发展为一套完整的生态,最新的版本是 5.X,支持现代 Java 开发的各个技术领域,家族两大核心成员 Spring Boot 和 Spring Cloud 更是当下 Java 领域最为热门的技术栈。

毋庸置疑,Spring 已经成为 Java 开发的行业标准,无论你是初级程序员还是架构师,只要是做 Java 开发的,工作中或多或少一定会接触到 Spring 相关技术栈。

我们所说的 Spring 全家桶各个模块都是基于 Spring Framework 衍生而来,通常所说的 Spring 框架一般泛指 Spring Framework,它包含 IoC 控制反转、DI 依赖注入、AOP 面向切面编程、Context 上下文、bean 管理、Spring Web MVC 等众多功能模块,其他的 Spring 家族成员都需要依赖 Spring Framework。

可以简单理解 Spring Framework 是一个设计层面的框架,通过分层思想来实现组件之间的解耦合,开发者可以根据需求选择不同的组件,并且可以非常方便的进行集成,Spring Framework 的这一特性使得企业级项目开发变得更加简单方便。

Spring 的两大核心机制是 IoC(控制反转)和 AOP(面向切面编程),对于初学者来讲,搞清楚这两个核心机制就掌握了 Spring 的基本应用。这两大核心机制也是 Java 设计模式的典型代表,其中 IoC 是工厂模式,AOP 是代理模式。

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

什么是 IoC 和 AOP

下面来详细了解 IoC,IoC 是 Spring 框架的灵魂,非常重要,理解了 IoC 才能真正掌握 Spring 框架的使用。

IoC 也叫控制反转,首先从字面意思理解,什么叫控制反转?反转的是什么?

在传统的程序开发中,需要获取对象时,通常由开发者来手动创建实例化对象,但是在 Spring 框架中创建对象的工作不再由开发者完成,而是交给 IoC 容器来创建,我们直接获取即可,整个流程完成反转,因此是控制反转。

举个例子:超市购物。

  • 传统方式:你去超市买东西,需要自己拿着袋子去超市购买商品,然后自己把袋子提回来。
  • IoC 容器:你只需要把袋子放在家门口,袋子里面会自动装满你需要的商品,直接取出来用就可以了。

我们通过创建一个 Student 对象的例子来对比两种方式的区别。

传统方式

(1)创建 Student 类

public class Student {
    private int id;
    private String name;
    private int age;
}

(2)测试方法中调用构造函数创建对象

Student student = new Student();

IoC 容器

实现步骤
  • 在 pom.xml 中添加 Spring 依赖
  • 创建配置文件,可以自定义文件名 spring.xml
  • 在 spring.xml 中配置 bean 标签,IoC 容器通过加载 bean 标签来创建对象
  • 调用 API 获取 IoC 创建的对象

IoC 容器可以调用无参构造或者有参构造来创建对象,我们先来看无参构造的方式。

无参构造
<!-- 配置 student 对象-->
<bean id="stu" class="com.southwind.entity.Student"</bean>

配置一个 bean 标签:

  • id,对象名
  • class,对象的模板类

接下来调用 API 获取对象,Spring 提供了两种方式来获取对象:id 或者运行时类。

(1)通过 id 获取对象

//1.加载 spring.xml 配置文件
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring.xml");
//2.通过 id 值获取对象
Student stu = (Student) applicationContext.getBean("stu");
System.out.println(stu);

第一步:加载 spring.xml 配置文件,生成 ApplicationContext 对象。

第二步:调用 ApplicationContext 的 getBean 方法获取对象,参数为配置文件中的 id 值。程序在加载 spring.xml 时创建 stu 对象,通过反射机制调用无参构造函数,所有要求交给 IoC 容器管理的类必须有无参构造函数。

运行结果请点击《Spring 全家桶》内容查看。

可以看到,此时 stu 对象的属性全部为空,因为调用无参构造只会创建对象而不会进行赋值,如何赋值呢?只需要在 spring.xml 中进行相关配置即可,如下所示。

<!-- 配置 student 对象 -->
<bean id="stu" class="com.southwind.entity.Student">
    <property name="id" value="1"></property>
    <property name="name" value="张三"></property>
    <property name="age" value="23"></property>
</bean>

添加 property 标签:name 对应属性名,value 是属性的值。若包含特殊字符,比如 name="<张三>",使用 <![CDATA[<张三>]]> 进行配置,如下所示。

<!-- 配置 student 对象 -->
<bean id="stu" class="com.southwind.entity.Student">
   <property name="id" value="1"></property>
   <property name="name">
       <value><![CDATA[<张三>]]></value>
   </property>
   <property name="age" value="23"></property>
</bean>

运行结果请点击《Spring 全家桶》内容查看。

Spring 通过调用每个属性的 setter 方法来完成属性的赋值,因此实体类必须有 setter 方法,否则加载时报错,getter 方法可省略。

(2)通过运行时类获取对象

//1.加载 spring.xml 配置文件
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring.xml");
//2.通过运行时类获取对象
Student stu = applicationContext.getBean(Student.class);
System.out.println(stu);

此方法有一个弊端,当 spring.xml 中配置两个 Student 的 bean 时程序会抛出异常,因为此时两个 bean 都是由 Student 类生成的,IoC 容器无法将两个 bean 都返回,必须指定一个唯一的 bean。

<bean id="stu" class="com.hzit.entity.Student">
   <property name="id" value="1"></property>
   <property name="name">
       <value><![CDATA[<张三>]]></value>
   </property>
        <property name="age" value="23"></property>
</bean>
<bean id="stu2" class="com.hzit.entity.Student">
   <property name="id" value="1"></property>
   <property name="name" value="李四"></property>
   <property name="age" value="23"></property>
</bean>

异常信息如下图所示。

运行结果请点击《Spring 全家桶》内容查看。

以上是 IoC 容器通过无参构造创建对象的方式,同时 IoC 容器也可以调用有参构造来创建对象。

有参构造

(1)在实体类中创建有参构造

public Student(int id, String name, int age) {
        super();
        this.id = id;
        this.name = name;
        this.age = age;
}

(2)spring.xml 中进行配置

<!-- 通过有参构造函数创建对象 -->
<bean id="stu3" class="com.hzit.entity.Student">
   <constructor-arg name="id" value="3"></constructor-arg>
   <constructor-arg name="name" value="小明"></constructor-arg>
   <constructor-arg name="age" value="22"></constructor-arg>
</bean>

(3)此时 IoC 容器会根据 constructor-arg 标签去加载对应的有参构造函数,创建对象并完成属性赋值。name 的值需要与有参构造的形参名对应,value 是对应的值。除了使用 name 对应参数外,还可以通过下标 index 对应,如下所示。

<!-- 通过有参构造函数创建对象 -->
<bean id="stu3" class="com.hzit.entity.Student">
   <constructor-arg index="0" value="3"></constructor-arg>
   <constructor-arg index="1" value="小明"></constructor-arg>
   <constructor-arg index="2" value="22"></constructor-arg>
</bean>

以上是 IoC 容器通过有参构造创建对象的方式,获取对象同样有两种方式可以选择:id 和运行时类。

如果 IoC 容器管理多个对象,并且对象之间有级联关系,如何实现?

(1)创建 Classes 类

public class Classes {
    private int id;
    private String name;
}

(2)在 Student 类中添加 Classes 属性

public class Student {
    private int id;
    private String name;
    private int age;
    private Classes classes;
}

(3)spring.xml 中配置 classes 对象,然后将该对象赋值给 stu 对象

<!-- 创建 classes 对象 -->
<bean id="classes" class="com.hzit.entity.Classes">
   <property name="id" value="1"></property>
   <property name="name" value="Java班"></property>
</bean>
<!-- 创建 stu 对象 -->
<bean id="stu" class="com.hzit.entity.Student">
   <property name="id" value="1"></property>
   <property name="name">
       <value><![CDATA[<张三>]]></value>
   </property>
   <property name="age" value="23"></property>
   <!-- 将 classes 对象赋给 stu 对象 -->
   <property name="classes" ref="classes"></property>
</bean>

再次获取 Student 对象,运行结果请点击《Spring 全家桶》内容查看。

在 spring.xml 中,通过 ref 属性将其他 bean 赋给当前 bean 对象,这种方式叫做依赖注入(DI),是 Spring 非常重要的机制,DI 是将不同对象进行关联的一种方式,是 IoC 的具体实现方式,通常 DI 和 IoC 是紧密结合在一起的,因此一般说的 IoC 包括 DI。

如果是集合属性如何依赖注入?

(1)Classes 类中添加 List<Student> 属性。

public class Classes {
    private int id;
    private String name;
    private List<Student> students;
}

(2)spring.xml 中配置 2 个 student 对象、1 个 classes 对象,并将 2 个 student 对象注入到 classes 对象中。

<!-- 配置 classes 对象 -->
<bean id="classes" class="com.hzit.entity.Classes">
   <property name="id" value="1"></property>
   <property name="name" value="Java班"></property>
   <property name="students">
       <!-- 注入 student 对象 -->
       <list>
           <ref bean="stu"/>
           <ref bean="stu2"/>
       </list>
   </property>
</bean>
<bean id="stu" class="com.hzit.entity.Student">
   <property name="id" value="1"></property>
   <property name="name">
        <value><![CDATA[<张三>]]></value>
   </property>
   <property name="age" value="23"></property>
</bean>
<bean id="stu2" class="com.hzit.entity.Student">
   <property name="id" value="2"></property>
   <property name="name" value="李四"></property>
   <property name="age" value="23"></property>
</bean>

运行结果请点击《Spring 全家桶》内容查看。

集合属性通过 list 标签和 ref 标签完成注入,ref 的 bean 属性指向需要注入的 bean 对象。

总结

这一讲我们讲解了 Spring IoC 的基本概念以及如何使用,IoC 是 Spring 的核心,这很重要。使用 Spring 开发项目时,控制层、业务层、DAO 层都是通过 IoC 来完成依赖注入的。

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

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

推荐阅读更多精彩内容