Spring入門介紹:

Spring入門介紹
Spring誕生:html

建立Spring的目的就是用來替代更加劇量級的的企業級Java技術
簡化Java的開發java

基於POJO輕量級和最小侵入式開發
經過依賴注入和麪向接口實現鬆耦合
基於切面和慣例進行聲明式編程
經過切面和模板減小樣板式代碼
侵入式概念
Spring是一種非侵入式的框架...web

侵入式spring

對於EJB、Struts2等一些傳統的框架,一般是要實現特定的接口,繼承特定的類才能加強功能express

改變了java類的結構
非侵入式編程

對於Hibernate、Spring等框架,對現有的類結構沒有影響,就可以加強JavaBean的功能
鬆耦合
前面咱們在寫程序的時候,都是面向接口編程,經過DaoFactroy等方法來實現鬆耦合segmentfault

private CategoryDao categoryDao = DaoFactory.getInstance().createDao("zhongfucheng.dao.impl.CategoryDAOImpl", CategoryDao.class);session

private BookDao bookDao = DaoFactory.getInstance().createDao("zhongfucheng.dao.impl.BookDaoImpl", BookDao.class);mvc

private UserDao userDao = DaoFactory.getInstance().createDao("zhongfucheng.dao.impl.UserDaoImpl", UserDao.class);app

private OrderDao orderDao = DaoFactory.getInstance().createDao("zhongfucheng.dao.impl.OrderDaoImpl", OrderDao.class);

這裏寫圖片描述

DAO層和Service層經過DaoFactory來實現鬆耦合

若是Serivce層直接new DaoBook(),那麼DAO和Service就緊耦合了【Service層依賴牢牢依賴於Dao】。
而Spring給咱們更加合適的方法來實現鬆耦合,而且更加靈活、功能更增強大!---->IOC控制反轉

切面編程
切面編程也就是AOP編程,其實咱們在以前也接觸過...動態代理就是一種切面編程了...

當時咱們使用動態代理+註解的方式給Service層的方法添加權限.


@Override
@permission("添加分類")
/*添加分類*/
public void addCategory(Category category) {
categoryDao.addCategory(category);
}


/*查找分類*/
@Override
public void findCategory(String id) {
categoryDao.findCategory(id);
}

@Override
@permission("查找分類")
/*查看分類*/
public List<Category> getAllCategory() {
return categoryDao.getAllCategory();
}

/*添加圖書*/
@Override
public void addBook(Book book) {
bookDao.addBook(book);

}
Controller調用Service的時候,Service返回的是一個代理對象
代理對象獲得Controller想要調用的方法,經過反射來看看該方法上有沒有註解
若是有註解的話,那麼就判斷該用戶是否有權限來調用 此方法,若是沒有權限,就拋出異常給Controller,Controller接收到異常,就能夠提示用戶沒有權限了。
AOP編程能夠簡單理解成:在執行某些代碼前,執行另外的代碼

Struts2的攔截器也是面向切面編程【在執行Action業務方法以前執行攔截器】
Spring也爲咱們提供更好地方式來實現面向切面編程!

引出Spring
咱們試着回顧一下沒學Spring的時候,是怎麼開發Web項目的

1. 實體類--->class User{ }
2. daoclass--> UserDao{ .. 訪問db}
3. service--->class UserService{ UserDao userDao = new UserDao();}
4. actionclass UserAction{UserService userService = new UserService();}
用戶訪問:

Tomcat->action->service->dao
咱們來思考幾個問題:

①:對象建立建立可否寫死?
②:對象建立細節

對象數量

action 多個 【維護成員變量】
service 一個 【不須要維護公共變量】
dao 一個 【不須要維護公共變量】
建立時間

action 訪問時候建立
service 啓動時候建立
dao 啓動時候建立
③:對象的依賴關係

action 依賴 service
service依賴 dao
對於第一個問題和第三個問題,咱們能夠經過DaoFactory解決掉(雖然不是比較好的解決方法)

對於第二個問題,咱們要控制對象的數量和建立時間就有點麻煩了....

而Spring框架經過IOC就很好地能夠解決上面的問題....

IOC控制反轉
Spring的核心思想之一:Inversion of Control , 控制反轉 IOC

那麼控制反轉是什麼意思呢???對象的建立交給外部容器完成,這個就作控制反轉。

Spring使用控制反轉來實現對象不用在程序中寫死
控制反轉解決對象處理問題【把對象交給別人建立】
那麼對象的對象之間的依賴關係Spring是怎麼作的呢??依賴注入,dependency injection.

Spring使用依賴注入來實現對象之間的依賴關係
在建立完對象以後,對象的關係處理就是依賴注入
上面已經說了,控制反轉是經過外部容器完成的,而Spring又爲咱們提供了這麼一個容器,咱們通常將這個容器叫作:IOC容器.

不管是建立對象、處理對象之間的依賴關係、對象建立的時間仍是對象的數量,咱們都是在Spring爲咱們提供的IOC容器上配置對象的信息就行了。

那麼使用IOC控制反轉這一思想有什麼做用呢???咱們來看看一些優秀的回答...

來自知乎:https://www.zhihu.com/question/23277575/answer/24259844

我摘取一下核心的部分:

ioc的思想最核心的地方在於,資源不禁使用資源的雙方管理,而由不使用資源的第三方管理,這能夠帶來不少好處。第一,資源集中管理,實現資源的可配置和易管理。第二,下降了使用資源雙方的依賴程度,也就是咱們說的耦合度。
也就是說,甲方要達成某種目的不須要直接依賴乙方,它只須要達到的目的告訴第三方機構就能夠了,好比甲方須要一雙襪子,而乙方它賣一雙襪子,它要把襪子賣出去,並不須要本身去直接找到一個賣家來完成襪子的賣出。它也只須要找第三方,告訴別人我要賣一雙襪子。這下好了,甲乙雙方進行交易活動,都不須要本身直接去找賣家,至關於程序內部開放接口,賣家由第三方做爲參數傳入。甲乙互相不依賴,並且只有在進行交易活動的時候,甲才和乙產生聯繫。反之亦然。這樣作什麼好處麼呢,甲乙能夠在對方不真實存在的狀況下獨立存在,並且保證不交易時候無聯繫,想交易的時候能夠很容易的產生聯繫。甲乙交易活動不須要雙方見面,避免了雙方的互不信任形成交易失敗的問題。由於交易由第三方來負責聯繫,並且甲乙都認爲第三方可靠。那麼交易就能很可靠很靈活的產生和進行了。這就是ioc的核心思想。生活中這種例子比比皆是,支付寶在整個淘寶體系裏就是龐大的ioc容器,交易雙方以外的第三方,提供可靠性可依賴可靈活變動交易方的資源管理中心。另外人事代理也是,僱傭機構和我的以外的第三方。
==========================update===========================

在以上的描述中,誕生了兩個專業詞彙,依賴注入和控制反轉所謂的依賴注入,則是,甲方開放接口,在它須要的時候,可以講乙方傳遞進來(注入)所謂的控制反轉,甲乙雙方不相互依賴,交易活動的進行不依賴於甲乙任何一方,整個活動的進行由第三方負責管理。

參考優秀的博文①:https://www.tianmaying.com/tutorial/spring-ioc

參考優秀的博文②:這裏寫連接內容

知乎@Intopass的回答:

不用本身組裝,拿來就用。
享受單例的好處,效率高,不浪費空間。
便於單元測試,方便切換mock組件。
便於進行AOP操做,對於使用者是透明的。
統一配置,便於修改。
Spring模塊
Spring能夠分爲6大模塊:

Spring Core spring的核心功能: IOC容器, 解決對象建立及依賴關係
Spring Web Spring對web模塊的支持。

能夠與struts整合,讓struts的action建立交給spring
spring mvc模式
Spring DAO Spring 對jdbc操做的支持 【JdbcTemplate模板工具類】
Spring ORM spring對orm的支持:

既能夠與hibernate整合,【session】
也可使用spring的對hibernate操做的封裝
Spring AOP 切面編程
SpringEE spring 對javaEE其餘模塊的支持
這裏寫圖片描述

上面文主要引出了爲啥咱們須要使用Spring框架,以及大體瞭解了Spring是分爲六大模塊的....下面主要講解Spring的core模塊!

Core模塊快速入門
搭建配置環境
引入jar包:

本博文主要是core模塊的內容,涉及到Spring core的開發jar包有五個:

commons-logging-1.1.3.jar 日誌
spring-beans-3.2.5.RELEASE.jar bean節點
spring-context-3.2.5.RELEASE.jar spring上下文節點
spring-core-3.2.5.RELEASE.jar spring核心功能
spring-expression-3.2.5.RELEASE.jar spring表達式相關表
我主要使用的是Spring3.2版本...

編寫配置文件:

Spring核心的配置文件applicationContext.xml或者叫bean.xml

那這個配置文件怎麼寫呢??通常地,咱們都知道框架的配置文件都是有約束的...咱們能夠在spring-framework-3.2.5.RELEASE\docs\spring-framework-reference\htmlsingle\index.html找到XML配置文件的約束


<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:p="http://www.springframework.org/schema/p"
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">

</beans>
我是使用Intellij Idea集成開發工具的,能夠選擇自帶的Spring配置文件,它長的是這樣:


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

</beans>
前面在介紹Spring模塊的時候已經說了,Core模塊是:IOC容器,解決對象建立和之間的依賴關係。

所以Core模塊主要是學習如何獲得IOC容器,經過IOC容器來建立對象、解決對象之間的依賴關係、IOC細節。

獲得Spring容器對象【IOC容器】
Spring容器不僅僅只有一個,能夠歸爲兩種類型

Bean工廠,BeanFactory【功能簡單】
應用上下文,ApplicationContext【功能強大,通常咱們使用這個】
經過Resource獲取BeanFactory
加載Spring配置文件
經過XmlBeanFactory+配置文件來建立IOC容器


//加載Spring的資源文件
Resource resource = new ClassPathResource("applicationContext.xml");

//建立IOC容器對象【IOC容器=工廠類+applicationContext.xml】
BeanFactory beanFactory = new XmlBeanFactory(resource);

類路徑下XML獲取ApplicationContext
直接經過ClassPathXmlApplicationContext對象來獲取

// 獲得IOC容器對象
ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");

System.out.println(ac);


在Spring中整體來看能夠經過三種方式來配置對象:

使用XML文件配置
使用註解來配置
使用JavaConfig來配置
XML配置方式
在上面咱們已經能夠獲得IOC容器對象了。接下來就是在applicationContext.xml文件中配置信息【讓IOC容器根據applicationContext.xml文件來建立對象】

首先咱們先有個JavaBean的類

/**
* Created by ozc on 2017/5/10.
*/
public class User {

private String id;
private String username;


public String getId() {
return id;
}

public void setId(String id) {
this.id = id;
}

public String getUsername() {
return username;
}

public void setUsername(String username) {
this.username = username;
}
}
之前咱們是經過new User的方法建立對象的....

User user = new User();
如今咱們有了IOC容器,可讓IOC容器幫咱們建立對象了。在applicationContext.xml文件中配置對應的信息就好了

<!--
使用bean節點來建立對象
id屬性標識着對象
name屬性表明着要建立對象的類全名
-->
<bean id="user" class="User"/>
經過IOC容器對象獲取對象:

在外界經過IOC容器對象獲得User對象


// 獲得IOC容器對象
ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");

User user = (User) ac.getBean("user");

System.out.println(user);
這裏寫圖片描述

上面咱們使用的是IOC經過無參構造函數來建立對象,咱們來回顧一下通常有幾種建立對象的方式:

無參構造函數建立對象
帶參數的構造函數建立對象
工廠建立對象

靜態方法建立對象
非靜態方法建立對象
使用無參的構造函數建立對象咱們已經會了,接下來咱們看看使用剩下的IOC容器是怎麼建立對象的。

帶參數的構造函數建立對象
首先,JavaBean就要提供帶參數的構造函數:


public User(String id, String username) {
this.id = id;
this.username = username;
}
接下來,關鍵是怎麼配置applicationContext.xml文件了。


<bean id="user" class="User">

<!--經過constructor這個節點來指定構造函數的參數類型、名稱、第幾個-->
<constructor-arg index="0" name="id" type="java.lang.String" value="1"></constructor-arg>
<constructor-arg index="1" name="username" type="java.lang.String" value="zhongfucheng"></constructor-arg>
</bean>
這裏寫圖片描述

在constructor上若是構造函數的值是一個對象,而不是一個普通類型的值,咱們就須要用到ref屬性了,而不是value屬性

好比說:我在User對象上維護了Person對象的值,想要在構造函數中初始化它。所以,就須要用到ref屬性了


<bean id="person" class="Person"></bean>

<bean id="user" class="User" >

<!--經過constructor這個節點來指定構造函數的參數類型、名稱、第幾個-->
<constructor-arg index="0" name="id" type="java.lang.String" value="1"></constructor-arg>
<constructor-arg index="1" name="username" type="java.lang.String" ref="person"></constructor-arg>
</bean>
工廠靜態方法建立對象
首先,使用一個工廠的靜態方法返回一個對象


public class Factory {

public static User getBean() {

return new User();
}

}
配置文件中使用工廠的靜態方法返回對象


<!--工廠靜態方法建立對象,直接使用class指向靜態類,指定靜態方法就好了-->
<bean id="user" class="Factory" factory-method="getBean" >

</bean>
這裏寫圖片描述

工廠非靜態方法建立對象
首先,也是經過工廠的非非靜態方法來獲得一個對象


public class Factory {


public User getBean() {

return new User();
}


}
配置文件中使用工廠的非靜態方法返回對象


<!--首先建立工廠對象-->
<bean id="factory" class="Factory"/>

<!--指定工廠對象和工廠方法-->
<bean id="user" class="User" factory-bean="factory" factory-method="getBean"/>
這裏寫圖片描述

c名稱空間
咱們在使用XML配置建立Bean的時候,若是該Bean有構造器,那麼咱們使用<constructor-arg>這個節點來對構造器的參數進行賦值...

<constructor-arg>未免有點太長了,爲了簡化配置,Spring來提供了c名稱空間...

要想c名稱空間是須要導入xmlns:c="http://www.springframework.org/schema/c"的


<bean id="userService" class="bb.UserService" c:userDao-ref="">

</bean>
c名稱空間有個缺點:不能裝配集合,當咱們要裝配集合的時候仍是須要<constructor-arg>這個節點

裝載集合
若是對象上的屬性或者構造函數擁有集合的時候,而咱們又須要爲集合賦值,那麼怎麼辦?

在構造函數上,普通類型

<bean id="userService" class="bb.UserService" >
<constructor-arg >
<list>
//普通類型
<value></value>
</list>
</constructor-arg>
</bean>
在屬性上,引用類型

<property name="userDao">

<list>
<ref></ref>
</list>
</property>
註解方式
自從jdk5有了註解這個新特性,咱們能夠看到Struts2框架、Hibernate框架都支持使用註解來配置信息...

經過註解來配置信息就是爲了簡化IOC容器的配置,註解能夠把對象添加到IOC容器中、處理對象依賴關係,咱們來看看怎麼用吧:

使用註解步驟:

1)先引入context名稱空間

xmlns:context="http://www.springframework.org/schema/context"
2)開啓註解掃描器

<context:component-scan base-package=""></context:component-scan>
第二種方法:也能夠經過自定義掃描類以@CompoentScan修飾來掃描IOC容器的bean對象。。以下代碼:

//代表該類是配置類
@Configuration

//啓動掃描器,掃描bb包下的
//也能夠指定多個基礎包
//也能夠指定類型
@ComponentScan("bb")
public class AnnotationScan {

}
在使用@ComponentScan()這個註解的時候,在測試類上須要@ContextConfiguration這個註解來加載配置類...

@ContextConfiguration這個註解又在Spring的test包下..
建立對象以及處理對象依賴關係,相關的註解:

@ComponentScan掃描器
@Configuration代表該類是配置類
@Component 指定把一個對象加入IOC容器--->@Name也能夠實現相同的效果【通常少用】
@Repository 做用同@Component; 在持久層使用
@Service 做用同@Component; 在業務邏輯層使用
@Controller 做用同@Component; 在控制層使用
@Resource 依賴關係

若是@Resource不指定值,那麼就根據類型來找,相同的類型在IOC容器中不能有兩個
若是@Resource指定了值,那麼就根據名字來找
測試代碼:

UserDao
package aa;

import org.springframework.stereotype.Repository;

/**
* Created by ozc on 2017/5/10.
*/

//把對象添加到容器中,首字母會小寫
@Repository
public class UserDao {

public void save() {
System.out.println("DB:保存用戶");
}


}
userService

package aa;


import org.springframework.stereotype.Service;

import javax.annotation.Resource;


//把UserService對象添加到IOC容器中,首字母會小寫
@Service
public class UserService {

//若是@Resource不指定值,那麼就根據類型來找--->UserDao....固然了,IOC容器不能有兩個UserDao類型的對象
//@Resource

//若是指定了值,那麼Spring就在IOC容器找有沒有id爲userDao的對象。
@Resource(name = "userDao")
private UserDao userDao;

public void save() {
userDao.save();
}
}
userAction

package aa;

import org.springframework.stereotype.Controller;

import javax.annotation.Resource;

/**
* Created by ozc on 2017/5/10.
*/

//把對象添加到IOC容器中,首字母會小寫
@Controller
public class UserAction {

@Resource(name = "userService")
private UserService userService;

public String execute() {
userService.save();
return null;
}
}
測試

package aa;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

/**
* Created by ozc on 2017/5/10.
*/
public class App {

public static void main(String[] args) {

// 建立容器對象
ApplicationContext ac = new ClassPathXmlApplicationContext("aa/applicationContext.xml");

UserAction userAction = (UserAction) ac.getBean("userAction");

userAction.execute();
}
}
這裏寫圖片描述

經過Java方式
因爲Spring的自動裝配並不能將第三方庫組件裝配到應用中,因而須要顯式裝配配置。顯示裝配有兩種方式

經過java代碼裝配bean
經過XML裝配bean
Spring In Action做者首推使用自動裝配的功能,然後是經過java代碼配置bean,最後才用XML文件配置的方式..

那麼怎麼經過java代碼來配置Bean呢??

編寫一個java類,使用@Configuration修飾該類
被@Configuration修飾的類就是配置類
編寫配置類:


@org.springframework.context.annotation.Configuration
public class Configuration {

}
使用配置類建立bean:

使用@Bean來修飾方法,該方法返回一個對象。
無論方法體內的對象是怎麼建立的,Spring能夠獲取獲得對象就好了。
Spring內部會將該對象加入到Spring容器中
容器中bean的ID默認爲方法名

 

@org.springframework.context.annotation.Configuration
public class Configuration {

@Bean
public UserDao userDao() {

UserDao userDao = new UserDao();
System.out.println("我是在configuration中的"+userDao);
return userDao;
}

}
測試代碼:要使用@ContextConfiguration加載配置類的信息【引入test包】

package bb;

import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.test.context.ContextConfiguration;

/**
* Created by ozc on 2017/5/11.
*/
//加載配置類的信息
@ContextConfiguration(classes = Configuration.class)
public class Test2 {

@Test
public void test33() {

ApplicationContext ac =
new ClassPathXmlApplicationContext("bb/bean.xml");

UserDao userDao = (UserDao) ac.getBean("userDao");

System.out.println(userDao);
}
}
這裏寫圖片描述

三種方式混合使用
註解和XML配置是能夠混合使用的,JavaConfig和XML也是能夠混合使用的...

若是JavaConfig的配置類是分散的,咱們通常再建立一個更高級的配置類(root),而後使用@Import來將配置類進行組合
若是XML的配置文件是分散的,咱們也是建立一個更高級的配置文件(root),而後使用<import>來將配置文件組合

在JavaConfig引用XML

使用@ImportResource()
在XML引用JavaConfig

使用<bean>節點就好了
bean對象建立細節
在Spring第一篇中,咱們爲何要引入Spring提出了這麼一些問題:

這裏寫圖片描述

既然咱們如今已經初步瞭解IOC容器了,那麼這些問題咱們都是能夠解決的。而且是十分簡單【對象寫死問題已經解決了,IOC容器就是控制反轉建立對象】

scope屬性
指定scope屬性,IOC容器就知道建立對象的時候是單例仍是多例的了。

屬性的值就只有兩個:單例/多例

這裏寫圖片描述

當咱們使用singleton【單例】的時候,從IOC容器獲取的對象都是同一個:
這裏寫圖片描述

當咱們使用prototype【多例】的時候,從IOC容器獲取的對象都是不一樣的:
這裏寫圖片描述

scope屬性除了控制對象是單例仍是多例的,還控制着對象建立的時間!

咱們在User的構造函數中打印出一句話,就知道User對象是何時建立了。
public User() {

System.out.println("我是User,我被建立了");
}
當使用singleton的時候,對象在IOC容器以前就已經建立了

這裏寫圖片描述
當使用prototype的時候,對象在使用的時候才建立

這裏寫圖片描述
lazy-init屬性
lazy-init屬性只對singleton【單例】的對象有效.....lazy-init默認爲false....

有的時候,可能咱們想要對象在使用的時候才建立,那麼將lazy-init設置爲ture就好了

這裏寫圖片描述

init-method和destroy-method
若是咱們想要對象在建立後,執行某個方法,咱們指定爲init-method屬性就好了。。

若是咱們想要IOC容器銷燬後,執行某個方法,咱們指定destroy-method屬性就好了。


<bean id="user" class="User" scope="singleton" lazy-init="true" init-method="" destroy-method=""/>
Bean建立細節總結

/**
* 1) 對象建立: 單例/多例
* scope="singleton", 默認值, 即 默認是單例 【service/dao/工具類】
* scope="prototype", 多例; 【Action對象】
*
* 2) 何時建立?
* scope="prototype" 在用到對象的時候,才建立對象。
* scope="singleton" 在啓動(容器初始化以前), 就已經建立了bean,且整個應用只有一個。
* 3)是否延遲建立
* lazy-init="false" 默認爲false, 不延遲建立,即在啓動時候就建立對象
* lazy-init="true" 延遲初始化, 在用到對象的時候才建立對象
* (只對單例有效)
* 4) 建立對象以後,初始化/銷燬
* init-method="init_user" 【對應對象的init_user方法,在對象建立以後執行 】
* destroy-method="destroy_user" 【在調用容器對象的destroy方法時候執行,(容器用實現類)】
*/
此文轉載於知乎博主,原文連接以下:https://segmentfault.com/a/1190000013700859

相關文章
相關標籤/搜索