一、Spring框架概述
1、簡介
Spring是一個開放源代碼的設計層面框架,他解決的是業務邏輯層和其他各層的鬆耦合問題,因此它將面向接口的編程思想貫穿整個系統應用。Spring是於2003 年興起的一個輕量級的Java 開發框架,由Rod Johnson創建。簡單來說,Spring是一個分層的JavaSE/EE full-stack(一站式) 輕量級開源框架。
Spring的核心是控制反轉(IoC)和麪向切面(AOP)。簡單來說,Spring是一個分層的JavaSE/EEfull-stack(一站式)輕量級開源框架。
2、優勢
方便解耦,簡化開發;
AOP編程的支持;
聲明式事務的支持;
方便程序的測試;
方便集成各種優秀框架;
降低JavaEE API的使用難度;
3、Spring的體系結構
二、程序間耦合
1、編寫JDBC的工程代碼用於分析程序的耦合
正常運行的代碼:
package com.zibo.jdbc;
import java.sql.*;
public class MyJdbc {
public static void main(String[] args) throws SQLException {
//1、註冊驅動-問題:在沒有這個com.mysql.jdbc.Driver包的時候程序是不能正常編譯的,
//這就是程序的耦合(可以簡單德認爲就是程序間的依賴關係)
DriverManager.registerDriver(new com.mysql.jdbc.Driver());
//2、獲取連接
Connection connection = DriverManager.getConnection("jdbc:mysql://rm-wz94daz94uktn3x7x4o.mysql.rds.aliyuncs.com/zibo?useUnicode=true&characterEncoding=UTF-8","root","zibo@709570094");
//3、獲取操作數據庫的預處理對象
PreparedStatement preparedStatement = connection.prepareStatement("select * from student");
//4、執行SQL,得到結果集
ResultSet resultSet = preparedStatement.executeQuery();
//5、遍歷結果集
while (resultSet.next()){
System.out.println(resultSet.getString("name"));
}
//6、釋放資源
resultSet.close();
preparedStatement.close();
connection.close();
}
}
說明:
當我們將程序包註釋的時候,程序編譯就會報錯,體現了程序之間相互依賴(即程序之間的耦合性),如圖:
程序的耦合:
耦合:(簡單認爲)程序間的依賴關係;
包括:類之間的依賴,方法之間的依賴;
解耦:降低程序間的依賴關係;
實際開發中:編譯器不依賴,運行時才依賴;
解耦的思路:
1、使用反射來創建對象,而避免使用New關鍵字;
2、通過讀取配置文件來獲取要創建的對象全限定類名;
註冊驅動的另一種方法:
Class.forName("com.mysql.jdbc.Driver");
//說明:這種方式雖然也不能運行,但是這種情況出現的錯誤是運行時異常,而不是編譯器異常;
//但是,這種寫法把數據庫寫死了,可以通過讀取配置文件的方式讀取要創建的對象的全限定類名;
2、曾經代碼中的問題
代碼示例:
IAccountDao接口:
package com.zibo.dao;
public interface IAccountDao {
void saveAccount();
}
IAccountDaoImpl接口實現類:
package com.zibo.dao.impl;
import com.zibo.dao.IAccountDao;
public class IAccountDaoImpl implements IAccountDao {
@Override
public void saveAccount() {
System.out.println("保存了賬戶");
}
}
IAccountService接口:
package com.zibo.services;
/**
* 業務層接口
*/
public interface IAccountService {
/**
* 模擬保存賬戶
*/
void saveAccount();
}
IAccountServiceImpl接口是實現類:
package com.zibo.services.impl;
import com.zibo.dao.IAccountDao;
import com.zibo.dao.impl.IAccountDaoImpl;
import com.zibo.services.IAccountService;
/**
* 賬戶的業務層實現類
*/
public class IAccountServiceImpl implements IAccountService {
private IAccountDao iAccountDao = new IAccountDaoImpl();
@Override
public void saveAccount() {
iAccountDao.saveAccount();
}
}
Client類:
package com.zibo.ui;
import com.zibo.services.IAccountService;
import com.zibo.services.impl.IAccountServiceImpl;
/**
* 模擬表現層,調用業務層
*/
public class Client {
public static void main(String[] args) {
IAccountService service = new IAccountServiceImpl();
service.saveAccount();
}
}
各文件位置:
說明:
本示例中各程序之間有着嚴重的依賴關係,一旦某個程序出錯會導致整個程序編譯失敗!那該怎麼解決這個問題呢?
編寫工廠類和配置文件!
3、編寫工廠類和配置文件
BeanFactory工廠類:
package com.zibo.factory;
import java.io.IOException;
import java.io.InputStream;
import java.util.Properties;
/**
* 創建Bean對象的工廠:
* 創建我們的service和dao對象;
* 如何創建:
* 1、需要一個配置文件來配置service和dao;
* 配置內容:唯一標識=全限定類名(key=value)
* 2、通過服務配置文件中的內容,反射創建對象;
* 配置文件可以是xml也可以是properties;
*
* Bean:在計算機英語中,有可重用組件的含義;
* JavaBean不等於實體類,是用Java語言編寫的可重用組件;
*
*
*/
public class BeanFactory {
//定義一個Properties對象
private static Properties properties;
//使用靜態代碼塊爲properties賦值
static {
//實例化對象
properties = new Properties();
//獲取properties文件的流對象
InputStream in = BeanFactory.class.getClassLoader().getResourceAsStream("bean.properties");
try {
properties.load(in);
} catch (IOException e) {
throw new ExceptionInInitializerError("初始化properties失敗!");
}
}
public static Object getBean(String beanName){
Object bean = null;
String property = properties.getProperty(beanName);
System.out.println(property);
try {
bean = Class.forName(property).getDeclaredConstructor().newInstance();
} catch (Exception e){
e.printStackTrace();
}
return bean;
}
}
bean.properties配置配置文件:
accountService=com.zibo.services.impl.IAccountServiceImpl
accountDao=com.zibo.dao.impl.IAccountDaoImpl
使用工廠類進行解耦:
//改寫IAccountServiceImpl代碼:
// private IAccountDao iAccountDao = new IAccountDaoImpl();
private IAccountDao iAccountDao = (IAccountDao)BeanFactory.getBean("AccountDao");
//改寫Client代碼:
// IAccountService service = new IAccountServiceImpl();
IAccountService service = (IAccountService)BeanFactory.getBean("AccountService");
運行結果(成功):
4、分析工廠模式中的問題並改造
分析問題:
打印一下IAccountService對象:
for (int i = 0; i <5 ; i++) {
IAccountService service = (IAccountService)BeanFactory.getBean("accountService");
System.out.println(service);
}
打印結果:
說明:
可見,這個程序是多例模式,程序的運行創建了多個對象,單例模式中對象智慧被創建一次,而類中的成員也只會被初始化一次;
多例模式中,由於對象被創建多次,運行效率沒有單例模式高,但是單例模式存在線程問題;
改造:
思想:將創建對象的方法寫在靜態代碼塊中,類加載的時候只執行一次,創建的對象存起來,以遍重複使用;
改造後的代碼:
package com.zibo.factory;
import java.io.IOException;
import java.io.InputStream;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;
/**
* 創建Bean對象的工廠:
* 創建我們的service和dao對象;
* 如何創建:
* 1、需要一個配置文件來配置service和dao;
* 配置內容:唯一標識=全限定類名(key=value)
* 2、通過服務配置文件中的內容,反射創建對象;
* 配置文件可以是xml也可以是properties;
*
* Bean:在計算機英語中,有可重用組件的含義;
* JavaBean不等於實體類,是用Java語言編寫的可重用組件;
*
*
*/
public class BeanFactory {
//定義一個Properties對象
private static Properties properties;
//定義一個Map,擁有存放我們要創建的對象,我們把它稱之爲容器
private static Map<String,Object> beans;
//使用靜態代碼塊爲properties賦值
static {//靜態代碼塊只在類加載的時候執行一次
//實例化對象
properties = new Properties();
//獲取properties文件的流對象
InputStream in = BeanFactory.class.getClassLoader().getResourceAsStream("bean.properties");
try {
properties.load(in);
//實例化容器
beans = new HashMap<>();
//去除配置文件中所有的key
Enumeration<Object> keys = properties.keys();
//遍歷枚舉
while (keys.hasMoreElements()){
//取出每個key
String key = keys.nextElement().toString();
//根據key獲取value
String property = properties.getProperty(key);
//反射創建對象
Object value = Class.forName(property).getDeclaredConstructor().newInstance();//newInstance表明每次都會調用默認構造函數創建對象
//把key和value存入容器中
beans.put(key,value);
}
} catch (Exception e) {
throw new ExceptionInInitializerError("初始化properties失敗!");
}
}
public static Object getBean(String beanName){
return beans.get(beanName);
// Object bean = null;
// String property = properties.getProperty(beanName);
//// System.out.println(property);
// try {
// bean = Class.forName(property).getDeclaredConstructor().newInstance();//newInstance表明每次都會調用默認構造函數創建對象
// } catch (Exception e){
// e.printStackTrace();
// }
// return bean;
}
}
運行結果:
注意:無論是業務層還是持久層很少包含可以修改的類成員(因爲對象是一個,在多線程的情況下容易出錯,變量可聲明在方法中);