Spring IOC
1. 主要内容

2. Spring 架構
2.1. Spring 架構概念
Spring 是衆多開源java項⽬中的⼀員,基于分層的javaEE應⽤⼀站式輕量級開源架構,主要核⼼是IOC(控制反轉/依賴注⼊)與 AOP(⾯向切⾯)兩⼤技術,實作項⽬在開發過程中的輕松解耦,提⾼項⽬的開發效率。
在項⽬中引⼊Spring⽴即可以帶來下⾯的好處
- 降低元件之間的耦合度,實作軟體各層之間的解耦。
- 可以使⽤容器提供的衆多服務,如:事務管理服務、消息服務等等。
- 當我們使⽤容器管理事務時,開發⼈員就不再需要⼿⼯控制事務.也不需處理複雜的事務傳播。
- 容器提供單例模式⽀持,開發⼈員不再需要⾃⼰編寫實作代碼。
- 容器提供了AOP技術,利⽤它很容易實作如權限攔截、運⾏期監控等功能。
2.2. Spring 源碼架構
Spring 總共⼤約有20個子產品,由1300多個不同的⽂件構成。⽽這些元件被分别整合在核⼼容器(Core Container)、Aop(Aspect Oriented Programming)和裝置⽀持(Instrmentation)、資料通路及內建(Data Access/Integeration)、Web、報⽂發送(Messaging)、測試6個子產品集合中。
-
核⼼容器:Spring-beans 和 Spring-core 子產品是 Spring 架構的核⼼子產品,包含控制反轉(Inversion of Control, IoC)和依賴注⼊(Dependency Injection, DI),核⼼容器提供 Spring 架構的基本功能。核⼼容器的主要元件是 BeanFactory,⼯⼚模式的實作。BeanFactory 使⽤控制反轉(IOC) 思想将應⽤程式的配置和依賴性規範與實際的應⽤程式代碼分開。
Spring 上下⽂Spring Context:Spring 上下⽂是⼀個配置⽂件,向 Spring 架構提供上下⽂資訊。Spring 上下⽂包括企業服務,例如 JNDI、EJB、電⼦郵件、國際化、校驗和排程功能。
Spring-Expression 子產品是統⼀表達式語⾔(unified EL)的擴充子產品,可以查詢、管理運⾏中的對象,同時也⽅便的可以調⽤對象⽅法、操作數組、集合等。它的文法類似于傳統EL,但提供了額外的功能,最出⾊的要數函數調⽤和簡單字元串的模闆函數。
- Spring-AOP:Spring-aop是Spring的另⼀個核⼼子產品, 在Spring中,他是以JVM的動态代理技術為基礎,然後設計出了⼀系列的Aop橫切實作,⽐如前置通知、傳回通知、異常通知等。通過其配置管理特性,Spring AOP 子產品直接将⾯向切⾯的程式設計功能內建到了 Spring 架構中。是以,可以很容易地使 Spring 架構管理的任何對象⽀持 AOP。
-
Spring Data Access(資料通路):由Spring-jdbc、Spring-tx、Spring-orm、Spring-jms和Springoxm 5個子產品組成 Spring-jdbc 子產品是 Spring 提供的JDBC抽象架構的主要實作子產品,⽤于簡化Spring JDBC。
Spring-tx 子產品是SpringJDBC事務控制實作子產品。使⽤Spring架構,它對事務做了很好的封裝,通過它的Aop配置,可以靈活的配置在任何⼀層。
Spring-Orm 子產品是ORM架構⽀持子產品,主要內建 hibernate, Java Persistence API (JPA) 和 Java Data Objects (JDO) ⽤于資源管理、資料通路對象(DAO)的實作和事務政策。
Spring-Jms 子產品(Java Messaging Service)能夠發送和接受資訊。
Spring-Oxm 子產品主要提供⼀個抽象層以⽀撐OXM(OXM 是 Object-to-XML-Mapping 的縮寫,
它是⼀個O/M-mapper,将java對象映射成 XML 資料,或者将 XML 資料映射成 java 對象),例如:JAXB, Castor, XMLBeans, JiBX 和 XStream 等。
- Web 子產品:由Spring-web、Spring-webmvc、Spring-websocket和Spring-webmvc-portlet 4個子產品組成,Web 上下⽂子產品建⽴在應⽤程式上下⽂子產品之上,為基于 Web 的應⽤程式提供了上下⽂。Web 子產品還簡化了處理多部分請求以及将請求參數綁定到域對象的⼯作。
-
報⽂發送:即Spring-messaging子產品。
Spring-messaging是Spring4 新加⼊的⼀個子產品,主要職責是為Spring 架構內建⼀些基礎的報⽂傳送應⽤。
- 單元測試:即Spring-test子產品。Spring-test子產品主要為測試提供⽀持
2.3. Spring 架構環境搭建
2.3.1. 環境要求
JDK 版本:
JDK 1.7 及以上版本
Spring版本:
Spring 5.x版本
2.3.2. 建立 Maven 項⽬
- 建立 Maven 的普通 Java 項⽬
Spring IOC詳細介紹Spring IOC - 設定項⽬的坐标
Spring IOC詳細介紹Spring IOC - 設定項⽬的 Maven 環境
Spring IOC詳細介紹Spring IOC - 設定項⽬的名稱和存放的⼯作空間
Spring IOC詳細介紹Spring IOC
2.3.3. 調整項⽬環境
- 修改 JDK 版本
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
</properties>
- 修改單元測試 JUnit 版本
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
- build标簽中的pluginManagement标簽
<!--删除build标簽中的pluginManagement标簽-->
<build>
</build>
2.3.4. 添加 Spring 架構的依賴坐标
Maven倉庫:https://mvnrepository.com/
<!-- 添加Spring架構的核⼼依賴 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.2.4.RELEASE</version>
</dependency>
2.3.5. 編寫 Bean 對象
package com.suntao.service;
public class UserService {
public void test(){
System.out.println("Hello Spring!");
}
}
2.3.6. 添加Spring 配置⽂件
- 在項⽬的src下建立⽂件夾 resources(Alt+insert)
- 将 resources 标記為資源⽬錄
Spring IOC詳細介紹Spring IOC -
在 src\main\resources ⽬錄下建立 spring.xml ⽂件,并拷⻉官⽹⽂檔提供的模闆内容到 xml中。配置 bean 到 xml 中,把對應 bean 納⼊到 Spring 容器來管理
spring.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
https://www.springframework.org/schema/beans/spring-beans.xsd">
<!--
xmlns 即 xml namespace xml使⽤的命名空間
xmlns:xsi 即xml schema instance xml 遵守的具體規範
xsi:schemaLocation 本⽂檔xml遵守的規範 官⽅指定
-->
<bean id="userService" class="com.suntao.service.UserService"></bean>
</beans>
- 在 spring.xml 中配置 Bean 對象
<!--
id:bean對象的id,唯⼀辨別。⼀般是Bean對象的名稱的⾸字⺟⼩寫
class:bean對象的類路徑
-->
<bean id="userService" class="com.suntao.service.UserService"></bean>
2.3.7. 加載配置⽂件,擷取執行個體化對象
package com.suntao;
import com.suntao.service.UserService;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class App {
public static void main(String[] args) {
// 擷取Spring上下⽂環境 (加載配置⽂件)
ApplicationContext ac = new ClassPathXmlApplicationContext("spring.xml");
// 通過getBean⽅法得到Spring容器中執行個體化好的Bean對象 (執行個體化Bean對象)
// userService代表的是配置⽂件中bean标簽的id屬性值
UserService userService = (UserService) ac.getBean("userService");
// 調⽤⽅法 (使⽤執行個體化對象)
userService.test();
}
}
3. Spring IOC 容器 Bean 對象執行個體化模拟
思路:
- 定義Bean ⼯⼚接⼝,提供擷取bean⽅法
- 定義Bean⼯⼚接⼝實作類,解析配置⽂件,執行個體化Bean對象
- 實作擷取Bean⽅法
4. Spring IOC 配置⽂件加載
4.1. Spring 配置⽂件加載
spring.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
https://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="userService" class="com.xxxx.service.UserService"></bean>
</beans>
4.1.1. 根據相對路徑加載資源
4.1.2. 根據絕對路徑加載資源(了解)
ApplicationContext ac = new
FileSystemXmlApplicationContext("C:/IdeaWorkspace/spring01/src/main/resources/
spring.xml");
4.2. Spring 多配置⽂件加載
Spring 架構啟動時可以加載多個配置⽂件到環境中。對于⽐較複雜的項⽬,可能對應的配置⽂件有多個,項⽬在啟動部署時會将多個配置⽂件同時加載進來。
service.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
https://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="userService" class="com.suntao.service.UserService"></bean>
</beans>
dao.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
https://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="userDao" class="com.suntao.dao.UserDao"></bean>
</beans>
4.2.1. 可變參數,傳⼊多個⽂件名
// 同時加載多個資源⽂件
ApplicationContext ac = new
ClassPathXmlApplicationContext("spring.xml","dao.xml");
4.2.2. 通過總的配置⽂件import其他配置⽂件
spring.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
https://www.springframework.org/schema/beans/spring-beans.xsd">
<!--導⼊需要包含的資源⽂件-->
<import resource="service.xml"/>
<import resource="dao.xml"/>
</beans>
加載時隻需加載總的配置⽂件即可
// 加載總的資源⽂件
ApplicationContext ac = new ClassPathXmlApplicationContext("spring.xml");
5. Spring IOC 容器 Bean 對象執行個體化
5.1. 構造器執行個體化
注:通過預設構造器建立 ,空構造⽅法必須存在 否則建立失敗
- 設定配置⽂件 spring.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
https://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="userService" class="com.suntao.service.UserService"></bean>
</beans>
- 擷取執行個體化對象
ApplicationContext ac = new ClassPathXmlApplicationContext("spring.xml");
UserService userService = (UserService) ac.getBean("userService");
userService.test();
5.2. 靜态⼯⼚執行個體化(了解)
注:
- 要有該⼯⼚類及⼯⼚⽅法
- ⼯⼚⽅法為靜态的
- 定義靜态⼯⼚類
package com.suntao.factory;
import com.suntao.service.UserService;
/**
* 定義靜态⼯⼚類
*/
public class StaticFactory {
public static UserService createUserService() {
return new UserService();
}
}
- 設定配置⽂件 spring.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
https://www.springframework.org/schema/beans/spring-beans.xsd">
<!--靜态⼯⼚-->
<bean id="userService" class="com.suntao.factory.StaticFactory" factorymethod="createUserService"></bean>
</beans>
- 擷取執行個體化對象
ApplicationContext ac = new ClassPathXmlApplicationContext("spring.xml");
UserService userService = (UserService) ac.getBean("userService");
userService.test();
當我們指定Spring使⽤靜态⼯⼚⽅法來建立Bean執行個體時,Spring将先解析配置⽂件,并根據配置⽂件指定的資訊,通過反射調⽤靜态⼯⼚類的靜态⼯⼚⽅法,并将該靜态⼯⼚⽅法的傳回值作為Bean執行個體,在這個過程中,Spring不再負責建立Bean執行個體,Bean執行個體是由⽤戶提供的靜态⼯⼚⽅法提供的。
5.3. 執行個體化⼯⼚執行個體化(了解)
注:
- ⼯⼚⽅法為⾮靜态⽅法
- 需要配置⼯⼚bean,并在業務bean中配置factory-bean,factory-method屬性
package com.suntao.factory;
import com.suntao.service.UserService;
public class InstanceFactory {
/**
* 定義⽅法,傳回執行個體化對象
* @return
*/
public UserService createUserService() {
return new UserService();
}
}
- 設定配置⽂件 spring.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
https://www.springframework.org/schema/beans/spring-beans.xsd">
<!--
執行個體化⼯⼚
1.定義執行個體化⼯⼚bean
2.引⽤⼯⼚bean 指定⼯⼚建立⽅法(⽅法為⾮靜态)
-->
<bean id="instanceFactory" class="com.suntao.factory.InstanceFactory">
</bean>
<bean id="userService" factory-bean="instanceFactory" factorymethod="createUserService"></bean>
</beans>
- 擷取執行個體化對象
ApplicationContext ac = new ClassPathXmlApplicationContext("spring.xml");
UserService userService = (UserService) ac.getBean("userService");
userService.test();
5.4. Spring三種執行個體化Bean的⽅式⽐較
- ⽅式⼀:通過bean的預設構造函數建立,當各個bean的業務邏輯互相⽐較獨⽴的時候或者和外界關聯較少的時候可以使⽤。
- ⽅式⼆:利⽤靜态factory⽅法建立,可以統⼀管理各個bean的建立,如各個bean在建立之前需要相同的初始化處理,則可⽤這個factory⽅法險進⾏統⼀的處理等等。
-
⽅式三:利⽤執行個體化factory⽅法建立,即将factory⽅法也作為了業務bean來控制,1可⽤于內建其他架構的bean建立管理⽅法,2能夠使bean和factory的⻆⾊互換。
開發中項⽬⼀般使⽤⼀種⽅式執行個體化bean,項⽬開發基本采⽤第⼀種⽅式,交給Spring托管,使⽤時直接拿來使⽤即可。另外兩種了解
6. Spring IOC 注⼊
⼿動執行個體化與外部引⼊
圖⼀:
圖⼆:
對⽐發現:圖⼆中對于 UserDao 對象的建立并沒有像圖⼀那樣主動的去執行個體化,⽽是通過帶參⽅法形式将UserDao 傳⼊過來,從⽽實作 UserService 對UserDao類 的依賴。
⽽實際建立對象的幕後對象即是交給了外部來建立。
6.1. Spring IOC ⼿動裝配(注⼊)
Spring ⽀持的注⼊⽅式共有四種:set 注⼊、構造器注⼊、靜态⼯⼚注⼊、執行個體化⼯⼚注⼊。
6.1.1. set⽅法注⼊
注:
- 屬性字段需要提供set⽅法
- 四種⽅式,推薦使⽤set⽅法注⼊
6.1.1.1. 業務對象 JavaBean
- 屬性字段提供set⽅法
public class UserService {
// 業務對象UserDao set注⼊(提供set⽅法)
private UserDao userDao;
public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}
}
- 配置⽂件的bean标簽設定property标簽
<?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
https://www.springframework.org/schema/beans/spring-beans.xsd">
<!--
IOC通過property标簽⼿動裝配(注⼊):
Set⽅法注⼊
name:bean對象中屬性字段的名稱
ref:指定bean标簽的id屬性值
-->
<bean id="userDao" class="com.suntao.dao.UserDao"></bean>
<bean id="userService" class="com.suntao.service.UserService">
<!--業務對象注⼊-->
<property name="userDao" ref="userDao"/>
</bean>
</beans>
6.1.1.2. 常⽤對象和基本類型
- 屬性字段提供set⽅法
public class UserService {
// 常⽤對象String set注⼊(提供set⽅法)
private String host;
public void setHost(String host) {
this.host = host;
}
// 基本類型Integer set注⼊(提供set⽅法)
private Integer port;
public void setPort(Integer port) {
this.port = port;
}
}
- 配置⽂件的bean标簽設定property标簽
<?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
https://www.springframework.org/schema/beans/spring-beans.xsd">
<!--
IOC通過property标簽⼿動裝配(注⼊):
Set⽅法注⼊
name:bean對象中屬性字段的名稱
value:具體的值(基本類型 常⽤對象|⽇期 集合)
-->
<bean id="userService" class="com.suntao.service.UserService">
<!--常⽤對象String 注⼊-->
<property name="host" value="127.0.0.1"/>
<!--基本類型注⼊-->
<property name="port" value="8080"/>
</bean>
</beans>
6.1.1.3. 集合類型和屬性對象
- 屬性字段提供set⽅法
public class UserService {
// List集合 set注⼊(提供set⽅法)
public List<String> list;
public void setList(List<String> list) {
this.list = list;
}
// Set集合 set注⼊(提供set⽅法)
private Set<String> set;
public void setSet(Set<String> set) {
this.set = set;
}
// Map set注⼊(提供set⽅法)
private Map<String,Object> map;
public void setMap(Map<String, Object> map) {
this.map = map;
}
// Properties set注⼊(提供set⽅法)
private Properties properties;
public void setProperties(Properties properties) {
this.properties = properties;
}
}
- 配置⽂件的bean标簽設定property标簽
<?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
https://www.springframework.org/schema/beans/spring-beans.xsd">
<!--
IOC通過property标簽⼿動裝配(注⼊):
Set⽅法注⼊
name:bean對象中屬性字段的名稱
value:具體的值(基本類型 常⽤對象|⽇期 集合)
-->
<!--List集合 注⼊-->
<property name="list">
<list>
<value>上海</value>
<value>北京</value>
<value>杭州</value>
</list>
</property>
<!--Set集合注⼊-->
<property name="set">
<set>
<value>上海SH</value>
<value>北京BJ</value>
<value>杭州HZ</value>
</set>
</property>
<!--Map注⼊-->
<property name="map">
<map>
<entry>
<key><value>周傑倫</value></key>
<value>我是如此相信</value>
</entry>
<entry>
<key><value>林俊傑</value></key>
<value>可惜沒如果</value>
</entry>
<entry>
<key><value>陳奕迅</value></key>
<value>⼗年</value>
</entry>
</map>
</property>
<!--Properties注⼊-->
<property name="properties">
<props>
<prop key="上海">東⽅明珠</prop>
<prop key="北京">天安⻔</prop>
<prop key="杭州">⻄湖</prop>
</props>
</property>
</beans>
6.1.1.4. 測試代碼
UserService.java
public class UserService {
// 業務對象UserDao set注⼊(提供set⽅法)
private UserDao userDao;
public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}
// 常⽤對象String set注⼊(提供set⽅法)
private String host;
public void setHost(String host) {
this.host = host;
}
// 基本類型Integer set注⼊(提供set⽅法)
private Integer port;
public void setPort(Integer port) {
this.port = port;
}
// List集合 set注⼊(提供set⽅法)
public List<String> list;
public void setList(List<String> list) {
this.list = list;
}
// List集合輸出
public void printList() {
list.forEach(s -> System.out.println(s));
}
// Set集合 set注⼊(提供set⽅法)
private Set<String> set;
public void setSet(Set<String> set) {
this.set = set;
}
// Set集合輸出
public void printSet() {
set.forEach(s -> System.out.println(s));
}
// Map set注⼊(提供set⽅法)
private Map<String,Object> map;
public void setMap(Map<String, Object> map) {
this.map = map;
}
// Map輸出
public void printMap() {
map.forEach((k,v) -> System.out.println(k + "," + v));
}
// Properties set注⼊(提供set⽅法)
private Properties properties;
public void setProperties(Properties properties) {
this.properties = properties;
}
// Properties輸出
public void printProperties(){
properties.forEach((k,v) -> System.out.println(k + ","+ v ));
}
public void test(){
System.out.println("UserService Test...");
userDao.test();
studentDao.test();
System.out.println("Host:" + host + ",port:" + port);
// List集合
printList();
// Set集合
printSet();
// Map
printMap();
// Properties
printProperties();
}
}
spring.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
https://www.springframework.org/schema/beans/spring-beans.xsd">
<!--
IOC通過property标簽⼿動裝配(注⼊):
Set⽅法注⼊
name:bean對象中屬性字段的名稱
ref:指定bean标簽的id屬性值
value:具體的值(基本類型 常⽤對象|⽇期 集合)
-->
<bean id="userDao" class="com.suntao.dao.UserDao"></bean>
<bean id="userService" class="com.suntao.service.UserService">
<!--業務對象 注⼊-->
<property name="userDao" ref="userDao"/>
<property name="studentDao" ref="studentDao"/>
<!--常⽤對象String 注⼊-->
<property name="host" value="192.168.1.109"/>
<!--基本類型注⼊-->
<property name="port" value="8080"/>
<!--List集合 注⼊-->
<property name="list">
<list>
<value>上海</value>
<value>北京</value>
<value>杭州</value>
</list>
</property>
<!--Set集合注⼊-->
<property name="set">
<set>
<value>上海SH</value>
<value>北京BJ</value>
<value>杭州HZ</value>
</set>
</property>
<!--Map注⼊-->
<property name="map">
<map>
<entry>
<key><value>周傑倫</value></key>
<value>我是如此相信</value>
</entry>
<entry>
<key><value>林俊傑</value></key>
<value>可惜沒如果</value>
</entry>
<entry>
<key><value>陳奕迅</value></key>
<value>⼗年</value>
</entry>
</map>
</property>
<!--Properties注⼊-->
<property name="properties">
<props>
<prop key="上海">東⽅明珠</prop>
<prop key="北京">天安⻔</prop>
<prop key="杭州">⻄湖</prop>
</props>
</property>
</bean>
</beans>
6.1.2. 構造器注⼊
注:
- 提供帶參構造器
6.1.2.1. 單個Bean對象作為參數
Java 代碼
public class UserService {
private UserDao userDao; // JavaBean 對象
public UserService(UserDao userDao) {
this.userDao = userDao;
}
public void test(){
System.out.println("UserService Test...");
userDao.test();
}
}
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
https://www.springframework.org/schema/beans/spring-beans.xsd">
<!--
IOC通過構造器注⼊:
通過constructor-arg标簽進⾏注⼊
name:屬性名稱
ref:指定bean标簽的id屬性值
-->
<bean id="userDao" class="com.xxxx.dao.UserDao" ></bean>
<bean id="accountDao" class="com.xxxx.dao.AccountDao" ></bean>
<bean id="userService" class="com.xxxx.service.UserService">
<constructor-arg name="userDao" ref="userDao"></constructor-arg>
<constructor-arg name="accountDao" ref="accountDao"></constructor-arg>
</bean>
</beans>
6.1.2.3. Bean對象和常⽤對象作為參數
Java 代碼
public class UserService {
private UserDao userDao; // JavaBean 對象
private AccountDao accountDao; // JavaBean 對象
private String uname; // 字元串類型
public UserService(UserDao userDao, AccountDao accountDao, String uname) {
this.userDao = userDao;
this.accountDao = accountDao;
this.uname = uname;
}
public void test(){
System.out.println("UserService Test...");
userDao.test();
accountDao.test();
System.out.println("uname:" + uname);
}
}
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
https://www.springframework.org/schema/beans/spring-beans.xsd">
<!--
IOC通過構造器注⼊:
通過constructor-arg标簽進⾏注⼊
name:屬性名稱
ref:指定bean标簽的id屬性值
value:基本類型 常⽤對象的值
index:構造器中參數的下标,從0開始
-->
<bean id="userDao" class="com.xxxx.dao.UserDao" ></bean>
<bean id="accountDao" class="com.xxxx.dao.AccountDao" ></bean>
<bean id="userService" class="com.xxxx.service.UserService">
<constructor-arg name="userDao" ref="userDao"></constructor-arg>
<constructor-arg name="accountDao" ref="accountDao"></constructor-arg>
<constructor-arg name="uname" value="admin"></constructor-arg>
</bean>
</beans>
6.1.2.4. 循環依賴問題
循環問題産⽣的原因:
Bean通過構造器注⼊,之間彼此互相依賴對⽅導緻bean⽆法執行個體化。
問題展示:
- Java 代碼
public class AccountService {
private RoleService roleService;
public AccountService(RoleService roleService) {
this.roleService = roleService;
}
public void test() {
System.out.println("AccountService Test...");
}
}
public class RoleService {
private AccountService accountService;
public RoleService(AccountService accountService) {
this.accountService = accountService;
}
public void test() {
System.out.println("RoleService Test...");
}
}
- XML配置
<!--
如果多個bean對象中互相注⼊,則會出現循環依賴的問題
可以通過set⽅法注⼊解決
-->
<bean id="accountService" class="com.suntao.service.AccountService">
<constructor-arg name="roleService" ref="roleService"/>
</bean>
<bean id="roleService" class="com.suntao.service.RoleService">
<constructor-arg name="accountService" ref="accountService"/>
</bean>
如何解決:将構造器注⼊改為set⽅法注⼊
3. Java代碼
public class AccountService {
private RoleService roleService;
/* public AccountService(RoleService roleService) {
this.roleService = roleService;
}*/
public void setRoleService(RoleService roleService) {
this.roleService = roleService;
}
public void test() {
System.out.println("AccountService Test...");
}
}
public class RoleService {
private AccountService accountService;
/* public RoleService(AccountService accountService) {
this.accountService = accountService;
}*/
public void setAccountService(AccountService accountService) {
this.accountService = accountService;
}
public void test() {
System.out.println("RoleService Test...");
}
}
- XML配置
<!--
<bean id="accountService" class="com.xxxx.service.AccountService">
<constructor-arg name="roleService" ref="roleService"/>
</bean>
<bean id="roleService" class="com.xxxx.service.RoleService">
<constructor-arg name="accountService" ref="accountService"/>
</bean>
-->
<!--修改為set⽅法注⼊-->
<bean id="accountService" class="com.xxxx.service.AccountService">
<property name="roleService" ref="roleService"/>
</bean>
<bean id="roleService" class="com.xxxx.service.RoleService">
<property name="accountService" ref="accountService"/>
</bean>
6.1.3. 靜态⼯⼚注⼊
- 定義靜态⼯⼚類
public class StaticFactory {
// 定義靜态⽅法
public static TypeDao createTypeDao() {
return new TypeDao();
}
}
- Java代碼
public class TypeService {
private TypeDao typeDao;
public void setTypeDao(TypeDao typeDao) {
this.typeDao = typeDao;
}
public void test() {
System.out.println("TypeService Test...");
}
}
-
XML配置
在配置⽂件中設定bean标簽,指定⼯⼚對象并設定對應的⽅法
<bean id="typeService" class="com.suntao.service.TypeService">
<property name="typeDao" ref="typeDao"/>
</bean>
<!--
靜态⼯⼚注⼊:
靜态⼯⼚注⼊也是借助set⽅法注⼊,隻是被注⼊的bean對象的執行個體化是通過靜态⼯⼚執行個體化的
-->
<bean id="typeDao" class="com.suntao.factory.StaticFactory" factorymethod="createTypeDao"></bean
6.1.4. 執行個體化⼯⼚注⼊
- 定義⼯⼚類
public class InstanceFactory {
public TypeDao createTypeDao() {
return new TypeDao();
}
}
- Java代碼
public class TypeService {
private TypeDao typeDao;
public void setTypeDao(TypeDao typeDao) {
this.typeDao = typeDao;
}
public void test() {
System.out.println("TypeService Test...");
}
}
-
XML配置
聲明⼯⼚bean标簽,聲明bean對象,指明⼯⼚對象和⼯⼚⽅法
<bean id="typeService" class="com.suntao.service.TypeService">
<property name="typeDao" ref="typeDao"/>
</bean>
<!--
執行個體化⼯⼚注⼊:
執行個體化⼯⼚注⼊也是借助set⽅法注⼊,隻是被注⼊的bean對象的執行個體化是通過執行個體化⼯⼚執行個體
化的
-->
<bean id="instanceFactory" class="com.suntao.factory.InstanceFactory">
</bean>
<bean id="typeDao" factory-bean="instanceFactory" factorymethod="createTypeDao"></bean>
重點掌握set注⼊和構造器注⼊,⼯⼚⽅式了解即可。實際開發中基本使⽤set⽅式注⼊bean。
6.1.5. 注⼊⽅式的選擇
開發項⽬中set⽅式注⼊⾸選
使⽤構造注⼊可以在建構對象的同時⼀并完成依賴關系的建⽴,對象⼀建⽴則所有的⼀切也就準備好了,但如果要建⽴的對象關系很多,使⽤構造器注⼊會在建構函數上留下⼀⻓串的參數,且不易記憶,這時使⽤Set注⼊會是個不錯的選擇。
使⽤Set注⼊可以有明确的名稱,可以了解注⼊的對象會是什麼,像setXXX()這樣的名稱會⽐記憶Constructor上某個參數的位置代表某個對象更好。
p名稱空間的使⽤
spring2.5以後,為了簡化setter⽅法屬性注⼊,引⽤p名稱空間的概念,可以将 ⼦元素,簡化為元素屬性配置。
- 屬性字段提供 set ⽅法
public class UserService {
// 業務對象UserDao set注⼊(提供set⽅法)
private UserDao userDao;
public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}
// 常⽤對象String set注⼊(提供set⽅法)
private String host;
public void setHost(String host) {
this.host = host;
}
}
- 在配置⽂件 spring.xml 引⼊ p 名稱空間
xmlns:p="http://www.springframework.org/schema/p"
<?xml version="1.0" encoding="UTF-8"?>
<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"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="userDao" class="com.suntao.dao.UserDao"></bean>
<!--
p:屬性名:="xxx" 引⼊常量值
p:屬性名-ref:="xxx" 引⼊其他Bean對象的id屬性值
-->
<bean id="userService" class="com.suntao.service.UserService"
p:userDao-ref="userDao"
p:host="127.0.0.1" />
</beans>
6.2. Spring IOC ⾃動裝配(注⼊)
注解⽅式注⼊ Bean
對于 bean 的注⼊,除了使⽤ xml 配置以外,可以使⽤注解配置。注解的配置,可以簡化配置⽂件,提⾼開發的速度,使程式看上去更簡潔。對于注解的解釋,Spring對于注解有專⻔的解釋器,對定義的注解進⾏解析,實作對應bean對象的注⼊。通過反射技術實作。
6.2.1. 準備環境
- 修改配置⽂件
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
- 開啟⾃動化注⼊
<!--開啟⾃動化裝配(注⼊)-->
<context:annotation-config/>
<bean id="userDao" class="com.xxxx.dao.UserDao"></bean>
<bean id="userService" class="com.xxxx.service.UserService"></bean>
- 給注⼊的bean對象添加注解
6.2.2. @Resource注解
@Resource注解實作⾃動注⼊(反射)
- 預設根據屬性字段名稱查找對應的bean對象 (屬性字段的名稱與bean标簽的id屬性值相等)
- 如果屬性字段名稱未找到,則會通過類型(Class類型)查找
- 屬性可以提供set⽅法,也可以不提供set⽅法
- 注解可以聲明在屬性級别 或 set⽅法級别
- 可以設定name屬性,name屬性值必須與bean标簽的id屬性值⼀緻;如果設定了name屬性值,就隻會按照name屬性值查找bean對象
-
當注⼊接⼝時,如果接⼝隻有⼀個實作則正常執行個體化;如果接⼝存在多個實作,則需要使⽤name屬性指定需要被執行個體化的bean對象
代碼示例
- 預設根據屬性字段名稱查找對應的bean對象 (屬性字段的名稱與bean标簽的id屬性值相等)
/**
* @Resource注解實作⾃動注⼊(反射)
* 預設根據屬性字段名稱查找對應的bean對象 (屬性字段的名稱與bean标簽的id屬性值相等)
*/
public class UserService {
@Resource
private UserDao userDao; // 屬性字段的名稱與bean标簽的id屬性值相等
public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}
public void test() {
// 調⽤UserDao的⽅法
userDao.test();
}
}
- 如果屬性字段名稱未找到,則會通過類型(Class類型)查找
/**
* @Resource注解實作⾃動注⼊(反射)
* 如果屬性字段名稱未找到,則會通過類型(Class類型)查找
*/
public class UserService {
@Resource
private UserDao ud; // 當在配置⽂件中屬性字段名(ud)未找到,則會查找對應的class(UserDao類型)
public void setUd(UserDao ud) {
this.ud = ud;
}
public void test() {
// 調⽤UserDao的⽅法
ud.test();
}
}
- 屬性可以提供set⽅法,也可以不提供set⽅法
/**
* @Resource注解實作⾃動注⼊(反射)
* 屬性可以提供set⽅法,也可以不提供set⽅法
*/
public class UserService {
@Resource
private UserDao userDao; // 不提供set⽅法
public void test() {
// 調⽤UserDao的⽅法
userDao.test();
}
}
- 注解可以聲明在屬性級别 或 set⽅法級别
/**
* @Resource注解實作⾃動注⼊(反射)
* 注解可以聲明在屬性級别 或 set⽅法級别
*/
public class UserService {
private UserDao userDao;
@Resource // 注解也可設定在set⽅法上
public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}
public void test() {
// 調⽤UserDao的⽅法
userDao.test();
}
}
- 可以設定name屬性,name屬性值必須與bean标簽的id屬性值⼀緻;如果設定了name屬性值,就隻會按照name屬性值查找bean對象
/**
* @Resource注解實作⾃動注⼊(反射)
* 可以設定name屬性,name屬性值必須與bean的id屬性值⼀緻;
* 如果設定了name屬性值,就隻會按照name屬性值查找bean對象
*/
public class UserService {
@Resource(name = "userDao") // name屬性值與配置⽂件中bean标簽的id屬性值⼀緻
private UserDao ud;
public void test() {
// 調⽤UserDao的⽅法
ud.test();
}
}
-
當注⼊接⼝時,如果接⼝隻有⼀個實作則正常執行個體化;如果接⼝存在多個實作,則需要使⽤name屬性指定需要被執行個體化的bean對象
定義接⼝類 IUserDao.java
package com.xxxx.dao;
/**
* 定義接⼝類
*/
public interface IUserDao {
public void test();
}
定義接⼝實作類 UserDao01.java
package com.xxxx.dao;
/**
* 接⼝實作類
*/
public class UserDao01 implements IUserDao {
@Override
public void test(){
System.out.println("UserDao01...");
}
}
定義接⼝實作類 UserDao02.java
package com.xxxx.dao;
/**
* 接⼝實作類
*/
public class UserDao02 implements IUserDao {
@Override
public void test(){
System.out.println("UserDao02...");
}
}
XML配置⽂件
<!--開啟⾃動化裝配(注⼊)-->
<context:annotation-config/>
<bean id="userService" class="com.xxxx.service.UserService"></bean>
<bean id="userDao01" class="com.xxxx.dao.UserDao01"></bean>
<bean id="userDao02" class="com.xxxx.dao.UserDao01"></bean>
使⽤注解 UserService.java
/**
* @Resource注解實作⾃動注⼊(反射)
* 當注⼊接⼝時,如果接⼝隻有⼀個實作則正常執行個體化;如果接⼝存在多個實作,則需要使⽤
name屬性指定需要被執行個體化的bean對象
*/
public class UserService {
@Resource(name = "userDao01") // name屬性值與其中⼀個實作類的bean标簽的id屬性值⼀緻
private IUserDao iUserDao; // 注⼊接⼝(接⼝存在多個實作)
public void test() {
iUserDao.test();
}
}
6.2.3. @Autowired注解
@Autowired注解實作⾃動化注⼊:
- 預設通過類型(Class類型)查找bean對象 與屬性字段的名稱⽆關
- 屬性可以提供set⽅法,也可以不提供set⽅法
- 注解可以聲明在屬性級别 或 set⽅法級别
- 可以添加@Qualifier結合使⽤,通過value屬性值查找bean對象(value屬性值必須要設定,且值要與bean标簽的id屬性值對應)
- 預設通過類型(Class類型)查找bean對象 與屬性字段的名稱⽆關
/**
* @Autowired注解實作⾃動化注⼊
* 預設通過類型(Class類型)查找bean對象 與屬性字段的名稱⽆關
*/
public class UserService {
@Autowired
private UserDao userDao; // 預設通過類型(Class類型)查找bean對象 與屬性字段
public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}
public void test() {
// 調⽤UserDao的⽅法
userDao.test();
}
}
- 屬性可以提供set⽅法,也可以不提供set⽅法
/**
* @Autowired注解實作⾃動化注⼊
* 屬性可以提供set⽅法,也可以不提供set⽅法
*/
public class UserService {
@Autowired
private UserDao userDao; // 不提供set⽅法
public void test() {
// 調⽤UserDao的⽅法
userDao.test();
}
}
- 注解可以聲明在屬性級别 或 set⽅法級别
/**
* @Autowired注解實作⾃動化注⼊
* 注解可以聲明在屬性級别 或 set⽅法級别
*/
public class UserService {
private UserDao userDao;
@Autowired// 注解可以聲明在set⽅法級别
public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}
public void test() {
// 調⽤UserDao的⽅法
userDao.test();
}
}
- 可以添加@Qualifier結合使⽤,通過value屬性值查找bean對象(value屬性值必須要設定,且值要與bean标簽的id屬性值對應
/**
* @Autowired注解實作⾃動化注⼊
* 可以添加@Qualifier結合使⽤,通過value屬性值查找bean對象
value屬性值必須要設定,且值要與bean标簽的id屬性值對應
*/
public class UserService {
@Autowired
@Qualifier(value="userDao") // value屬性值必須要設定,且值要與bean标簽的id屬性值對應
private UserDao userDao;
public void test() {
userDao.test();
}
}
推薦使⽤@Resource 注解是屬于J2EE的,減少了與Spring的耦合。
7. Spring IOC 掃描器
實際的開發中,bean的數量⾮常多,采⽤⼿動配置bean的⽅式已⽆法滿⾜⽣産需要,Spring這時候同樣提供了掃描的⽅式,對掃描到的bean對象統⼀進⾏管理,簡化開發配置,提⾼開發效率。
7.1. Spring IOC 掃描器的配置
Spring IOC 掃描器
作⽤:bean對象統⼀進⾏管理,簡化開發配置,提⾼開發效率
1、設定⾃動化掃描的範圍
如果bean對象未在指定包範圍,即使聲明了注解,也⽆法執行個體化
2、使⽤指定的注解(聲明在類級别) bean對象的id屬性預設是 類的⾸字⺟⼩寫
Dao層:
@Repository
Service層:
@Service
Controller層:
@Controller
任意類:
@Component
注:開發過程中建議按照指定規則聲明注解
- 設定⾃動化掃描範圍
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<!-- 設定⾃動化掃描的範圍 -->
<context:component-scan base-package="com.xxxx"/>
</beans>
-
使⽤特定的注解
@Repository (Dao層)
@Repository
public class ResourceDao {
public void test() {
System.out.println("ResourceDao...");
}
}
@Service(Service層 )
@Service
public class ResourceService {
@Resource
private ResourceDao resourceDao; // service層注⼊dao層的bean對象
public void test() {
System.out.println("ResourceService...");
resourceDao.test();
}
}
@Controller (Controller 層 )
@Controller
public class ResourceController {
@Autowired
private ResourceService resourceService; // Controller層注⼊service層的bean對象
public void test() {
System.out.println("ResourceController...");
resourceService.test();
}
}
@Component (任意層)
@Component
public class PropertyUtils {
public void test(){
System.out.println("PropertyUtils...");
}
}
7.2. Spring 模拟⽤戶登入流程
7.2.1. Dao層 (查詢⽤戶記錄)
- 定義JavaBean User.java
package com.xxxx.po;
/**
* User ⽤戶實體類
*/
public class User {
private String userName; // ⽤戶名稱
private String userPwd; // ⽤戶密碼
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
public String getUserPwd() {
return userPwd;
}
public void setUserPwd(String userPwd) {
this.userPwd = userPwd;
}
}
- 編寫Dao層 UserDao.java
package com.xxxx.dao;
import com.xxxx.po.User;
import org.springframework.stereotype.Repository;
@Repository
public class UserDao {
private final String USERNAME = "admin";
private final String USERPWD = "admin";
/**
* 通過⽤戶名稱查詢⽤戶對象
* @param userName
* @return
*/
public User queryUserByUserName(String userName){
User user = null;
// 判斷⽤戶名稱是否正确
if(!USERNAME.equals(userName)){
// 如果不正确,傳回null
return null;
}
// 如果正确,将⽤戶名稱和密碼設定到user對象中
user = new User();
user.setUserName(USERNAME);
user.setUserPwd(USERPWD);
return user;
}
}
7.2.2. Service層 (業務邏輯處理)
- 定義業務處理傳回消息模型 MessageModel.java
package com.xxxx.po.vo;
/**
* 定義業務處理傳回消息模型
* 封裝傳回結果
*/
public class MessageModel {
private Integer resultCode = 1; // 結果狀态碼 1=成功,0=失敗
private String resultMsg = "操作成功!"; // 結果提示資訊
public Integer getResultCode() {
return resultCode;
}
public void setResultCode(Integer resultCode) {
this.resultCode = resultCode;
}
public String getResultMsg() {
return resultMsg;
}
public void setResultMsg(String resultMsg) {
this.resultMsg = resultMsg;
}
}
- 編寫Service層 UserService.java
package com.xxxx.service;
import com.xxxx.dao.UserDao1;
import com.xxxx.po.User;
import com.xxxx.po.vo.MessageModel;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
@Service
public class UserService {
@Resource
private UserDao userDao;
/**
* 驗證⽤戶登入
* @param userName
* @param userPwd
* @return
*/
public MessageModel userLoginCheck(String userName, String userPwd){
// 定義業務處理傳回消息模型
MessageModel messageModel = new MessageModel();
// 判斷⽤戶名稱是否⾮空
if(null == userName || "".equals(userName.trim())){
messageModel.setResultCode(0);
messageModel.setResultMsg("⽤戶名不能為空!");
return messageModel;
}
// 判斷⽤戶密碼是否為空
if(null == userPwd || "".equals(userPwd.trim())){
messageModel.setResultCode(0);
messageModel.setResultMsg("密碼不能為空!");
return messageModel;
}
// 通過⽤戶名稱查詢⽤戶對象
User user = userDao.queryUserByUserName(userName);
// 判斷⽤戶對象是否為空
if(null == user){
messageModel.setResultCode(0);
messageModel.setResultMsg("該⽤戶不存在!");
return messageModel;
}
// 如果⽤戶對象不為空,判斷密碼是否正确
if(!user.getUserPwd().equals(userPwd)){
messageModel.setResultCode(0);
messageModel.setResultMsg("⽤戶密碼不正确!");
return messageModel;
}
// 登入成功
messageModel.setResultMsg("登入成功!");
return messageModel;
}
}
7.2.3. Controller層 (接收請求)
- 編寫Controller層 UserController.java
package com.xxxx.controller;
import com.xxxx.po.vo.MessageModel;
import com.xxxx.service.UserService1;
import org.springframework.stereotype.Controller;
import javax.annotation.Resource;
@Controller
public class UserController {
@Resource
private UserService userService;
/**
* ⽤戶登入
* @param userName
* @param userPwd
* @return
*/
public MessageModel login(String userName, String userPwd){
// 調⽤Dao層判斷⽤戶登入操作,傳回結果
MessageModel messageModel = userService.userLoginCheck(userName,userPwd);
return messageModel;
}
}
7.2.4. 通過 JUnit 進⾏測試
package com.xxxx;
import com.xxxx.controller.UserController;
import com.xxxx.po.vo.MessageModel;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class TestLogin {
@Test
public void test() {
// 得到Spring容器上下⽂環境
ApplicationContext ac = new ClassPathXmlApplicationContext("spring.xml");
// 得到UserController執行個體化對象
UserController userController = (UserController)ac.getBean("userController");
// 傳⼊參數調⽤UserController的⽅法,傳回封裝類
MessageModel messageModel= userController.login("admin", "admin");
System.out.println("狀态碼:" + messageModel.getResultCode() + ",提示資訊:" + messageModel.getResultMsg());
}
}
8. Bean的作⽤域與⽣命周期
8.1. Bean的作⽤域
預設情況下,我們從Spring容器中拿到的對象均是單例的,對于bean的作⽤域類型如下:
8.1.1. singleton 作⽤域
注意: lazy-init是懶加載, 如果等于true時作⽤是指Spring容器啟動的時候不會去執行個體化這個bean,⽽是在程式調⽤時才去執行個體化. 預設是false即Spring容器啟動時執行個體化.
預設情況下,被管理的bean隻會IOC容器中存在⼀個執行個體,對于所有擷取該Bean的操作Spring容器将隻傳回同⼀個Bean。
容器在啟動的情況下就執行個體化所有singleton 的 bean對象,并緩存與容器中
lazy-init屬性(懶加載)
如果為false,則在IOC容器啟動時會執行個體化bean對象,預設false
如果為true,則IOC容器啟動時不會執行個體化Bean對象,在使⽤bean對象時才會執行個體化
lazy-init設定為false有什麼好處?
1)可以提前發現潛在的配置問題
2)Bean 對象存在于緩存中,使⽤時不⽤再去執行個體化bean,加快程式運⾏效率
什麼對象适合作為單例對象?
⼀般來說對于⽆狀态或狀态不可改變的對象适合使⽤單例模式。(不存在會改變對象狀态的成員變量)
⽐如:controller層、service層、dao層
什麼是⽆狀态或狀态不可改變的對象?
實際上對象狀态的變化往往均是由于屬性值得變化⽽引起的,⽐如user類 姓名屬性會有變化,屬性姓名的變化⼀般會引起user對象狀态的變化。對于我們的程式來說,⽆狀态對象沒有執行個體變量的存在,保證了線程的安全性,service 層業務對象即是⽆狀态對象。線程安全的。
8.1.2. prototype 作⽤域
通過scope=“prototype” 設定bean的類型 ,每次向Spring容器請求擷取Bean都傳回⼀個全新的Bean,相對于"singleton"來說就是不緩存Bean,每次都是⼀個根據Bean定義建立的全新Bean。
8.1.3. Web應⽤中的作⽤域
-
request作⽤域
表示每個請求需要容器建立⼀個全新Bean。⽐如送出表單的資料必須是對每次請求建立⼀個Bean來保持這些表單資料,請求結束釋放這些資料。
-
session作⽤域
表示每個會話需要容器建立⼀個全新Bean。⽐如對于每個⽤戶⼀般會有⼀個會話,該⽤戶的⽤戶資訊需要存儲到會話中,此時可以将該Bean作⽤域配置為session級别。
-
globalSession作⽤域
類似于session作⽤域,其⽤于portlet(Portlet是基于Java的Web元件,由Portlet容器管理,并由容器處理請求,⽣産動态内容)環境的web應⽤。如果在⾮portlet環境将視為session作⽤域。
配置⽅式和基本的作⽤域相同,隻是必須要有web環境⽀持,并配置相應的容器監聽器或攔截器從⽽能應⽤這些作⽤域,⽬前先熟悉概念,後續內建web時講解具體使⽤,⼤家隻需要知道有這些作⽤域就可以了。
8.2. Bean的⽣命周期
對⽐已經學過的servlet ⽣命周期(容器啟動裝載并執行個體化servlet類,初始化servlet,調⽤service⽅法,銷毀servlet)。
同樣對于Spring容器管理的bean也存在⽣命周期的概念
在Spring中,Bean的⽣命周期包括Bean的定義、初始化、使⽤和銷毀4個階段
8.2.1. Bean的定義
在Spring中,通常是通過配置⽂檔的⽅式來定義Bean的。
在⼀個配置⽂檔中,可以定義多個Bean。
8.2.2. Bean 的初始化
預設在IOC容器加載時,執行個體化對象。
Spring bean 初始化有兩種⽅式:
⽅式⼀:在配置⽂檔中通過指定 init-method 屬性來完成。
public class RoleService {
// 定義初始化時需要被調⽤的⽅法
public void init() {
System.out.println("RoleService init...");
}
}
<!-- 通過init-method屬性指定⽅法 -->
<bean id="roleService" class="com.xxxx.service.RoleService" initmethod="init"></bean>
⽅式⼆: 實作 org.springframework.beans.factory.InitializingBean 接⼝。
public class RoleService implements InitializingBean {
@Override
public void afterPropertiesSet() throws Exception {
System.out.println("RoleService init...");
}
}
Bean對象執行個體化過程是在Spring容器初始化時被執行個體化的,但也不是不可改變的,可以通過 lazyinit=“true” 屬性延遲bean對象的初始化操作,此時再調⽤getBean ⽅法時才會進⾏bean的初始化操作
8.2.3. Bean 的使⽤
⽅式⼀:使⽤ BeanFactory
// 得到Spring的上下⽂環境
BeanFactory factory = new ClassPathXmlApplicationContext("spring.xml");
RoleService roleService = (RoleService) factory.getBean("roleService");
⽅式⼆:使⽤ ApplicationContext
// 得到Spring的上下⽂環境
ApplicationContext ac = new ClassPathXmlApplicationContext("spring.xml");
RoleService roleService = (RoleService) ac.getBean("roleService");
8.2.4. Bean的銷毀
實作銷毀⽅式(Spring容器會維護bean對象的管理,可以指定bean對象的銷毀所要執⾏的⽅法)。
步驟⼀:實作銷毀⽅式(Spring容器會維護bean對象的管理,可以指定bean對象的銷毀所要執⾏的⽅法)
步驟⼆:通過 AbstractApplicationContext 對象,調⽤其close⽅法實作bean的銷毀過程
AbstractApplicationContext ctx=new ClassPathXmlApplicationContext("spring.xml");
ctx.close();
IOC/DI-控制反轉和依賴注⼊
将對象執行個體化的建立過程轉交給外部容器(IOC容器 充當⼯⼚⻆⾊)去負責;屬性指派的操作;