以前对象之间的引用是通过new来调用实现,有了Spring IOC,我们可以把对象之间的引用交给他来管理,这样就把控制权交给了Spring,所以就叫做控制反转。

Spring

  Spring IOC的实现用到了设计模式:简单工厂,他也是从简单工厂进化而来的,下面我们看看Spring的IOC是如何进化来的。

image

一、简单工厂模式

1.1 接口Fruit

1
2
3
4
5
6
7
8
9
10
11
12

package com.study.service;

/**
* 简单工厂模式
* 抽象接口
*
* @author Howe Hsiang
*/
public interface Fruit {
void eat();
}

1.2 实现类Apple和Orange

Apple实现类:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17


import com.study.service.Fruit;

/**
* 简单工厂模式
* 实现类Apple
*
* @author Howe Hsiang
*/
public class Apple implements Fruit {

@Override
public void eat() {
System.out.println("吃苹果");
}
}

Orange实现类:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

package com.study.service.Impl;

import com.study.service.Fruit;

/**
* 简单工厂模式
* 实现类Orange
*
* @author Howe Hsiang
*/
public class Orange implements Fruit {

@Override
public void eat() {
System.out.println("吃橘子");
}
}

1.3 工厂类SimpleFactory

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

package com.study.controller;

import com.study.service.Fruit;
import com.study.service.Impl.Apple;
import com.study.service.Impl.Orange;

/**
* 简单工厂模式
* 工厂类
* @author Howe Hsiang
*/

public class SimpleFactory {

public static Fruit getInstance(String className){

Fruit f = null;
if(className.equals("apple")){
f = new Apple();
}
if(className.endsWith("orange")){
f = new Orange();
}
return f;
}

}

1.4 测试类SimpleFactoryTest

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.study.controller;

import com.study.service.Fruit;
import org.junit.Test;

/**
* 测试简单工厂模式
*
* @author Howe Hsiang
*/
public class SimpleFactoryTest extends BaseTest {

@Test
public void getInstance() {

//苹果
Fruit apple = SimpleFactory.getInstance("apple");
apple.eat();

//橘子
Fruit orange = SimpleFactory.getInstance("orange");
orange.eat();
}

}

二、反射+简单工厂模式

如果增加了水果,比如香蕉,那么在工厂类里面也要进行相关的修改了,这样不合理,而java的反射机制可以解决这个问题.

2.1 反射工厂类ReflexFactory

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.study.controller;

import com.study.service.Fruit;

/**
* 反射+简单工厂模式
* 反射工厂类
* @author Howe Hsiang
*/

public class ReflexFactory {

public static Fruit getInstance(String className){

Fruit f = null;
try {
f = (Fruit) Class.forName(className).newInstance();
} catch (Exception e) {
e.printStackTrace();
}
return f;
}

}

2.2 测试类ReflexFactoryTest

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.study.controller;

import com.study.service.Fruit;
import org.junit.Test;

/**
* 测试反射+简单工厂模式
*
* @author Howe Hsiang
*/
public class ReflexFactoryTest extends BaseTest {

@Test
public void getInstance() {

//苹果
Fruit apple = ReflexFactory.getInstance("com.study.service.Impl.Apple");
apple.eat();

//橘子
Fruit orange = ReflexFactory.getInstance("com.study.service.Impl.Orange");
orange.eat();
}
}

三、反射+简单工厂模式+xml配置文件

3.1 实例配置类PropertiesOperate

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

package com.study.controller;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.util.Properties;

/**
* 反射+简单工厂模式+xml配置文件
* 实例配置类PropertiesOperate
*
* @author Howe Hsiang
*/

public class PropertiesOperate {

private Properties pro = null;

String url = this.getClass().getResource("/").getPath();

private File file = new File(url+"fruit.properties");

public PropertiesOperate(){
this.pro = new Properties();
if(file.exists()){
try {
pro.loadFromXML(new FileInputStream(file));
} catch (Exception e) {
e.printStackTrace();
}
}else{
this.save();
}
}

private void save(){
this.pro.setProperty("apple","com.study.service.Impl.Apple");
this.pro.setProperty("orange", "com.study.service.Impl.Orange");
try {
this.pro.storeToXML(new FileOutputStream(this.file),"Fruit");
} catch (Exception e) {
e.printStackTrace();
}
}
public Properties getProperties(){
return this.pro;
}

}

3.2 测试类PropertiesOperateTest

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.study.controller;

import com.study.service.Fruit;
import org.junit.Test;
import java.util.Properties;

/**
* 测试反射+简单工厂模式+xml配置文件
*
* @author Howe Hsiang
*/
public class PropertiesOperateTest extends BaseTest {

@Test
public void getProperties(){

Properties pro=new PropertiesOperate().getProperties();

//苹果
Fruit apple = ReflexFactory.getInstance(pro.getProperty("apple"));
apple.eat();

//橘子
Fruit orange = ReflexFactory.getInstance(pro.getProperty("orange"));
orange.eat();

}
}

四、终极版本Spring IOC

以后如果要增加新的水果类,都要在这个配置文件里面登记。这时我们可以说配置文件可以控制程序的执行,现在看起来有点像spring的ioc了。下面我们来看看Spring IOC是如何实现的。

4.1 实体类Person和Grade

实体类Person:

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

package com.study.entity;

/**
* 人类
*/
public class Person {

private String name;

private int age;

private Grade grade;

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

public int getAge() {
return age;
}

public void setAge(int age) {
this.age = age;
}

public Grade getGrade() {
return grade;
}

public void setGrade(Grade grade) {
this.grade = grade;
}
}

实体类Grade:

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

package com.study.entity;

/**
* 科目
*/
public class Grade {

private int math;

private int english;

public int getMath() {
return math;
}

public void setMath(int math) {
this.math = math;
}

public int getEnglish() {
return english;
}

public void setEnglish(int english) {
this.english = english;
}
}

4.2 配置文件Bean.xml

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

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

<!--第一个Bean,是一个Person类,id名字随便取,还要写上类的全名-->
<bean id="Person" class="com.study.entity.Person">
<!--下面开始把这个类里面的所有属性列出来,并赋值,至于你说难道一定要赋值吗?我想可以,我刚学,不知道-->
<property name="name">
<!--这里的名字是通过程序里面的set来赋值的,不信你去掉程序里面相关的set,就出错了-->
<value>Howe Hsiang</value>
</property>

<property name="age">
<value>23</value>
</property>

<!--这里有点特别,这个grade变量是一个对象,和一般的变量要区别对待-->
<property name="grade">
<!--这里指向了本配置文件里面一个名字叫Grade(即id=Grade)的bean-->
<ref bean = "Grade"/>
</property>
</bean>

<!--同上-->
<bean id="Grade" class="com.study.entity.Grade">
<property name="math">
<value>99</value>
</property>

<property name="english" >
<value>59</value>
</property>
</bean>

</beans>

4.3 测试类SpringIocTest

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

import com.study.controller.BaseTest;
import com.study.entity.Grade;
import com.study.entity.Person;
import org.junit.Test;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.xml.XmlBeanFactory;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource;

import java.io.File;

/**
* 测试类
*
* @author Howe Hsiang
*/
public class SpringIocTest extends BaseTest {

@Test
public void getSpringIocInfo() {

//把input扔到工厂里面去,这个工厂就能为你提供实例了(装载配置文件)
BeanFactory factory = new ClassPathXmlApplicationContext("Bean.xml");

//你要一个叫Person的东西,那好,工厂就去找“Person"给你
Person person =(Person) factory.getBean("Person");
Grade grade=(Grade)factory.getBean("Grade");

//person可以调用里面相关的方法,就相当于new了一个Person一样
System.out.println(
"姓名:"+person.getName()+ "; " +
"年龄:"+person.getAge() + "; " +
"数学成绩:"+grade.getMath()+ "; " +
"英语成绩:"+grade.getEnglish());
}

}

五、其他

1、关于Spring读取配置文件的方法

applicationcontext—

FileSystemXmlApplicationContext—这个方法是从文件绝对路径加载配置文

ClassPathXmlApplicationContext—这个方法是从classpath下加载配置文件(适合于相对路径方式加载)
XmlWebApplicationContext—-专为web工程定制的方法,推荐Web项目中使用。

beanfactory—

ClassPathResource — 从系统的类路径中加载

FileSystemResource — 从文件系统加载,比如说自己指定配置文件的全路径

InputStreamResource — 从输入流中加载

ServletContextResource — 从Servlet 上下文环境中加载

UrlResource — 从指定的Url加载


2、BeanFactory和ApplicationContext的区别

ApplicationContext接口,它由BeanFactory接口派生而来,因而提供BeanFactory所有的功能。
ApplicationContext以一种更向面向框架的方式工作以及对上下文进行分层和实现继承,ApplicationContext包还提供了以下的功能:

• MessageSource, 提供国际化的消息访问

• 资源访问,如URL和文件

• 事件传播

• 载入多个(有继承关系)上下文,使得每一个上下文都专注于一个特定的层次,比如应用的web层

最主要的就是BeanFactory延迟加载,当使用到getBean的时候才会抛异常,而ApplicationContext在刚开始启动加载的时候就会抛出异常,这样有利于检查所依赖属性是否注入;所以通常情况下我们选择使用ApplicationContext。

注: SpringIOC详解

本文标题: Spring系列-详解核心IOC-开发篇

本文作者: 狂欢马克思

发布时间: 2020年06月01日 00:00

最后更新: 2021年04月15日 16:08

原始链接: https://www.hosiang.cn/2552a56/

版权声明: 本文著作权归作者所有,均采用CC BY-NC-SA 4.0许可协议,转载请注明出处!

× 喜欢就赞赏一下呗!
打赏二维码