欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Spring学习笔记——IOC和DI

程序员文章站 2022-06-22 16:06:34
...

什么是IOC?

所谓控制反转,是将我们代码里面需要实现的创建对象,以及创建对象所依赖的对象的创建的过程,全部交由容器来进行实现。这样我们就不必使用new关键字来进行创建对象,从而降低了耦合度。

 

那么什么是DI呢?

2004年,Martin Fowler探讨了同一个问题,既然IOC是控制反转,那么到底“哪些方面的控制被反转了呢?”,经过详细的分析和讨论后,他得出了答案:“获得依赖对象的这个过程被反转了”。控制被反转之后,获得依赖对象由自身管理变为了由IOC容器主动注入。于是,他给控制反转取了一个更合适的名称:“依赖注入(Dependency Injection)”。他的这个答案,实际上就给出了IOC的实现方法:注入。所谓依赖注入,就是由IOC容器在运行期间,动态的将某种依赖注入到对象之中。不是对象从容器中查找依赖,而是容器在对象初始化时不等对象请求就主动将依赖传递给它。

那么如何理解Spring中的ioc和DI呢?

 

先来谈谈对IOC的理解。

  1. Spring中的IOC承担的作用,举个例子,现在我们有三个东西:人、房屋中介、房屋。人想要去租或者买一个房子的时候,首先需要找到房屋中介,在房屋中介处登记你的联系方式,当你需要房子的时候,房屋中介就会给你打电话说,房子已经安排上了,你提个包就去住吧。如果中介给我们的房子选不符合我们的预期,我们就会打电话向他投诉并抛出异常。整个过程不再由我自己控制,而是有中介这样一个类似容器的机构来控制。
  2. 上例中的人、房屋中介、房子就分别对应了Spring中的对象、IOC容器、以及依赖。就像房屋中间不仅登记了人的信息,同时也登记了空闲房间(依赖对象)的信息,Spring中的IOC容器中需要创建的对象和被依赖的对象也必须全部配置。这样才能在你需要依赖的时候,动态的将对象送过去。所有的类的创建、销毁都由 spring来控制,也就是说控制对象生存周期的不再是引用它的对象,而是spring。对于某个具体的对象而言,以前是它控制其他对象,现在是所有对象都被spring控制,所以这叫控制反转

那么接下来我们来了解一下IOC实现的底层原理

  1. IOC底层主要使用的技术有
    1. xml配置文件
    2. Dom4j访问xml配置文件
    3. 工厂模式
    4. 反射

 

IOC的底层原理

在说IOC的底层原理之前,我们先来谈谈我们为什么要用IOC来管理对象。

Spring学习笔记——IOC和DI

传统的创建对象都是通过new来对对象进行创建,但是这种硬编码的方式的耦合度太高,如上图红框部分,若是对User类进行修改,那么在其调用的地方也需要对其进行修改。我们这里只有两个类,所以修改起来并没有什么困难,但是当做一个大项目其中拥有很多类,并且类与类之间的相互引用特别多的时候,这种方式的维护代价就非常高了。

后来我们又使用工厂模式来进行一定程度的解耦,但是工厂又会和调用工厂的对象耦合在一起,还是有一定的缺陷。

Spring学习笔记——IOC和DI

  1. 先创建xml配置文件,配置创建对象类。
  2. 创建工厂类,我们依旧使用getService()方法来获得User对象,但是这里获得对象的方式与上面的工厂不同。这里首先使用classValue来获得bean中的class属性,即类的路径(和其所在的包),然后通过反射的方式来获得User的对象。

下面我我们通过具体的Demo来看理解Spring。

先创建一个User类,

package cn.itcast.ioc;

public class User {
	
	private String username;
	public void add() {
		System.out.println("add.....");
	}
}

再xml文件中配置(使用了上述底层原理中的1xml配置文件)

<bean id="user" class="cn.itcast.ioc.User"><bean/>

然后再通过工厂的接口ApplicationContext来获取对象

package cn.itcast.ioc;

public class TestIoc {
	@Test
	public void TestUser() {
		//1加载springp配置文件,根据配置文件创建对象
		ApplicationContext context = 
				new ClassPathXmlApplicationContext("bean1.xml");
		//2得到配置创建的对象
		Userservice service = (Userservice)context.getBean("userService");
		service.add();
	}	
}

输出结果为add。。。。。

ApplicationContext是BeanFactory的一个子接口,BeanFactory 可以理解为含有 bean 集合的工厂类。BeanFactory 包含了种 bean 的定义,以便在接收到客户端请求时将对应的 bean 实例化。
BeanFactory 还能在实例化对象的时生成协作类之间的关系。此举将bean 自身与 bean 客户端的配置中解放出来。BeanFactory 还包含了 bean 生命周期的控制,调用客户端的初始化方法(initialization methods)和销毁方法(destruction methods)。

这就对应了上面IOC理解中的一句话:

这样才能在你需要依赖的时候,动态的将对象送过去。所有的类的创建、销毁都由 spring来控制,也就是说控制对象生存周期的不再是引用它的对象,而是spring。

Spring通过反射的具体使用如下

public static Object newInstance(String className) {   
        Class<?> cls = null;   
        Object obj = null;   
        try {   
            cls = Class.forName(className);   
            obj = cls.newInstance();   
        } catch (ClassNotFoundException e) {   
            throw new RuntimeException(e);   
        } catch (InstantiationException e) {   
            throw new RuntimeException(e);   
        } catch (IllegalAccessException e) {   
            throw new RuntimeException(e);   
        }   
        return obj;   
    }  

 

Spring中对bean标签的管理:

Spring里边是通过配置文件来创建对象的,即其有IOC控制反转机制,每一个bean的实例化就是在创建一个对象。bean实例化有三种实现方式:

1.使用无参的构造方法来进行创建bean(最常用)

<bean id="此处可以任意设置,起到唯一标识符的作用" class="类的全路径"></bean>

使用这种方法要求bean必须包含有无参的构造方法

2.使用静态工厂来创建bean

工厂实现:

public class Bean2Factory {
	//静态方法,返回Bean2对象
	public static Bean2 getBean2() {
		return new Bean2();
	}
}

配置实现

<bean id="staticBeanFactory" class="cn.itcast.bean.Bean2Factory"
    factory-method="getBean2"></bean>

3.使用实例工厂来创建bean

工厂实现

public class Bean3Factory{
    public Bean3 getBean3(){
        return new Bean3();
    }
}

配置实现

<bean id="Bean3Factory" class="cn.itcast.bean.Bean3Factory"></bean>
<bean id="bean3" factory-bean="Bean3Factory" factory-method="getBean3">

 

Bean标签的常用属性

1.id属性:起名称,id属性值任意命名

   id属性值不能包含中文,不能包含特殊符号,根据id值得到配置对象,起到唯一标识符的作用

2.class属性:创建对象所在类的全路径

3.name属性:功能和id属性一样,都可以根据id或name得到对象,但id的属性值中不可以包含特殊符号,name中可以包含特殊符号(现已不常用)

4.scope属性:(前两个掌握,后两个了解)

 * singleton :默认值,单例的.(即全局只会创建一个实例对象)

* prototype :多例的.

* request :WEB 项目中,Spring 创建一个Bean 的对象,将对象存入到request 域中.

* session :WEB 项目中,Spring 创建一个Bean 的对象,将对象存入到session 域中.

* globalSession :WEB 项目中,应用在Porlet 环境.如果没有Porlet 环境那么globalSession 相当

session.*(登陆一次之后别的同类网页不需要再次登陆,单点登陆)

属性注入

在spring中只支持set方法有参构造函数进行属性注入

有参构造函数注入

public class PropertyDemo {
	
	private String username;
	
	public PropertyDemo(String username) {
		this.username = username;
	}
	
	public void test1() {
		System.out.println("demo......."+username);
	}

}

在bean中配置

<bean id="demo" class="cn.itcast.property.PropertDemo">
    <!-- 使用有参构造函数进行注入 -->
    <constructor-arg name="username" value="My name is van"></constructor-arg>
</bean>

name的值必须要和需要注入的属性的名称相同,如需注入username,则name的值就是username

使用set方法注入属性

public class Book {
	
	private String bookname;

	public void setBookname(String bookname) {
		this.bookname = bookname;
	}
	
	public void demobook() {
		System.out.println(bookname);
	}
}

配置

<!-- set方法注入属性 -->
<bean id="book" class="cn.itcast.property.Book">
    <property name="bookname" value="my name is van"></property>
</bean>

name的属性值是类里面定义的属性的名称,value属性用于设置具体的值   

注入对象类型的属性(重点)

比如我现在创建了一个service类,我需要向service类中注入dao,即在service中得到dao对象

实现过程

创建UserService类

public class UserService{

   private UserDao userDao;
    
    public void setUserDao(UserDao userDao){
        this.userDao = userDao;   
    }

    public void add(){
       System.out.println("My name is van");    
   }

}

创建UserDao类

public class UserDao {
	
	public void add() {
		System.out.println("dao.........");
	}

}

在xml中配置

Spring学习笔记——IOC和DI

使用set方法注入基本数据类型时,property标签中使用的是value属性,而注入对象时则使用的就是ref属性了,且ref的值必须要和被注入对象在xml中配置的bean的id值相同。

 

两种注入方式的对比:

设值注入(set)有如下优点:

与传统的JavaBean的写法更相似,程序开发人员更容易理解、接受。通过setter方法设定依赖关系显得更加直观、自然;

对于复杂的依赖关系,如果采用构造注入,会导致构造器过于臃肿,难以阅读。Spring在创建Bean实例时,需要同时实例化其依赖的全部实例,因而导致性能下降。而使用设值注入,则能避免这些问题。

尤其在某些成员变量可选的情况下,多参数的构造器更加笨重。

构造注入优势如下:

构造注入可以在构造器中决定依赖关系的注入顺序,优先依赖的优先注入;

对于依赖关系无需变化的Bean,构造注入更有用处。因为没有setter方法,所有的依赖关系全部在构造器内设定,无须担心后续的代码对依赖关系产生破坏;

依赖关系只能在构造器中设定,则只有组件的创建者才能改变组件的依赖关系,对组件的调用者而言,组件内部的依赖关系完全透明,更符合高内聚的原则。

Notes建议采用设值注入为主,构造注入为辅的注入策略。对于依赖关系无须变化的注入,尽量采用构造注入;而其他依赖关系的注入,则考虑采用设值注入。

 

 

文章内容如有雷同,可能是我们看的教学视频是同一个啦~~~

 

 

 

 

 

 

 

 

 

相关标签: java ioc spring