1. 問題分析
代碼目錄如下:
/**
* 賬戶持久層接口
*/
public interface IAccountDao {
/**
* 模擬保存賬戶
*/
void saveAccount();
}
/**
* 賬戶持久層實現類
*/
public class AccountDaoImpl implements IAccountDao {
public void saveAccount() {
System.out.println("保存賬戶 ");
}
}
/**
* 賬戶業務層的接口
*/
public interface IAccountService {
/**
* 模擬保存賬戶
*/
void saveAccount();
}
/**
* 賬戶業務層實現類
*/
public class AccountServiceImpl implements IAccountService {
private IAccountDao accountDao = new AccountDaoImpl();
public void saveAccount() {
accountDao.saveAccount();
}
}
代碼 private IAccountDao accountDao = new AccountDaoImpl();
耦合程度較高,代碼獨立性差。
2.使用工廠模式解耦
首先,我們需要一個創建bean對象的工廠(bean:在計算機英語中,有可重用組件的含義),
這個工廠需要:
-
需要一個配置文件來配置我們的service和dao
配置文件的內容:唯一標識=全限定類名 (key=value)
-
通過讀取配置文件中配置的內容,反射創建對象
Class.forName
Class.forName
:返回與給定的字符串名稱相關聯類或接口的Class對象。
Class.forName
是一個靜態方法,同樣可以用來加載類。該方法有兩種形式:
-
Class.forName(String name, boolean initialize, ClassLoader loader)
參數 name表示的是類的全名;initialize表示是否初始化類;loader表示加載時使用的類加載器 -
Class.forName(String className)
相當於設置了參數 initialize的值爲 true,loader的值爲當前類的類加載器。
public class BeanFactory {
//定義一個properties對象
private static Properties props;
//使用靜態代碼塊爲Properties對象賦值
static {
try {
props = new Properties();
InputStream in = BeanFactory.class.getClassLoader().getResourceAsStream("bean.properties");
props.load(in);
}catch (Exception e){
throw new ExceptionInInitializerError("初始化properties失敗");
}
}
/**
* 根據bean的名稱獲取bean對象
* @param beanName
* @return
*/
public static Object getBean(String beanName){
Object bean =null;
try {
String beanPath = props.getProperty(beanName);
bean = Class.forName(beanPath).newInstance();
}catch (Exception e){
e.printStackTrace();
}
return bean;
}
}
/**
* 賬戶業務層實現類
*/
public class AccountServiceImpl implements IAccountService {
private IAccountDao accountDao = (IAccountDao) BeanFactory.getBean("accountDao");
public void saveAccount() {
accountDao.saveAccount();
}
}
accountService = com.zcj.service.impl.AccountServiceImpl
accountDao = com.zcj.dao.impl.AccountDaoImpl
這樣就可以減少代碼的耦合程度,同時,在我們需要更改接口爲其他接口版本時,就可以通過更改配置文件來更改接口版本。
3.容器
每調用BeanFactory.getBean
就會產生一個新的對象,我們需要記住對象中的屬性是會在調用該函數後重新初始化的。當我們需要獲取的對象是一個單例時,就需要使用容器來存儲單例對象。
public class BeanFactory {
//定義一個properties對象
private static Properties props;
//定義一個map,用於存放我們要創建的對象,我們把它稱之爲容器
private static Map<String,Object> beans;
//使用靜態代碼塊爲Properties對象賦值
static {
try {
props = new Properties();
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){
throw new ExceptionInInitializerError("初始化properties失敗");
}
}
/**
* 根據bean的名稱獲取bean對象
* @param beanName
* @return
*/
public static Object getBean(String beanName){
return beans.get(beanName);
}
}
/**
* 賬戶業務層實現類
*/
public class AccountServiceImpl implements IAccountService {
public void saveAccount() {
IAccountDao accountDao = (IAccountDao) BeanFactory.getBean("accountDao");
accountDao.saveAccount();
}
}
4.IoC(Inversion of Control)
當使用new創建對象時,是app主動的。
當使用工廠模式創建對象時,相當於把主動權從app中讓出,交給factory,app斷開與資源的聯繫,這種控制權的轉移,稱爲控制反轉,可以減低依賴關係,削減耦合程度。
控制反轉(Inversion of Control,縮寫爲IoC),是面向對象編程中的一種設計原則,可以用來減低計算機代碼之間的耦合度。其中最常見的方式叫做依賴注入(Dependency Injection,簡稱DI),還有一種方式叫“依賴查找”(Dependency Lookup)。通過控制反轉,對象在被創建的時候,由一個調控系統內所有對象的外界實體將其所依賴的對象的引用傳遞給它。也可以說,依賴被注入到對象中。
spring能主動地幫我們做好IoC。