spring-ioc筆記
程式的耦合:
-
耦合:程式間的依賴關係。包括:
-
類之間的依賴
-
方法間的依賴
-
-
解耦:降低程式間的依賴關係
-
實際開發:
-
應該做到,編譯期不依賴,執行時才依賴
-
-
解耦思路:
-
第一步:使用反射來建立物件,而避免使用new關鍵字。
-
第二步:通過讀取配置檔案來獲取要建立的物件全限定類名。
-
1.1、bean工廠
自己建立一個簡單的beanfactory(bean工廠)
-
BeanFactory
/**
* 一個建立Bean物件的工廠
*
* Bean:在計算機英語中,有可重用元件的含義。
* JavaBean:用java語言編寫的可重用元件。
* 他就是建立我們的service和dao物件的。
*
* 第一個:需要一個配置檔案來配置我們的service和dao
* 配置的內容:唯一標識=全限定類名(key=value)
* 第二個:通過讀取配置檔案中配置內容,反射建立物件
*
* 我的配置檔案可以是xml也可以是properties
*/
public class BeanFactory {
//定義一個Properties物件
private static Properties props;
//使用靜態程式碼塊位properties物件賦值
try {
//例項化物件
props = new Properties();
//獲取properties檔案的流物件
//InputStream in = new FileInputStream(""); //這是要輸入具體地址,一般要採用相對地址,也就是下面這個
InputStream in = BeanFactory.class.getClassLoader().getResourceAsStream("bean.properties");
props.load(in);
}catch (Exception e){
}
}
public static Object getBean(String beanName){
Object bean = null;
try {
String beanPath = props.getProperty(beanName);
bean = Class.forName(beanPath).newInstance(); //每次都會呼叫預設建構函式建立物件
}catch (Exception e){
System.out.println("初始化錯誤");
}
return bean;
}
}
-
UserMapper介面(相當與UserDao)
public interface UserMapper {
void queryUser();
}UserMapperImpl(相當與UserDaoImpl)
public class UserMapperImpl implements UserMapper {
public void queryUser() {
System.out.println("查詢");
}
} -
UserService介面
public interface UserService {
void queryUser();
}UserServiceImpl
public class UserServiceImpl implements UserService {
//private UserMapper userMapper = new UserMapperImpl();
private UserMapper userMapper = (UserMapper) BeanFactory.getBean("UserMapper");
public void queryUser() {
userMapper.queryUser();
}
} -
Client(測試類)
public class Client {
public static void main(String[] args) {
//UserService userService = new UserServiceImpl();
UserService userService = (UserService)BeanFactory.getBean("UserService");
userService.queryUser();
}
} -
bean.properties
UserService=com.gzk.service.UserServiceImpl
UserMapper=com.gzk.mapper.UserMapperImpl -
執行Client
1.2、單例,原型
我們的1.1中的bean工廠是單例的還是原型的呢?
有程式碼來解釋:
public class UserServiceImpl implements UserService {
private UserMapper userMapper = (UserMapper) BeanFactory.getBean("UserMapper");
private int i = 1;
public void queryUser() {
userMapper.queryUser();
System.out.println(i++);
}
}
public class Client {
public static void main(String[] args) {
for(int i=0;i<5;i++) {
UserService userService = (UserService) BeanFactory.getBean("UserService");
System.out.println(userService);
userService.queryUser();
}
}
}
由程式碼測試可得,我們這個工廠是原型的
原型(也就是說,每呼叫一次就new一個物件【一般用在存線上程安全問題的情況下使用】)
單例(也就是說,無論呼叫多少次都只有一個例項物件【只被建立你一次,物件只會初始化一側】)
物件被建立多次,執行效率就會比較低,所以我們一般情況採用單例
所以我們要將這個工廠改造成單例的
/**
* 一個建立Bean物件的工廠
*
* Bean:在計算機英語中,有可重用元件的含義。
* JavaBean:用java語言編寫的可重用元件。
* javvabean >> 實體類(javabean遠遠大於實體類)
* 他就是建立我們的service和dao物件的。
*
* 第一個:需要一個配置檔案來配置我們的service和dao
* 配置的內容:唯一標識=全限定類名(key=value)
* 第二個:通過讀取配置檔案中配置內容,反射建立物件
*
* 我的配置檔案可以是xml也可以是properties
*/
public class BeanFactory {
//定義一個Properties物件
private static Properties props;
//建立一個HashMap容器
private static Map<String ,Object> beans;
//使用靜態程式碼塊位properties物件賦值
static{
try {
//例項化物件
props = new Properties();
//獲取properties檔案的流物件
//InputStream in = new FileInputStream(""); //這是要輸入具體地址,一般要採用相對地址,也就是下面這個
InputStream in = BeanFactory.class.getClassLoader().getResourceAsStream("bean.properties");
props.load(in);
//例項化容器
beans = new HashMap<String, Object>();
//取出配置檔案中所有的key
Enumeration keys = props.keys();
//遍歷列舉
while(keys.hasMoreElements()){
//取出每一個key
String key = keys.nextElement().toString();
//根據key獲取value
String beanPath = props.getProperty(key);
//通過反射建立物件
Object value = Class.forName(beanPath).newInstance();
//把key和value存入容器中
beans.put(key ,value);
}
}catch (Exception e){
System.out.println("錯誤");
}
}
public static Object getBean(String beanName){
return beans.get(beanName);
}
}
1.3、IOC控制反轉
傳統方式
IOC方式
控制反轉(Inversion of Control,英文縮寫IOC)把建立物件的權利交給框架,是框架的重要特徵,並非面向物件程式設計的專業術語,它包括依賴注入(Dependency Injection,簡稱DI)和依賴查詢(Dependency Lookup)。
除main函式之外,其他程式碼都在【1.1、bean工廠】中。
Client
public class Client {
/**
* 獲取spring的IOC核心容器,並根據id獲取物件
*
* ApplicationContext的三個常用實現類
* ClassPathXmlApplicationContext --> 它可以載入路徑下的配置檔案,要求配置檔案必須在類路徑下。不在的話,載入不了【相對路徑】
* FileSystem.XmlApplicationContext --> 它可以載入磁碟任意路徑下的配置檔案(必須要有訪問許可權)【絕對路徑】
* AnnotationConfigApplicationContext --> 它是用於讀取註解建立容器的
*
* 核心容器的兩個介面引發的問題
* ApplicationContext:
* 它在構建核心容器時,建立物件採取的策略時採用立即載入的方式。也就是說,只要一讀取完配置檔案馬上就建立配置檔案中配置的物件。(單例時)
* 它在構建核心容器時,建立物件採取的策略時採用延遲載入的方式。也就是說,什麼時候根據id獲取物件了,什麼時候才真正的建立物件。(原型時)
* BeanFactory:
* 它在構建核心容器時,建立物件採取的策略時採用延遲載入的方式。也就是說,什麼時候根據id獲取物件了,什麼時候才真正的建立物件。
* @param args
*/
public static void main(String[] args) {
//1.獲取核心容器物件
//ApplicationContext context = new FileSystemXmlApplicationContext("D:\\idea-workspace\\my-site\\spring02-study\\spring-01\\src\\main\\resources\\bean.xml");
ApplicationContext context = new ClassPathXmlApplicationContext("bean.xml");
//2.根據id查詢對應的bean例項
//UserService userService = (UserService) context.getBean("userService");
UserService userService = context.getBean("userService", UserService.class);
userService.queryUser();
}
}
1.4、bean的三種建立方式
1.5、bean物件的作用範圍
bean標籤的scopc屬性:
-
作用:用於指定bean的作用範圍
-
取值:
-
singleton:單例的(預設值)
-
prototype:原型的
-
request:作用於web應用的請求範圍
-
session:作用於web應用的會話範圍
-
global-session:作用於叢集環境的會話範圍【當有多臺伺服器時(全域性會話範圍)】,當不是叢集環境時,它就是session
-
<bean id="userService" class="com.gzk.service.UserServiceImpl" scope="prototype"/>
bean物件的生命週期
-
單例物件:
-
出生:當容器建立時物件出生
-
活著:只要容器還在,物件一直活著
-
死亡:容器銷燬,物件消亡
-
總結:單例物件的生命週期和容器相同
-
-
原型物件(多例):
-
出生:當我們使用物件時spring框架為我們建立
-
活著:物件只要是在使用過程中就一直活著
-
死亡:當物件長時間不用,且沒有別的物件引用時,由Java垃圾回收器回收
-
public class AccountServiceImpl implements AccountService {
public AccountServiceImpl(){
System.out.println("AccountServiceImpl的無參建構函式");
}
public void init(){
System.out.println("物件初始化!");
}
public void getAccountService(){
System.out.println("方法執行中");
}
public void destroy(){
System.out.println("物件銷燬!");
}
}
public interface AccountService {
void init();
void getAccountService();
void destroy();
}
public class Client {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("bean.xml");
AccountService accountService = null;
for(int i=0;i<5;i++) {
accountService = context.getBean("accountService", AccountService.class);
System.out.println(accountService);
accountService.getAccountService();
System.out.println();
}
}
<!--單例-->
<bean id="accountService" class="com.gzk.service.AccountServiceImpl" scope="singleton" init-method="init" destroy-method="destroy"/>
<!--原型-->
<!--<bean id="accountService" class="com.gzk.service.AccountServiceImpl" scope="prototype" init-method="init" destroy-method="destroy"/>-->
單例:
原型:
1.6、spring的依賴注入
依賴注入:Dependency Injection
IOC的作用:
-
降低程式間的耦合(依賴關係)
依賴關係的管理:
-
以後都交給spring來維護
在當前類需要用到其他類的物件,由spring為我們提供,我們只需要在配置檔案中說明
依賴注入:
-
能注入的資料:有三類
-
基本型別和String
-
其他bean型別(在配置檔案中或者註解配置過的bean)
-
複雜型別/集合型別
-
-
注入的方式:有三種
-
使用建構函式
-
使用set方法
-
使用註解
-
1.6.1、建構函式注入:
-
使用的標籤:constructor-arg
-
標籤出現的位置:bean標籤的內部
-
標籤中的屬性
-
type:用於指定要注入大的資料的資料型別,該資料型別也是建構函式中某個或某些引數的型別
-
index:用於指定要注入的資料給建構函式中指定索引位置的引數賦值。索引的位置是從0開始
-
name:用於指定給建構函式中指定名稱的引數賦值 =====以上三個用於指定給建構函式中某些引數賦值=====
-
value:用於提供基本型別和String型別的資料
-
ref:用於指定其他的bean型別資料。他指的就是再spring的IOC核心容器中出現過的bean物件
-
-
優勢:
-
在獲取bean物件時,注入資料是必須的操作,否則物件無法建立成功。
-
<bean id="constructorService" class="com.gzk.service.ConstructorService">
<constructor-arg index="0" value="111"/>
<constructor-arg index="1" value="張三"/>
<constructor-arg index="2" ref="dateTime"/>
</bean>
<bean id="dateTime" class="java.util.Date"/>
1.6.2、set方法注入
-
使用的標籤:property
-
標籤出現的位置:bean標籤的內部
-
標籤中的屬性
-
name:用於指定注入時所呼叫的set方法
-
value:用於提供基本型別和String型別的資料
-
ref:用於指定其他的bean型別資料。他指的就是再spring的IOC核心容器中出現過的bean物件
-
-
優勢
-
建立物件時,沒有明確的限制
-
-
ss
<bean id="constructorService2" class="com.gzk.service.ConstructorService2">
<property name="id" value="111"/>
<property name="name" value="張三"/>
<property name="dateTime" ref="dateTime"/>
</bean>
<bean id="dateTime" class="java.util.Date"/>
-
複雜型別注入/集合型別注入
-
用於給List結構【set<T>,List<T>,String[]】集合注入的標籤:
-
list array set【三者可以混用,無明確規定】
-
-
用於給Map結構【Map<K ,V>,Properties】集合注入的標籤:
-
map props【二者可以混用,無明確規定】
-
-
public class SetService {
private String name;
private String[] stringList;
private List<String> list;
private Set<String> set;
private Map<String ,Object> map;
private Properties properties;
public void setName(String name) {
this.name = name;
}
public void setStringList(String[] stringList) {
this.stringList = stringList;
}
public void setList(List<String> list) {
this.list = list;
}
public void setSet(Set<String> set) {
this.set = set;
}
public void setMap(Map<String, Object> map) {
this.map = map;
}
public void setProperties(Properties properties) {
this.properties = properties;
}
<bean id="setService" class="com.gzk.service.SetService">
<property name="name" value="張三"/>
<property name="stringList">
<list>
<value>AAA</value>
<value>BBB</value>
<value>CCC</value>
</list>
</property>
<property name="list">
<array>
<value>AAA</value>
<value>BBB</value>
<value>CCC</value>
</array>
</property>
<property name="set">
<set>
<value>AAA</value>
<value>BBB</value>
<value>CCC</value>
</set>
</property>
<property name="map">
<props>
<prop key="1">DDD</prop>
<prop key="2">EEE</prop>
<prop key="3">FFF</prop>
</props>
</property>
<property name="properties">
<map>
<entry key="1" value="DDD"/>
<entry key="2">
<value>EEE