1. 程式人生 > >spring框架(二)——spring概述與spring的核心之IOC

spring框架(二)——spring概述與spring的核心之IOC

上面一節我們說了spring的體系和jar包的依賴關係,這節筆者主要詳解一下spring3.x 

一、Spring概述

1、Spring是什麼?

Spring是分層的Java SE/EE應用 full-stack輕量級開源框架,以IoC(Inverse Of Control:反轉控制)和AOP(Aspect Oriented Programming:面向切面程式設計)為核心,提供了展現層Spring MVC和持久層Spring JDBC以及業務層事務管理等眾多的企業級應用技術,還能整合開源世界眾多著名的第三方框架和類庫,逐漸成為使用最多的Java EE企業應用開源框架。

2、Spring3.X的體系結構


4、Spring帶來的好處

方便解耦,簡化開發
通過Spring提供的IoC容器,可以將物件間的依賴關係交由Spring進行控制,避免硬編碼所造成的過度程式耦合。使用者也不必再為單例模式類、屬性檔案解析等這些很底層的需求編寫程式碼,可以更專注於上層的應用。
AOP程式設計的支援
通過Spring的AOP功能,方便進行面向切面的程式設計,許多不容易用傳統OOP實現的功能可以通過AOP輕鬆應付。
宣告式事務的支援
可以將我們從單調煩悶的事務管理程式碼中解脫出來,通過宣告式方式靈活的進行事務的管理,提高開發效率和質量。
方便程式的測試

可以用非容器依賴的程式設計方式進行幾乎所有的測試工作,測試不再是昂貴的操作,而是隨手可做的事情。
方便整合各種優秀框架
Spring可以降低各種框架的使用難度,提供了對各種優秀框架(Struts、Hibernate、Hessian、Quartz等)的直接支援。
降低JavaEE API的使用難度
Spring對JavaEE API(如JDBC、JavaMail、遠端呼叫等)進行了薄薄的封裝層,使這些API的使用難度大為降低。
Java原始碼是經典學習範例
Spring的原始碼設計精妙、結構清晰、匠心獨用,處處體現著大師對Java設計模式靈活運用以及對Java技術的高深造詣。它的原始碼無意是Java技術的最佳實踐的範例。

5、Spring的資源

5.1 下載開發資源包

官方網站: http://spring.io/

5.2、Spring的資源包介紹


spring Framework的官方發行包內部結構為:

5.3、關於資源包的說明

Spring Framework官方最新版本是4.3.2。但是能下載到的就是3.2,這是Spring3這個版本的最後一個官方資源包。之後的版本全用MAVEN整合了。
Spring依賴的第三方資源包能下載的版本就是3.0.2。後面的也都整合到MAVEN裡面去了。

6、搭建Spring核心開發環境

6.1、拷貝最基本的核心jar包


根據第一節。新增這4個jar包以及它的依賴包(spring-Context其實還依賴了aop但是這裡因為講解IOC所以不用依賴也是可以的)。所以上圖為他們的4個jar包名稱。

6.2、核心依賴的jar包(日誌輸出)

commons-logging或log4j:用哪個都行。


6.3、Spring的配置檔案:基於xml或註解的方式

(下面搭建專案時在講解)

概述部分講解完畢。下面開始spring的核心之一IOC部分

二、Spring的核心之IoC(Inversion of Control 控制反轉)

1、IoC是什麼?


注意:這裡需要理解什麼是注入。(舉例,比如普通的set方法就是一種注入等等。)

2、Spring容器

Spring容器簡單理解就是用於存放物件的一個boss那麼一個東西。

3、Spring的IoC入門(注意:這裡只是IOC的入門案例,瞭解ioc是怎麼個過程)

3.1、建立一個java工程(沒有整合web專案,所以jar工程就夠用了)

3.2、匯入Spring的核心jar包(5個jar包)


3.3、建立一個XML配置檔案


注意:現在xml檔案用什麼名字無所謂。

3.4、資源交給Spring容器管理(當你寫好了controller層,service層,以及dao層時需要交給spring來管理他們的關係)

比如:寫好了dao層之後:


3.5、Spring容器的初始化及資源的獲取

配置依賴注入:service與dao層之間的關係:


在service層中寫一個register方法,呼叫dao層的方法(注入的dao中有save方法)如下:

package springIoc.service;

import springIoc.dao.XXDao1;

public class XXService1 {

	private XXDao1 xxDao1;

	public void setXxDao1(XXDao1 xxDao1) {
		this.xxDao1 = xxDao1;
	}
	public void rgeister(){
		xxDao1.save();
	}
	
}
初始化和資源的獲取測試:

package springIoc.test;

import org.springframework.context.support.ClassPathXmlApplicationContext;

import springIoc.service.XXService1;

public class Client {

	public static void main(String[] args) {
		/* 注意這是直接new物件獲取,耦合度很高,沒有呼叫spring來管理的bean == 不採用
		 * XXService1 xxService1 = new XXService1();
			xxService1.rgeister();
		*/
		//Spring容器的初始化
		
		ClassPathXmlApplicationContext ac = new ClassPathXmlApplicationContext("springIoc/bean.xml");
		//獲取資源
		//XXDao1 xxdao1 = (XXDao1) ac.getBean("xxDao1");
		//xxdao1.save();
		
		XXService1 xxService1 = (XXService1) ac.getBean("xxService1");
		xxService1.rgeister();
	}
}
結果成功!

4、Spring中API的體系結構(瞭解)


4.1、核心介面和類

BeanFactory:它使用的是延遲載入思想。當我們需要用bean時,才去建立物件。
ApplicationContext:它繼承自BeanFactory介面,還繼承了很多其他介面。功能比BeanFactory強大。它用的不是延遲載入思想,而是在載入配置檔案時就建立了。(推薦)
FileSystemXmlApplicationContext:配置檔案在本地磁碟上,使用該類載入。
ClassPathXmlApplicationContext :配置檔案在classpath中,使用此類載入。

特別注意:以上主要ioc已經講解完畢!下面的Ioc配置部分很重要,兩種方式在實際開發中用到的都很多!

5、基於XML的spring的IoC配置

5.1、Spring例項化bean的方式

a、呼叫預設的構造方法 (推薦)

 <!-- dao層 -->
<bean id="xxDao1" class="springIoc.dao.XXDao1"></bean>
<bean id="xxDao2" class="springIoc.dao.XXDao2"></bean>
b、靜態工廠方法初始化:老系統才有可能用到(現在已經完全不用了)
c、例項工廠方法初始化:老系統才有可能用到(現在已經完全不用了)

5.2、Bean的作用範圍(Bean下的scope標籤)

* scope屬性的取值:
* singleton			:預設值.單例的.Spring生成Bean的時候採用的是單例的模式.
* prototype			:多例的.Spring生成Bean的時候採用的是多例的模式.每次getBean的時候都會得到一個新的Bean的例項.(與Struts2整合時要使用此值)
* request			:應用在web應用中.建立一個Bean的例項將Bean例項存入到request域中.
* session			:應用在web應用中.建立一個Bean的例項將Bean的例項存入session域中.
* globalsession			:應用在web應用中.全域性session.一般用於Porlet應用環境.如果沒有Porlet環境.配置全域性session等價於session.

5.3、Bean的生命週期方法

init-method:
Bean初始化的時候執行的方法.
destroy-method:
Bean銷燬的時候執行的方法.必須是在scope=”singleton”情況下使用.

<bean id="xxDao1" class="springIoc.dao.XXDao1" init-method="init" destroy-method="destory"></bean>
拓展:

Bean的完整的生命週期:

Spring框架管理Bean的時候,非常精確.Bean的生成過程中有11個步驟.11個步驟才構成了Bean的完整的生命週期.

1.instantiate bean物件例項化
2.populate properties 封裝屬性
3.如果Bean實現BeanNameAware 執行 setBeanName
4.如果Bean實現BeanFactoryAware 或者 ApplicationContextAware 設定工廠 setBeanFactory 或者上下文物件 setApplicationContext
5.如果存在類實現 BeanPostProcessor(後處理Bean) ,執行postProcessBeforeInitialization
6.如果Bean實現InitializingBean 執行 afterPropertiesSet 
7.呼叫<bean init-method="init"> 指定初始化方法 init
8.如果存在類實現 BeanPostProcessor(處理Bean) ,執行postProcessAfterInitialization
9.執行業務處理
10.如果Bean實現 DisposableBean 執行 destroy
11.呼叫<bean destroy-method="customerDestroy"> 指定銷燬方法 customerDestroy

* 第三步和第四部為了讓Bean瞭解Spring的環境的..
* 第五步和第八部是實現了一個介面BeanPostProcessor.
注意:當非單例時,初始化方法正常執行,但是銷燬方法就不會執行了。

5.4、依賴注入(DI)

a、構造器注入(呼叫預設構造方法):通過傳引數


0 先定義一個bean類,只提供有參的構造方法(並給每個引數提供get方法,方便測試時判斷是否注入進去值)

package springIoc.bean;

import java.util.Date;

public class Bean1 {
	private String name;
	private int age;
	private Date birthday;
	
	//提供了有參構造方法,沒有提供預設的構造方法
	public Bean1(String name, int age, Date birthday) {
		super();
		this.name = name;
		this.age = age;
		this.birthday = birthday;
	}

	public String getName() {
		return name;
	}

	public int getAge() {
		return age;
	}

	public Date getBirthday() {
		return birthday;
	}
	
}
1、編寫bean.xml交給spring管理這個類。

2.編寫測試類,判斷是否注入成功

package springIoc.test;

import org.springframework.context.support.ClassPathXmlApplicationContext;

import springIoc.bean.Bean1;
public class Client {

	public static void main(String[] args) {
		//Spring容器的初始化
		ClassPathXmlApplicationContext ac = new ClassPathXmlApplicationContext("springIoc/bean.xml");
		//獲取資源
		Bean1 bean1 = (Bean1) ac.getBean("bean1");
		System.out.println("姓名:"+bean1.getName()+"年齡:"+bean1.getAge()+"現在時間:"+bean1.getBirthday());
	}
}
打印出:姓名:小馬同志年齡:26現在時間:Fri Oct 27 09:47:20 CST 2017 
b、屬性注入:推薦(也是常用的)

0 先定義一個bean類,定義的欄位需要提供setter方法(不然注入不進去)並提供預設的構造方法(必須)

package springIoc.bean;

import java.util.Date;

public class Bean1 {
	private String name;
	private int age;
	private Date birthday;
	public Bean1() {
		super();
	}

	public String getName() {
		return name;
	}

	public int getAge() {
		return age;
	}

	public Date getBirthday() {
		return birthday;
	}

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

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

	public void setBirthday(Date birthday) {
		this.birthday = birthday;
	}
	
}
1、編寫bean.xml交給spring管理這個類。
<bean id="now" class="java.util.Date"></bean>
    			
<bean name="bean1" class="springIoc.bean.Bean1">
	<!-- 屬性注入 -->
	<property name="name" value="小馬"></property>
	<property name="age" value="26"></property>
	<property name="birthday" ref="now"></property>
</bean>
2.編寫測試類
package springIoc.test;

import org.springframework.context.support.ClassPathXmlApplicationContext;

import springIoc.bean.Bean1;
public class Client {

	public static void main(String[] args) {
		//Spring容器的初始化
		ClassPathXmlApplicationContext ac = new ClassPathXmlApplicationContext("springIoc/bean.xml");
		//獲取資源
		Bean1 bean1 = (Bean1) ac.getBean("bean1");
		System.out.println("姓名:"+bean1.getName()+"年齡:"+bean1.getAge()+"現在時間:"+bean1.getBirthday());
	}
}
測試成功。
c、p名稱空間(不做細講)
d、SpEL(Spring Expression Lanaguage)

e、注入集合資料

5.5、團隊開發:多個Spring配置檔案

兩種形式:
* 載入配置檔案的時候一併引入.
* ApplicationContext applicationContext = new ClassPathXmlApplicationContext(
				"applicationContext.xml", "applicationContext2.xml");

* 總的配置檔案中引入其他的配置檔案.
* <import resource="applicationContext2.xml"/>
基於xml的配置檔案就講解到這。需要會。下面說一下基於註解的配置也是同樣重要,

6、基於註解的spring的IoC配置

6.1、註解的使用前提(3個步驟必須有的!)

a、引入context的名稱空間

b、指定要掃描的包

c、在Bean上面加入@Component註解

0、配置a和b的步驟


1 之後執行c在指定的類中添加註解(control層和service層以及dao層都需要新增各自的註解)(稍後會有詳細的註解介紹)

package springIoc.dao;

import org.springframework.stereotype.Component;

/**
 * @Component是spring的一個註解,它是把XXDao1看做一個元件。
 * 就相當於xml中 <bean id="xxDao1" class="springIoc.dao.XXDao1"></bean>
 * @author mch
 *
 */
@Component
public class XXDao1 {

	public void save() {
		System.out.println("SPRING3.XIOC成功了");
	}

}
注意:@Component(value="xxDao1") 也可以寫成這種註解而value值相當於bean的id

2、編寫測試類執行結果

package springIoc.test;

import org.springframework.context.support.ClassPathXmlApplicationContext;

import springIoc.dao.XXDao1;
public class Client {

	public static void main(String[] args) {
		//Spring容器的初始化
		ClassPathXmlApplicationContext ac = new ClassPathXmlApplicationContext("springIoc/springxml.xml");
		//獲取資源
		XXDao1 xxDao1 = (XXDao1) ac.getBean("xxDao1");
		xxDao1.save();
	}
}

6.2、常用註解

a、@Component
作用:讓Spring容器管理當前的Bean(例項化)
屬性:
value:指定bean的名稱。預設值是當前類簡單名稱(不是全類名)首字母小寫。

b、@Component的衍生註解@Controller @Service @Repository
作用:和@Component作用一樣,讓Spring容器管理(例項化)當前的bean。
屬性: value:指定bean的名稱。預設值是當前類的簡單類名首字母小寫
特點:在三成架構中,每個註解對應一層,使語義更加明確。
@Controller:一般用在表現層,比如struts2的action上
@Service:一般用在業務層,比如Service實現
@Repository:一般用在持久層,比如Dao實現

c、@Autowired
作用:自動按型別注入需要的物件。當使用了該註解時,setter就不是必須的了。
屬性:
required:是否必須注入成功。預設值是true。
true:必須注入成功,如果出現注入失敗,丟擲異常。
false:不一定注入成功,不拋異常。
注意事項:
一個Service介面:IBookService
兩個Service實現:BookServiceImpl1 BookServiceImpl2
由於@Autowired是自動按型別注入,當使用介面型別時,就看變數的名稱,如果變數名稱是bookServiceImpl1,則使用BookServiceImp1這個實現類,
如果變數名是bookServiceImpl2,則使用BookServiceImpl2這個實現類。如果沒有符合規範的名稱(類名首字母小寫),則報錯。
到底注入哪個實現類:
@Autowried
private BookService bookServiceImpl1;//注入BookServiceImpl1
@Autowried
private BookService bookServiceImpl2;//注入BookServiceImpl2
@Autowried
private BookService bookService;//注入失敗

d、@Value
作用:注入基本型別和String。
屬性:value:SpEL表示式,要注入的值

package springIoc.dao;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

/**
 * @Component是spring的一個註解,它是把XXDao1看做一個元件。
 * 就相當於xml中 <bean id="xxDao1" class="springIoc.dao.XXDao1"></bean>
 * @author mch
 *
 */
@Component(value="xxDao1")
public class XXDao1 {

	@Value("小碼")
	private String name;
	
	public void save() {
		System.out.println("SPRING3.XIOC成功了");
	}

	public String getName() {
		return name;
	}
	
}
提供get方法檢視是否注入成功
package springIoc.test;

import org.springframework.context.support.ClassPathXmlApplicationContext;

import springIoc.dao.XXDao1;
public class Client {

	public static void main(String[] args) {
		//Spring容器的初始化
		ClassPathXmlApplicationContext ac = new ClassPathXmlApplicationContext("springIoc/springxml.xml");
		//獲取資源
		XXDao1 xxDao1 = (XXDao1) ac.getBean("xxDao1");
		xxDao1.save();
		System.out.println(xxDao1.getName());
	}
}
結果為成功
e、@Qualifier
作用:要配合@Autowried來一起使用,通過它指定要注入的bean的名稱。按型別注入失效了。
屬性:value:要注入的bean的名稱

f、@Resource
作用:如同@Autowire和@Qualifier,是規範JSR-250中定義的(JCP)。通過指定bean的名稱注入物件。
屬性: name:要注入的bean的名稱

g、@PostConstruct(瞭解)
作用:用在初始化方法上。生命週期有關
h、@PreDestroy(瞭解)
作用:用在銷燬方法上。生命週期有關
i、@Configuration和@Bean(瞭解)
作用:@Configuration指定當前類為配置類,用於載入@Bean的定義。@Bean用於定義bean的名稱,用法是@Bean(name="beanName")
注意:該類要被設定在註解自動掃描對應的包下。

6.3、Spring中使用單元測試

a、匯入jar包:
spring-test-3.2.0.RELEASE.jar
b、設定Junit執行器和Spring配置檔案

步驟:

0、編寫一個類(dao層)

package springIoc.dao;

import org.springframework.stereotype.Component;

/**
 * @Component是spring的一個註解,它是把XXDao1看做一個元件。
 * 就相當於xml中 <bean id="xxDao1" class="springIoc.dao.XXDao1"></bean>
 * @author mch
 *
 */
@Component(value="xxDao1")
public class XXDao1 {


	public void save() {
		System.out.println("SPRING3.XIOC成功了");
	}
}
1、設定是spring配置檔案

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
 	   xmlns:context="http://www.springframework.org/schema/context"
       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-3.2.xsd
       			http://www.springframework.org/schema/context   
    			http://www.springframework.org/schema/context/spring-context-4.2.xsd ">
      <!-- 指定Spring要掃描的包
      		注意:它會掃描當前包和當前包的子包下的所有類(base-package:以點分割包名)
       -->
		<context:component-scan base-package="springIoc"></context:component-scan>
</beans>
2、進行編寫測試類。進行測試

package springIoc.dao;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

@RunWith(SpringJUnit4ClassRunner.class)//指定新的執行器,該執行前會初始化Spring容器,並注入資源,執行Junit單元測試
@ContextConfiguration(locations={"classpath:springIoc/springxml.xml"})//指定spring容器要載入的配置檔案
public class XXDao1Test {

	@Autowired
	private XXDao1 xxDao1;
	
	@Test
	public void test1(){
		xxDao1.save();
	}
}
結果成功!

總結:

其實spring的Ioc原理是 IOC原理:工廠 + 反射 + 配置檔案.


那麼Spring的Ioc就說到這,後面在繼續瞭解它。