Spring回顾(一)IoC & DI

xiaoxiao2021-02-28  92

什么是IoC?何为DI?

IoC是Sprnig框架实现的一种代替程序员,管理Java类的模式。IoC全称为Inversion of Control,即控制反转。在Spring项目中,将Java类的控制权交给了Spring的IoC容器。类与类之间的泛化关系、依赖关系都能通过Spring的配置来实现,而不需要手动编写。

DI,全称为Dependency Injection,即依赖注入。在Spring通过配置文件配置了依赖关系之后,可以通过多种方式对对象进行注入,比如说Service实现类中的方法需要依赖dao实现类,我们就能通过Spring配置文件写好注入配置,在使用时,我们只需要定义好dao接口,而不需要去获取它的实例,由Spring为我们注入。


能通过Spring的IoC容器和DI做什么?

1. IoC(控制反转)

控制反转,能让spring容器为我们创建对象。简单来说,我们只需要准备好Object object Spring能帮我们实现余下的new Object()部分。在Spring中,每个被放入容器的类被成为bean,ApplicationContext代表了IoC容器,我们可以通过这个类来读取配置文件,根据配置文件实例化bean。下面看看如何通过配置实现控制反转

1.1 配置文件

配置文件一般放在class目录下,即开发中的src目录。配置文件的主要结构如下:

<?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-2.5.xsd"> <!-- beans 存放了很多个类 把一个类放入到spring容器中,该类就是bean --> <!-- 一个bean就是描述一个类 id就是标示符 命名规范:类的第一个字母变成小写,其他的字母保持不变 class为类的全名 --> <bean id="helloWorld" class="com.spring.ioc.HelloWorld"></bean> </beans>

1.2 ApplicationContext 的使用

ApplicationContext在Spring中代表一个IoC容器,ApplicationContext只是一个接口,在这里我用ClassPathXmlApplicationContext这个实现类来直接获取配置文件。其中在容器中对象的创建又有三种方法:根据默认构造方法创建、静态工厂方法创建、实例工厂方法创建

①默认构造方法创建

springIoc容器默认情况下会根据bean的构造函数来实例化对象。所以在bean的配置中我们可以添加参数,参数可以是变量也可以是另一个bean的引用,如果是bean的引用,则使用ref=”bean_id”来关联。 配置方式applicationContext.xml

<bean id="helloWorld" class="com.spring.ioc.HelloWorld"></bean>

HelloWorld.java

package com.spring.ioc; public class HelloWorld { public HelloWorld(){ System.out.println("create a HelloWorld!"); } public void hello(String name){ System.out.println("Hello," + name); } }

IoCTest.java

package com.spring.ioc; import org.junit.Test; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; public class IoCTest { @Test public void testIoC(){ //读取配置文件 ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml"); //通过getBean方法得到 HelloWorld helloWorld = (HelloWorld) context.getBean("helloWorld"); helloWorld.hello("Cindy"); } }
②静态工厂方法创建对象

可以在配置bean的时候可以配置使用静态工厂来实例化对象。Spring容器会在内部调用静态工厂的实例化方法来实例化对象。 配置方式:applicationContext.xml

<bean id="helloWorld" class="com.spring.ioc.HelloWorldFactory" factory-method="getInstance"> </bean>

静态工厂HelloWorldFactory.java

package com.spring.ioc; public class HelloWorldFactory { public static HelloWorld getInstance(){ return new HelloWorld(); } }
③实例工厂创建对象

使用实例工厂方法实例化对象,在定义bean的时候使用factory-bean属性和factory-method属性,来指定实例工厂的实例方法。与静态工厂方法不同,这里是先实例化了一个工厂类,然后调用了实例化工厂方法,而静态工厂方法不实例化工厂类,直接调用其静态实例化方法。 配置方式:applicationContext.xml

<bean id="helloWorldFactory" class="com.spring.ioc.HelloWorldFactory2"></bean> <bean id="helloWorld" factory-method="getInstance" factory-bean="helloWorldFactory"></bean>

实例工厂HelloWorldFactory2.java

package com.spring.ioc; public class HelloWorldFactory2 { public HelloWorldFactory2(){ System.out.println("create a HelloWorldFactory"); } public HelloWorld getInstance(){ return new HelloWorld(); } }

1.3 什么时候实例化对象?

①加载Spring容器,加载过程中,调用bean的构造函数为bean创建对象。 ②将bean的lazy-init属性,设置为true(默认为false),设置延迟加载,spring容器加载时不实例化,在getBean()的时候才实例化。 一般情况下使用第一种方式,因为在启动过程中就能检测出spring配置文件中包含的错误,更加安全。第二种在启动过程中发现不了配置文件错误。如果要加载的bean实在太消耗性能,再考虑使用第二种方法。

1.4 bean的单例模式和多例模式

在配置bean的过程中,可以为其指定是否使用单例模式实例化对象,默认为单例模式,即只实例化一次,多次使用。设置的方法为: ① 多例模式:scope=prototype

<bean id="helloWorld" class="com.spring.ioc.HelloWorld" scope="prototype"></bean>

②单例模式(defalut): scope=singleton

<bean id="helloWorld" class="com.spring.ioc.HelloWorld" scope="singleton"></bean>

1.5 bean的init方法,destory方法

在配置bean的时候,可以为其指定init()方法和destory()方法,分别在实例化和销毁的时候执行。其中在容器关闭时,bean销毁。 配置了之后整个流程可表示为:构造方法(创建对象)->init方法->其他方法->destory方法

*当scope=prototype的时候,spring容器不会执行destory方法

配制方法 applicationContext.xml :

<bean id="helloWorld" class="com.spring.ioc.HelloWorld" init-method="init" destroy-method="destroy"></bean>

HelloWorld.java

package com.spring.ioc; public class HelloWorld { public HelloWorld(){ System.out.println("create a HelloWorld!"); } public void hello(String name){ System.out.println("Hello," + name); } public void init(){ System.out.println("init HelloWorld"); } public void destroy(){ System.out.println("destroy HelloWorld"); } }

1.6 bean继承关系

在spring容器中使用parent属性指定父类,实现继承。例子:

package com.spring.xml.extend; public class Animal { public void walk(){ System.out.println("Animal walking"); } } package com.spring.xml.extend; public class Dog extends Animal { }

配置:

<bean class="com.spring.xml.extend.Animal" id="animal"></bean> <bean class="com.spring.xml.extend.Dog" id="dog" parent="animal"></bean>

2. DI(依赖注入)

2.1 setter方式注入

配置bean的时候,我们可以在配置文件中指定实例化时,要注入的属性值。这些值可以是基本数据类型、引用类型、集合类型、或者Propertie,只要在bean的类定义中提供setter方法就能通过配置注入,配置的方法如下: applicationContext.xml

<bean id="person" class="com.spring.di.Person"> <!-- property描述的就是bean中的属性 name属性就是描述属性的名称 value就是值 如果是基本属性(String),就用value赋值 ref 如果是引用类型,用ref赋值 --> <property name="pid" value="1"></property> <property name="name" value="Cindy"></property> <!-- 注入bean --> <property name="student" ref="student"></property> <property name="lists"> <list> <value>list1</value> <value>list2</value> <!-- list中存放一个student对象 --> <ref bean="student"/> </list> </property> <property name="objects"> <list> <value>obj1</value> <ref bean="student"/> </list> </property> <property name="sets"> <set> <value>set1</value> <ref bean="student"/> </set> </property> <property name="map"> <map> <entry key="m1"> <value>m1</value> </entry> <entry key="m2"> <ref bean="student"/> </entry> </map> </property> <property name="properties"> <props> <prop key="p1">p1</prop> <prop key="p2">p2</prop> </props> </property> </bean> <bean id="student" class="com.spring.di.Student" scope="prototype"></bean>

Student.java

package com.spring.di; public class Student { public Student(){ System.out.println("student"); } public void say(){ System.out.println("student"); } }

Person.java

public class Person { private Long pid; private String name; private Student student; private List lists; private Set sets; private Map map; private Object[] objects; private Properties properties; public Person(){ System.out.println("person"); } //... //省略setter方法 }

2.2 Constructor 注入

除了调用setter方法注入,还能通过类的构造器来注入参数,同样是从配置文件中对bean进行配置。首先在类中定义好构造器方法,然后在配置文件中设置参数。 配置方法: applicationContext.xml

<bean id="person1" class="com.spring.di.Person1"> <constructor-arg value="Cindy" index="0"></constructor-arg> <constructor-arg ref="student" index="1"></constructor-arg> </bean>

Person1.java

package com.spring.di; public class Person1 { private String name; private Student student; public Person1(String name, Student student) { this.name = name; this.student = student; } }

IoC 和 DI 带来的好处(优势)

让客户端实现完全面向接口编程,在编写客户端的时候,不用关心实现类为什么具体类型,只用接口类型来完成工作,接口需要具体为何种类型的实例,由配置文件决定。举个例子,在一个struts2的aciton中,用接口定义了一个service,而这个service通过spring注入,那么开发action的人员就不需要管service具体是如何实现的,把编写service的工作分离出去,方便团队开发,避免编写过程中的冲突。

Spring是如何实现IoC和DI的

了解Spring框架底层是如何实现控制反转和依赖注入,需要深入研究源码。因为我还没有深入研究过,只知道大概是使用了Java的反射机制,来操作类。有待自己深入研究。

转载请注明原文地址: https://www.6miu.com/read-84743.html

最新回复(0)