文章目錄
1. 從jdbc的標準代碼瞭解程序的耦合性
import java.sql.Connection;
import java.sql.Driver;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
public class JdbcDemo1 {
public static void main(String[] args) throws Exception{
//Class.forName("com.mysql.jdbc.Driver");
DriverManager.registerDriver(new com.mysql.jdbc.Driver());
Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/test?characterEncoding=utf-8", "root", "123456");
PreparedStatement ps = conn.prepareStatement("insert into account(name, money) values(?,?)");
ps.setString(1,"bbb");
ps.setFloat(2,2000);
ps.executeUpdate();
ps.close();
conn.close();
}
}
程序執行過程,
- 註冊驅動
- 獲取鏈接
- 獲取操作數據庫的預處理對象
- 執行sql語句,得到結果集(本例是新增操作,故沒有結果集)
- 遍歷結果集
- 依次關閉結果集,預處理對象,連接。
當我們把相關jar包去掉
可以發現,沒有myql的驅動,將無法編譯。此時可以理解爲程序的耦合。
簡單理解耦合:程序之間的依賴關係
耦合包括:
- 類之間的依賴關係
- 方法之間的依賴關係
如何解耦
降低程序之間的依賴
- 編譯期間不依賴,運行時才依賴(典型例子:
DriverManager.registerDriver()
轉化爲用反射來加載驅動Class.forName
)
解耦的思路:
- 使用反射來創建對象,避免使用new關鍵字。
- 通過讀取配置文件來獲取要創建的對象全限定類名。
2. 基本的三層
dao層
package com.ssm.dao.impl;
import com.ssm.dao.IAccountDao;
public class AccountDao implements IAccountDao {
public void saveAccount() {
System.out.println("賬戶已保存!");
}
}
package com.ssm.dao;
/**
* 賬戶的持久層接口
*/
public interface IAccountDao {
//模擬保存賬戶
void saveAccount();
}
service層
package com.ssm.service.impl;
import com.ssm.dao.IAccountDao;
import com.ssm.dao.impl.AccountDao;
import com.ssm.factory.BeanFactory;
import com.ssm.service.IAccountService;
import java.util.PriorityQueue;
public class AccountServiceImpl implements IAccountService {
private IAccountDao accountDao = new AccountDao();
public void saveAccount(){
accountDao.saveAccount();
}
}
package com.ssm.service;
/**
* 賬戶業務層
*/
public interface IAccountService {
//保存賬戶
void saveAccount();
}
模擬的controller層(servlet)
package com.ssm.ul;
import com.ssm.factory.BeanFactory;
import com.ssm.service.IAccountService;
import com.ssm.service.impl.AccountServiceImpl;
/**
模擬表現層
*/
public class Client {
public static void main(String[] args){
IAccountService as = new AccountServiceImpl();
as.saveAccount();
}
}
存在的問題
- controller層調用service層使用了new關鍵字,如果
AccountServiceImpl.java
不存在,則會編譯不通過。代碼耦合性太強。 - 假如有多次調用controller(main()多次執行),每次執行都會new一個AccountServiceImpl,AccountDao。假如執行5次controller,就會有10個service和dao對象在內存中。在高併發下性能不好
3. 使用工廠模式解決代碼耦合性問題
什麼是簡單工廠模式
簡單工廠模式又稱爲靜態工廠模式,它屬於創建型模式,但非23種設計模式之一。它可以根據傳入的參數來返回不同類的實例。簡單工廠模式專門定義一個類來負責創建其他類的實例,被創建的實例通常都具有共同的父類。
角色 | 作用 |
---|---|
工廠角色(Creator) | 是簡單工廠模式的核心,它負責實現創建所有具體產品類的實例。工廠類可以被外界直接調用,創建所需的產品對象。 |
抽象產品角色(Product) | 是所有具體產品角色的父類,它負責描述所有實例所共有的公共接口。 |
具體產品角色(Concrete Product) | 繼承自抽象產品角色,一般爲多個,是簡單工廠模式的創建目標。工廠類返回的都是該角色的某一具體產品。 |
簡單工廠模式如何解耦(後續有待深究)
直接new一個對象是最簡單的創建對象的方式。但是它也加強了兩個對象A和B之間的耦合性。使用簡單工廠模式,在A想使用B對象的時候,可以向工廠角色(Creator)請求創建一個實例對象,這樣就避免了直接實例化B對象。降低了耦合性。
使用工廠模式解決代碼耦合
package com.ssm.factory;
import java.io.IOException;
import java.io.InputStream;
import java.util.Properties;
/**
* 創建一個bean對象的工廠
*/
public class BeanFactory {
private static Properties props;
static {
try {
props = new Properties();
//getResourceAsStream(String path):默認則是從ClassPath根下獲取,path不能以’/'開頭,最終是由ClassLoader獲取資源。
InputStream in = BeanFactory.class.getClassLoader().getResourceAsStream("bean.properties");
System.out.println(in);
props.load(in);
} catch (IOException e) {
e.printStackTrace();
throw new ExceptionInInitializerError("初始化properties失敗!");
}
}
/**
* 根據Bean名稱獲取Bean對象
* @param beanName
* @return
*/
public static Object getBean(String beanName){
Object bean = null;
try {
String beanPath = props.getProperty(beanName);
System.out.println("beanPath:"+beanPath);
bean = Class.forName(beanPath).newInstance();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
return bean;
}
}
工廠讀取的配置文件
accountService=com.ssm.service.impl.AccountServiceImpl
accuountDao=com.ssm.dao.impl.AccountDao
修改各處的對象實例化方式
比如controller
//IAccountService as = new AccountServiceImpl();
IAccountService as = (IAccountService) BeanFactory.getBean("accountService");
4. 使用單例模式調對象複用性
前面已經使用簡單工廠模式降低了Client
對AccountServiceImpl
的耦合性,但是代碼仍然存在一個問題,那就是每次調用main()
都會讓BeanFactory.getBean()
實例化一個對象,多次調用就會有多個實例對象產生,從而影響代碼性能。那麼如何讓程序在運行中只讓一個類只實例化一個對象呢?
讓工廠不僅負責實例化對象還管理對象
前面實例化對象的工作是交給BeanFactory
的,每次有實例化對象的請求過來,它就是實例化一個請求。那麼能不能讓BeanFactory
先實例化所有對象,把它們管理起來。每次有實例化請求過來,就返回需要實例化對象的引用。這樣就能保證內存中IAccountService
只有一個實例化對象。
修改BeanFactory
修改靜態代碼塊
static {
try {
props = new Properties();
//getResourceAsStream(String path):默認則是從ClassPath根下獲取,path不能以’/'開頭,最終是由ClassLoader獲取資源。
InputStream in = BeanFactory.class.getClassLoader().getResourceAsStream("bean.properties");
props.load(in);
beans = new HashMap<String, Object>();
Enumeration keys = props.keys();
while (keys.hasMoreElements()){
String key = keys.nextElement().toString();
String keyPath = props.getProperty(key);
Object bean = Class.forName(keyPath).newInstance();
beans.put(key,bean);
}
} catch (Exception e) {
e.printStackTrace();
throw new ExceptionInInitializerError("初始化properties失敗!");
}
}
此時,BeanFactory中已經有所需要的對象實例了,那麼獲取bean的方法也要改
public static Object getBean(String beanName){
return beans.get(beanName);
}
什麼是單例模式
單例模式(Singleton Pattern)是 Java 中最簡單的設計模式之一。與前面說的簡單工廠模式一樣,這種類型的設計模式也屬於創建型模式,它提供了一種創建對象的最佳方式。
這種模式涉及到一個單一的類,該類負責創建自己的對象,同時確保只有單個對象被創建。這個類提供了一種訪問其唯一的對象的方式,可以直接訪問,不需要實例化該類的對象。
5. 開始Spring框架
抽象前面的模型
更改前的
更改後的
5.1 控制反轉IOC
百度詞條解釋
作用:降低程序代碼的耦合(解除代碼間的依賴關係)
5.2 創建一個spring工程
首先,創建一個基本的maven工程。
配置pom文件,導入jar包
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.1.0.RELEASE</version>
</dependency>
創建配置文件bean.xml
- 創建配置文件
- 導約束
- 把對象的創建交給spring來管理
<?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">
<!--把對象的創建交給spring來管理-->
<bean id="accountService" class="com.ssm.service.impl.AccountServiceImpl" ></bean>
<bean id="accuountDao" class="com.ssm.dao.impl.AccountDao"></bean>
</beans>
Client的配置
public class Client {
public static void main(String[] args){
ApplicationContext applicationContext = null;
applicationContext = new ClassPathXmlApplicationContext("bean.xml");
//兩種獲取實例的方式
IAccountService as = (IAccountService) applicationContext.getBean("accountService");
IAccountDao accountDao = applicationContext.getBean("accuountDao",IAccountDao.class);
System.out.println(as);
System.out.println(accountDao);
}
}
ApplicationContext
的三個實現類:
ClassPathXmlApplicationContext
:加載類路徑下的配置文件,要求配置文件必須在類路徑下。否則加載不了。FileSystemXmlApplicationContext
:加載磁盤任意路徑下的配置文件(必須要有訪問權限)AnnotationConfigApplicationContext
:用於讀取註解創建容器。
核心容器的兩個接口引發
ApplicationContext