本教程原始碼請訪問:tutorial_demo
上篇教程我們學習了如何使用工廠模式解耦,把物件的創建由程式員交給自定義的工廠類,在這篇教程我們將學到如何使用Spring的IOC解決程式的耦合問題,
一、什么是IOC
IOC:Inversion of Control,控制反轉,將創建物件的權力交給框架,過去創建物件由開發人員通過new的方式創建,有了IOC之后,開發人員不需要new了,只需要從Spring容器(我們可以認為是保存物件的容器)中獲取就可以了,創建物件的控制權發生了轉移,由開發人員轉移給了Spring容器或者說轉移給了Spring框架,這種控制權的轉移,我們稱之為控制反轉,
目的:減少計算機程式的耦合,解除代碼之間的依賴關系,
二、使用IOC(第一個Spring程式)
2.1、創建專案
-
在Idea中新建Maven工程;
-
工程創建完成后添加相應的坐標,
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>org.example</groupId> <artifactId>ioc</artifactId> <version>1.0-SNAPSHOT</version> <packaging>jar</packaging> <dependencies> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>5.2.6.RELEASE</version> </dependency> </dependencies> </project>
2.2、添加相關類
2.2.1、創建持久層介面
package org.codeaction.dao;
public interface IAccountDao {
void saveAccount();
}
2.2.2、創建持久層介面實作類
package org.codeaction.dao.impl;
import org.codeaction.dao.IAccountDao;
public class AccountDaoImpl implements IAccountDao {
@Override
public void saveAccount() {
System.out.println("賬戶保存成功");
}
}
2.2.3、創建業務層介面
package org.codeaction.service;
public interface IAccountService {
void saveAccount();
}
2.2.4、創建業務層介面實作類
package org.codeaction.service.impl;
import org.codeaction.dao.IAccountDao;
import org.codeaction.dao.impl.AccountDaoImpl;
import org.codeaction.service.IAccountService;
public class AccountServiceImpl implements IAccountService {
private IAccountDao accountDao = new AccountDaoImpl();
@Override
public void saveAccount() {
accountDao.saveAccount();
}
}
注意這個類的實作,本教程最后會說明,
2.3、添加XML組態檔
XML檔案在resource目錄下,
<?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">
<!--把物件的創建交給spring來管理-->
<bean id="accountDao" ></bean>
<bean id="accountService" ></bean>
</beans>
bean標簽作用:配置讓Spring創建物件,默認情況下呼叫無參建構式,如果沒有無參建構式則不能創建成功,
bean標簽屬性:
- id:為物件在容器中提供一個唯一標識,用于獲取物件;
- class:指定類的全限定類名,用于反射創建物件,默認情況下呼叫無參建構式;
- scope:指定物件的作用范圍,默認是singleton(單例),3.1中會講到;
- init-method:指定類中的初始化方法名稱;
- destroy-method:指定類中銷毀方法名稱,
2.4、添加測驗類
創建帶有main方法的類,用來進行測驗
package org.codeaction.ui;
import org.codeaction.dao.IAccountDao;
import org.codeaction.service.IAccountService;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class AccountUI {
public static void main(String[] args) {
//1.獲取核心容器物件
ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
//2.根據id獲取Bean物件,這個id是在bean標簽中配置的id
IAccountService accountService = (IAccountService) context.getBean("accountService");
IAccountDao accountDao = context.getBean("accountDao", IAccountDao.class);
System.out.println(accountService);
System.out.println(accountDao);
accountService.saveAccount();
}
}
運行main方法,控制臺輸出如下:
org.codeaction.service.impl.AccountServiceImpl@754ba872
org.codeaction.dao.impl.AccountDaoImpl@146ba0ac
賬戶保存成功
2.4.1、BeanFactory和ApplicationContext的區別
- BeanFactory是Spring容器中的頂層介面,ApplicationContext是它的子介面;
- ApplicationContext只要一讀取組態檔,默認情況下就會創建物件;
- BeanFactory什么時候使用時候創建物件;
- ApplicationContext用來創建單例物件,BeanFactory用來創建多例物件,
2.4.2、ApplicationContext介面的實作類
- ClassPathXmlApplicationContext可以加載類路徑下的組態檔,要求組態檔必須在類路徑下,不在的話,加載不了,
- FileSystemXmlApplicationContext可以加載磁盤任意路徑下的組態檔(必須有訪問權限)
- AnnotationConfigApplicationContext用于讀取注解創建容器的,后面的文章會講到,
三、深入說明
3.1、bean的作用范圍和生命周期
bean的作用范圍由bean標簽中的scope屬性設定,scope屬性可以有如下值:
- singleton:默認值,單例的;
- prototype:多例的;
- request:web專案中,Spring創建一個Bean的物件,將物件存入到request域中;
- session:web專案中,Spring創建一個Bean的物件,將物件存入到session域中;
- global session:web專案中,應用在Portlet環境,如果沒有Portlet環境那么globalSession相當于session,
下面我們重點說一下singleton和prototype,
3.1.1、singleton
一個應用只有一個物件的實體,它的作用范圍就是整個應用,
生命周期:
- 物件出生:當應用加載,創建容器時,物件就被創建了;
- 物件活著:只要容器在,物件一直活著;
- 物件死亡:當應用卸載,銷毀容器時,物件就被銷毀了,
3.1.2、prototype
每次訪問物件時,都會重新創建物件實體,
生命周期:
- 物件出生:當使用物件時,創建新的物件實體;
- 物件活著:只要物件在使用中,就一直活著;
- 物件死亡:當物件長時間不用時,被Java的垃圾回收器回收了,
3.1.3、案例(代碼基于第一個Spring程式)
3.1.3.1、修改業務層實作類
添加init和destroy方法
package org.codeaction.dao.impl;
import org.codeaction.dao.IAccountDao;
public class AccountDaoImpl implements IAccountDao {
@Override
public void saveAccount() {
System.out.println("賬戶保存成功");
}
public void init() {
System.out.println("dao init");
}
public void destroy() {
System.out.println("dao destroy");
}
}
3.1.3.2、修改持久層實作類
添加init和destroy方法
package org.codeaction.dao.impl;
import org.codeaction.dao.IAccountDao;
public class AccountDaoImpl implements IAccountDao {
@Override
public void saveAccount() {
System.out.println("賬戶保存成功");
}
public void init() {
System.out.println("dao init");
}
public void destroy() {
System.out.println("dao destroy");
}
}
3.1.3.3、修改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">
<!--把物件的創建交給spring來管理-->
<!--配置bean時指定初始化和銷毀方法及作用范圍-->
<bean
id="accountDao"
scope="singleton"
init-method="init"
destroy-method="destroy"></bean>
<bean
id="accountService"
scope="prototype"
init-method="init"
destroy-method="destroy"></bean>
</beans>
3.1.3.4、修改測驗類
package org.codeaction.ui;
import org.codeaction.dao.IAccountDao;
import org.codeaction.service.IAccountService;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class AccountUI {
public static void main(String[] args) {
//1.獲取核心容器物件
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
//2.根據id獲取Bean物件,這個id是在bean標簽中配置的id
IAccountService accountService1 = (IAccountService) context.getBean("accountService");
IAccountDao accountDao1 = (IAccountDao) context.getBean("accountDao");
IAccountService accountService2 = (IAccountService) context.getBean("accountService");
IAccountDao accountDao2 = (IAccountDao) context.getBean("accountDao");
System.out.println("accountDao1 == accountDao2 ? " + (accountDao1 == accountDao2));
System.out.println("accountService1 == accountService2 ? " + (accountService1 == accountService2));
//容器銷毀
context.close();
}
}
單步除錯該程式,輸出如下:
dao init
service init
service init
accountDao1 == accountDao2 ? true
accountService1 == accountService2 ? false
dao destroy
通過輸出我們驗證了:
- singleton的bean物件在容器中只會創建一次,并且創建容器時,就被創建了;
- prototype的bean物件在容器中能夠創建多次,當使用時,就創建新的物件;
- singleton的bean物件在容器銷毀時,也被銷毀;
- prototype的bean物件會被垃圾回收(通過控制臺觀察不到),
3.2、實體化bean的三種方式
3.2.1、三種方式說明
- 使用默認無參建構式;
- 使用靜態工廠的方法創建物件;
- 使用實體工廠的方法創建物件,
3.2.2、案例(代碼基于第一個Spring程式)
3.2.2.1、創建靜態工廠類
package org.codeaction.factory;
import org.codeaction.service.IAccountService;
import org.codeaction.service.impl.AccountServiceImpl;
/**
* 模擬一個靜態工廠,創建業務層實作類
*/
public class StaticFactory {
public static IAccountService createAccountService() {
return new AccountServiceImpl();
}
}
3.2.2.2、創建實體工廠類
package org.codeaction.factory;
import org.codeaction.service.IAccountService;
import org.codeaction.service.impl.AccountServiceImpl;
/**
* 模擬一個實體工廠,創建業務層實作類
* 此工廠創建物件,必須現有工廠實體物件,再呼叫方法
*/
public class InstanceFactory {
public IAccountService createAccountService(){
return new AccountServiceImpl();
}
}
3.2.2.3、修改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 id="accountService1" ></bean>
<!--
方式2:使用靜態工廠的方法創建物件
id屬性:指定bean的id,用于從容器中獲取
class屬性:指定靜態工廠的全限定類名
factory-method屬性:指定生產物件的靜態方法
-->
<bean
id="accountService2"
factory-method="createAccountService"></bean>
<!--
方式3:使用實體工廠的方法創建物件
先把工廠的創建交給spring來管理,
然后在使用工廠的bean來呼叫里面的方法,
factory-bean屬性:用于指定實體工廠bean的id,
factory-method屬性:用于指定實體工廠中創建物件的方法,
-->
<bean id="factory" ></bean>
<bean id="accountService3" factory-bean="factory" factory-method="createAccountService"></bean>
</beans>
3.2.2.4、修改測驗類
package org.codeaction.ui;
import org.codeaction.dao.IAccountDao;
import org.codeaction.service.IAccountService;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class AccountUI {
public static void main(String[] args) {
//1.獲取核心容器物件
ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
//2.根據id獲取Bean物件,這個id是在bean標簽中配置的id
IAccountService accountService1 = (IAccountService) context.getBean("accountService1");
IAccountService accountService2 = (IAccountService) context.getBean("accountService2");
IAccountService accountService3 = (IAccountService) context.getBean("accountService3");
System.out.println(accountService1);
System.out.println(accountService2);
System.out.println(accountService3);
}
}
運行測驗類,控制臺輸出如下:
org.codeaction.service.impl.AccountServiceImpl@42dafa95
org.codeaction.service.impl.AccountServiceImpl@6500df86
org.codeaction.service.impl.AccountServiceImpl@402a079c
四、目前存在的問題
通過這篇教程的學習,我們對IOC有了一個初步的認識,通過IOC將物件創建的權力交給Spring容器,實作控制權的轉移,本篇教程2.2.4中,創建業務層介面的實作類,里面的屬性依然使用new物件的方式賦值,在這里依然沒有解耦,service和dao的物件依然攪在一起,那么怎么解決這個問題呢?下一篇我們將學習DI(依賴注入),DI就可以解決目前存在的問題,
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/182408.html
標籤:Java
