开课吧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}
只是做了个大致的效果,其他的触底状态显示、触发距离等未做优化,大致效果如下:

猜你喜欢

转载自blog.csdn.net/lsttwo/article/details/113094721