這篇文章中我們將會介紹Spring的框架以及本體內容,包括核心容器,注解開發,AOP以及事務等內容
那么簡單說明一下Spring的必要性:
- Spring技術是JAVAEE開發的必備技能,企業開發技術選型率高達90%!
- Spring可以幫助簡化開發,降低企業級開發的復雜度
- Spring可以進行框架整合,高效整合其他技術,提高企業級應用開發與運行效率
Spring的核心內容:
- IoC技術
- DI技術
- AOP
- 事務處理
Spring可進行的框架整合:
- MaBatis
- MyBatis-plus
- Struts
- Struts2
- Hibernate
在接下來的文章中,我們會學習Spring的框架思想,學習Spring的基本操作,結合案例熟練掌握
溫馨提醒:在學習本篇文章前請先學習JavaWeb相關內容 (HTTP,Tomcat,Servlet,Request,Response,MVC,Cookie,Session,Ajax,Vue等內容)初識Spring
官網:Spring | Home
Spring發展至今已經形成了一套開發的生態圈,Spring提供了相當多的項目,每個項目用于完成特定功能
我們常用的主流技術包括有:
- Spring Framework:Spring框架
- Spring Boot:Spring簡化代碼開發
- Spring Cloud:Spring分布設計
在系統學習Spring之前,我們需要先來了解FrameWork系統結構
- Spring FrameWork是Spring生態圈中最基本的項目,是其他項目的根基
我們現在所使用的Spring FrameWork是4.0版本,已經趨于穩定
下面我們對架構圖進行解釋:
- Core Container:核心容器
- AOP:面向切面編程
- Aspects:AOP思想實現
- Data Access:數據訪問
- Data Intergration:數據集成
- Web:Web開發
- Test:單元測試與集成測試
我們可以在官方中獲得如此評價:
- 強大的基于 JavaBeans 的采用控制反轉(Inversion of Control,IoC)原則的配置管理,使得應用程序的組建更加快捷簡易。
- 數據庫事務的一般化抽象層,允許插件式事務管理器,簡化事務的劃分使之與底層無關。
- 一個可用于從 Applet 到 Java EE 等不同運行環境的核心 Bean 工廠。
首先我們思索一下我們之前的業務層與數據層:
// 數據層接口 public interface BookDao { public void save(); }
// 數據層實現 public class BookDaoImpl implements BookDao { public void save() { System.out.println("book dao save ..."); } }
// 業務層接口 public interface BookService { public void save(); }
// 業務層實現 public class BookServiceImpl implements BookService { private BookDao bookDao; public void save() { bookDao.save(); } }
如果我們修改BookDaoImpl內容,那么相對應的業務層實現中的bookDao的new實現也要進行修改,甚至下方方法的對象也要進行修改
Spring使用前問題
代碼書寫現狀:
- 耦合度偏高
解放方案:
- 使用對象時,在程序中不要主動使用new產生對象,轉換為由外部提供對象
IoC(Inversion of Control)控制反轉思想:
- 使用對象時,由主動new創建對象轉換為由外部提供對象
- 此過程中對象創建控制權由程序轉移到外部,被稱為控制反轉
DI(Dependency Injection)依賴注入:
- 在容器中建立Bean與Bean之間的依賴關系和整個過程,被稱為依賴注入
Spring技術對Ioc思想進行了實現:
- Spring提供了一個容器,被稱為Ioc容器,用來充當IoC思想的外部
- IoC容器負責對象的創建,初始化等一系列工作,被創建和管理的對象在IoC容器中被稱為Bean
// 數據層實現 public class BookDaoImpl implements BookDao { public void save() { System.out.println("book dao save ..."); } }
// IoC容器 /* 包含 dao service 兩者可以建立連接 */
// 業務層實現 public class BookServiceImpl implements BookService { private BookDao bookDao; public void save() { bookDao.save(); } }
目的:充分解耦
- IoC:使用IoC容器管理bean
- DI:在IoC容器內將有依賴關系的bean進行關系綁定
最終效果:
- 使用對象不僅可以直接從IoC容器中獲取,還可以將已獲得的Bean之間綁定依賴關系
首先我們需要明白IoC的使用規則:
- IoC負責管理什么:Service和Dao
- 如何被管理的對象告知IoC容器:(配置)
- 被管理的對象交給IoC容器,如何獲得IoC容器:(接口)
- IoC容器得到之后,如何獲得Bean:(接口方法)
- 使用Spring所需要導入的坐標:(pom.xml)
下面我們給出IoC入門的詳細步驟:
- 創建Maven項目,在pom.xml中導入坐標
org.springframework spring-context 5.2.10.RELEASE
- 創建Spring.xml的配置包(applicationContext.xml,導入坐標后xml中更新該XML)
- 主函數
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(根據bean配置id獲取) //BookDao bookDao = (BookDao) ctx.getBean("bookDao"); //bookDao.save(); // 注意:需要類型轉化 BookService bookService = (BookService) ctx.getBean("bookService"); bookService.save(); } }
DI入門
首先我們需要明白DI的使用規則:
- 基于IoC管理bean
- Service中使用new形式創建Dao對象是否保留:(否)
- Service中需要Dao對象如何進入到Service中:(提供方法)
- Service與Dao之間的關系如何描述:(配置)
下面我們給出DI入門的詳細步驟(基于IoC入門):
- 刪除new方法
public class BookServiceImpl implements BookService { //5.刪除業務層中使用new的方式創建的dao對象 private BookDao bookDao; public void save() { System.out.println("book service save ..."); bookDao.save(); } }
- 創建對象的set方法
public class BookServiceImpl implements BookService { //5.刪除業務層中使用new的方式創建的dao對象 private BookDao bookDao; public void save() { System.out.println("book service save ..."); bookDao.save(); } //6.提供對應的set方法 public void setBookDao(BookDao bookDao) { this.bookDao = bookDao; } }
- 創建Dao和Service的連接
Bean是保存在IoC中的對象,我們通過配置的方式獲得Bean
下面我們從三個方面分別講解Bean:
bean基本配置
首先我們先介紹bean本身性質:
類別
描述
名稱
bean
類型
所屬
beans標簽
功能
定義Spring核心容器管理對象
格式
屬性列表
id:bean的id,使用容器可以通過id值獲得對應的bean,在一個容器中id值唯一
class:bean的類型,即配置的bean的全路徑類名
范例
然后我們介紹一下bean的別名:
類別
描述
名稱
name
類型
所屬
bean標簽
功能
定義bean的別名,可定義多個,使用逗號,分號,空格分隔
范例
正常情況下,使用id和name都可以獲得bean,但推薦還是使用唯一id 獲得bean無論通過id還是name獲取,如果無法找到則拋出異常NosuchBeanDefinitionException
最后我們介紹一下bean的作用范圍scope:
類別
描述
名稱
scope
類型
所屬
bean標簽
功能
定義bean的作用范圍,可選范圍如下:
singleton:單列(默認)
prototype:非單列
范例
這里的scope指產生對象的數量 我們的scope在默認情況下是singleton,因為很多對象只需要創建一次,多次創建會導致內存膨脹 合適交給容器進行管理的bean(singleton): 表現層對象業務層對象數據層對象工具對象 不合適交給容器進行管理的bean(prototype): 封裝實體的域對象(帶有狀態的bean)bean實例化
bean的實例化通常分為四種方法,我們在下面一一介紹:
- 構造方法(常用)
我們需要在數據類中提供構造方法,配置條件中不需要改變
// 數據類 public class BookDaoImpl implements BookDao { public BookDaoImpl() { System.out.println("book dao constructor is running ...."); } public void save() { System.out.println("book dao save ..."); } }
若無參構造方法不存在,則拋出異常BeanCreationException
- 靜態工廠(了解)
我們在之前的案例中存在有對象工廠的說法,我們可以設置工廠并調用其方法得到bean
// 靜態工廠 package com.itheima.factory; import com.itheima.dao.OrderDao; import com.itheima.dao.impl.OrderDaoImpl; //靜態工廠創建對象 public class OrderDaoFactory { public static OrderDao getOrderDao(){ System.out.println("factory setup...."); return new OrderDaoImpl(); } }
- 實例工廠(了解)
和靜態工廠相同,但不同點是方法不是靜態,我們需要提前創建一個bean
// 實例工廠 package com.itheima.factory; import com.itheima.dao.UserDao; import com.itheima.dao.impl.UserDaoImpl; //實例工廠創建對象 public class UserDaoFactory { public UserDao getUserDao(){ return new UserDaoImpl(); } }
- FactoryBean(重要實用)
除了我們之前自己定義的工廠外,Spring提供了一種官方版本的FactoryBean
// FactoryBean工廠(需接口,< >中填寫數據類接口) package com.itheima.factory; import com.itheima.dao.UserDao; import com.itheima.dao.impl.UserDaoImpl; import org.springframework.beans.factory.FactoryBean; //FactoryBean創建對象 public class UserDaoFactoryBean implements FactoryBean { //代替原始實例工廠中創建對象的方法 // 返回創建對象類型為UserDaoImpl() public UserDao getObject() throws Exception { return new UserDaoImpl(); } // 這里填寫接口類型 public Class getObjectType() { return UserDao.class; } // 可以修改來修改其scope屬性 public boolean isSingleton() { return false; } }
bean生命周期
我們先來接單介紹生命周期相關概念:
- 生命周期:從創建到消亡的完整過程
- bean生命周期:bean從創建到銷毀的整體過程
- bean生命周期控制:在bean創建后到銷毀前做一些事情
接下來我們介紹生命周期控制方法:
- 數據層提供控制方法
由數據層提供方法,在xml配置文件中設置該方法
// 數據層 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初始化對應的操作 public void init(){ System.out.println("init..."); } //表示bean銷毀前對應的操作 public void destory(){ System.out.println("destory..."); } }
- 接口控制方法(了解)
Spring為創建提供了兩個接口,我們只需要繼承并實現該方法即可
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 分別對應afterPropertiesSet,destroy方法,代表創建和銷毀 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的銷毀時機:(了解即可)
- 因為默認情況下,我們的bean不會被銷毀,因為虛擬機會直接退出,ClassPathXmlApplicationContext會被忽略銷毀過程
所以如果我們希望銷毀bean觀察到destroy的實現,需要手動關閉:
- 手動關閉容器方法:
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作為對象,因為只有這個類才具有close方法 ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml"); BookDao bookDao = (BookDao) ctx.getBean("bookDao"); bookDao.save(); //關閉容器 ctx.close(); } }
- 注冊關閉鉤子,在虛擬機退出前先關閉容器再推出虛擬機
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(); //注冊關閉鉤子函數,在虛擬機退出之前回調此函數,關閉容器 ctx.registerShutdownHook(); } }
最后我們統計一下整體生命周期: 初始化容器:創建對象(分配內存)->執行構造方法->執行屬性注入(set操作)->執行bean初始化方法使用bean:執行業務操作關閉/銷毀容器:執行bean銷毀方法依賴注入方式
首先我們要知道類中傳遞數據的方法有兩種:
- 普通方法(Set方法)
- 構造方法
然后我們要知道數據的類型大體分為兩種:
- 引入類型(數據層)
- 簡單類型(基本數據類型和String)
所以我們把依賴注入方式分為四種:
- setter注入簡單類型引用類型
- 構造器注入簡單類型引入類型
首先我們需要在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標簽value屬性注入簡單類型數據
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標簽ref屬性注入引用類型數據
構造器注入簡單類型(了解)
在bean中定義簡單類型屬性并提供可訪問的set方法
public class BookDaoImpl implements BookDao{ private int connectionNumber; pubilc void setConnectionNumber(int connectionNumber){ this.connectionNumber = connectionNumber; } }
配置中使用constructor-arg標簽value屬性注入簡單類型數據
根據構造方法參數名稱注入
構造器注入引用類型(了解)
在bean中定義引用類型屬性并提供可訪問的構造方法
public class BookDaoImpl implements BookDao{ private BookBao bookBao; pubilc void setConnectionNumber(int connectionNumber){ this.bookBao = bookBao; } }
配置中使用constructor-arg標簽ref屬性注入簡單類型數據
構造器注入參數配置問題(了解)
在前面我們已經介紹了構造器的注入方法
但如果我們在bean中的數據名稱發生改變,配置就不再適配,所以提供了一些方法來解決參數配置問題:
- 配置中使用constructor-arg標簽type屬性設置按形參類型注入
根據構造方法參數類型注入
- 配置中使用constructor-arg標簽index屬性設置按形參類型注入
依賴注入方式有以下選擇標準:
- 強制依賴使用構造器進行,使用setter注入有概率不進行注入導致null對象出現
- 可選依賴使用setter注入進行,靈活性高
- Spring框架倡導使用構造器,第三方框架內部大多數采用構造器注入的形式進行數據初始化,相對嚴謹
- 如果有必要可以兩者并用,使用構造器注入完成強制依賴的注入,使用setter注入完成可選依賴的注入
- 實際開發中根據情況分析,如果受控對象沒有提供setter方法則只能采用構造器注入
- 自己開發的模塊盡量推薦setter注入
在前面我們學習了手動注入的方法,但Spring其實為我們提供了一種依賴自動裝配的語法:
- IoC容器根據bean所依賴的資源在容器中自動查找并注入bean中的過程稱為自動裝配
自動裝配方式:
- 按類型(常用)
- 按名稱
- 按構造方法
- 不啟用
自動裝配語法:
依賴自動裝配特征: 自動裝配用于引用類型注入,不能對簡單類型進行操作使用按類型裝配時(byType)必須保障容器中相同類型的bean唯一,推薦使用使用按名稱裝配時(byName)必須保障容器中具有指定名稱的bean,因變量名與配置耦合,不推薦使用自動裝配優先級低于setter注入和構造器注入,同時出現時,自動裝配配置失效依賴集合注入
除了基本類型和引入類型外,我們有時也需要注入集合
下面我們簡單介紹一下結合的注入:
// 數據類 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("遍歷數組:" + 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
案例:數據源對象管理
針對一個新的數據源對象,我們采用兩步來創建bean(我們以druid為案例):
- 導入druid坐標
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
- 配置數據源對象作為Spring管理的bean
這個案例我們將會介紹如何加載properties文件,并將文件帶入到property基本信息中
我們大致將步驟分為以下三步:
- 開辟context命名空間:
- 使用context命名空間,加載指定properties文件
- 使用${}讀取加載的屬性值
除了上述的基本操作,我們在context命名空間的使用中有很多需要注意的點:
- 不加載系統屬性
- 加載多個properties文件
- 加載所有properties文件
- 加載properties文件標準格式
- 從類路徑或jar包中搜索并加載properties文件
前面已經完成bean與依賴注入的相關知識學習,接下來我們主要學習的是IOC容器中的核心容器。
這里所說的核心容器,大家可以把它簡單的理解為ApplicationContext,接下來我們從以下幾個問題入手來學習下容器的相關知識:
- 如何創建容器?
- 創建好容器后,如何從容器中獲取bean對象?
- 容器類的層次結構是什么?
- BeanFactory是什么?
案例中創建ApplicationContext的方式為(類路徑下的XML配置文件):
ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
除了上面這種方式,Spring還提供了另外一種創建方式為(文件的絕對路徑):
ApplicationContext ctx = new FileSystemXmlApplicationContext("D:\workspace\spring\spring_10_container\src\main\resources\applicationContext.xml");
Bean的三種獲取方式
方式一,就是目前案例中獲取的方式:
BookDao bookDao = (BookDao) ctx.getBean("bookDao");
這種方式存在的問題是每次獲取的時候都需要進行類型轉換
方式二:
BookDao bookDao = ctx.getBean("bookDao",BookDao.class);
這種方式可以解決類型強轉問題,但是參數又多加了一個,相對來說沒有簡化多少。
方式三:
BookDao bookDao = ctx.getBean(BookDao.class);
這種方式就類似我們之前所學習依賴注入中的按類型注入。必須要確保IOC容器中該類型對應的bean對象只能有一個。
容器類層次結構
下面我們給出容器的層次圖
BeanFactory的使用
使用BeanFactory來創建IOC容器的具體實現方式為:
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之間的區別,在BookDaoImpl添加如下構造函數:
public class BookDaoImpl implements BookDao { public BookDaoImpl() { System.out.println("constructor"); } public void save() { System.out.println("book dao save ..." ); } }
如果不去獲取bean對象,打印會發現:
- BeanFactory是延遲加載,只有在獲取bean對象的時候才會去創建
- ApplicationContext是立即加載,容器加載的時候就會創建bean對象
- ApplicationContext要想成為延遲加載,只需要按照如下方式進行配置
接下來我們對前面知識的一個總結,共包含如下內容:
容器相關
- BeanFactory是IoC容器的頂層接口,初始化BeanFactory對象時,加載的bean延遲加載
- ApplicationContext接口是Spring容器的核心接口,初始化時bean立即加載
- ApplicationContext接口提供基礎的bean操作相關方法,通過其他接口擴展其功能
- ApplicationContext接口常用初始化類ClassPathXmlApplicationContext(常用)FileSystemXmlApplicationContext
依賴注入相關
注解開發
在上述的開發中,我們采用xml配置文件的形式來說依舊顯得有些復雜
這時我們就需要發揮Spring的優點:簡化開發,通過注解來簡化開發過程
下面我們會通過多個方面將Bean逐步轉化為注解
注解開發Bean
在前面的內容中,我們的bean在xml配置文件中裝配
在后期,我們的bean可以采用注解的形式,直接在實現類中注解表示為bean
我們采用@Component定義bean,可以添加參數表示id,也可以不添加參數,后期我們采用class類的類型來進行匹配
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延伸出了三種類型,在實現手法上是一致,但可以具體使用于各種類中(僅用于自我識別)
- @Controller:用于表現層bean定義
- @Service:用于業務層bean定義
- @Repository:用于數據層定義
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文件是無法探測到的,所以我們需要配置相關掃描組件來掃描bean
純注解開發
我們前面所提到的注解開發屬于2.5的附屬版本
在Spring3.0版本,Spring就提供了純注解開發模式,利用java類代替配置文件,開啟了Spring快速開發時代
在之前我們的xml配置文件是很繁瑣的:
但是我們可以通過創建單獨的類來表示配置文件:
- @Configuration:用于聲明當前類為Spring配置類
- @ComponentScan:用于掃描類文件(類似于)
package com.itheima.config; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; //聲明當前類為Spring配置類 @Configuration //設置bean掃描路徑,多個路徑書寫為字符串數組格式 @ComponentScan({"com.itheima.service","com.itheima.dao"}) public class SpringConfig { }
注意:因為該類屬于配置類,我們通常單列一個文件夾來表示 常用文件夾:config 命名規范:SpringConfig,UserConfig...
因為我們的開發不再依靠于xml配置文件,所以在主函數中的Spring容器獲得方式也發生了改變:
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); } }
注解開發Bean作用范圍與管理
既然我們的Bean開發從xml轉移到注解開發,那么一些配置設置同樣發生改變
首先我們介紹Scope范圍的設置方式:
- @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設置bean的作用范圍(singleton或prototype),可以不添加默認singleton @Scope("singleton") public class BookDaoImpl implements BookDao { public void save() { System.out.println("book dao save ..."); } }
然后我們介紹一下bean生命周期的init和destroy操作:
- @PostConstruct:定義init操作,表示構造后操作
- @PreDestroy:定義destroy操作,表示銷毀前操作
在Spring3.0中,省略掉了前面繁瑣的依賴注入,我們的bean依賴注入只留下了自動裝配這一操作:
- 使用@Autowired注解開啟自動裝配模式(按類型)
- 當存在相同類型時,我們采用@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:注入引用類型,自動裝配模式,默認按類型裝配 @Autowired //@Qualifier:自動裝配bean時按bean名稱裝配 @Qualifier("bookDao") private BookDao bookDao; public void save() { System.out.println("book service save ..."); bookDao.save(); } }
注意:自動裝配基于反射設計創建對象并暴力反射對應屬性為私有屬性初始化數據,因此無需提供setter方法 注意:自動轉配建議使用無參構造方法創建對象(默認),如果不提供對應構造方法,請提供唯一的構造方法 注意:@Qualifier是基于@Autowired實現的,必須保證先有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的形式配置,是因為我們的類型值不一定是由手動輸入的,有可能來自于Properties資源:
- 首先我們需要在Springconfig中配置相關資源
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 { }
- 然后我們在數據層調用時,采用${}來匹配數據
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); } }
注解開發第三方bean
我們在實際開發中不僅僅需要對自己的bean進行管理,有時候可能需要引進其他的bean
下面我們以Druid為例進行講解:
- 首先在pom.xml中導入Druid坐標
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文件夾下 // 在后續我們將會講解如何進行連接 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,表示當前方法的返回值是一個bean // @Bean修飾的方法,形參根據類型自動裝配 @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; } }
- 將獨立的配置類加入核心配置(導入法)
// 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:導入配置信息(如果需要多個,同樣采用{}數組形式) @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修飾的方法,形參根據類型自動裝配 @Bean public DataSource dataSource(){ DruidDataSource ds = new DruidDataSource(); // 配置信息 return ds; } }
注意:除了上述的導入法外還存在有其他方法,但導入法屬于主流,因此我們不介紹其他流派,感興趣的同學可以去查閱一下注解開發為第三方導入資源
我們的第三方bean也可能需要導入部分資源,下面我們進行簡單介紹:
- 簡單類型依賴注入
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,表示當前方法的返回值是一個bean //@Bean修飾的方法,形參根據類型自動裝配 @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){ // 我們只需要調用即可,系統會為我們自動裝配 System.out.println(bookDao); } }
引入類型注入只需要為bean定義方法設置形參即可,容器會根據類型自動裝配對象
原文鏈接:https://www.cnblogs.com/qiuluoyuweiliang/p/16750470.html