用项目案例解读Spring IOC容器的控制反转思想

本篇介绍Spring IOC容器,通过具体的实例详细地讲解IOC概念,彻底理解Spring反转控制的思想。通过本篇的学习,可以达成如下目标。

● 运用工厂模式设计程序

● 理解JavaBean和POJO对象

● 理解控制反转思想

● 理解IOC容器

 

1、一个简单的项目需求

在一个乡村小学校,一天只上三节课,有三名老师和一个校长。张老师负责教学生语文,王老师教学生数学,李老师教音乐,校长负责安排三位老师每天的上课时间,并提前通知各位老师上课时间,通知方式包括邮件、电话,后续可能会有更多方式。

现在需要编写一个Java程序实现校长安排老师老师上课时间,并通知到老师,要考虑程序的可扩展性。

2、用工厂模式设计程序

项目中通知老师上课的方式包括邮件、电话,后续可能还有所扩展。虽然通知方式不同,但通知功能是一致的,适合用工厂模式来设计通知功能,后续增加通知方式时,再增加一个通知实现类和修改工厂类代码就可以了,无需修改其它实现类的代码。

工厂模式主要用于对功能相似的类进行抽象,抽象出的功能通过接口方式由实现类来实现,然后由工厂类装配不同的实现类,实现一个工厂生产不同产品的功能。

blob.png

图1 Notice工厂模式

代码实现步骤:

(1)定义通知类接口

1

2

3

4

package com.milihua.springprogram.notice;

public interface NoticeInterface {

    void  sendMessage();

}

(2)定义EmailNotice类,实现NoticeInterface接口

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

package com.milihua.springprogram.notice;

import com.milihua.springprogram.entity.Teacher;

public class EmailNotice implements NoticeInterface {

  

    private Teacher teacher;

    String  message;

    

    public String getMessage() {

        return message;

    }

    public void setMessage(String message) {

        this.message = message;

    }

    @Override

    public void sendMessage() {

        // TODO Auto-generated method stub

        teacher.setClasstime(message + "_邮件发送");

    }

    public Teacher getTeacher() {

        return teacher;

    }

    public void setTeacher(Teacher teacher) {

        this.teacher = teacher;

    }

  

}

(3)定义PhoneNotice类,实现NoticeInterface接口

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

package com.milihua.springprogram.notice;

import com.milihua.springprogram.entity.Teacher;

public class PhoneNotice implements NoticeInterface {

  

    private Teacher teacher;

    String  message;

    

    public String getMessage() {

        return message;

    }

  

    public void setMessage(String message) {

        this.message = message;

    }

  

    @Override

    public void sendMessage() {

        // TODO Auto-generated method stub

        teacher.setClasstime(message + "_电话通知");

    }

  

    public Teacher getTeacher() {

        return teacher;

    }

  

    public void setTeacher(Teacher teacher) {

        this.teacher = teacher;

    }

}

(4)定义NoticeFactory类,负责装配不同实现方式的通知类

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

package com.milihua.springprogram.factory;

import com.milihua.springprogram.notice.EmailNotice;

import com.milihua.springprogram.notice.NoticeInterface;

import com.milihua.springprogram.notice.PhoneNotice;

public class NoticeFactory {

    //使用 getNotic方法获取通知对象

    public static NoticeInterface getNotic(String noticeType) {

        if (noticeType == null) {

            return null;

        }

        if (noticeType.equalsIgnoreCase("email")) {

            return new EmailNotice();

        else if (noticeType.equalsIgnoreCase("phone")) {

            return new PhoneNotice();

        }

        return null;

    }

}

3、项目的实体类——老师

项目的唯一实体类是老师类,实体类也是POJO类(简单的Java对象),实体类仅有属性以及获取和设置属性的get和set方法,没有事务处理方法,这是和Javabean不同的地方。

哪些类适合作为POJO类呢?项目中用于描述事物本身以及需要数据传递和序列化的类。例如,项目中的数据库表、实体对象、序列化对象等。在本项目案例中,老师类属于实体对象类。

定义老师类的代码如下:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

package com.milihua.springprogram.entity;

public class Teacher {

    private String name;

    private String classtime;

     public String getNotify() {

        return name + "在" + classtime;

    }

  

    public String getName() {

        return name;

    }

  

    public void setName(String name) {

        this.name = name;

    }

  

    public String getClasstime() {

        return classtime;

    }

  

    public void setClasstime(String classtime) {

        this.classtime = classtime;

    }

  

}

4、项目的业务类——校长

业务类也称为BO(业务对象),用于处理项目中的业务逻辑。业务逻辑主要用于项目涉及的各类业务操作。例如,在本项目案例中,校长需要安排上课时间,并发送上课时间给老师。在业务对象中,需要组织和协调实体类、组件类、DAO(数据访问对象)完成整个业务逻辑的处理操作。其中,组件类是JavaBean,是用于处理具体事务的类。例如,在本项目案例中,PhoneNotice、EmailNotice类就是组件类,用于处理发送通知事务。

定义校长类的代码如下:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

package com.milihua.springprogram.business;

import com.milihua.springprogram.entity.Teacher;

import com.milihua.springprogram.factory.NoticeFactory;

import com.milihua.springprogram.notice.EmailNotice;

import com.milihua.springprogram.notice.PhoneNotice;

public class Principal {

    /*

      校长类,负责装配产品,关联老师类和通知类

    **/

    public String  notifyTeacher()

    {

        StringBuilder  notifyReturn = new StringBuilder();

        //获取邮件通知类

        EmailNotice  eamilNotice = (EmailNotice)NoticeFactory.getNotic("email");

        //获取电话通知类

        PhoneNotice  phoneNotice = (PhoneNotice)NoticeFactory.getNotic("phone");

        /*

          发送通知给张老师

        **/

        //

        Teacher teacherZhang = new Teacher();

        teacherZhang.setName("张老师");

        //注入老师类到邮件通知类

        eamilNotice.setTeacher(teacherZhang);

        //注入消息到邮件通知类

        eamilNotice.setMessage("8:45上语文课");

        //发送消息给张老师

        eamilNotice.sendMessage();

        notifyReturn.append(teacherZhang.getNotify() + "\n");

        

        /*

          发送通知给王老师

        **/

        //

        Teacher teacherWang = new Teacher();

        teacherWang.setName("王老师");

        //注入老师类到电话通知类

        phoneNotice.setTeacher(teacherWang);

        //注入消息到电话通知类

        phoneNotice.setMessage("9:50上数学课");

        //发送消息给张老师

        phoneNotice.sendMessage();

        notifyReturn.append(teacherWang.getNotify() + "\n");

        

        /*

          发送通知给李老师

        **/

        //

        Teacher teacherLi = new Teacher();

        teacherLi.setName("李老师");

        //注入老师类到电话通知类

        phoneNotice.setTeacher(teacherLi);

        //注入消息到电话通知类

        phoneNotice.setMessage("13:50上课");

        //发送消息给张老师

        phoneNotice.sendMessage();

        notifyReturn.append(teacherLi.getNotify() + "\n");

  

        return  notifyReturn.toString();

        

    }

}

5、项目技术架构存在的问题

blob.png

 

图2 项目技术架构图

项目技术架构主要由javaBean组件、业务逻辑处理、POJO(实体)、前端四部分组成。JavaBean组件实现通知发送,应用工厂模式便于组件扩展。业务逻辑处理部分调用NoticeFactory创建通知组件和Teacher类,并将Teacher类实例和消息注入到组件,最后调用组件发送消息。

从技术架构图可以看出,NoticeFactory(组件工厂)负责通知组件的创建,Principal(业务类)调用NoticeFactory获取组件,并将Teacher类实例和消息注入到组件。Principal是主要控制类,控制了组件的创建和组件属性的注入。

Principal类对组件的较强控制,对程序的扩展性和易维护性显然是不利的。例如,当程序需要增加微信通知方式,且老师都希望用微信通知时,麻烦就来了,需要修改大量程序代码。再如,老师的上课时间可能每周或每天都有变化,把时间安排写在程序代码中显然是不妥的,应该写在程序外面,由外面对通知组件的属性进行注入。

要解决上面的问题,就需要弱化Principal类对组件的控制权,将组件的创建和属性的注入(图2红色点划线指示的功能)交给第三方托管,这个第三方就是Spring框架的IOC容器,控制反转就是将Principal类对组件的控制权移交给IOC容器。

6、Spring IOC容器的控制反转

Spring IOC容器是框架的核心,IOC是控制反转的意思,可以用来降低程序代码之间的耦合度。把强耦合的代码依赖从代码中移出去,放到统一的XML配置文件中,将程序对组件的主要控制权交给IOC,由IOC统一加载和管理。例如,可以把本案例中的JavaBean组件的创建、实体类的创建、以及JavaBean组件的属性注入等代码从Principal类移出,放入到Spring的XML配置文件中。这样就实现了Principal类与JavaBean组件的代码解耦,也解决了项目案例技术架构所存在的问题。

Spring配置文件代码如下:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

<?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"

    xmlns:mvc="http://www.springframework.org/schema/mvc"

    xmlns:p="http://www.springframework.org/schema/p"

    xsi:schemaLocation="http://www.springframework.org/schema/beans

                           http://www.springframework.org/schema/beans/spring-beans-3.2.xsd

                           http://www.springframework.org/schema/context

                           http://www.springframework.org/schema/context/spring-context-3.2.xsd

                           http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.2.xsd">

    <bean id="teacherZhang" class="com.milihua.springprogram.entity.Teacher">

        <property name="name" value="张老师"></property>

    </bean>

    <bean id="teacherWang" class="com.milihua.springprogram.entity.Teacher">

        <property name="name" value="王老师"></property>

    </bean>

    <bean id="teacherLi" class="com.milihua.springprogram.entity.Teacher">

        <property name="name" value="李老师"></property>

    </bean>

    <bean id="eamilNoticeZhang" class="com.milihua.springprogram.notice.EmailNotice" p:teacher-ref="teacherZhang">

        <property name="message" value="8:45上语文课"></property>

    </bean>

    <bean id="phoneNoticeWang" class="com.milihua.springprogram.notice.PhoneNotice" p:teacher-ref="teacherWang">

        <property name="message" value="9:50上数学课"></property>

    </bean>

    <bean id="phoneNoticeLi" class="com.milihua.springprogram.notice.PhoneNotice" p:teacher-ref="teacherLi">

        <property name="message" value="13:50上音乐课"></property>

    </bean>

</beans>

定义新的业务类,用于从IOC上下文环境中读取组件和POJO实例,代码如下:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

package com.milihua.springprogram.business;

import org.springframework.context.ApplicationContext;

import com.milihua.springprogram.entity.Teacher;

import com.milihua.springprogram.notice.EmailNotice;

import com.milihua.springprogram.notice.PhoneNotice;

public class IocPrincipal {

    /*

      基于Spring IOC容器的校长类,从配置文件获取通知组件

    **/

    public String  notifyTeacher(ApplicationContext ctx)

    {

        StringBuilder  notifyReturn = new StringBuilder();

  

        //从容器中获取通知张老师的组件

        EmailNotice  noticeZhang = ctx.getBean("eamilNoticeZhang",EmailNotice.class);

        noticeZhang.sendMessage();

        //从容器中获取张老师的实例

        Teacher  teacherZhang = ctx.getBean("teacherZhang",Teacher.class);

        notifyReturn.append(teacherZhang.getNotify() + "<p>");

  

        //从容器中获取通知王老师的组件

        PhoneNotice  phoneNoticWang = ctx.getBean("phoneNoticeWang",PhoneNotice.class);

        phoneNoticWang.sendMessage();

        //从容器中获取王老师的实例

        Teacher  teacherWang = ctx.getBean("teacherWang",Teacher.class);

        notifyReturn.append(teacherWang.getNotify() + "<p>");

  

        //从容器中获取通知李老师的组件

        PhoneNotice  phoneNoticLi = ctx.getBean("phoneNoticeLi",PhoneNotice.class);

        phoneNoticLi.sendMessage();

        //从容器中获取王老师的实例

        Teacher  teacherLi = ctx.getBean("teacherLi",Teacher.class);

        notifyReturn.append(teacherLi.getNotify() + "<p>");

  

        return  notifyReturn.toString();

    }

}

课程小结

Spring IOC容器的核心是把程序业务代码与事物(组件、POJO类)代码进行分离,程序有关事物的创建、属性和依赖对象的注入、以及生命周期交由容器进行加载和管理。业务代码只需从容器中获取组件或POJO实例对象即可,无需再考虑组件之间、组件与POJO之间的依赖关系以及属性的注入。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值