自學ssm->springboot->springcloud,所以很多東西會用但理解較淺,所以現在從最開始的ssm開始進行對原理以及運行過程的整理知識歸納,若有錯誤感謝指正。
Spring
Spring運行原理
1. Data Access/Integration(數據訪問/集成)
數據訪問/集成層包括 JDBC、ORM、OXM、JMS 和 Transactions 模塊,具體介紹如下。
- JDBC 模塊:提供了一個 JDBC 的抽象層,大幅度減少了在開發過程中對數據庫操作的編碼。
- ORM 模塊:對流行的對象關系映射 API,包括 JPA、JDO、Hibernate 和 iBatis 提供了的集成層。
- OXM 模塊:提供了一個支持對象/XML 映射的抽象層實現,如 JAXB、Castor、XMLBeans、JiBX 和 XStream。
- JMS 模塊:指 Java 消息服務,包含的功能為生產和消費的信息。
- Transactions 事務模塊:支持編程和聲明式事務管理實現特殊接口類,並為所有的 POJO。
2. Web 模塊
Spring 的 Web 層包括 Web、Servlet、Struts 和 Portlet 組件,具體介紹如下。
- Web 模塊:提供了基本的 Web 開發集成特性,例如多文件上傳功能、使用的 Servlet 監聽器的 IoC 容器初始化以及 Web 應用上下文。
- Servlet模塊:包括 Spring 模型—視圖—控制器(MVC)實現 Web 應用程序。
- Struts 模塊:包含支持類內的 Spring 應用程序,集成了經典的 Struts Web 層。
- Portlet 模塊:提供了在 Portlet 環境中使用 MVC實現,類似 Web-Servlet 模塊的功能。
3. Core Container(核心容器)
Spring 的核心容器是其他模塊建立的基礎,由 Beans 模塊、Core 核心模塊、Context 上下文模塊和 Expression Language 表達式語言模塊組成,具體介紹如下。
- Beans 模塊:提供了 BeanFactory,是工廠模式的經典實現,Spring 將管理對象稱為 Bean。
- Core 核心模塊:提供了 Spring 框架的基本組成部分,包括 IoC 和 DI 功能。
- Context 上下文模塊:建立在核心和 Beans 模塊的基礎之上,它是訪問定義和配置任何對象的媒介。ApplicationContext 接口是上下文模塊的焦點。
- Expression Language 模塊:是運行時查詢和操作對象圖的強大的表達式語言。
4. 其他模塊
Spring的其他模塊還有 AOP、Aspects、Instrumentation 以及 Test 模塊,具體介紹如下。
- AOP 模塊:提供了面向切面編程實現,允許定義方法攔截器和切入點,將代碼按照功能進行分離,以降低耦合性。
- Aspects 模塊:提供與 AspectJ 的集成,是一個功能強大且成熟的面向切面編程(AOP)框架。
- Instrumentation 模塊:提供了類工具的支持和類加載器的實現,可以在特定的應用服務器中使用。
- Test 模塊:支持 Spring 組件,使用 JUnit 或 TestNG 框架的測試。
Spring IoC容器的基本概念
2.基本講解
在傳統方法中,Java對象需要調用另外一個Java對象時(如①調用②),調用者(①)通常采用“new”被調用者(②)來創建對象,這種方式會增加調用者和被調用者之間的耦合性。
當Spring框架出現后,對象實例不再由調用者來創建,即不通過直接new被調用者來創建對象,而是交給Spring容器來創建。這時候調用者的程序將不再進行直接控制,而實轉交給了Spring容器,這就是Spring的控制反轉。
從Spring容器角度出發,Spring容器負責將被依賴對象賦值給調用者的成員變量,相當於調用者注入它所有依賴的實例,這就是Spring的依賴注入。
舉一個很直白的例子:海綿寶寶做出第一個美味蟹黃堡,當海綿寶寶想做多幾個蟹黃堡(調用者)時就得參照第一個蟹黃堡(被調用者)來進行制作。一旦第一個蟹堡王丟了或者變味了,他后面就沒辦法做出一模一樣的美味蟹黃堡(耦合度高),所以這時候海綿寶寶把第一個蟹黃堡放到一個按照配方運轉的制造機器里(Spring容器),往后創造新的蟹黃堡都交給制造機來控制(Spring的控制反轉)。
2.通過Web服務其方式實例化ApplicationContext容器(最常用的方式)
Spring IoC容器的設計主要是基於BeanFactory和ApplicationContext兩個接口,而ApplicationContext是BeanFactory的子接口
<?xml version="1.0" encoding="UTF-8"?> <web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://xmlns.jcp.org/xml/ns/javaee" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd" id="WebApp_ID" version="3.1"> <display-name>StudentShopping</display-name> <!-- needed for ContextLoaderListener --> <!-- Spring加載ApplicationContext容器 --> <context-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:applicationContext.xml</param-value> </context-param> <!-- Bootstraps the root web application context before servlet initialization --> <listener> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener>
web.xml中ContextLoaderListener的作用:就是啟動Web容器時,讀取在contextConfigLocation中定義的xml文件,自動裝配ApplicationContext的配置信息,並產生WebApplicationContext對象,然后將這個對象放置在ServletContext的屬性里,這樣我們只要得到Servlet就可以得到WebApplicationContext對象,並利用這個對象訪問spring容器管理的bean。
依賴注入的理解
以下只進行簡單代碼講解,關於spring依賴注入類型具體實現可參考這篇文章:https://blog.csdn.net/lyc_liyanchao/article/details/82428726
而這兩種注入可以分別用兩種注解來實現一個是 @Autowired 和 @Resource :@Autowired 可以用作構造注入 /@Resource 用作 setter注入。
- @Autowired注解是按照類型(byType)裝配依賴對象,默認情況下它要求依賴對象必須存在,如果允許null值,可以設置它的required屬性為false。
- @Resource有兩個重要的屬性:name和type,而Spring將@Resource注解的name屬性解析為bean的名字,而type屬性則解析為bean的類型。所以,如果使用name屬性,則使用byName的自動注入策略,而使用type屬性時則使用byType自動注入策略。
另外在構造方法注入中,<constructor-arg>元素【以下黃色標注部分】表示構造方法的一個參數,且使用時不區分順序。當構造方法的參數出現混淆、無法區分時,可以通過<constructor-arg>元素的index屬性指定該參數的位置索引,索引從0開始。<constructor-arg>元素還提供了type屬性用來指定參數的類型,避免字符串和基本數據類型的混淆。這也解釋了為什么@Autowired 可用作構造注入
public class Car { private String brand; private String corp; private double price; private int maxSpeed; public Car(String brand, String corp, double price) { this.brand = brand; this.corp = corp; this.price = price; } public Car(String brand, String corp, int maxSpeed) { this.brand = brand; this.corp = corp; this.maxSpeed = maxSpeed; } @Override public String toString() { return "Car [brand=" + brand + ", corp=" + corp + ", price=" + price + ", maxSpeed=" + maxSpeed + "]"; } }
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.1.xsd"> <bean id="car" class="com.auguigu.spring.beans.Car"> <constructor-arg value="Audi" type="java.lang.String"></constructor-arg> <constructor-arg value="Shanghai" type="java.lang.String"></constructor-arg> <constructor-arg value="30000" type="int"></constructor-arg> </bean> <bean id="car2" class="com.auguigu.spring.beans.Car"> <constructor-arg value="BMW" type="java.lang.String"></constructor-arg> <constructor-arg value="BeiJing" type="java.lang.String"></constructor-arg> <constructor-arg value="300.00" type="double"></constructor-arg> </bean> </beans>
關於@Autowired的使用(拓展)
方法1:
//構造函數注入的方式: public class TestController { private final TestService testService; @Autowired public TestController(TestService testService) { this.testService = testService; } … }
方法2:
//變量注入的方式: public class TestController { @Autowired private TestService testService; … }
采用方法2可能會導致NPE(Null Pointer Exception),產生代碼如下:
public class TestController { @Autowired private TestService testService; private String testname;
//先執行此處構造函數,而此時testService相當於並未注入,所以報控制針異常 public TestController(){ this.testname = testService.getTestName(); } }
該類的構造函數中的變量值是通過TestService實例來調用TestService類中的方法獲得,而Java類會先執行構造函數,然后在通過@Autowired注入實例,因此在執行構造函數的時候就會報錯。
解決方法:
//解決方案就是采用構造函數的注入方式,如下: public class TestController { private TestService testService; private String testname; @Autowired public TestController(TestService testService){ this.testService = testService; this.testname = testService.getTestName(); } }
因此,采用構造函數注入有以下號處:依賴不為空(省去了我們對其檢查),當要實例化TestController的時候,由於自己實現了有參數的構造函數,所以不會調用默認構造函數,那么就需要Spring容器傳入所需要的參數,所以就兩種情況:1、有該類型的參數->傳入,OK 。2:無該類型的參數->報錯。所以保證不會為空。
MyBatis
MyBatis運行流程
MyBatis運行流程解析
- 讀取 MyBatis 配置文件:mybatis-config.xml 為 MyBatis 的全局配置文件,配置了 MyBatis 的運行環境等信息,例如數據庫連接信息。
- 加載映射文件。映射文件即 SQL 映射文件,該文件中配置了操作數據庫的 SQL 語句,需要在 MyBatis 配置文件 mybatis-config.xml 中加載。mybatis-config.xml 文件可以加載多個映射文件,每個文件對應數據庫中的一張表。
- 構造會話工廠:通過 MyBatis 的環境等配置信息構建會話工廠 SqlSessionFactory。
- 創建會話對象:由會話工廠創建 SqlSession 對象,該對象中包含了執行 SQL 語句的所有方法。
- Executor 執行器:MyBatis 底層定義了一個 Executor 接口來操作數據庫,它將根據 SqlSession 傳遞的參數動態地生成需要執行的 SQL 語句,同時負責查詢緩存的維護。
- MappedStatement 對象:在 Executor 接口的執行方法中有一個 MappedStatement 類型的參數,該參數是對映射信息的封裝,用於存儲要映射的 SQL 語句的 id、參數等信息。【MappedStatement維護了一條<select|update|delete|insert>節點的封裝】
<select id="selectAuthorLinkedHashMap" resultType="java.util.LinkedHashMap"> select id, username from author where id = #{value} </select>
- 輸入參數映射:輸入參數類型可以是 Map、List 等集合類型,也可以是基本數據類型和 POJO 類型。輸入參數映射過程類似於 JDBC 對 preparedStatement 對象設置參數的過程。
- 輸出結果映射:輸出結果類型可以是 Map、 List 等集合類型,也可以是基本數據類型和 POJO 類型。輸出結果映射過程類似於 JDBC 對結果集的解析過程。
MyBatis核心組件
- SqlSessionFactoryBuilder(構造器):它會根據配置或者代碼來生成 SqlSessionFactory,采用的是分步構建的 Builder 模式。【SqlSessionFactoryBuilder 的作用在於創建 SqlSessionFactory,創建成功后,SqlSessionFactoryBuilder 就失去了作用,所以它只能存在於創建 SqlSessionFactory 的方法中,而不要讓其長期存在】
- SqlSessionFactory(工廠接口):依靠它來生成 SqlSession,使用的是工廠模式。【SqlSessionFactory 可以被認為是一個數據庫連接池,它的作用是創建 SqlSession 接口對象。因為 MyBatis 的本質就是 Java 對數據庫的操作,所以 SqlSessionFactory 的生命周期存在於整個 MyBatis 的應用之中,所以一旦創建了 SqlSessionFactory,就要長期保存它,直至不再使用 MyBatis 應用,所以可以認為 SqlSessionFactory 的生命周期就等同於 MyBatis 的應用周期。我們往往希望 SqlSessionFactory 作為一個單例,讓它在應用中被共享。所以說 SqlSessionFactory 的最佳作用域是應用作用域。】
- SqlSession(會話):一個既可以發送 SQL 執行返回結果,也可以獲取 Mapper 的接口。在現有的技術中,一般我們會讓其在業務邏輯代碼中“消失”,而使用的是 MyBatis 提供的 SQL Mapper 接口編程技術,它能提高代碼的可讀性和可維護性。【SqlSessionFactory 相當於數據庫連接池,那么 SqlSession 就相當於一個數據庫連接(Connection 對象),你可以在一個事務里面執行多條 SQL,然后通過它的 commit、rollback 等方法,提交或者回滾事務。它應該存活在一個業務請求中,處理完整個請求后,應該關閉這條連接,讓它歸還給 SqlSessionFactory,否則數據庫資源就很快被耗費精光,系統就會癱瘓,所以用 try...catch...finally... 語句來保證其正確關閉。】
- SQL Mapper(映射器):MyBatis 新設計存在的組件,它由一個 Java 接口和 XML 文件(或注解)構成,需要給出對應的 SQL 和映射規則。它負責發送 SQL 去執行,並返回結果。【Mapper 是一個接口,它由 SqlSession 所創建,所以它的最大生命周期至多和 SqlSession 保持一致,盡管它很好用,但是由於 SqlSession 的關閉,它的數據庫連接資源也會消失,所以它的生命周期應該小於等於 SqlSession 的生命周期。Mapper 代表的是一個請求中的業務處理,所以它應該在一個請求中,一旦處理完了相關的業務,就應該廢棄它。】
為什么要利用Spring來整合MyBatis?
1、MyBatis與Spring的整合步驟
<!--配置數據源--> <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource"> <property name="driverClassName" value="com.mysql.jdbc.Driver" /> <property name="url" value="jdbc:mysql://127.0.0.1:3306/springtest?seUnicode=true&characterEncoding=utf-8" /> <property name="username" value="root" /> <property name="password" value="1128" /> <!-- 最大連接數 --> <property name="maxTotal" value="30"/> <!-- 最大空閑連接數 --> <property name="maxIdle" value="10"/> <!-- 初始化連接數 --> <property name="initialSize" value="5"/> </bean> <!-- 配置SqlSessionFactoryBean --> <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean"> <!-- 引用數據源組件 --> <property name="dataSource" ref="dataSource" /> <!-- 引用MyBatis配置文件中的配置 --> <property name="configLocation" value="classpath:mybatis-config.xml" /> </bean>
由上面可以得知,我們都知道Spring在整合Mybatis的時候都會配置一個SqlSessionFactoryBean對象來生成一個SqlSessionFactory,而這個SqlSessionFactory就是作為SqlSession(數據庫會話)的關鍵部分,那么Spring又怎么把這個對象與DAO接口類關聯放在mapperRegistry里。
@Mapper public interface AdminDao { public List<Auser> login(Auser auser); }
AdminMapper.xml文件
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <!-- com.dao.AdminDao對應Dao接口 --> <mapper namespace="com.dao.AdminDao"> <!-- 查詢用戶信息 --> <select id="login" resultType="Auser" parameterType="Auser"> select * from ausertable where aname=#{aname} AND apwd=#{apwd} </select> </mapper>
使用 Spring 管理 MyBatis 數據操作接口的方式有多種,其中最常用、最簡潔的一種是基於 MapperScannerConfigurer 的整合。該方式需要在 Spring 的配置文件中加入以下內容:
<!-- Mapper代理開發,使用Spring自動掃描MyBatis的接口並裝配 (Sprinh將指定包中的所有被@Mapper注解標注的接口自動裝配為MyBatis的映射接口) -->
<!--配置掃描,將MyBatis接口加入IoC容器中提供給外部使用--> <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer"> <!-- mybatis-spring組件的掃描器,com.dao只需要接口(接口方法與SQL映射文件中的相同) --> <property name="basePackage" value="com.dao" /> <property name="sqlSessionFactoryBeanName" value="sqlSessionFactory" /> </bean>
如果理解不了上面橙色標記的話的含義,根據上面的代碼其實可以理解成:采用MapperScannerConfigurer掃描類讓dao包的接口和SQL聯系起來,從而實現調用者通過接口自由使用相關數據庫訪問操作的功能。
2、為什么要利用Spring來整合MyBatis
通過對Spring的講解可以得知Spring的控制反轉機制降低了調用者和被調用者之間的耦合性,因此將MyBatis放入Spring容器后開發者只需要進行業務處理,不需要再寫 SqlSession 對象的創建、數據庫事務的處理等煩瑣代碼,提高了開發效率。
SpringMVC
基礎知識
1、MVC概念:
- 模型(Model):用於存儲數據以及處理用戶請求的用戶邏輯
- 視圖(View):向控制器提交數據,顯示模型中的數據
- 控制器(Controller):根據視圖提出的請求判斷將請求和數據提交給哪個模型處理,將處理后的有關接結果交給哪個視圖顯示更新
2、基於Servlet的MVC模式
- 模型:一個或多個JavaBean對象,用於存儲數據和處理業務邏輯
- 視圖:一個或多個JSP頁面,向控制器提交數據和為模型提供數據顯示,JSP頁面主要使用HTML標記和JavaBean標記顯示數據
- 控制器:一個或多個Servlet對象,根據視圖提交的請求進行控制,將請求轉發給處理業務的JavaBean,並將處理結果放到實體模型JavaBean中,輸出給視圖顯示
3、SpringMVC工作原理
- 客戶端請求提交到DispatcherServlet;
- 由DispatcherServlet控制器尋找到一個或多個HandlerMapping,找到處理請求Controller;
- DispatcherServlet將請求提交到Controller
- Controller調用業務邏輯處理后返回ModelAndView
- DispatcherServlet心找一個或多個視圖解析器,找到ModelAndView指定的視圖
- 視圖負責把結果顯示在客戶端
4、SpringMVC接口在工作流程中起的作用
- SpringMVC所有請求都經過DispatcherServlet來統一分布,在DispatcherServlet借助SpringMVC提供的HandlerMapping定位到具體的Controller。
- Controller處理完用戶請求后將返回ModelAndView(包含模型和視圖)對象給DispatcherServlet前端控制器。
- ViewResolver接口(視圖解析器)在Web應用中查找View對象,從而將相應的結果渲染給客戶
從宏觀角度考慮,DispatcherServlet是整個Web應用的控制器;從微觀角度考慮,Controller是單個Http請求處理過程中的控制器,而ModelAndView是Http請求過程中返回的模型和視圖。
4、在ssm整合項目中SpringMvc相關:
web.xml配置文件
<?xml version="1.0" encoding="UTF-8"?> <web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://xmlns.jcp.org/xml/ns/javaee" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd" id="WebApp_ID" version="3.1"> <display-name>StudentShopping</display-name> <!-- needed for ContextLoaderListener --> <!-- Spring加載ApplicationContext容器 --> <context-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:applicationContext.xml</param-value> </context-param> <!-- Bootstraps the root web application context before servlet initialization --> <listener> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener> <!-- 部署DispatcherServlet --> <servlet> <servlet-name>springmvc</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <load-on-startup>1</load-on-startup> </servlet> <!-- springmvc配置文件 --> <servlet-mapping> <servlet-name>springmvc</servlet-name> <url-pattern>/</url-pattern> </servlet-mapping> <filter> <filter-name>encodingFilter</filter-name> <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class> <init-param> <param-name>encoding</param-name> <param-value>UTF-8</param-value> </init-param> <init-param> <param-name>forceEncoding</param-name> <param-value>true</param-value> </init-param> </filter> <filter-mapping> <filter-name>encodingFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> <absolute-ordering /> </web-app>
SpringMVC的配置文件內包含的 Controller映射以及視圖解析器:
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xmlns:mvc="http://www.springframework.org/schema/mvc" xmlns:p="http://www.springframework.org/schema/p" xsi:schemaLocation="http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.3.xsd http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd"> <context:component-scan base-package="com.controller" /> <mvc:annotation-driven /> <mvc:resources location="/css/" mapping="/css/**"></mvc:resources> <mvc:resources location="/images/" mapping="/images/**"></mvc:resources> <mvc:resources location="/logos/" mapping="/logos/**"></mvc:resources> <!-- LoginController控制類將映射到“/login”接口,之后都用@RequetMapping代替 --> <bean name="/login" class="com.controller.admin.LoginController"/> <!-- 視圖解析器,從InternalResourceViewResolver的后綴可以看出這個就是視圖解析器--> <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver" id="internalResourceViewResolver"> <property name="prefix" value="/WEB-INF/jsp/" /> <property name="suffix" value=".jsp" /> </bean> <bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver" p:defaultEncoding="UTF-8" p:maxUploadSize="5400000" p:uploadTempDir="fileUpload/temp"> </bean> <!-- <bean class="com.exception.MyExceptionHandler"/> --> </beans>