本篇介绍Spring IOC容器,通过具体的实例详细地讲解IOC概念,彻底理解Spring反转控制的思想。通过本篇的学习,可以达成如下目标。
● 运用工厂模式设计程序
● 理解JavaBean和POJO对象
● 理解控制反转思想
● 理解IOC容器
1、一个简单的项目需求
在一个乡村小学校,一天只上三节课,有三名老师和一个校长。张老师负责教学生语文,王老师教学生数学,李老师教音乐,校长负责安排三位老师每天的上课时间,并提前通知各位老师上课时间,通知方式包括邮件、电话,后续可能会有更多方式。
现在需要编写一个Java程序实现校长安排老师老师上课时间,并通知到老师,要考虑程序的可扩展性。
2、用工厂模式设计程序
项目中通知老师上课的方式包括邮件、电话,后续可能还有所扩展。虽然通知方式不同,但通知功能是一致的,适合用工厂模式来设计通知功能,后续增加通知方式时,再增加一个通知实现类和修改工厂类代码就可以了,无需修改其它实现类的代码。
工厂模式主要用于对功能相似的类进行抽象,抽象出的功能通过接口方式由实现类来实现,然后由工厂类装配不同的实现类,实现一个工厂生产不同产品的功能。
代码实现步骤:
(1)定义通知类接口
//消息通知接口
public interface NoticeInterface {
void sendMessage();
}
(2)定义EmailNotice类,实现NoticeInterface接口
//email通知实现类,实现NoticeInterface接口
public class EmailNoticeImpl implements NoticeInterface {
private Teacher teacher;
private String message;
public Teacher getTeacher() {
return teacher;
}
public void setTeacher(Teacher teacher) {
this.teacher = teacher;
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
public void sendMessage() {
teacher.setTimes(message+"_邮件发送");
}
}
(3)定义PhoneNotice类,实现NoticeInterface接口
//手机通知实现类,实现NoticeInterface接口
public class PhoneNoticeImpl implements NoticeInterface {
private Teacher teacher;
private String message;
public Teacher getTeacher() {
return teacher;
}
public void setTeacher(Teacher teacher) {
this.teacher = teacher;
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
@Override
public String toString() {
return "PhoneNoticeImpl{" +
"teacher=" + teacher +
", message='" + message + '\'' +
'}';
}
public void sendMessage() {
teacher.setTimes(message+"_手机发送");
}
}
项目的实体类——老师
项目的唯一实体类是老师类,实体类也是POJO类(简单的Java对象),实体类仅有属性以及获取和设置属性的get和set方法,没有事务处理方法,这是和Javabean不同的地方。
哪些类适合作为POJO类呢?项目中用于描述事物本身以及需要数据传递和序列化的类。例如,项目中的数据库表、实体对象、序列化对象等。在本项目案例中,老师类属于实体对象类。
//老师实体类
public class Teacher {
private String name;
private String times;
public String getNotify(){
return name+"在" + times;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getTimes() {
return times;
}
public void setTimes(String times) {
this.times = times;
}
@Override
public String toString() {
return "Teacher{" +
"name='" + name + '\'' +
", times=" + times +
'}';
}
}
(4)定义NoticeFactory类,负责装配不同实现方式的通知类
//定义NoticeFactory类,负责装配不同实现方式的通知类
public class NoticeFactory {
public static NoticeInterface getNotic(String noticType){
if(StringUtils.isEmpty(noticType)){
return null;
}
if(noticType.equals("email")){
return new EmailNoticeImpl();
}else if(noticType.equals("phone")){
return new PhoneNoticeImpl();
}
return null;
}
}
项目的业务类——校长
业务类也称为BO(业务对象),用于处理项目中的业务逻辑。业务逻辑主要用于项目涉及的各类业务操作。例如,在本项目案例中,校长需要安排上课时间,并发送上课时间给老师。在业务对象中,需要组织和协调实体类、组件类、DAO(数据访问对象)完成整个业务逻辑的处理操作。其中,组件类是JavaBean,是用于处理具体事务的类。例如,在本项目案例中,PhoneNotice、EmailNotice类就是组件类,用于处理发送通知事务。
//校长类,负责装配产品,关联老师和通知类
public class Principal {
public String notifyTeacher(){
StringBuilder sb = new StringBuilder();
// 获取邮件通知类
EmailNoticeImpl email = (EmailNoticeImpl) NoticeFactory.getNotic("email");
// 获取电话通知类
PhoneNoticeImpl phone = (PhoneNoticeImpl) NoticeFactory.getNotic("phone");
// 发送通知给王老师
Teacher teacher1 = new Teacher();
teacher1.setName("王老师");
//注入老师类到邮件通知类
email.setTeacher(teacher1);
//注入消息到邮件通知类
email.setMessage("上午第一,二节课");
//发送消息给王老师
email.sendMessage();
sb.append(teacher1.getNotify()+"\n");
Teacher teacher2 = new Teacher();
teacher2.setName("李老师");
//注入老师类到手机通知类
phone.setTeacher(teacher2);
//注入消息到手机通知类
phone.setMessage("上午第三,四节课");
//发送消息给李老师
phone.sendMessage();
sb.append(teacher2.getNotify()+"\n");
Teacher teacher3 = new Teacher();
teacher3.setName("白老师");
email.setMessage("下午第一,二节课");
email.setTeacher(teacher3);
email.sendMessage();
sb.append(teacher3.getNotify()+"\n");
return sb.toString();
}
}
项目技术架构存在的问题
项目技术架构主要由javaBean组件、业务逻辑处理、POJO(实体)、前端四部分组成。JavaBean组件实现通知发送,应用工厂模式便于组件扩展。业务逻辑处理部分调用NoticeFactory创建通知组件和Teacher类,并将Teacher类实例和消息注入到组件,最后调用组件发送消息。
从技术架构图可以看出,NoticeFactory(组件工厂)负责通知组件的创建,Principal(业务类)调用NoticeFactory获取组件,并将Teacher类实例和消息注入到组件。Principal是主要控制类,控制了组件的创建和组件属性的注入。
Principal类对组件的较强控制,对程序的扩展性和易维护性显然是不利的。例如,当程序需要增加微信通知方式,且老师都希望用微信通知时,麻烦就来了,需要修改大量程序代码。再如,老师的上课时间可能每周或每天都有变化,把时间安排写在程序代码中显然是不妥的,应该写在程序外面,由外面对通知组件的属性进行注入。
要解决上面的问题,就需要弱化Principal类对组件的控制权,将组件的创建和属性的注入(图2红色点划线指示的功能)交给第三方托管,这个第三方就是Spring框架的IOC容器,控制反转就是将Principal类对组件的控制权移交给IOC容器。
三、Spring IOC容器的控制反转
Spring IOC容器是框架的核心,IOC是控制反转的意思,可以用来降低程序代码之间的耦合度。把强耦合的代码依赖从代码中移出去,放到统一的XML配置文件中,将程序对组件的主要控制权交给IOC,由IOC统一加载和管理。例如,可以把本案例中的JavaBean组件的创建、实体类的创建、以及JavaBean组件的属性注入等代码从Principal类移出,放入到Spring的XML配置文件中。这样就实现了Principal类与JavaBean组件的代码解耦,也解决了项目案例技术架构所存在的问题。
Spring配置文件代码如下:
<?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="teacher1" class="com.link.pojo.Teacher">
<property name="name" value="王老师"></property>
</bean>
<bean id="teacher2" class="com.link.pojo.Teacher">
<property name="name" value="李老师"></property>
</bean>
<bean id="teacher3" class="com.link.pojo.Teacher">
<property name="name" value="白老师"></property>
</bean>
<bean id="emailWang" class="com.link.service.impl.EmailNoticeImpl">
<property name="teacher" ref="teacher1"></property>
<property name="message" value="上午第一,二节课"></property>
</bean>
<bean id="phoneLi" class="com.link.service.impl.PhoneNoticeImpl">
<property name="teacher" ref="teacher2"></property>
<property name="message" value="上午第三,四节课"></property>
</bean>
<bean id="emailBai" class="com.link.service.impl.EmailNoticeImpl">
<property name="teacher" ref="teacher3"></property>
<property name="message" value="下午第一,二节课"></property>
</bean>
</beans>
定义新的业务类,用于从IOC上下文环境中读取组件和POJO实例,代码如下:
public class IocPrincipal {
public String notifyTeacher(ApplicationContext context){
StringBuilder builder = new StringBuilder();
//从容器中获取通知王老师的组件
EmailNoticeImpl emailWang = context.getBean("emailWang", EmailNoticeImpl.class);
emailWang.sendMessage();
//从容器中获取王老师的实例
Teacher teacher1 = context.getBean("teacher1", Teacher.class);
builder.append(teacher1.getNotify()+"\n");
//从容器中获取通知李老师的组件
PhoneNoticeImpl phoneLi = context.getBean("phoneLi", PhoneNoticeImpl.class);
phoneLi.sendMessage();
//从容器中获取李老师的实例
Teacher teacher2 = context.getBean("teacher2", Teacher.class);
builder.append(teacher2.getNotify()+"\n");
//从容器中获取通知王老师的组件
EmailNoticeImpl emailBai = context.getBean("emailBai", EmailNoticeImpl.class);
emailBai.sendMessage();
//从容器中获取王老师的实例
Teacher teacher3 = context.getBean("teacher3", Teacher.class);
builder.append(teacher3.getNotify()+"\n");
return builder.toString();
}
}
课程小结
Spring IOC容器的核心是把程序业务代码与事物(组件、POJO类)代码进行分离,程序有关事物的创建、属性和依赖对象的注入、以及生命周期交由容器进行加载和管理。业务代码只需从容器中获取组件或POJO实例对象即可,无需再考虑组件之间、组件与POJO之间的依赖关系以及属性的注入。
转载自: https://baijiahao.baidu.com/s?id=1609595005078804580&wfr=spider&for=pc