1. 程式人生 > 實用技巧 >JavaWeb - Sping & Sping IOC

JavaWeb - Sping & Sping IOC

JavaWeb - Sping & Sping IOC

目錄

1 Sping

1.1 Sping 概念

Spring是分層的 Java SE/EE應用 full-stack(全棧式) 輕量級開源框架

  • 全棧式:

    • 對各種主流技術和框架都進進行了整合,同時對三層架構都提供瞭解決方案
  • 輕量級:

    • 輕量級和重量級的劃分主要依據服務使用的多少,啟動時資源載入的多少及耦合度等等

提供了表現層 SpringMVC

持久層 Spring JDBC Template

業務層事務管理,物件控制等眾多的企業級應用技術

還能整合開源世界眾多著名的第三方框架和類庫

1.1.1 Sping 兩大核心:
  • IOC(Inverse Of Control:控制反轉),把物件的建立權交給sping

  • AOP(Aspect Oriented Programming:面向切面程式設計),在不修改原始碼的情況下對方法進行增強

1.1.2 耦合和解耦的概念

耦合:程式間的依賴關係

解耦:降低程式間的依賴關係,並不是完全消除

例項:

//1. 註冊驅動 存在編譯期依賴,這就是耦合重的體現
//new 關鍵字就會存在編譯期依賴,解耦思路為去掉new改用反射
DriverManager.registerDriver(new com.mysql.jdbc.Driver());

解耦:使用反射機制讓其編譯期不依賴,執行期才依賴,但還存在硬編碼問題
Class.forName("com.mysql.jdbc.Driver");

//最終解耦思路為:配置檔案+反射,自定義IOC用到此思路
1.1.3 Sping 的優點
  • 方便解耦,簡化開發

    • Spring就是一個容器,可以將所有物件建立和關係維護交給Spring管理
  • AOP程式設計的支援

    • Spring提供面向切面程式設計,方便實現程式進行許可權攔截,執行監控等功能
  • 宣告式事務的支援

    • 通過配置完成事務的管理,無需手動程式設計
  • 方便測試,降低JavaEE API的使用

    • Spring對Junit4支援,可以使用註解測試
  • 方便整合各種優秀框架

    • 不排除各種優秀的開源框架,內部提供了對各種優秀框架的直接支援

2 Spring IOC

2.1 IOC的概念

控制反轉(Inverse Of Control)不是什麼技術,而是一種設計思想。它的目的是指導我們設計出更加鬆耦合的程式

  • 控制:在java中指的是物件的控制權限(建立、銷燬)
  • 反轉:指的是物件控制權由原來 由開發者在類中手動控制 反轉到 由Spring容器控制

2.2 自定義IOC容器

核心思想為將物件的建立通過反射機制來實現進而實現解耦,再通過配置檔案的方式來實現反射建立物件時產生的硬編碼問題

步驟:

  • 建立maven專案,匯入需要使用包的座標
<dependencies>
//dom4j 用來解析xml檔案
<dependency>
<groupId>dom4j</groupId>
<artifactId>dom4j</artifactId>
<version>1.6.1</version>
</dependency>
<dependency>
<groupId>jaxen</groupId>
<artifactId>jaxen</artifactId>
<version>1.1.6</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
</dependencies>
  • 編寫userDao介面及實現類,編寫userService介面及實現類並在實現類中建立userDao例項物件並呼叫方法,最後編寫測試類建立userService例項物件並呼叫方法
  • 編寫bean.xml 檔案,將需要容器管理類編寫到該檔案中
<beans>
    <bean id = "userDao" class = "com.lagou.dao.userDaoImpl"></bean>
    <bean id="userService" class="com.lagou.service.userServiceImpl"></bean>
</beans>
  • 編寫BeanFactory工廠類,用來完成ioc容器的初始化載入和分配容器中的物件
    • 靜態程式碼塊用來載入配置檔案並解析,獲取到配置檔案中類的路徑和名稱,再通過該路徑來建立該類的物件
    • 宣告一個Map型別的成員變數用來儲存解類與其對應的物件
    • 宣告一個方法用來通過類名獲取物件
public class BeanFactory {
    private static Map<String, Object> ioc = new HashMap<>();

    static {
        try {
        //1.將xml檔案讀取,通過呼叫類載入器中的方法
            InputStream stream = BeanFactory.class.getClassLoader().getResourceAsStream("beans.xml");
		//2.解析xml檔案
            SAXReader saxReader = new SAXReader();

            Document document = saxReader.read(stream);
		//3.編寫xpath來指定需要獲取的標籤
            String xpath = "//bean";
		//4.獲取所有的bean 標籤
            List<Element> list = document.selectNodes(xpath);
		//將標籤中的元素取出並新建物件再放入集合中
            for (Element element : list) {
                ioc.put(element.attributeValue("id"),Class.forName(element.attributeValue("class")).newInstance());
            }
        } catch (DocumentException e) {
            e.printStackTrace();
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }
//宣告方法用來獲取物件
    public static Object getBean(String id){
        return ioc.get(id);
    }
}
  • 編寫測試類
userService service = (userService) BeanFactory.getBean("userService");
        service.save();

2.3 Sping 快速使用

  • 建立專案並匯入相關座標
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.1.5.RELEASE</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
</dependencies>
  • 建立Sping 的核心配置檔案,一般使用applicationContext.xml
// applicationContext.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
http://www.springframework.org/schema/beans/spring-beans.xsd">

</beans>
  • 在核心配置檔案中為Bean物件進行配置
<bean id="" class=""></bean>
  • 建立ApplicationContext物件,執行getBean
ApplicationContext applicationContext =
new ClassPathXmlApplicationContext("applicationContext.xml");
XXX xxx = (XXX) applicationContext.getBean("xxx");

2.4 Spring API

2.4.1 Spring 體系

2.4.2 BeanFactory 介面

BeanFactory是 IOC 容器的核心介面,它定義了IOC的基本功能

  • 通過XmlBeanFactory 實現類來載入核心配置檔案
BeanFactory beanFactory =
new XmlBeanFactory(new
ClassPathResource("applicationContext.xml"));
2.4.3 ApplicationContext 介面

代表應用上下文物件,可以獲得spring中IOC容器的Bean物件

常用實現類:

  • ClassPathXmlApplicationContext

    • 是從類的根路徑下載入配置檔案 推薦使用這種
  • FileSystemXmlApplicationContext

    • 從磁碟路徑上載入配置檔案,配置檔案可以在磁碟的任意位置
  • AnnotationConfigApplicationContext

    • 使用註解配置容器物件時,需要使用此類來建立 spring 容器。它用來讀取註解
ApplicationContext app =
new ClassPathXmlApplicationContext("applicationContext.xml");
2.4.4 BeanFactory 和 ApplicationContext 的區別
  • BeanFactory 在第一次呼叫 getBean() 方法的時候建立指定物件的例項
//該語句執行並沒有建立例項
new XmlBeanFactory(new ClassPathResource("xxx.xml"));
  • ApplicationContext在啟動容器時就載入和建立了所有物件的例項
2.4.5 常用方法 - getBean()
  • Object getBean(String name);

    • 根據Bean的id從容器中獲得Bean例項,返回是Object,需要強轉
  • T getBean(Class requiredType);

    • 根據型別從容器中匹配Bean例項,當容器中相同型別的Bean有多個時,則此方法會報錯
    • 一個介面的每個實現類都需要在核心配置檔案中配置
    • 對於有多個實現類的介面該方法不適用
  • T getBean(String name,Class requiredType);

    • 根據Bean的id和型別獲得Bean例項,解決容器中相同型別Bean有多個情況
2.4.6 Bean 標籤配置
2.4.6.1 Bean 標籤的基本配置

基本屬性:

  • id:Bean例項在Spring容器中的唯一標識
  • class:Bean的全限定名

預設情況下它呼叫的是類中的無參建構函式,如果沒有無參建構函式則不能建立成功

2.4.6.2 Bean 標籤的範圍配置

該屬性指物件的的作用範圍,取值如下

  • singleton

    • 不配置時的預設值,代表單例的
  • prototype

    • 代表多例的
  • request

    • WEB專案中,Spring建立一個Bean的物件,將物件存入到request域中
  • session

    • WEB專案中,Spring建立一個Bean的物件,將物件存入到session域中
  • global session

    • WEB專案中,應用在Portlet環境,如果沒有Portlet環境那麼globalSession 相當於 session

單例和多例的比較:

當scope的取值為singleton時

  • Bean的例項化個數:1個
  • Bean的例項化時機:當Spring核心檔案被載入時,例項化配置的Bean例項
  • Bean的生命週期:
    • 物件建立:當應用載入,建立容器時,物件就被建立了
    • 物件執行:只要容器在,物件一直活著
    • 物件銷燬:當應用解除安裝,銷燬容器時,物件就被銷燬了

當scope的取值為prototype時

  • Bean的例項化個數:多個
  • Bean的例項化時機:當呼叫getBean()方法時例項化Bean
  • Bean的生命週期:
    • 物件建立:當使用物件時,建立新的物件例項
    • 物件執行:只要物件在使用中,就一直活著
    • 物件銷燬:當物件長時間不用時,被 Java 的垃圾回收器回收了
2.4.6.3 Bean 生命週期的配置
  • init-method:指定類中的初始化方法名稱
  • destroy-method:指定類中銷燬方法名稱
2.4.6.4 Bean 例項化的三種方式

無參構造方法例項化:

工廠靜態方法例項化:

工廠普通方法例項化:

2.4.7 Bean 依賴注入

依賴注入:DI,Dependency Injection,是 Spring 框架核心 IOC 的具體實現

將不同層之間呼叫的依賴關係交給Spring維護,及通過框架將層物件傳入別的層

2.4.7.1 Bean 依賴注入方式 - 構造方法

(1)在Service 層裡新增另一層介面的例項

  • private xxxDao xxxdao;

(2)為Service 層新增有參構造方法,將屬性xxxdao傳入

(3)在實現介面所重寫的方法中適用另一層介面的例項(xxxdao)呼叫方法

(4)配置核心配置檔案

<bean id = "xxxService" class = "service 層裡的實現類">
	<constructor-arg index="引數下標從0開始" type="構造方法引數介面型別" ref="構造方法引數bean id"/>
	或
	<constructor-arg name="構造方法引數的形參名" ref="構造方法引數bean id"/>
<bean/>

2.4.7.2 依賴注入方式 - set方法

(1)在Service 層裡新增另一層介面的例項

  • private xxxDao xxxdao;

(2)該方法使用預設的無參構造方法

(3)為該屬性xxxdao 新增set 方法

(4)在實現介面所重寫的方法中使用另一層介面的例項(xxxdao)呼叫方法

(5)配置核心配置檔案

<bean id = "xxxService" class = "service層介面的實現類">
<property name = "set方法set後首字母小寫" ref="屬性對應介面Bean id"/>
<bean/>

set 方法 - 拓展 - P名稱空間注入

在核心配置檔案中檔案頭新增

xmlns:p="http://www.springframework.org/schema/p"

配置Bean

<bean id = "xxxService" class = "service層介面的實現類" p:xxxDao-ref="xxxDao/>
2.4.7.3 Bean 依賴注入的的資料型別
  • 普通資料類:基本資料型別及String
  • 引用資料型別
  • 集合資料型別:List,Map,Set,Array,Properties

List:

<property name="">
<list>
<value>基本資料型別的值<value/>
<ref bean = "引用資料類 Bean id"/>
<list/>
<property/>

Set:和List 配置基本一致,區別在於List集合和Set集合本身的區別

<property name="">
<set>
<value>基本資料型別的值<value/>
<ref bean = "引用資料類 Bean id"/>
<set/>
<property/>

Array:和前面基本一致,只是標籤有區別

<property name="">
<array>
<value>基本資料型別的值<value/>
<ref bean = "引用資料類 Bean id"/>
<array/>
<property/>

Map:除了標籤的區別,需要同時設定key 和 value 的值

<property name="">
<map>
<entry key="基本資料型別" value="基本資料型別">
<entry key-ref="引用資料型別Bean id" value-ref="引用資料型別Bean id">
<map/>
<property/>

Properties配置檔案注入:key - value形式,值都是字串

<property name="">
<props>
<prop key="">值</prop>
<props/>
<property/>
2.4.8 配置檔案模組化

意義:Sping 配置內容多且都配置在核心配置檔案造成繁雜,可將部分配置檔案拆解到其他配置檔案中,即配置檔案模組化

拆分原則:

  • 按層進行拆分
  • 按業務模組進行拆分

拆分配置檔案之後需要載入拆分後的配置檔案,載入方式有兩種:

(1)並列的多個配置檔案

ApplicationContext act =
new
ClassPathXmlApplicationContext("ApplicationContext.xml","xxx.xml","...");

(2)主從配置檔案

  • 在核心配置檔案標籤中新增
<import resource="applicationContext-xxx.xml"/>

注意:

  • 同一個xml中不能出現相同名稱的bean,如果出現會報錯
  • 多個xml如果出現相同名稱的bean,不會報錯,但是後加載的會覆蓋前載入的bean
2.4.9 Bean 標籤配置,依賴注入和配置檔案模組化小結
<bean>標籤:建立物件並放到spring的IOC容器
id屬性:在容器中Bean例項的唯一標識,不允許重複
class屬性:要例項化的Bean的全限定名
scope屬性:Bean的作用範圍,常用是Singleton(預設)和prototype

<constructor-arg>標籤:屬性注入
name屬性:屬性名稱
value屬性:注入的普通屬性值
ref屬性:注入的物件引用值

<property>標籤:屬性注入
name屬性:屬性名稱
value屬性:注入的普通屬性值
ref屬性:注入的物件引用值
<list>
<set>
<array>
<map>
<props>

<import>標籤:匯入其他的Spring的分檔案

2.5 DbUtils (IOC 實戰)

交給Spring 容器的類:

  • 各層介面實現類

  • DataSource

<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
        <property name="driverClassName" value="${jdbc.driver}"></property> //特別注意該地方的set方法
        <property name="url" value="${jdbc.url}"></property>
        <property name="username" value="${jdbc.username}"></property>
        <property name="password" value="${jdbc.password}"></property>
    </bean>
  • 使用properties 檔案的方式進行資料庫初始化配置
在核心配置檔案檔案頭中新增:
名稱空間
xmlns:context="http://www.springframework.org/schema/context"

約束路徑
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd

標籤內配置:
<context:property-placeholder location="classpath:jdbc.properties" />
  • QueryQunner 有參構造初始化(傳遞dataSource)
<bean id="queryRunner" class="org.apache.commons.dbutils.QueryRunner">
        <constructor-arg name="ds" ref="dataSource"></constructor-arg>
    </bean>

2.6 Spring 註解開發

使用註解開發需要在核心配置檔案中先新增context,再為需要註解掃描的包進行配置

<!--註解的元件掃描-->
<context:component-scan base-package="com.lagou"></context:component-scan>
2.6.1 Spring 註解開發常用註解
2.6.1.1 自定義類相關注解
對應標籤的註解 value = "" 相當於 註解中 id = ""
@Component 使用在類上用於例項化Bean
@Controller 使用在Web層類上用於例項化Bean
@Service 使用在Service層類上用於例項化Bean
@Repository 使用在dao層類上用於例項化Bean

註解開發依賴注入使用的是反射機制,和構造方法和set方法傳參不同

對於有多個實現類的介面,會先根據介面匹配,再根據引數名匹配實現類或設定@Qualifier 指定

JDK11以後完全移除了javax擴充套件導致不能使用@resource註解,使用需要導包

<dependency>
<groupId>javax.annotation</groupId>
<artifactId>javax.annotation-api</artifactId>
<version>1.3.2</version>
</dependency>
對應標籤註解 前三種對應ref引用資料型別
@Autowired 使用在欄位上用於根據型別依賴注入/該方法可用於引數前
@Qualifier 必須結合@Autowired使用,根據名稱依賴注入
@Resource 相當於@Autowired+@Qualifier,name="" 按名稱
@Value 注入普通資料型別

配置Bean的作用範圍和生命週期:

對應 標籤
@Scope 標註Bean的作用範圍
@PostConstruct 使用在方法上標註該方法時Bean的初始化方法
@PreDestroy 使用在方法上標註該方法是Bean的銷燬方法
2.6.1.2 非自定義類和核心配置檔案相關注解

(1)非自定義類,jar包中的原始碼類需要Spring 管理時

@Bean

  • 使用在方法上,標註將該方法的返回值儲存到 Spring 容器中
  • value = "" 相當於 不寫預設等於方法名

(2)載入properties檔案的配置

載入properties檔案的配置:<context:property-placeholder location="classpath:jdbc.properties">

@PropertySource

  • 用於載入 properties 檔案中的配置
  • 傳入: classpath: 檔案位置

(3)元件掃描的配置

元件掃描的配置:<context:component-scan>

@ComponentScan

  • 用於指定 Spring 在初始化容器時要掃描的包

(4)引入其他檔案

引入其他檔案:<import>

@Import

  • 用於匯入其他配置類
  • 傳入Class 類

(5)使用核心配置類來替換核心配置檔案

@Configuration

  • 用於指定當前類是一個Spring 配置類,當建立容器時會從該類上載入註解

在使用春註解開發時

new AnnotationConfigApplicationContext(核心配置檔案.class);
2.6.2 Spring 整合Junit

整合Junit的含義是在測試類中無需手動載入核心配置檔案並手動獲取目標類,將其交給Spring 來建立

(1)匯入Spring 整合 Juint 的座標

<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>5.1.5.RELEASE</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>

注意:spring5 及以上版本要求 junit 的版本必須是 4.12 及以上

(2)測試類上新增@Runwith註解

@RunWith(SpringJUnit4ClassRunner.class)

(3)載入核心配置檔案或核心配置類

@ContextConfiguration(classes = {SpringConfig.class}) // 載入spring核心配置類

或

@ContextConfiguration(value = {"classpath:applicationContext.xml"}) //載入spring
核心配置檔案

(4)使用@Autowired 注入物件

(5)測試方法中直接呼叫被注入物件

測試時出現異常:

javax.net.ssl.SSLException: closing inbound before receiving peer's close_notify

解決方法:

在url 後加上 &useSSL=false