1. 程式人生 > 其它 >Spring的IoC和DI

Spring的IoC和DI

Spring的IoC和DI

Contents

Spring簡介
Spring快速入門
Spring配置檔案
Spring相關API

1.1 Spring是什麼

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

1.2 Spring發展歷程

1997 年,IBM提出了EJB 的思想
1998 年,SUN制定開發標準規範EJB1.
1999 年,EJB1.1 釋出
2001 年,EJB2.0 釋出
2003 年,EJB2.1 釋出
2006 年,EJB3.0 釋出
Rod Johnson (Spring 之父)
Expert One-to-One J2EE Design and Development(2002)
闡述了J2EE 使用EJB 開發設計的優點及解決方案
Expert One-to-One J2EE Development without EJB(2004)
闡述了J2EE 開發不使用EJB的解決方式(Spring 雛形)
2017 年 9 月份釋出了Spring 的最新版本Spring5.0 通用版(GA)

1.3 Spring的優勢

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

1.3 Spring的優勢

5 )方便整合各種優秀框架
Spring對各種優秀框架(Struts、Hibernate、Hessian、Quartz等)的支援。
6 )降低JavaEEAPI 的使用難度
Spring對JavaEEAPI(如JDBC、JavaMail、遠端呼叫等)進行了薄薄的封裝層,使這些API 的使用難度大為降低。
7 )Java 原始碼是經典學習範例
Spring的原始碼設計精妙、結構清晰、匠心獨用,處處體現著大師對Java 設計模式靈活運用以及對Java技術的高深
造詣。它的原始碼無意是Java 技術的最佳實踐的範例。

1.4 Spring的體系結構

2.1 Spring程式開發步驟

com.itheima.service.UserServiceImpl com.itheima.dao.UserDaoImpl
UserDaouserDao= new UserDaoImpl() save方法
update方法
... ... ...

2.1 Spring程式開發步驟

com.itheima.service.UserServiceImpl com.itheima.dao.UserDaoImpl
UserDaouserDao= new UserDaoImpl() save方法
update方法
... ... ...
id標識= com.itheima.dao.UserDaoImpl
Spring框架 xml配置檔案
讀取xml配置檔案
根據id標識獲得Bean全限定名
UserDaouserDao= Spring客戶端.getBean(id標識)
通過反射建立Bean物件
返回物件
① 匯入Spring 開發的基本包座標
② 編寫Dao 介面和實現類
③ 建立Spring 核心配置檔案
④ 在Spring 配置檔案中配置UserDaoImpl
⑤ 使用Spring 的API 獲得Bean 例項

2.1 Spring程式開發步驟

2.2 匯入Spring開發的基本包座標

<properties>
<spring.version>5.0.5.RELEASE</spring.version>
</properties>
<dependencies>
<!-- 匯入 spring 的 context 座標, context 依賴 core 、 beans 、 expression-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>${spring.version}</version>
</dependency>
</dependencies>

2.3 編寫Dao介面和實現類

public interface UserDao {
public void save();
}
public class UserDaoImpl implements UserDao {
@Override
public void save() {
System. out .println("UserDao save method running....");
}
}

2.4 建立Spring核心配置檔案

在類路徑下(resources)建立applicationContext.xml配置檔案
<? 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>

2.5 在Spring配置檔案中配置UserDaoImpl

<? 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">
<bean id="userDao" class="com.itheima.dao.impl.UserDaoImpl"></bean>
</beans>

2.6 使用Spring的API獲得Bean例項

@Test
public void test1(){
ApplicationContext applicationContext = new
ClassPathXmlApplicationContext("applicationContext.xml");
UserDao userDao = (UserDao) applicationContext.getBean("userDao");
userDao.save();
}
Spring的開發步驟

2.7 知識要點

① 匯入座標
② 建立Bean
③ 建立applicationContext.xml
④ 在配置檔案中進行配置
⑤ 建立ApplicationContext物件getBean

3.1 Bean標籤基本配置

用於配置物件交由 Spring 來建立。
預設情況下它呼叫的是類中的 無參建構函式 ,如果沒有無參建構函式則不能建立成功。
基本屬性:
 id :Bean例項在Spring容器中的唯一標識
 class :Bean的全限定名稱

3.2 Bean標籤範圍配置

scope:指物件的作用範圍,取值如下:
取值範圍 說明
singleton 預設值,單例的
prototype 多例的
request WEB 專案中,Spring 建立一個Bean 的物件,將物件存入到request 域中
session WEB 專案中,Spring 建立一個Bean 的物件,將物件存入到session 域中
global session WEB 於session專案中,應用在Portlet環境,如果沒有Portlet環境那麼globalSession 相當

3.2 Bean標籤範圍配置

1 )當scope的取值為singleton時
Bean的例項化個數: 1 個
Bean的例項化時機:當Spring核心檔案被載入時,例項化配置的Bean例項
Bean的生命週期:
 物件建立:當應用載入,建立容器時,物件就被建立了
 物件執行:只要容器在,物件一直活著
 物件銷燬:當應用解除安裝,銷燬容器時,物件就被銷燬了
2 )當scope的取值為prototype時
Bean的例項化個數:多個
Bean的例項化時機:當呼叫getBean()方法時例項化Bean
 物件建立:當使用物件時,建立新的物件例項
 物件執行:只要物件在使用中,就一直活著
 物件銷燬:當物件長時間不用時,被Java 的垃圾回收器回收了

3.3 Bean生命週期配置

 init-method :指定類中的初始化方法名稱
 destroy-method :指定類中銷燬方法名稱

3.4 Bean例項化三種方式

 無參 構造 方法例項化
 工廠 靜態 方法例項化
 工廠 例項 方法例項化

3.4 Bean例項化三種方式

1 )使用無參構造方法例項化
它會根據預設無參構造方法來建立類物件,如果bean中沒有預設無參建構函式,將會建立失敗
<bean id="userDao" class="com.itheima.dao.impl.UserDaoImpl"/>

3.4 Bean例項化三種方式

2 )工廠靜態方法例項化
工廠的靜態方法返回Bean例項
<bean id="userDao" class="com.itheima.factory.StaticFactoryBean"
factory-method="createUserDao" />
public class StaticFactoryBean {
public static UserDao createUserDao(){
return new UserDaoImpl();
}
}

3.4 Bean例項化三種方式

3 )工廠例項方法例項化
工廠的非靜態方法返回Bean例項
<bean id="factoryBean" class="com.itheima.factory.DynamicFactoryBean"/>
<bean id="userDao" factory-bean="factoryBean" factory-method="createUserDao"/>
public class DynamicFactoryBean {
public UserDao createUserDao(){
return new UserDaoImpl();
}
}

3.5 Bean的依賴注入入門

① 建立UserService,UserService 內部在呼叫UserDao的save() 方法
public class UserServiceImpl implements UserService {
@Override
public void save() {
ApplicationContext applicationContext = new
ClassPathXmlApplicationContext("applicationContext.xml");
UserDao userDao = (UserDao) applicationContext.getBean("userDao");
userDao.save();
}
}
② 將UserServiceImpl 的建立權交給Spring
<bean id="userService" class="com.itheima.service.impl.UserServiceImpl"/>
③ 從Spring 容器中獲得UserService 進行操作
ApplicationContext applicationContext = new
ClassPathXmlApplicationContext("applicationContext.xml");
UserService userService = (UserService) applicationContext.getBean("userService");
userService.save();

3.5 Bean的依賴注入入門

3.6 Bean的依賴注入分析

目前UserService例項和UserDao例項都存在與Spring容器中,當前的做法是在容器外部獲得UserService
例項和UserDao例項,然後在程式中進行結合。
Spring容器
UserService例項
程式程式碼
UserDao例項
getBean(name:"userService")
getBean(name:"userDao")
獲得UserService例項,發現UserSrvice例項內
部需要UserDao例項的save()方法,所以在
UserService內部獲得UserDao例項
相當於是在Spring容器外將Dao組裝到Service
內部完成的操作

3.6 Bean的依賴注入分析

因為UserService和UserDao都在Spring容器中,而最終程式直接使用的是UserService,所以可以在
Spring容器中,將UserDao設定到UserService內部。
Spring容器
UserService例項
程式程式碼
UserDao例項
getBean(name:"userService")
獲得UserService例項,內部已經存在UserDao
例項了,直接呼叫UserDao的save()方法即可

3.7 Bean的依賴注入概念

依賴注入( Dependency Injection ):它是Spring 框架核心IOC 的具體實現。
在編寫程式時,通過控制反轉,把物件的建立交給了Spring,但是程式碼中不可能出現沒有依賴的情況。
IOC 解耦只是降低他們的依賴關係,但不會消除。例如:業務層仍會呼叫持久層的方法。
那這種業務層和持久層的依賴關係,在使用Spring 之後,就讓Spring 來維護了。
簡單的說,就是坐等框架把持久層物件傳入業務層,而不用我們自己去獲取。
怎麼將UserDao怎樣注入到UserService內部呢?
 構造方法
 set方法
public class UserServiceImpl implements UserService {
private UserDao userDao;
public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}
@Override
public void save() {
userDao.save();
}
}
1 )set方法注入
在UserServiceImpl中新增setUserDao方法
<bean id="userDao" class="com.itheima.dao.impl.UserDaoImpl"/>
<bean id="userService" class="com.itheima.service.impl.UserServiceImpl">
<property name="userDao" ref="userDao"/>
</bean>
1 )set方法注入
配置Spring容器呼叫set方法進行注入
<bean id="userService" class="com.itheima.service.impl.UserServiceImpl" p:userDao-
ref="userDao"/>
xmlns:p="http://www.springframework.org/schema/p"
其次,需要修改注入方式
1 )set方法注入
P名稱空間注入本質也是set方法注入,但比起上述的set方法注入更加方便,主要體現在配置檔案中,如下:
首先,需要引入P名稱空間:
public class UserServiceImpl implements UserService {
@Override
public void save() {
ApplicationContext applicationContext = new
ClassPathXmlApplicationContext("applicationContext.xml");
UserDao userDao = (UserDao) applicationContext.getBean("userDao");
userDao.save();
}
}
2 )構造方法注入
建立有參構造
2 )構造方法注入
配置Spring容器呼叫有參構造時進行注入
<bean id="userDao" class="com.itheima.dao.impl.UserDaoImpl"/>
<bean id="userService" class="com.itheima.service.impl.UserServiceImpl">
<constructor-arg name="userDao" ref="userDao"></constructor-arg>
</bean>

3.8 Bean的依賴注入的資料型別

上面的操作,都是注入的引用Bean,處了物件的引用可以注入,普通資料型別,集合等都可以在容器中進行注入。
注入資料的三種資料型別
 普通資料型別
 引用資料型別
 集合資料型別
其中引用資料型別,此處就不再贅述了,之前的操作都是對UserDao物件的引用進行注入的,下面將以set方法注入為
例,演示普通資料型別和集合資料型別的注入。
1 )普通資料型別的注入
public class UserDaoImpl implements UserDao {
private String company;
private int age;
public void setCompany(String company) {
this.company = company;
}
public void setAge(int age) {
this.age = age;
}
public void save() {
System. out .println(company+"==="+age);
System. out .println("UserDao save method running....");
}
}
1 )普通資料型別的注入
<bean id="userDao" class="com.itheima.dao.impl.UserDaoImpl">
<property name="company" value="傳智播客"></property>
<property name="age" value="15"></property>
</bean>
2 )集合資料型別( List<String> )的注入
public class UserDaoImpl implements UserDao {
private List<String> strList;
public void setStrList(List<String> strList) {
this.strList = strList;
}
public void save() {
System. out .println(strList);
System. out .println("UserDao save method running....");
}
}
2 )集合資料型別( List<String> )的注入
<bean id="userDao" class="com.itheima.dao.impl.UserDaoImpl">
<property name="strList">
<list>
<value>aaa</value>
<value>bbb</value>
<value>ccc</value>
</list>
</property>
</bean>
3 )集合資料型別( List<User> )的注入
public class UserDaoImpl implements UserDao {
private List<User> userList;
public void setUserList(List<User> userList) {
this.userList = userList;
}
public void save() {
System. out .println(userList);
System. out .println("UserDao save method running....");
}
}
3 )集合資料型別( List<User> )的注入
<bean id="u1" class="com.itheima.domain.User"/>
<bean id="u2" class="com.itheima.domain.User"/>
<bean id="userDao" class="com.itheima.dao.impl.UserDaoImpl">
<property name="userList">
<list>
<bean class="com.itheima.domain.User"/>
<bean class="com.itheima.domain.User"/>
<ref bean="u1"/>
<ref bean="u2"/>
</list>
</property>
</bean>
4 )集合資料型別( Map<String,User> )的注入
public class UserDaoImpl implements UserDao {
private Map<String,User> userMap;
public void setUserMap(Map<String, User> userMap) {
this.userMap = userMap;
}
public void save() {
System. out .println(userMap);
System. out .println("UserDao save method running....");
}
}
4 )集合資料型別( Map<String,User> )的注入
<bean id="u1" class="com.itheima.domain.User"/>
<bean id="u2" class="com.itheima.domain.User"/>
<bean id="userDao" class="com.itheima.dao.impl.UserDaoImpl">
<property name="userMap">
<map>
<entry key="user1" value-ref="u1"/>
<entry key="user2" value-ref="u2"/>
</map>
</property>
</bean>
5 )集合資料型別( Properties )的注入
public class UserDaoImpl implements UserDao {
private Properties properties;
public void setProperties(Properties properties) {
this.properties = properties;
}
public void save() {
System. out .println(properties);
System. out .println("UserDao save method running....");
}
}
<bean id="userDao" class="com.itheima.dao.impl.UserDaoImpl">
<property name="properties">
<props>
<prop key="p1">aaa</prop>
<prop key="p2">bbb</prop>
<prop key="p3">ccc</prop>
</props>
</property>
</bean>
實際開發中,Spring的配置內容非常多,這就導致Spring配置很繁雜且體積很大,所以,可以將部分配置拆解到其他
配置檔案中,而在Spring主配置檔案通過import標籤進行載入
<import resource="applicationContext-xxx.xml"/>

3.9引入其他配置檔案(分模組開發)

Spring的重點配置
<bean>標籤
id屬性:在容器中Bean例項的唯一標識,不允許重複
class屬性:要例項化的Bean的全限定名
scope屬性:Bean的作用範圍,常用是Singleton(預設)和prototype
<property>標籤:屬性注入
name屬性:屬性名稱
value屬性:注入的普通屬性值
ref屬性:注入的物件引用值
<list>標籤
<map>標籤
<properties>標籤
<constructor-arg>標籤
<import>標籤:匯入其他的Spring的分檔案

4. Spring相關API

4.1 ApplicationContext的繼承體系

applicationContext: 介面型別,代表應用上下文,可以通過其例項獲得Spring 容器中的Bean 物件

4. Spring相關API

4.2 ApplicationContext的實現類

1 )ClassPathXmlApplicationContext
它是從類的根路徑下載入配置檔案推薦使用這種
2 )FileSystemXmlApplicationContext
它是從磁碟路徑上載入配置檔案,配置檔案可以在磁碟的任意位置。
3 )AnnotationConfigApplicationContext
當使用註解配置容器物件時,需要使用此類來建立spring 容器。它用來讀取註解。
public Object getBean(String name) throws BeansException {
assertBeanFactoryActive();
return getBeanFactory().getBean(name);
}
public <T> T getBean(Class<T> requiredType) throws BeansException {
assertBeanFactoryActive();
return getBeanFactory().getBean(requiredType);
}

4. Spring相關API

4.3 getBean()方法使用

其中,當引數的資料型別是字串時,表示根據Bean的id從容器中獲得Bean例項,返回是Object,需要強轉。
當引數的資料型別是Class型別時,表示根據型別從容器中匹配Bean例項,當容器中相同型別的Bean有多個時,
則此方法會報錯。
ApplicationContext applicationContext = new
ClassPathXmlApplicationContext("applicationContext.xml");
UserService userService1 = (UserService)
applicationContext.getBean("userService");
UserService userService2 = applicationContext.getBean(UserService.class);
ApplicationContext app = new ClasspathXmlApplicationContext("xml檔案")
app.getBean("id")
app.getBean(Class)