1. 程式人生 > 其它 >開課吧Web全棧架構師2021

開課吧Web全棧架構師2021

新建類後,要用@controller註解宣告將該類通過springboot進行統一controller管理。
通過@getmapper(’’)註解宣告一個函式為路由函式,將路由地址傳入,在瀏覽器通過localhost:8080(預設)路徑進行訪問。
直接通過return返回資料的話是無效的,需要將其封裝在ServletResponse中,通過例項化ServletResponse,呼叫getWriter().write()方法將資料寫入。
可以直接通過ResponseBody註解,省去自己手寫ServletResponse處理過程,這個註解同時會自動處理字串編碼問題。而且大多數情況下,返回的並不是簡單的字串,而是一個Object。若使用ServletResponse.Object.getWriter().write()方法還需要自己將Object轉換成字串才能返回,而使用註解會自動處理成json字串。

測試方式:⑴ POSTman. ⑵ 單元測試
一般文字的header是text/plain,而object的header是application/json
熱重啟配置:
pom設定中需要加入devtool依賴,並配置ide進行自動編譯。

<?xml version="1.0" encoding="UTF-8"?>


4.0.0

<mirror>
    <id>alimaven</id>
    <name>aliyun maven</name>
    <url>http://maven.aliyun.com/nexus/content/groups/public/</url>
    <mirrorOf>central</mirrorOf>
</mirror>
<mirror>
    <id>alimaven</id>
    <mirrorOf>central</mirrorOf>
    <name>aliyun maven</name>
    <url>http://maven.aliyun.com/nexus/content/repositories/central/</url>
</mirror>

<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.2.2.RELEASE</version>
    <relativePath/>
</parent>

<groupId>com.zhujiankai</groupId>
<artifactId>app</artifactId>
<version>1.0</version>
<name>webAPP</name>
<description>WebAPP API process</description>

<properties>
    <java.version>1.8</java.version>
</properties>

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-devtool</artifactId>
        <scope>runtime</scope>
        <optional>true</optional>
    </dependency>

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-test</artifactId>
        <scope>test</scope>
        <exclusions>
            <exclusion>
                <groupId>org.junit.vintage</groupId>
                <artifactId>junit-vintage-engine</artifactId>
            </exclusion>
        </exclusions>
    </dependency>
</dependencies>
自動編譯:setting==>build==>compiler==>勾選Build Project Automatically

常用註解
get:GetMapping
put:PutMapping
delete:DeleteMapping

全註解:RequestMapping(url,method)。預設不填寫method是可以進行處理全部的動作的。也可以通過RequestMethod指定註解動作,如{RequestMethod.GET}

如果在class上加入ResponseBody註解,而不是在某個方法上加,則意味著這個類下面的所有方法都是以ServletResponse方式處理

可以通過RestController來代替Controller+ResponseBody兩個註解,簡化書寫。

url路由地址需要有自解釋性。一般建議格式為
host:port/version/resource/function

(版本號一般可以寫在url中間,也可以通過header指定,也可以通過?version查詢引數指定)

所有軟體的複雜性,都是為了程式碼的可維護性。
軟體工程中的一些關鍵原則:
開閉原則(OCP open closed principle):對擴充套件開放,對修改封閉。
里氏替換原則、狄米特法則,都是開閉原則的子原則,不需要特別關注。
IOC/DI也是為了實現開閉原則而產生的。
實現開閉原則,最基本的準則就是面向抽象程式設計(interface,abstract),這樣三大特性之一的多型性才能得到實現。
oic/di出現的過程:
interface==>設計模式中的工廠模式==>ioc/di
單純的interface介面無法實現物件例項化的統一,只能夠完成方法呼叫的統一。
面向物件主要就是完成物件例項化和方法呼叫兩件事情。
只有一段程式碼中沒有new的出現才能實現程式碼的穩定,才能實現ocp(實質是一段程式碼如果要保持穩定,就不應該負責物件的例項化.)。但是物件例項化是不可能消除的,因此需要把例項化的程式碼轉移到其他地方的程式碼片段中去。
工程模式:簡單工廠模式、普通工廠模式、抽象工廠模式。
2019年12月20日更新

程式碼中總會存在不穩定。需要隔離不穩定的程式碼片段,保證其他程式碼是穩定的。是需要把變化隔離封裝起來的。
由於使用簡單工程模式時,在程式碼中還是需要例項化工廠或使用工廠的靜態方法,依然存在不穩定性,因此可以使用抽象工廠。但是一般來說不需要再引入抽象工廠模式,使用簡單工廠即可,因為穩定性總是相對的,不存在完全的穩定。
變化是導致不穩定的原因。變化包括使用者輸入、使用者操作、軟體的技術選擇、業務需求的更改等。隔離不穩定其實就是在隔離變化。
工廠模式下需要通過if、switch case等判斷使用者輸入,並生成返回對應的物件。可以通過反射機制來消除這種因輸入不同帶來的不穩定性,即直接通過使用者輸入字串找到對應的類並返回例項化的物件。

string class_name

反射需要傳入的是一個完整的類(或包)的路徑,而不是類名或包名。

string classname ="(path)"+class_name
Class<?> cls = class.forName(classname);
Object obj = cls.newInstance();
return (介面)obj

<?>表示的是一個元類。類是物件的抽象,元類是對類的描述。

使用上述方法(工廠模式+反射機制),解決了程式碼的穩定性問題。但是這依然是正向思維只是解決了OCP的問題,並沒有應用到控制反轉依賴注入思想。
配置檔案是屬於外部檔案,不是系統內部檔案(即可以理解為使用者的輸入)。因此改動配置檔案並不是違反OCP原則。
2020年01月05日更新

Spring、SpringMVC、SpringBoot的關係
初期,java常用框架為SSM,即Spring+SpringMVC+Mybatis。但是實際上SpringMVC是Spring的一個模組,是屬於Spring的,也就是說SSM實際上是兩個框架的組合。
Spring全稱是Spring Framework,即SSM是Spring Framework+Mybatis,其中SpringMVC是Spring Framework中一個專門用於開發web的模組。
Spring Framework和Spring Boot
Spring Boot是Spring Framework的一個應用。或者說Spring Frame是Spring Boot的基礎。
因此說"因為Spring Framework需要使用xml做依賴注入,而Spring Boot可以使用註解進行注入,所以Spring Boot比Spring Framework優秀"是一種錯誤的理解。
Spring Boot的核心優勢:自動配置
什麼是自動配置?
自動配置有什麼用?好處和優勢是什麼?
IOC的意義
1)具體意義(實現):起到一個容器的功能,能夠將我們需要的類加入到容器中,同時能夠在需要的時候將類物件注入進指定的位置。
目的:
2)抽象意義:把控制權交給使用者
靈活地實現OCP

整體思路:在page/index.wxml頁面引入自定義好的瀑布流元件,通過page/index.js初始化一個Paging的例項物件,這個物件是主要是用於通過api獲取資料並記錄當前請求狀態的。onload時預載入指定count數量的資料,並將物件及物件返回的資料儲存在data中,並通過瀑布流進行渲染。當觸發觸底事件時,通過呼叫物件的getMoreData方法獲取下一批資料bin進行渲染。

page/index.wxml:

自定義元件s-spu-preview:

/*json部分主要是引入價格元件,方便wxml呼叫
{
“component”: true,
“usingComponents”: {
“l-price”: “…/…/miniprogram_npm/lin-ui/price/index”
}

js部分主要是接受一個data資料。
Component({
properties: {
data:Object
}…}
*/

<image src="{{data.img}}" mode="widthFix" class="image"></image>
<view class="title">{{data.title}}</view>
<l-price color="#157658" value="{{data.price}}" l-class="price" size="32"></l-price>
<view class="subtitle">{{data.subtitle}}</view>
} page/index.js:

//主頁的js主要是用於資料的初始化、資料繫結、事件處理等。
page({
data:{
//用於渲染的資料,返回的實際上是一個物件,
//它具有一個items屬性(Array),我們渲染的既是這個列表。
pagingData = null ,
//這個paging是一個例項化物件,每次需要通過它來獲得新的資料
paging = Object
},
onLoad:async function(){
//Paging類主要進行資料的請求發起處理。
//其data屬性用於儲存返回的結果,getMoreData()方法用於請求下一批資料。
const paging = new Paging()
await paging.getMoreData()
this.setData({
pagingData:paging.data,
paging:paging
})
//當資料繫結完成後呼叫wx.lin.renderWaterFlow傳入資料進行渲染
wx.lin.renderWaterFlow(this.data.pagingData.items,false)
},
onReachBottom:function(){
/*
renderWaterflow渲染資料時,refresh引數若為true,則會重新渲染一遍全部的資料。
而設定為false時則會保留已渲染過的,將只渲染指定的資料。
測試時設定true,重新渲染畫面會有閃爍的情況,體驗較差。
因此選擇了通過slice選擇選擇新增的資料,並單獨渲染這部分資料。
*/
const total = this.data.pagingData.total //分頁載入api總體的資料量
let start = this.data.paging.count //當前已渲染過的資料量
//每頁載入的資料量,在Paging類中定義,我們這裡設定為3。
let end = start+3
//每次新增加3個有可能會超出最大資料量範圍,通過三元表示式返回合適的結束值。
end = end>total?total:end
})
await this.data.paging.getMoreData() //再獲取一批新的資料
this.setData({pagingI:this.data.paging.data}) //同樣進行資料繫結

    /*當api擁有的資料全部渲染完以後,通過上面start和end邏輯
    可以看出來start和end是一定相等的。因此可以通過判斷這兩個值的關係決定是否完成了
    全部資料的渲染(不再繼續渲染新的資料)*/
    //通過上面的的start和end,使用slice選擇新增加的那部分資料並進行渲染
    if(start!==end){
            wx.lin.renderWaterFlow(this.data.pagingI.items.slice(start,end),false)        
    }

}
Paging類:

import {Http} from “./Http”;
//這個Http只是個封裝好api基地址、header認證資訊等資訊的wx.request請求

class Paging {
url;
start;
count;
locker;
data;
lastcount;
//建構函式
constructor(url,width = 3,start = 0){
/*由於api引數分別需要提供請求的起始序號和終止序號,
而沒有分頁載入指定的每頁的數量,
因此我們需要通過每頁載入數量來更新start和end。
/
this.url = url;
this.start = start;
this.count = 0; //當前總共請了多少條資料
this.width = width; //每次請求幾條資料
this.totalitems = 0; //是api中返回結果的total屬性的值,全部數量
}
async getMoreData() {
if (this.count===this.totalitems && this.count!==0)
//當一次獲取記錄的條數count和api介面擁有的總體數量this.totalitems相同時,結束請求。
return ;
//判斷當前請求鎖的狀態,如果this._getLocker()
//返回true表示上一次請求已結束,可以繼續發起新的請求。
if (this._getLocker()) {
this.count = this.count+this.width; //更新count
if(this.count>this.totalitems && this.totalitems!==0){
//如過count已經超過了資料的最大請求數量,則令count等於這個最大請求數量
this.count=this.totalitems
}
const tmp_data = await Http.request({
url: ${this.url}?start=${this.start}&count=${this.count}
});//發起新的請求
this.start = this.count;//更新start
if (~this.data) {
//data是一個array,當陣列為空也就意味著這是第一次發起請求
this.data = tmp_data;
this.totalitems = tmp_data.total
}
else{
//之後再次發起請求則將請求結果append到陣列中即可
this.data.append(tmp_data);
}
this._releaseLocker(); //請求完成,釋放鎖
return
}
return false
}
_getLocker(){
/

獲取鎖的狀態。若為鎖的狀態this.locker為false表示當前沒有正在進行中的請求,
返回true表示允許發起新的請求,並將locker設定為true表示存在進行中的請求。
若鎖的狀態this.locker為true,則返回false表示已經有處理中的請求了,
不允許發出新的請求。
*/
if (this.locker){
return false
}
//鎖狀態為false,則另鎖狀態為true防止繼續點選,並進行request
this.locker = true;
return true
}
_releaseLocker(){
//將鎖狀態置為false,代表上一次請求已經完成,允許發出新的請求。
this.locker = false
}
}
export {Paging}
只是做了個大致的效果,其他的觸底狀態顯示、觸發距離等未做優化,大致效果如下: