Spring框架學習筆記(1)——控制反轉IOC與依賴注入DI


Spring框架的主要作用,就是提供了一個容器,使用該容器就可以創建並管理對象。比如說Dao類等,又或者是具有多依賴關系的類(Student類中包含有Teacher類的成員變量)

Spring有兩個核心概念,一個是控制反轉(IOC,全稱為Inverse of Control),另一個則是面向切面編程(AOP,全稱為 Aspect Oriented Program)

Spring 框架是 Java 應用最廣的框架,它的成功來源於理念,而不是技術本身,它的理念包括 IoC (Inversion of Control,控制反轉) 和 AOP(Aspect Oriented Programming,面向切面編程)

Spring 的根本使命:簡化 Java 開發

Spring框架可適用各種Java程序

架構組成:

概念介紹

控制反轉,從另外一個角度來說,和可以叫做依賴注入(DI,全稱為Dependency Injection),之后會說明,這里就大概說個概念。

下面我們通過兩個角度來了解控制反轉的思想,可能理解上有所出入,僅供參考

代碼角度解釋

情況1:按照我們普通編程,如果我們想要得到一個對象,就得根據這個類的構造方法,傳入合適的參數(資源),我們才能得到這個對象。

但是,有些情況下,這樣的操作會使得代碼過於冗雜及繁瑣(每次需要對象都要使用一次new關鍵字),而且也不便於資源的管理。

情況2:針對此情況,我們可以考慮創建一個工廠類,來進行優化,之后我們可以調用工廠類的getXX方法,把參數(資源)傳入方法,獲得需要的對象。

情況3:上面的情況比我們普通編程要好一些,但是,對資源的管理還是有些不方便,所以,這個時候就有了Spring,幫助我們進行資源的管理,Spring也可以看做是一個大工廠,包含了對資源的管理。

控制反轉的意思就是說,我們原本是要自己通過new關鍵字創建的對象(我們控制對象的創建)變為了Spring自動創建對象(Spring控制對象的創建)

現實角度解釋:

比如說我們想吃飯,然后自己利用資源(買回來的菜,米)來做飯,吃完飯后還要剩飯剩菜進行處理(放進冰箱或者倒掉),這就是對資源管理,這就是相當於情況1。

我們自己去買菜,買米,交給媽媽或飯店,讓媽媽或者飯店為我們做飯,媽媽和飯店就是類似工廠類的角色,但是剩飯剩菜我們是不能管理的(假設媽媽是不讓我們插手處理剩飯剩菜,飯店肯定也是不讓你插手的),這就是情況2

我們不准備買菜和買米,直接去飯店或者酒店吃飯,吃完飯也不管剩下的菜和飯是怎么處理(由飯店或者酒店處理),這里的飯店和酒店就是相當於Spring的角色,飯店和酒店會對剩飯剩菜處理(資源自動管理),這就是相當於情況3

入門

環境配置

Spring框架jar包比較多,我們可以根據需求來進行選擇,下面列出常用的jar包及說明

文件名 說明
spring-aop.jar 使用Spring的AOP特性所需要的庫
spring-beans.jar 包含訪問配置文件、創建和管理bean以及進行IoC/DI操作相關的所有類
spring-context.jar 為Spring核心提供了大量擴展
spring-core.jar Spring的框架的核心庫,Spring的各個組件都是要使用到這個包里的類
spring-expression.jar Spring表達式語言需要的庫
spring-test.jar spring內置的一個單元測試

我使用的Maven配置,我這里spring依賴的jar包都是使用最新的,所有的版本直接輸入RELEASE就可以了

<dependencies>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.11</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>commons-logging</groupId>
            <artifactId>commons-logging</artifactId>
            <version>RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-test</artifactId>
            <version>RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-core</artifactId>
            <version>RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-beans</artifactId>
            <version>RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-aop</artifactId>
            <version>RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-expression</artifactId>
            <version>RELEASE</version>
        </dependency>
    </dependencies>

需求

假設現在需要一個Student類,Student中有學號和姓名,同時,也包括一個Teacher類,我們通過Spring容器來生成對象

實體類

Student.java

public class Student{
	private String sname;
	private String sno;
	private Teacher teacher;
	//省略get/set方法
}

Teacher.java

public class Teacher{
	private String tname;
	private String tno;
	//省略get/set方法
}

spring配置文件

由於使用的是maven,所以,這些xml配置文件放在resources文件夾中,maven之后會自動地把這些配置文件放在合適的位置

spring配置文件的名字可以任意取

spring-config.xml

<?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">
	<!--這里相當於創建了一個對象,並通過set方法給該對象設置了各屬性的數值 -->   
    <bean class="com.wan.Teacher" id="teacher1">
        <property name="tname" value="張三"/>
        <property name="tno" value="t001"/>
    </bean>
	
    <bean class="com.wan.Student" id="student">
		<!--ref屬性后面的數值是上面Teacher對象的id屬性 -->
        <property name="teacher" ref="teacher1"/>
        <property name="sname" value="李四"/>
        <property name="sno" value="s001"/>
    </bean>
</beans>

獲得對象

ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml");
//這里的參數student就是上面的配置文件Student對象的id,需要強轉
Student s = (Student) context.getBean("student");

分析與補充

簡單地分析一下上面的例子,spring容器會自動調用實體類的構造方法,生成一個student對象,然后通過set方法,把xml定義的屬性值設置到對象中去。

之后,通過spring容器來獲得一個student對象

配置文件補充知識

bean標簽

屬性/子標簽 說明
id 唯一標識符, 用來代表唯一的bean
class 對象的類型(包名+類名)
property bean標簽的子標簽,對象的一個屬性

property標簽

屬性/子標簽 說明
name 屬性名(與類中的屬性名相同),實際會通過反射調用setXx方法進行屬性的設置
value 屬性值,用於給簡單類型賦值,簡單類型(int,double,string)
ref 屬性值,用於給自定義類型賦值,比如說上面我們Student中包含有一個Teacher類的屬性,引用的是另外一個bean的id

PS:如果類中有一個屬性的類型是某個接口,也是使用ref屬性,引用實現了此接口的類的bean的id

依賴注入DI

介紹

依賴注入和控制反轉都是同一個概念,只是從不同的角度看

我們從Student類來看,Student類中有幾個屬性,我們是通過spring的配置文件,把屬性值(資源)注入到Student對象中,這就叫依賴注入

依賴注入的三種方式

1.settter設值注入

這種方法本質上是通過反射來調用對象的setXx方法來進行設值,Spring首先通過實體類的無參構造方法來new了一個對象,然后再通過對象的set方法來進行設值。

所以,使用此方法的前提是保證實體類中存在有無參的構造方法和對應屬性的set方法(可以不同遵循Java Bean規范)

<property name="屬性名" value="屬性值"></property>

<property name="屬性名" ref="引用對象的id"></property>

2. 構造器注入

使用這種方法要按照構造方法的參數順序

<bean id="teacher" class="com.wan.Teacher">
	<constructor-arg value="t001"></constructor-arg>
	<!-- <constructor-arg value="t001"></constructor-arg> -->
</bean>

如果不想按照順序,可以使用index屬性

<bean id="teacher" class="com.wan.Teacher">
	<!-- 指定第一個參數的數值 -->
	<constructor-arg index="1" value="t001"></constructor-arg>
</bean>

如果有兩個構造方法,都是只有兩個參數,但是,參數的類型不同,可以使用type屬性

<bean id="teacher" class="com.wan.Teacher">
	<constructor-arg index="1" value="t001" type="int"></constructor-arg>
	<constructor-arg index="1" value="t001" type="java.lang.String"></constructor-arg>
</bean>

使用自定義數據,也是和之前使用ref屬性

<bean id="teacher" class="com.wan.Teacher">
	<constructor-arg index="1" ref="teacher1"></constructor-arg>
</bean>

3. p命名空間注入

使用這種方法可以更加的方便,可以不變使用property標簽,而是使用p:屬性名或者p:屬性名-ref

使用之前,得在spring的配置文件添加

xmlns:p="http://www.springframework.org/schema/p"
<bean id="teacher" class="com.wan.Student" p:sno="001" p:teacher-ref="teacher1">
</bean>

注入各種數據類型的屬性

之前使用的是value屬性,還可以使用value標簽,使用type定義類型

<property name="屬性名">
	<value type="java.util.String"></value>
</property>

xml文件中,<&都是特殊符號,得通過特別的方式輸入

有兩種方法,一個是使用xml預定義的實體引用,二是使用<![CDATA[ ]]>

value標簽,可以使用上面說的的兩種方法,而value屬性,只能使用xml預定義的實體引用

&amp; -> &

&lt; -> <

<null/> 設置為null

<!-- value標簽 -->
<property name="屬性名">
	<value type="java.util.String"><![CDATA[張&三]]></value>
	<value type="java.util.String">張&amp;三</value>
</property>

<!--value屬性  -->
<property name="屬性名" value="張&amp;三" />


集合類型

集合可以使用list、map、set標簽

類型 標簽
List或數組 外層使用list標簽,內層用value或ref
Set 外層使用set標簽,內層用value或ref
Map 外層使用map,中間層使用entry,最里層使用key和value標簽
Properties資源文件 外層使用props,子標簽為prop
<properties name="list">
	<list>
		<value>數值</value>
		<ref bean="引用bean的id"><ref>
	</list>
</properties>

<properties name="map">
	<entry>
		<key>
			<value>數值</value>
			或
			<ref bean="引用bean的id"><ref>
		</key>
		<value></value>
		<ref bean="引用bean的id"><ref>
	</entry>
</properties>

<!-- 相當於在properties資源文件有個 option=2 的配置 -->
<props>
	<prop key="option">2</prop>
</props>

PS:記得value和ref兩個標簽用來賦值就可以了,value給基本類型賦值,ref給自定義類型賦值

例子:

MyArray.java

package com.wan;

import java.util.List;

/**
 * @author StarsOne
 * @date Create in  2019/9/24 0024 15:54
 * @description
 */
public class MyArray {
    List<String> lines;
    List<Teacher> teachers;

    public List<String> getLines() {
        return lines;
    }

    public void setLines(List<String> lines) {
        this.lines = lines;
    }

    public List<Teacher> getTeachers() {
        return teachers;
    }

    public void setTeachers(List<Teacher> teachers) {
        this.teachers = teachers;
    }
    
}

spring的xml配置文件:

<bean class="com.wan.Teacher" id="teacher1">
	<property name="tno" value="001"/>
	<property name="tname" value="張三"/>
</bean>
<bean class="com.wan.Teacher" id="teacher2">
	<property name="tno" value="001"/>
	<property name="tname" value="張三"/>
</bean>

<bean id="array1" class="com.wan.MyArray">
	<property name="lines">
		<list>
			<value>第一行</value>
			<value>第二行</value>
		</list>
	</property>
	<property name="teachers">
		<list>
			<ref bean="teacher1"/>
			<ref bean="teacher2"/>
		</list>
	</property>
</bean>

測試:

lassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml");
MyArray myarray = (MyArray) context.getBean("array1");
List<String> lines = myarray.getLines();
List<Teacher> teachers = myarray.getTeachers();
for (String line : lines) {
	System.out.println(line);
}
for (Teacher teacher : teachers) {
	System.out.println(teacher.toString());
}

自動裝配

和Mybatis一樣,spring也有約定優於配置,只要符合約定,spring就會自動幫我們完成的上面的注入屬性值操作。

我們可以通過屬性名、屬性類型或者構造器實現自動裝配

自動裝配只適用於對象類型(引用類型,ref)

byName>byType>constructor

我們之前的Student類中包含有一個Teacher類屬性

<!-- 注意這個id與Student中的Teacher屬性名一致 -->
<bean id="teacher" class="com.wan.Teacher">
...
</bean>

<bean id="student" class="com.wan.Student" autowise="byName">
</bean>

一般大項目不推薦適用自動裝配,會影響程序的可讀性

參考鏈接:Spring 自動裝配

bean的作用域

作用域 描述
singleton 在spring IoC容器僅存在一個Bean實例,Bean以單例方式存在,默認值
prototype 每次從容器中調用Bean時,都返回一個新的實例,即每次調用getBean()時,相當於執行newXxxBean()
request 每次HTTP請求都會創建一個新的Bean,該作用域僅適用於WebApplicationContext環境
session 同一個HTTP Session共享一個Bean,不同Session使用不同的Bean,僅適用於WebApplicationContext環境
global-session 一般用於Portlet應用環境,該運用域僅適用於WebApplicationContext環境

參考鏈接:Spring Bean 作用域

注解方式

使用@Controller,@Service、@Repository、@Component等注解標識類(Bean),之后配置xml的context:component-scan標簽自動掃描

注解 范圍
@Component 通用
@Controller 用於標注Controller層的類(某個具體的Servlet)
@Repository 用於標注Dao層的類
@Service 用於標注Services層的類

一般推薦使用細化后的注解,也就是后面三種,使用的方法都是一樣的,參考下面的一個簡單例子


@Component("student1")
public class Student{
	@Value("001")
	private String sno;
}

<!-- 相當於下面的配置 -->
<bean id="student1" class="com.wan.Student">
	<property name="sno" value="001"/>
</bean>

使用的時候要在spring的那個配置文件添加下面的代碼

<beans xmlns:context="http://www.springframework.org/schema/context"
	   xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
		http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">

	<!-- 自動掃描某個包 -->
	<context:component-scan base-package="com.wan">
	</context:component-scan>
</beans>

注意,配置文件需要添加屬性xmlns:context="http://www.springframework.org/schema/context

xsi:schemaLocation屬性里面要有個http://www.springframework.org/schema/context/spring-context.xsd

不然就會報錯“通配符的匹配很全面, 但無法找到元素 'context:component-scan' 的聲明。”

使用的話和之前一樣

ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml");
Student student = (Student) context.getBean("student1");


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM