Spring之 IOC&依賴注入
0x01、Spring
1什麼是Spring
Spring 是一個開源框架,是為了解決企業應用程式開發複雜性而創建的(解耦)。
框架的主要優勢之一就是其分層架構,分層架構允許您選擇使用哪一個組件,同時為 J2EE 應用程式開發提供集成的框架。
簡單來說,Spring是一個分層的JavaSE/EE full-stack(一站式) 輕量級開源框架。
一站式:Spring提供了三層解決方案.
0x02、IOC
它的核心思想就是:
1、通過Bean工廠讀取配置文件使用反射創建對象。
2、把創建出來的對象都存起來,當使用者需要對象的時候,不再自己創建對象,而是調用Bean工廠的方法從容器中獲取對象
這裡面要解釋兩個問題:
第一個:存哪去?
分析:由於我們是很多對象,肯定要找個集合來存。這時候有 Map 和 List 供選擇。
到底選 Map 還是 List 就看我們有沒有查找需求。有查找需求,選 Map。
所以我們的答案就是:在應用載入時,創建一個 Map,用於存放三層對象。我們把這個 map 稱之為容器。
第二個: 什麼是工廠?
工廠就是負責給我們從容器中獲取指定對象的類。這時候我們獲取對象的方式發生了改變。
原來:我們在獲取對象時,都是採用 new 的方式。 是主動的。
1、Spring的IOC入門案例
步驟:
1. 創建Maven工程, 添加坐標
2. 準備好介面和實現類
3. 創建spring的配置文件 (applicationContext.xml), 配置bean標籤
4. 創建工廠對象 獲得bean 調用
- 1.1、引入spring的依賴(使用5.0.2版本)
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.0.2.RELEASE</version>
</dependency>
-
1.2、介面和實現類
- UserServiceImpl.java
public interface UserService { String getName(); }
-
UserServiceImpl.java
```java
import com.itheima.service.UserService;
public class UserServiceImpl implements UserService{
public void init(){
System.out.println(“UserServiceImpl對象創建了…”);
}
public void destroy(){
System.out.println("UserServiceImpl對象銷毀了...");
}
@Override
public String getName() {
return "周傑棍";
}
}
“`
- 1.3、在類的根路徑下創建spring的配置文件
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="//www.springframework.org/schema/beans"
xmlns:xsi="//www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="//www.springframework.org/schema/beans
//www.springframework.org/schema/beans/spring-beans.xsd">
<!--
每一個實現類就對應一個bean標籤
id屬性: 對象的唯一標識,根據這個唯一標識,就可以從核心容器中獲取對象
class屬性: 對象所屬的實現類的全限定名
-->
<bean class="org.example.Impl.UserServiceImpl" id="userService"></bean>
</beans>
- 1.4、測試程式碼
public class AppTest
{
@Test
public void test01(){
//調用UserServiceImpl類的方法
//1. 創建spring的核心容器(載入類路徑下的xml配置文件的核心容器)
//在創建核心容器的時候,就已經讀取了整個spring.xml配置文件,就已經創建好了它裡面的bean標籤對應的對象
//並且對象都放到核心容器中了
ApplicationContext act = new ClassPathXmlApplicationContext("classpath:springConfig.xml");
//ApplicationContext act = new FileSystemXmlApplicationContext("d:/a/spring.xml");
//2. 調用核心容器的方法,根據id獲取對象
UserService userService = (UserService) act.getBean("userService");
System.out.println(userService.getName());
}
}
2、Spring配置文件下的Bean標籤 配置
- 配置文件詳解(Bean標籤)
- bean的作用範圍和生命周期
(一)、配置文件詳解(Bean標籤)
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="//www.springframework.org/schema/beans"
xmlns:xsi="//www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="//www.springframework.org/schema/beans
//www.springframework.org/schema/beans/spring-beans.xsd">
<!--
每一個實現類就對應一個bean標籤
id屬性: 對象的唯一標識,根據這個唯一標識,就可以從核心容器中獲取對象
class屬性: 對象所屬的實現類的全限定名
scope屬性: 對象的範圍
1. singleton 單例(默認)
2. prototype 多例
lazy-init: 配置懶載入,核心容器創建的時候是否創建出該類對象
init-method: 配置類的對象初始化的時候,要調用哪個方法
destroy-method: 配置這個類的對象銷毀的時候,要調用哪個方法
單例模式下(默認沒有開啟懶載入),由核心容器進行管理的對象什麼時候創建什麼時候銷毀?
1. 核心容器創建的時候,會創建出它所配置的所有類的對象
2. 核心容器銷毀的時候,它裡面的對象才會被銷毀
多例模式下,由spring管理的對象什麼時候創建什麼時候銷毀
1. 當核心容器調用getBean(id)的時候,創建對象
2. 垃圾回收機制才能銷毀這個對象
-->
<bean class="com.itheima.service.impl.UserServiceImpl"
id="userService"
scope="prototype" lazy-init="false"
init-method="init"
destroy-method="destroy"></bean>
</beans>
-
id或者name屬性
用於標識bean , 其實id 和 name都必須具備唯一標識 ,兩種用哪一種都可以。但是一定要唯一、 一般開發中使用id來聲明.
-
class屬性: 用來配置要實現化類的全限定名
-
scope屬性: 用來描述bean的作用範圍
singleton: 默認值,單例模式。spring創建bean對象時會以單例方式創建。(默認)
prototype: 多例模式。spring創建bean對象時會以多例模式創建。
request: 針對Web應用。spring創建對象時,會將此對象存儲到request作用域。(不用管)
session: 針對Web應用。spring創建對象時,會將此對象存儲到session作用域。(不用管)
-
init-method屬性:spring為bean初始化提供的回調方法
-
destroy-method屬性:spring為bean銷毀時提供的回調方法. 銷毀方法針對的都是單例bean , 如果想銷毀bean , 可以關閉工廠
(二)、bean的作用範圍和生命周期
- 單例對象: scope=”singleton”,一個應用只有一個對象的實例。它的作用範圍就是整個引用。
- 核心容器創建的時候,會創建出它所配置的所有類的對象
- 核心容器銷毀的時候,它裡面的對象才會被銷毀
- 多例對象: scope=”prototype”,每次訪問對象時,都會重新創建對象實例。
- 當核心容器調用getBean(id)的時候,創建對象
- 垃圾回收機制才能銷毀這個對象
3、Spring的工廠模式
ApplicationContext介面的三種實現類
1、ClassPathXmlApplicationContext:它是從類的根路徑下載入配置文件
2、FileSystemXmlApplicationContext:它是從磁碟路徑上載入配置文件,配置文件可以在磁碟的任意位置。
3、AnnotationConfigApplicationContext:當我們使用註解配置容器對象時,需要使用此類來創建 spring 容器。它用來讀取註解。
繼承自 ApplicationContext
ApplicationContext
載入方式是框架啟動時就開始創建所有單例的bean,存到了容器裡面
- 非懶載入: 在核心容器創建的時候,創建出所有的bean對象,存到核心容器中
- 懶載入: 第一次調用getBean()的時候,創建出bean對象,存到核心容器中
4、實例化Bean
需要實例化的類,提供無參構造方法
配置程式碼
<bean class="com.itheima.service.impl.UserServiceImpl" id="userService"></bean>
0x03、依賴注入
依賴注入全稱是 dependency Injection 翻譯過來是依賴注入.其實就是如果我們託管的某一個類中存在屬性,需要spring在創建該類實例的時候,順便給這個對象裡面的屬性進行賦值。 這就是依賴注入。
現在, Bean的創建交給Spring了, 需要在xml裡面進行註冊
我們交給Spring創建的Bean裡面可能有一些屬性(欄位), Spring幫我創建的同時也把Bean的一些屬性(欄位)給賦值, 這個賦值就是注入.
簡單的來說如下:
- 註冊: 把bean的創建交給Spring
- 依賴注入: bean創建的同時, bean裡面可能有一些欄位需要賦值, 這個賦值交給Spring, 這個過程就是依賴注入
測試程式碼(環境):
- Controller.UserController
import com.idea.service.UserService;
public class UserController{
private UserService userService;
public String getName(){
return userService.getName();
}
}
- service.UserService
public interface UserService {
String getName();
}
- service.Impl.UserServiceImpl
import com.idea.dao.UserDao;
import com.idea.service.UserService;
public class UserServiceImpl implements UserService {
private UserDao userDao;
@Override
public String getName() {
return userDao.getName();
}
}
- dao.UserDao:
public interface UserDao {
String getName();
}
- dao.Impl.UserDaoImpl
import com.idea.dao.UserDao;
public class UserDaoImpl implements UserDao {
//模擬資料庫執行語句,並得到名字為王五
@Override
public String getName(){
return "王五";
}
}
然後我們創建springConfig.xml的配置文件,我們需要把實現類放進來
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="//www.springframework.org/schema/beans"
xmlns:xsi="//www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="//www.springframework.org/schema/beans
//www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="UserController" class="com.idea.controller.UserController"></bean>
<bean id="UserService" class="com.idea.service.Impl.UserServiceImpl"></bean>
<bean id="UserDao" class="com.idea.dao.Impl.UserDaoImpl"></bean>
</beans>
- 測試程式碼:(最外層的Controller不用解耦)
import com.idea.controller.UserController;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class demo {
@Test
public void test01(){
//創建容器
ApplicationContext act = new ClassPathXmlApplicationContext("classpath:springConfig.xml");
//1. 創建UserController對象
UserController userController = (UserController) act.getBean("UserController");
//2. 調用UserController對象的getName()方法
String name = userController.getName();
System.out.println("獲取到的name為:" + name);
}
}
1、構造方法方式注入
通過上面的環境搭建,我們執行一下
發現調用失敗了;UserService
和UserDao
還沒有賦值。但是我們不能直接在裡面賦值,這樣就耦合了,這時候就用到了依賴注入
1、構造方法賦值
有參構造方法賦值的時候,就必須要有無參構造方法,不然會報錯
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="//www.springframework.org/schema/beans"
xmlns:xsi="//www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="//www.springframework.org/schema/beans
//www.springframework.org/schema/beans/spring-beans.xsd">
<!--
它裡面的UserService屬性要進行賦值
使用有參構造進行屬性的注入,使用<constructor-arg>標籤
-->
<bean id="UserController" class="com.idea.controller.UserController">
<constructor-arg name="userService" ref="UserService"></constructor-arg>
</bean>
<!--
它裡面的UserDao屬性需要賦值
-->
<bean id="UserService" class="com.idea.service.Impl.UserServiceImpl"></bean>
<bean id="UserDao" class="com.idea.dao.Impl.UserDaoImpl"></bean>
</beans>
-
1、bean是實例化出對象,id就是對象名,
ref="UserService"
就是獲取UserService對象,要傳進去的內容 -
2、
name="userService"
是屬性名,也就是要傳的參數
完整程式碼
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="//www.springframework.org/schema/beans"
xmlns:xsi="//www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="//www.springframework.org/schema/beans
//www.springframework.org/schema/beans/spring-beans.xsd">
<!--
它裡面的UserService屬性要進行賦值
使用有參構造進行屬性的注入,使用<constructor-arg>標籤
-->
<bean id="UserController" class="com.idea.controller.UserController">
<constructor-arg name="userService" ref="UserService"></constructor-arg>
</bean>
<!--
它裡面的UserDao屬性需要賦值
-->
<bean id="UserService" class="com.idea.service.Impl.UserServiceImpl">
<constructor-arg name="userDao" ref="UserDao"></constructor-arg>
</bean>
<bean id="UserDao" class="com.idea.dao.Impl.UserDaoImpl"></bean>
</beans>
2、set方法方式的注入
要通過set方法注入,那就得有setter
- springConfig.xml
name:就是setter設置的參數名
ref: 對象UserService賦值給name標籤的userService
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="//www.springframework.org/schema/beans"
xmlns:xsi="//www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="//www.springframework.org/schema/beans
//www.springframework.org/schema/beans/spring-beans.xsd">
<!--
它裡面的UserService屬性要進行賦值
使用set方法進行屬性的注入
-->
<bean id="UserController" class="com.idea.controller.UserController">
<property name="userService" ref="UserService"></property>
</bean>
<!--
它裡面的UserDao屬性需要賦值
-->
<bean id="UserService" class="com.idea.service.Impl.UserServiceImpl">
<property name="userDao" ref="UserDao"></property>
</bean>
<bean id="UserDao" class="com.idea.dao.Impl.UserDaoImpl"></bean>
</beans>
- UserController
import com.idea.service.UserService;
public class UserController{
private UserService userService;
public void setUserService(UserService userService) {
this.userService = userService;
}
public String getName(){
return userService.getName();
}
}
- UserServiceImpl
import com.idea.dao.UserDao;
import com.idea.service.UserService;
public class UserServiceImpl implements UserService {
private UserDao userDao;
public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}
@Override
public String getName() {
return userDao.getName();
}
}
0x04、番外&總結
今天內容有IOC,和依賴注入
1、IOC是什麼?
IOC就是spring.xml中bean標籤:讓我們不用去new對象,讓spring實例化對象,進行解耦