這篇文章中我們將會介紹Spring的框架以及本體內(nèi)容,包括核心容器,注解開發(fā),AOP以及事務(wù)等內(nèi)容
那么簡單說明一下Spring的必要性:
- Spring技術(shù)是JAVAEE開發(fā)的必備技能,企業(yè)開發(fā)技術(shù)選型率高達(dá)90%!
- Spring可以幫助簡化開發(fā),降低企業(yè)級開發(fā)的復(fù)雜度
- Spring可以進(jìn)行框架整合,高效整合其他技術(shù),提高企業(yè)級應(yīng)用開發(fā)與運(yùn)行效率
Spring的核心內(nèi)容:
- IoC技術(shù)
- DI技術(shù)
- AOP
- 事務(wù)處理
Spring可進(jìn)行的框架整合:
- MaBatis
- MyBatis-plus
- Struts
- Struts2
- Hibernate
在接下來的文章中,我們會學(xué)習(xí)Spring的框架思想,學(xué)習(xí)Spring的基本操作,結(jié)合案例熟練掌握
溫馨提醒:在學(xué)習(xí)本篇文章前請先學(xué)習(xí)JavaWeb相關(guān)內(nèi)容 (HTTP,Tomcat,Servlet,Request,Response,MVC,Cookie,Session,Ajax,Vue等內(nèi)容)初識Spring
官網(wǎng):Spring | Home
Spring發(fā)展至今已經(jīng)形成了一套開發(fā)的生態(tài)圈,Spring提供了相當(dāng)多的項(xiàng)目,每個項(xiàng)目用于完成特定功能
我們常用的主流技術(shù)包括有:
- Spring Framework:Spring框架
- Spring Boot:Spring簡化代碼開發(fā)
- Spring Cloud:Spring分布設(shè)計
在系統(tǒng)學(xué)習(xí)Spring之前,我們需要先來了解FrameWork系統(tǒng)結(jié)構(gòu)
- Spring FrameWork是Spring生態(tài)圈中最基本的項(xiàng)目,是其他項(xiàng)目的根基
我們現(xiàn)在所使用的Spring FrameWork是4.0版本,已經(jīng)趨于穩(wěn)定
下面我們對架構(gòu)圖進(jìn)行解釋:
- Core Container:核心容器
- AOP:面向切面編程
- Aspects:AOP思想實(shí)現(xiàn)
- Data Access:數(shù)據(jù)訪問
- Data Intergration:數(shù)據(jù)集成
- Web:Web開發(fā)
- Test:單元測試與集成測試
我們可以在官方中獲得如此評價:
- 強(qiáng)大的基于 JavaBeans 的采用控制反轉(zhuǎn)(Inversion of Control,IoC)原則的配置管理,使得應(yīng)用程序的組建更加快捷簡易。
- 數(shù)據(jù)庫事務(wù)的一般化抽象層,允許插件式事務(wù)管理器,簡化事務(wù)的劃分使之與底層無關(guān)。
- 一個可用于從 Applet 到 Java EE 等不同運(yùn)行環(huán)境的核心 Bean 工廠。
首先我們思索一下我們之前的業(yè)務(wù)層與數(shù)據(jù)層:
// 數(shù)據(jù)層接口 public interface BookDao { public void save(); }
// 數(shù)據(jù)層實(shí)現(xiàn) public class BookDaoImpl implements BookDao { public void save() { System.out.println("book dao save ..."); } }
// 業(yè)務(wù)層接口 public interface BookService { public void save(); }
// 業(yè)務(wù)層實(shí)現(xiàn) public class BookServiceImpl implements BookService { private BookDao bookDao; public void save() { bookDao.save(); } }
如果我們修改BookDaoImpl內(nèi)容,那么相對應(yīng)的業(yè)務(wù)層實(shí)現(xiàn)中的bookDao的new實(shí)現(xiàn)也要進(jìn)行修改,甚至下方方法的對象也要進(jìn)行修改
Spring使用前問題
代碼書寫現(xiàn)狀:
- 耦合度偏高
解放方案:
- 使用對象時,在程序中不要主動使用new產(chǎn)生對象,轉(zhuǎn)換為由外部提供對象
IoC(Inversion of Control)控制反轉(zhuǎn)思想:
- 使用對象時,由主動new創(chuàng)建對象轉(zhuǎn)換為由外部提供對象
- 此過程中對象創(chuàng)建控制權(quán)由程序轉(zhuǎn)移到外部,被稱為控制反轉(zhuǎn)
DI(Dependency Injection)依賴注入:
- 在容器中建立Bean與Bean之間的依賴關(guān)系和整個過程,被稱為依賴注入
Spring技術(shù)對Ioc思想進(jìn)行了實(shí)現(xiàn):
- Spring提供了一個容器,被稱為Ioc容器,用來充當(dāng)IoC思想的外部
- IoC容器負(fù)責(zé)對象的創(chuàng)建,初始化等一系列工作,被創(chuàng)建和管理的對象在IoC容器中被稱為Bean
// 數(shù)據(jù)層實(shí)現(xiàn) public class BookDaoImpl implements BookDao { public void save() { System.out.println("book dao save ..."); } }
// IoC容器 /* 包含 dao service 兩者可以建立連接 */
// 業(yè)務(wù)層實(shí)現(xiàn) public class BookServiceImpl implements BookService { private BookDao bookDao; public void save() { bookDao.save(); } }
目的:充分解耦
- IoC:使用IoC容器管理bean
- DI:在IoC容器內(nèi)將有依賴關(guān)系的bean進(jìn)行關(guān)系綁定
最終效果:
- 使用對象不僅可以直接從IoC容器中獲取,還可以將已獲得的Bean之間綁定依賴關(guān)系
首先我們需要明白IoC的使用規(guī)則:
- IoC負(fù)責(zé)管理什么:Service和Dao
- 如何被管理的對象告知IoC容器:(配置)
- 被管理的對象交給IoC容器,如何獲得IoC容器:(接口)
- IoC容器得到之后,如何獲得Bean:(接口方法)
- 使用Spring所需要導(dǎo)入的坐標(biāo):(pom.xml)
下面我們給出IoC入門的詳細(xì)步驟:
- 創(chuàng)建Maven項(xiàng)目,在pom.xml中導(dǎo)入坐標(biāo)
org.springframework spring-context 5.2.10.RELEASE
- 創(chuàng)建Spring.xml的配置包(applicationContext.xml,導(dǎo)入坐標(biāo)后xml中更新該XML)
- 主函數(shù)
package com.itheima; import com.itheima.dao.BookDao; import com.itheima.service.BookService; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; public class App2 { public static void main(String[] args) { //3.獲取IoC容器 ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml"); //4.獲取bean(根據(jù)bean配置id獲取) //BookDao bookDao = (BookDao) ctx.getBean("bookDao"); //bookDao.save(); // 注意:需要類型轉(zhuǎn)化 BookService bookService = (BookService) ctx.getBean("bookService"); bookService.save(); } }
DI入門
首先我們需要明白DI的使用規(guī)則:
- 基于IoC管理bean
- Service中使用new形式創(chuàng)建Dao對象是否保留:(否)
- Service中需要Dao對象如何進(jìn)入到Service中:(提供方法)
- Service與Dao之間的關(guān)系如何描述:(配置)
下面我們給出DI入門的詳細(xì)步驟(基于IoC入門):
- 刪除new方法
public class BookServiceImpl implements BookService { //5.刪除業(yè)務(wù)層中使用new的方式創(chuàng)建的dao對象 private BookDao bookDao; public void save() { System.out.println("book service save ..."); bookDao.save(); } }
- 創(chuàng)建對象的set方法
public class BookServiceImpl implements BookService { //5.刪除業(yè)務(wù)層中使用new的方式創(chuàng)建的dao對象 private BookDao bookDao; public void save() { System.out.println("book service save ..."); bookDao.save(); } //6.提供對應(yīng)的set方法 public void setBookDao(BookDao bookDao) { this.bookDao = bookDao; } }
- 創(chuàng)建Dao和Service的連接
Bean是保存在IoC中的對象,我們通過配置的方式獲得Bean
下面我們從三個方面分別講解Bean:
bean基本配置
首先我們先介紹bean本身性質(zhì):
類別
描述
名稱
bean
類型
所屬
beans標(biāo)簽
功能
定義Spring核心容器管理對象
格式
屬性列表
id:bean的id,使用容器可以通過id值獲得對應(yīng)的bean,在一個容器中id值唯一
class:bean的類型,即配置的bean的全路徑類名
范例
然后我們介紹一下bean的別名:
類別
描述
名稱
name
類型
所屬
bean標(biāo)簽
功能
定義bean的別名,可定義多個,使用逗號,分號,空格分隔
范例
正常情況下,使用id和name都可以獲得bean,但推薦還是使用唯一id 獲得bean無論通過id還是name獲取,如果無法找到則拋出異常NosuchBeanDefinitionException
最后我們介紹一下bean的作用范圍scope:
類別
描述
名稱
scope
類型
所屬
bean標(biāo)簽
功能
定義bean的作用范圍,可選范圍如下:
singleton:單列(默認(rèn))
prototype:非單列
范例
這里的scope指產(chǎn)生對象的數(shù)量 我們的scope在默認(rèn)情況下是singleton,因?yàn)楹芏鄬ο笾恍枰獎?chuàng)建一次,多次創(chuàng)建會導(dǎo)致內(nèi)存膨脹 合適交給容器進(jìn)行管理的bean(singleton): 表現(xiàn)層對象業(yè)務(wù)層對象數(shù)據(jù)層對象工具對象 不合適交給容器進(jìn)行管理的bean(prototype): 封裝實(shí)體的域?qū)ο螅◣в袪顟B(tài)的bean)bean實(shí)例化
bean的實(shí)例化通常分為四種方法,我們在下面一一介紹:
- 構(gòu)造方法(常用)
我們需要在數(shù)據(jù)類中提供構(gòu)造方法,配置條件中不需要改變
// 數(shù)據(jù)類 public class BookDaoImpl implements BookDao { public BookDaoImpl() { System.out.println("book dao constructor is running ...."); } public void save() { System.out.println("book dao save ..."); } }
若無參構(gòu)造方法不存在,則拋出異常BeanCreationException
- 靜態(tài)工廠(了解)
我們在之前的案例中存在有對象工廠的說法,我們可以設(shè)置工廠并調(diào)用其方法得到bean
// 靜態(tài)工廠 package com.itheima.factory; import com.itheima.dao.OrderDao; import com.itheima.dao.impl.OrderDaoImpl; //靜態(tài)工廠創(chuàng)建對象 public class OrderDaoFactory { public static OrderDao getOrderDao(){ System.out.println("factory setup...."); return new OrderDaoImpl(); } }
- 實(shí)例工廠(了解)
和靜態(tài)工廠相同,但不同點(diǎn)是方法不是靜態(tài),我們需要提前創(chuàng)建一個bean
// 實(shí)例工廠 package com.itheima.factory; import com.itheima.dao.UserDao; import com.itheima.dao.impl.UserDaoImpl; //實(shí)例工廠創(chuàng)建對象 public class UserDaoFactory { public UserDao getUserDao(){ return new UserDaoImpl(); } }
- FactoryBean(重要實(shí)用)
除了我們之前自己定義的工廠外,Spring提供了一種官方版本的FactoryBean
// FactoryBean工廠(需接口,< >中填寫數(shù)據(jù)類接口) package com.itheima.factory; import com.itheima.dao.UserDao; import com.itheima.dao.impl.UserDaoImpl; import org.springframework.beans.factory.FactoryBean; //FactoryBean創(chuàng)建對象 public class UserDaoFactoryBean implements FactoryBean { //代替原始實(shí)例工廠中創(chuàng)建對象的方法 // 返回創(chuàng)建對象類型為UserDaoImpl() public UserDao getObject() throws Exception { return new UserDaoImpl(); } // 這里填寫接口類型 public Class getObjectType() { return UserDao.class; } // 可以修改來修改其scope屬性 public boolean isSingleton() { return false; } }
bean生命周期
我們先來接單介紹生命周期相關(guān)概念:
- 生命周期:從創(chuàng)建到消亡的完整過程
- bean生命周期:bean從創(chuàng)建到銷毀的整體過程
- bean生命周期控制:在bean創(chuàng)建后到銷毀前做一些事情
接下來我們介紹生命周期控制方法:
- 數(shù)據(jù)層提供控制方法
由數(shù)據(jù)層提供方法,在xml配置文件中設(shè)置該方法
// 數(shù)據(jù)層 package com.itheima.dao.impl; import com.itheima.dao.BookDao; public class BookDaoImpl implements BookDao { public void save() { System.out.println("book dao save ..."); } //表示bean初始化對應(yīng)的操作 public void init(){ System.out.println("init..."); } //表示bean銷毀前對應(yīng)的操作 public void destory(){ System.out.println("destory..."); } }
- 接口控制方法(了解)
Spring為創(chuàng)建提供了兩個接口,我們只需要繼承并實(shí)現(xiàn)該方法即可
package com.itheima.service.impl; import com.itheima.dao.BookDao; import com.itheima.service.BookService; import org.springframework.beans.factory.DisposableBean; import org.springframework.beans.factory.InitializingBean; // InitializingBean,DisposableBean 分別對應(yīng)afterPropertiesSet,destroy方法,代表創(chuàng)建和銷毀 public class BookServiceImpl implements BookService, InitializingBean, DisposableBean { private BookDao bookDao; public void setBookDao(BookDao bookDao) { System.out.println("set ....."); this.bookDao = bookDao; } public void save() { System.out.println("book service save ..."); bookDao.save(); } public void destroy() throws Exception { System.out.println("service destroy"); } public void afterPropertiesSet() throws Exception { System.out.println("service init"); } }
我們需要提及一下bean的銷毀時機(jī):(了解即可)
- 因?yàn)槟J(rèn)情況下,我們的bean不會被銷毀,因?yàn)樘摂M機(jī)會直接退出,ClassPathXmlApplicationContext會被忽略銷毀過程
所以如果我們希望銷毀bean觀察到destroy的實(shí)現(xiàn),需要手動關(guān)閉:
- 手動關(guān)閉容器方法:
package com.itheima; import com.itheima.dao.BookDao; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; public class AppForLifeCycle { public static void main( String[] args ) { // 注意:這里需要采用ClassPathXmlApplicationContext作為對象,因?yàn)橹挥羞@個類才具有close方法 ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml"); BookDao bookDao = (BookDao) ctx.getBean("bookDao"); bookDao.save(); //關(guān)閉容器 ctx.close(); } }
- 注冊關(guān)閉鉤子,在虛擬機(jī)退出前先關(guān)閉容器再推出虛擬機(jī)
package com.itheima; import com.itheima.dao.BookDao; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; public class AppForLifeCycle { public static void main( String[] args ) { ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml"); BookDao bookDao = (BookDao) ctx.getBean("bookDao"); bookDao.save(); //注冊關(guān)閉鉤子函數(shù),在虛擬機(jī)退出之前回調(diào)此函數(shù),關(guān)閉容器 ctx.registerShutdownHook(); } }
最后我們統(tǒng)計一下整體生命周期: 初始化容器:創(chuàng)建對象(分配內(nèi)存)->執(zhí)行構(gòu)造方法->執(zhí)行屬性注入(set操作)->執(zhí)行bean初始化方法使用bean:執(zhí)行業(yè)務(wù)操作關(guān)閉/銷毀容器:執(zhí)行bean銷毀方法依賴注入方式
首先我們要知道類中傳遞數(shù)據(jù)的方法有兩種:
- 普通方法(Set方法)
- 構(gòu)造方法
然后我們要知道數(shù)據(jù)的類型大體分為兩種:
- 引入類型(數(shù)據(jù)層)
- 簡單類型(基本數(shù)據(jù)類型和String)
所以我們把依賴注入方式分為四種:
- setter注入簡單類型引用類型
- 構(gòu)造器注入簡單類型引入類型
首先我們需要在bean種定義簡單類型屬性并提供可以訪問的set方法
package com.itheima.dao.impl; import com.itheima.dao.BookDao; public class BookDaoImpl implements BookDao { private String databaseName; private int connectionNum; //setter注入需要提供要注入對象的set方法 public void setConnectionNum(int connectionNum) { this.connectionNum = connectionNum; } //setter注入需要提供要注入對象的set方法 public void setDatabaseName(String databaseName) { this.databaseName = databaseName; } public void save() { System.out.println("book dao save ..."+databaseName+","+connectionNum); } }
然后在配置中使用property標(biāo)簽value屬性注入簡單類型數(shù)據(jù)
setter注入引用類型
首先我們需要在bean種定義引用類型屬性并提供可以訪問的set方法
package com.itheima.service.impl; import com.itheima.dao.BookDao; import com.itheima.dao.UserDao; import com.itheima.service.BookService; public class BookServiceImpl implements BookService{ private BookDao bookDao; private UserDao userDao; //setter注入需要提供要注入對象的set方法 public void setUserDao(UserDao userDao) { this.userDao = userDao; } //setter注入需要提供要注入對象的set方法 public void setBookDao(BookDao bookDao) { this.bookDao = bookDao; } public void save() { System.out.println("book service save ..."); bookDao.save(); userDao.save(); } }
然后在配置中使用property標(biāo)簽ref屬性注入引用類型數(shù)據(jù)
構(gòu)造器注入簡單類型(了解)
在bean中定義簡單類型屬性并提供可訪問的set方法
public class BookDaoImpl implements BookDao{ private int connectionNumber; pubilc void setConnectionNumber(int connectionNumber){ this.connectionNumber = connectionNumber; } }
配置中使用constructor-arg標(biāo)簽value屬性注入簡單類型數(shù)據(jù)
根據(jù)構(gòu)造方法參數(shù)名稱注入
構(gòu)造器注入引用類型(了解)
在bean中定義引用類型屬性并提供可訪問的構(gòu)造方法
public class BookDaoImpl implements BookDao{ private BookBao bookBao; pubilc void setConnectionNumber(int connectionNumber){ this.bookBao = bookBao; } }
配置中使用constructor-arg標(biāo)簽ref屬性注入簡單類型數(shù)據(jù)
構(gòu)造器注入?yún)?shù)配置問題(了解)
在前面我們已經(jīng)介紹了構(gòu)造器的注入方法
但如果我們在bean中的數(shù)據(jù)名稱發(fā)生改變,配置就不再適配,所以提供了一些方法來解決參數(shù)配置問題:
- 配置中使用constructor-arg標(biāo)簽type屬性設(shè)置按形參類型注入
根據(jù)構(gòu)造方法參數(shù)類型注入
- 配置中使用constructor-arg標(biāo)簽index屬性設(shè)置按形參類型注入
依賴注入方式有以下選擇標(biāo)準(zhǔn):
- 強(qiáng)制依賴使用構(gòu)造器進(jìn)行,使用setter注入有概率不進(jìn)行注入導(dǎo)致null對象出現(xiàn)
- 可選依賴使用setter注入進(jìn)行,靈活性高
- Spring框架倡導(dǎo)使用構(gòu)造器,第三方框架內(nèi)部大多數(shù)采用構(gòu)造器注入的形式進(jìn)行數(shù)據(jù)初始化,相對嚴(yán)謹(jǐn)
- 如果有必要可以兩者并用,使用構(gòu)造器注入完成強(qiáng)制依賴的注入,使用setter注入完成可選依賴的注入
- 實(shí)際開發(fā)中根據(jù)情況分析,如果受控對象沒有提供setter方法則只能采用構(gòu)造器注入
- 自己開發(fā)的模塊盡量推薦setter注入
在前面我們學(xué)習(xí)了手動注入的方法,但Spring其實(shí)為我們提供了一種依賴自動裝配的語法:
- IoC容器根據(jù)bean所依賴的資源在容器中自動查找并注入bean中的過程稱為自動裝配
自動裝配方式:
- 按類型(常用)
- 按名稱
- 按構(gòu)造方法
- 不啟用
自動裝配語法:
依賴自動裝配特征: 自動裝配用于引用類型注入,不能對簡單類型進(jìn)行操作使用按類型裝配時(byType)必須保障容器中相同類型的bean唯一,推薦使用使用按名稱裝配時(byName)必須保障容器中具有指定名稱的bean,因變量名與配置耦合,不推薦使用自動裝配優(yōu)先級低于setter注入和構(gòu)造器注入,同時出現(xiàn)時,自動裝配配置失效依賴集合注入
除了基本類型和引入類型外,我們有時也需要注入集合
下面我們簡單介紹一下結(jié)合的注入:
// 數(shù)據(jù)類 package com.itheima.dao.impl; import com.itheima.dao.BookDao; import java.util.*; public class BookDaoImpl implements BookDao { private int[] array; private List list; private Set set; private Map map; private Properties properties; public void setArray(int[] array) { this.array = array; } public void setList(List list) { this.list = list; } public void setSet(Set set) { this.set = set; } public void setMap(Map map) { this.map = map; } public void setProperties(Properties properties) { this.properties = properties; } public void save() { System.out.println("book dao save ..."); System.out.println("遍歷數(shù)組:" + Arrays.toString(array)); System.out.println("遍歷List" + list); System.out.println("遍歷Set" + set); System.out.println("遍歷Map" + map); System.out.println("遍歷Properties" + properties); } }
100 200 300 itcast itheima boxuegu chuanzhihui itcast itheima boxuegu boxuegu china henan kaifeng
案例:數(shù)據(jù)源對象管理
針對一個新的數(shù)據(jù)源對象,我們采用兩步來創(chuàng)建bean(我們以druid為案例):
- 導(dǎo)入druid坐標(biāo)
4.0.0 com.itheima spring_09_datasource 1.0-SNAPSHOT org.springframework spring-context 5.2.10.RELEASE com.alibaba druid 1.1.16 MySQL mysql-connector-java 5.1.47
- 配置數(shù)據(jù)源對象作為Spring管理的bean
這個案例我們將會介紹如何加載properties文件,并將文件帶入到property基本信息中
我們大致將步驟分為以下三步:
- 開辟context命名空間:
- 使用context命名空間,加載指定properties文件
- 使用${}讀取加載的屬性值
除了上述的基本操作,我們在context命名空間的使用中有很多需要注意的點(diǎn):
- 不加載系統(tǒng)屬性
- 加載多個properties文件
- 加載所有properties文件
- 加載properties文件標(biāo)準(zhǔn)格式
- 從類路徑或jar包中搜索并加載properties文件
前面已經(jīng)完成bean與依賴注入的相關(guān)知識學(xué)習(xí),接下來我們主要學(xué)習(xí)的是IOC容器中的核心容器。
這里所說的核心容器,大家可以把它簡單的理解為ApplicationContext,接下來我們從以下幾個問題入手來學(xué)習(xí)下容器的相關(guān)知識:
- 如何創(chuàng)建容器?
- 創(chuàng)建好容器后,如何從容器中獲取bean對象?
- 容器類的層次結(jié)構(gòu)是什么?
- BeanFactory是什么?
案例中創(chuàng)建ApplicationContext的方式為(類路徑下的XML配置文件):
ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
除了上面這種方式,Spring還提供了另外一種創(chuàng)建方式為(文件的絕對路徑):
ApplicationContext ctx = new FileSystemXmlApplicationContext("D:\workspace\spring\spring_10_container\src\main\resources\applicationContext.xml");
Bean的三種獲取方式
方式一,就是目前案例中獲取的方式:
BookDao bookDao = (BookDao) ctx.getBean("bookDao");
這種方式存在的問題是每次獲取的時候都需要進(jìn)行類型轉(zhuǎn)換
方式二:
BookDao bookDao = ctx.getBean("bookDao",BookDao.class);
這種方式可以解決類型強(qiáng)轉(zhuǎn)問題,但是參數(shù)又多加了一個,相對來說沒有簡化多少。
方式三:
BookDao bookDao = ctx.getBean(BookDao.class);
這種方式就類似我們之前所學(xué)習(xí)依賴注入中的按類型注入。必須要確保IOC容器中該類型對應(yīng)的bean對象只能有一個。
容器類層次結(jié)構(gòu)
下面我們給出容器的層次圖
BeanFactory的使用
使用BeanFactory來創(chuàng)建IOC容器的具體實(shí)現(xiàn)方式為:
public class AppForBeanFactory { public static void main(String[] args) { Resource resources = new ClassPathResource("applicationContext.xml"); BeanFactory bf = new XmlBeanFactory(resources); BookDao bookDao = bf.getBean(BookDao.class); bookDao.save(); } }
為了更好的看出BeanFactory和ApplicationContext之間的區(qū)別,在BookDaoImpl添加如下構(gòu)造函數(shù):
public class BookDaoImpl implements BookDao { public BookDaoImpl() { System.out.println("constructor"); } public void save() { System.out.println("book dao save ..." ); } }
如果不去獲取bean對象,打印會發(fā)現(xiàn):
- BeanFactory是延遲加載,只有在獲取bean對象的時候才會去創(chuàng)建
- ApplicationContext是立即加載,容器加載的時候就會創(chuàng)建bean對象
- ApplicationContext要想成為延遲加載,只需要按照如下方式進(jìn)行配置
接下來我們對前面知識的一個總結(jié),共包含如下內(nèi)容:
容器相關(guān)
- BeanFactory是IoC容器的頂層接口,初始化BeanFactory對象時,加載的bean延遲加載
- ApplicationContext接口是Spring容器的核心接口,初始化時bean立即加載
- ApplicationContext接口提供基礎(chǔ)的bean操作相關(guān)方法,通過其他接口擴(kuò)展其功能
- ApplicationContext接口常用初始化類ClassPathXmlApplicationContext(常用)FileSystemXmlApplicationContext
依賴注入相關(guān)
注解開發(fā)
在上述的開發(fā)中,我們采用xml配置文件的形式來說依舊顯得有些復(fù)雜
這時我們就需要發(fā)揮Spring的優(yōu)點(diǎn):簡化開發(fā),通過注解來簡化開發(fā)過程
下面我們會通過多個方面將Bean逐步轉(zhuǎn)化為注解
注解開發(fā)Bean
在前面的內(nèi)容中,我們的bean在xml配置文件中裝配
在后期,我們的bean可以采用注解的形式,直接在實(shí)現(xiàn)類中注解表示為bean
我們采用@Component定義bean,可以添加參數(shù)表示id,也可以不添加參數(shù),后期我們采用class類的類型來進(jìn)行匹配
package com.itheima.dao.impl; import com.itheima.dao.BookDao; import org.springframework.stereotype.Component; import org.springframework.stereotype.Controller; import org.springframework.stereotype.Repository; //@Component定義bean @Component("bookDao") public class BookDaoImpl implements BookDao { public void save() { System.out.println("book dao save ..."); } }
package com.itheima.service.impl; import com.itheima.dao.BookDao; import com.itheima.service.BookService; import org.springframework.stereotype.Component; import org.springframework.stereotype.Service; //@Component定義bean @Component public class BookServiceImpl implements BookService { private BookDao bookDao; public void setBookDao(BookDao bookDao) { this.bookDao = bookDao; } public void save() { System.out.println("book service save ..."); bookDao.save(); } }
@Componenty延伸出了三種類型,在實(shí)現(xiàn)手法上是一致,但可以具體使用于各種類中(僅用于自我識別)
- @Controller:用于表現(xiàn)層bean定義
- @Service:用于業(yè)務(wù)層bean定義
- @Repository:用于數(shù)據(jù)層定義
package com.itheima.dao.impl; import com.itheima.dao.BookDao; import org.springframework.stereotype.Component; import org.springframework.stereotype.Controller; import org.springframework.stereotype.Repository; //@Component定義bean //@Component("bookDao") //@Repository:@Component衍生注解 @Repository("bookDao") public class BookDaoImpl implements BookDao { public void save() { System.out.println("book dao save ..."); } }
package com.itheima.service.impl; import com.itheima.dao.BookDao; import com.itheima.service.BookService; import org.springframework.stereotype.Component; import org.springframework.stereotype.Service; //@Component定義bean //@Component //@Service:@Component衍生注解 @Service public class BookServiceImpl implements BookService { private BookDao bookDao; public void setBookDao(BookDao bookDao) { this.bookDao = bookDao; } public void save() { System.out.println("book service save ..."); bookDao.save(); } }
但是,在上述情況下,即使我們將@Component的類定義為bean
我們的xml文件是無法探測到的,所以我們需要配置相關(guān)掃描組件來掃描bean
純注解開發(fā)
我們前面所提到的注解開發(fā)屬于2.5的附屬版本
在Spring3.0版本,Spring就提供了純注解開發(fā)模式,利用java類代替配置文件,開啟了Spring快速開發(fā)時代
在之前我們的xml配置文件是很繁瑣的:
但是我們可以通過創(chuàng)建單獨(dú)的類來表示配置文件:
- @Configuration:用于聲明當(dāng)前類為Spring配置類
- @ComponentScan:用于掃描類文件(類似于)
package com.itheima.config; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; //聲明當(dāng)前類為Spring配置類 @Configuration //設(shè)置bean掃描路徑,多個路徑書寫為字符串?dāng)?shù)組格式 @ComponentScan({"com.itheima.service","com.itheima.dao"}) public class SpringConfig { }
注意:因?yàn)樵擃悓儆谂渲妙悾覀兺ǔ瘟幸粋€文件夾來表示 常用文件夾:config 命名規(guī)范:SpringConfig,UserConfig...
因?yàn)槲覀兊拈_發(fā)不再依靠于xml配置文件,所以在主函數(shù)中的Spring容器獲得方式也發(fā)生了改變:
package com.itheima; import com.itheima.dao.BookDao; import com.itheima.service.BookService; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; public class App { public static void main(String[] args) { // 這是我們之前的獲取方式,采用路徑獲取xml文件 // ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml"); // 這是新的獲取方式,直接提供配置類的類型 // AnnotationConfigApplicationContext加載Spring配置類初始化Spring容器 ApplicationContext ctx = new AnnotationConfigApplicationContext(SpringConfig.class); // 后面操作無需變化 BookDao bookDao = (BookDao) ctx.getBean("bookDao"); System.out.println(bookDao); //按類型獲取bean BookService bookService = ctx.getBean(BookService.class); System.out.println(bookService); } }
注解開發(fā)Bean作用范圍與管理
既然我們的Bean開發(fā)從xml轉(zhuǎn)移到注解開發(fā),那么一些配置設(shè)置同樣發(fā)生改變
首先我們介紹Scope范圍的設(shè)置方式:
- @Scope:定義bean的作用范圍
package com.itheima.dao.impl; import com.itheima.dao.BookDao; import org.springframework.context.annotation.Scope; import org.springframework.stereotype.Repository; import javax.annotation.PostConstruct; import javax.annotation.PreDestroy; @Repository //@Scope設(shè)置bean的作用范圍(singleton或prototype),可以不添加默認(rèn)singleton @Scope("singleton") public class BookDaoImpl implements BookDao { public void save() { System.out.println("book dao save ..."); } }
然后我們介紹一下bean生命周期的init和destroy操作:
- @PostConstruct:定義init操作,表示構(gòu)造后操作
- @PreDestroy:定義destroy操作,表示銷毀前操作
在Spring3.0中,省略掉了前面繁瑣的依賴注入,我們的bean依賴注入只留下了自動裝配這一操作:
- 使用@Autowired注解開啟自動裝配模式(按類型)
- 當(dāng)存在相同類型時,我們采用@Qualifier開啟按名自動裝配
package com.itheima.service.impl; import com.itheima.dao.BookDao; import com.itheima.service.BookService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Service; @Service public class BookServiceImpl implements BookService { //@Autowired:注入引用類型,自動裝配模式,默認(rèn)按類型裝配 @Autowired //@Qualifier:自動裝配bean時按bean名稱裝配 @Qualifier("bookDao") private BookDao bookDao; public void save() { System.out.println("book service save ..."); bookDao.save(); } }
注意:自動裝配基于反射設(shè)計創(chuàng)建對象并暴力反射對應(yīng)屬性為私有屬性初始化數(shù)據(jù),因此無需提供setter方法 注意:自動轉(zhuǎn)配建議使用無參構(gòu)造方法創(chuàng)建對象(默認(rèn)),如果不提供對應(yīng)構(gòu)造方法,請?zhí)峁┪ㄒ坏臉?gòu)造方法 注意:@Qualifier是基于@Autowired實(shí)現(xiàn)的,必須保證先有Autowired才能存在Qualifier
除了上述的bean類型裝配,我們的簡單類型裝配依舊存在:
- 我們采用@Value的形式來配置簡單類型的值
package com.itheima.dao.impl; import com.itheima.dao.BookDao; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Repository; @Repository("bookDao") public class BookDaoImpl implements BookDao { //@Value:注入簡單類型(無需提供set方法) @Value("123") private String name; public void save() { System.out.println("book dao save ..." + name); } }
之所以使用@Value的形式配置,是因?yàn)槲覀兊念愋椭挡灰欢ㄊ怯墒謩虞斎氲模锌赡軄碜杂赑roperties資源:
- 首先我們需要在Springconfig中配置相關(guān)資源
package com.itheima.config; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.PropertySource; @Configuration @ComponentScan("com.itheima") //@PropertySource加載properties配置文件 @PropertySource({"jdbc.properties"}) public class SpringConfig { }
- 然后我們在數(shù)據(jù)層調(diào)用時,采用${}來匹配數(shù)據(jù)
package com.itheima.dao.impl; import com.itheima.dao.BookDao; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Repository; @Repository("bookDao") public class BookDaoImpl implements BookDao { //@Value:注入簡單類型(無需提供set方法) @Value("${name}") private String name; public void save() { System.out.println("book dao save ..." + name); } }
注解開發(fā)第三方bean
我們在實(shí)際開發(fā)中不僅僅需要對自己的bean進(jìn)行管理,有時候可能需要引進(jìn)其他的bean
下面我們以Druid為例進(jìn)行講解:
- 首先在pom.xml中導(dǎo)入Druid坐標(biāo)
4.0.0 com.itheima spring_14_annotation_third_bean_manager 1.0-SNAPSHOT org.springframework spring-context 5.2.10.RELEASE com.alibaba druid 1.1.16
- 使用@Bean配置第三方Bean
// 該bean同樣屬于config文件,我們同樣放置在config文件夾下 // 在后續(xù)我們將會講解如何進(jìn)行連接 package com.itheima.config; import com.alibaba.druid.pool.DruidDataSource; import com.itheima.dao.BookDao; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import javax.sql.DataSource; public class JdbcConfig { // 1.定義一個方法獲得要管理的對象 // 2.添加@Bean,表示當(dāng)前方法的返回值是一個bean // @Bean修飾的方法,形參根據(jù)類型自動裝配 @Bean public DataSource dataSource(){ DruidDataSource ds = new DruidDataSource(); ds.setDriverClassName("com.mysql.jdbc.Driver"); ds.setUrl("jdbc:mysql://localhost:3306/spring_db"); ds.setUsername("root"); ds.setPassword("123456"); return ds; } }
- 將獨(dú)立的配置類加入核心配置(導(dǎo)入法)
// SpringConfig package com.itheima.config; import com.alibaba.druid.pool.DruidDataSource; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Import; import javax.sql.DataSource; @Configuration @ComponentScan("com.itheima") //@Import:導(dǎo)入配置信息(如果需要多個,同樣采用{}數(shù)組形式) @Import({JdbcConfig.class}) public class SpringConfig { }
// JdbcConfig package com.itheima.config; import com.alibaba.druid.pool.DruidDataSource; import com.itheima.dao.BookDao; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import javax.sql.DataSource; //@Configuration public class JdbcConfig { //@Bean修飾的方法,形參根據(jù)類型自動裝配 @Bean public DataSource dataSource(){ DruidDataSource ds = new DruidDataSource(); // 配置信息 return ds; } }
注意:除了上述的導(dǎo)入法外還存在有其他方法,但導(dǎo)入法屬于主流,因此我們不介紹其他流派,感興趣的同學(xué)可以去查閱一下注解開發(fā)為第三方導(dǎo)入資源
我們的第三方bean也可能需要導(dǎo)入部分資源,下面我們進(jìn)行簡單介紹:
- 簡單類型依賴注入
package com.itheima.config; import com.alibaba.druid.pool.DruidDataSource; import com.itheima.dao.BookDao; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import javax.sql.DataSource; //@Configuration public class JdbcConfig { //1.定義一個方法獲得要管理的對象 @Value("com.mysql.jdbc.Driver") private String driver; @Value("jdbc:mysql://localhost:3306/spring_db") private String url; @Value("root") private String userName; @Value("root") private String password; //2.添加@Bean,表示當(dāng)前方法的返回值是一個bean //@Bean修飾的方法,形參根據(jù)類型自動裝配 @Bean public DataSource dataSource(){ DruidDataSource ds = new DruidDataSource(); ds.setDriverClassName(driver); ds.setUrl(url); ds.setUsername(userName); ds.setPassword(password); return ds; } }
- 依賴類型依賴注入
package com.itheima.config; import com.alibaba.druid.pool.DruidDataSource; import com.itheima.dao.BookDao; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import javax.sql.DataSource; public class JdbcConfig { @Bean public DataSource dataSource(BookDao bookDao){ // 我們只需要調(diào)用即可,系統(tǒng)會為我們自動裝配 System.out.println(bookDao); } }
引入類型注入只需要為bean定義方法設(shè)置形參即可,容器會根據(jù)類型自動裝配對象
原文鏈接:https://www.cnblogs.com/qiuluoyuweiliang/p/16750470.html