2024年最全[评论送书 ]手撕源码,实现一个Koa。,BAT常见的20道Java面试题详解

最后

我还通过一些渠道整理了一些大厂真实面试主要有:蚂蚁金服、拼多多、阿里云、百度、唯品会、携程、丰巢科技、乐信、软通动力、OPPO、银盛支付、中国平安等初,中级,高级Java面试题集合,附带超详细答案,希望能帮助到大家。

新鲜出炉的蚂蚁金服面经,熬夜整理出来的答案,已有千人收藏

还有专门针对JVM、SPringBoot、SpringCloud、数据库、Linux、缓存、消息中间件、源码等相关面试题。

新鲜出炉的蚂蚁金服面经,熬夜整理出来的答案,已有千人收藏

本文已被CODING开源项目:【一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频+实战项目源码】收录

需要这份系统化的资料的朋友,可以点击这里获取

使用http搭建服务

step1:引入http

var http = require(‘http’);

step2: createServer方法创建http服务

let server=http.createServer(function (request, response) {

response.writeHead(200, {‘Content-Type’: ‘text/plain’});

response.end(‘Hello World’);

})

step3:监听端口

server.listen(8000);

koa简单用法

const Koa = require(‘koa’);

const app = new Koa();

app.use(async ctx => {

ctx.body = ‘Hello World’;

});

app.listen(3000);

核心目录


image.png

  • application.js: 入口,导出koa。

  • context.js:上下文

  • request.js:处理请求

  • response.js:处理响应

导出koa类


上面我们简单介绍了koa的用法,可以看出koa有两个核心的函数,一个use,一个listen。我们先来实现这两个函数。

我们已经预先学习了http的用法,那么事情就很好办了!

koa类

application.js

先定义一个koa的基本结构。

class Application {

use() {

}

listen() {

}

}

module.exports = Application

app.listen

主要是将http.listen封装一下。

class Application {

callback = (req, res) => {

res.end(‘Hello World\n’);

}

listen() {

const server = http.createServer(this.callback);

console.log(…arguments)

server.listen(…arguments)

}

}

测试一下:test.js

const Koa=require(‘./application.js’)

const app=new Koa()

app.listen(3000)

可以正常访问

image.png

app.use

在前置知识中我们看到,app.use接收一个回调函数,同时传入一个ctx上下文,这里ctx将request和response封装起来。为了清晰易懂,我们先不进行上下文的封装。

app.use(async ctx => {

ctx.body = ‘Hello World’;

});

那么use简单的处理如下:

class Application {

use(fn) {

this.fn=fn

}

}

此时use接收了一个函数,这个函数的执行的时机是在访问网站的时候,那么此时就需要在创建http服务的时候,传入这个函数。最好的方式就是放在listen的callbak中调用。

callback = (req, res) => {

this.fn(req, res)

}

最终代码

let http = require(‘http’)

class Application {

use(fn) {

this.fn=fn

}

callback = (req, res) => {

this.fn(req, res)

}

listen() {

const server = http.createServer(this.callback);

server.listen(…arguments)

}

}

module.exports = Application

测试:test.js

const Koa=require(‘./application.js’)

const app=new Koa()

app.use((req, res) => {

res.end(‘Hello World\n’);

})

app.listen(3000)

可以正常访问

image.png

封装ctx


明确一个事实:每个请求都是独立的,对于原生的http请求来说,每次请求的response和request是不同的。对于koa中的ctx,则表示每次请求的ctx都是一个新的ctx

ctx的结构

为了看到ctx的结构,我们先使用源koa打印一下ctx。最终得到的结果如下所示,有了这个结构我们就可以实现一个自己的ctx。

下面这个格式是console.dir(ctx)的结果(删掉了一些具体的内容),从下面的内容,我们可以得出ctx的结构。。

{

request: {

app: Application {

},

req: IncomingMessage {

},

res: ServerResponse {

},

ctx: [Circular],

response: {

app: [Application],

req: [IncomingMessage],

res: [ServerResponse],

ctx: [Circular],

request: [Circular]

},

originalUrl: ‘/’

},

response: {

app: Application {

},

req: IncomingMessage {

},

res: ServerResponse {

},

ctx: [Circular],

request: {

app: [Application],

req: [IncomingMessage],

res: [ServerResponse],

ctx: [Circular],

response: [Circular],

originalUrl: ‘/’

}

},

app: Application {

},

req: IncomingMessage {

},

res: ServerResponse {

},

originalUrl: ‘/’,

state: {}

}

context.js

context.js 主要定义了context的具体结构以及提供的方法。

Koa Context 将 node 的 requestresponse 对象封装到单个对象中,为编写 Web 应用程序和 API 提供了许多有用的方法。 这些操作在 HTTP 服务器开发中频繁使用,它们被添加到此级别而不是更高级别的框架,这将强制中间件重新实现此通用功能。

request.js和response.js文件

在核心目录,我们提到了这两个文件,这两个文件此时就派上了用场。这两个文件具体实现了啥呢?这两个文件定义了ctx.resopnse和ctx.request的结构,也就是上面使用dir输出的结果。在koa中文文档中可以具体的看到结构,可以自行查阅。

Koa Request 对象是在 node 的 原生请求对象之上的抽象,提供了诸多对 HTTP 服务器开发有用的功能。

Koa Response 对象是在 node 的原生响应对象之上的抽象,提供了诸多对 HTTP 服务器开发有用的功能。

实现ctx

定义context.js

const context={

}

module.exports=context

定义request.js

const request={

}

module.exports=request

定义response.js

const resposne={

}

module.exports=response

use中封装ctx

我们在上面导出koa章节中可以看到,在app.use的时候,我们传的参数是(request,response),源koa传的的ctx,所以我们就知道了,koa是在app.use的时候创建了一个ctx。

在本章开头的时候,我们又提到每次请求的ctx都是全新的ctx。

综合以上两点,我们可以基本编写出下面的代码。(为了代码的清晰易读,我们封装了一个createcontext函数来创建上下文。)

const Context = require(‘./context’)

const Request = require(‘./request’)

const Response = require(‘./response’)

class Application {

constructor(){

this.context = Object.create(Context);

this.request = Object.create(Request);

this.response = Object.create(Response);

}

use(fn) {

this.fn = fn

}

createContext = (req, res) => {

const ctx = Object.create(this.context);

const request = Object.create(this.request);

const response = Object.create(this.response);

ctx.app = request.app = response.app = this

ctx.request = request;

ctx.request.req = ctx.req = req;

ctx.response = response;

ctx.response.res = ctx.res = res;

ctx.originalUrl = request.originalUrl = req.url

ctx.state = {}

return ctx

}

callback = (req, res) => {

let ctx = this.createContext(req, res)

this.fn(ctx)

}

listen() {

const server = http.createServer(this.callback);

console.log(…arguments)

server.listen(…arguments)

}

}

首先我们在constructor中定义了一个context对象,这里会在constructor定义是因为Koa的app上默认导出context属性。

app.context 是从其创建 ctx 的原型。您可以通过编辑 app.contextctx 添加其他属性。这对于将 ctx 添加到整个应用程序中使用的属性或方法非常有用,这可能会更加有效(不需要中间件)和/或 更简单(更少的 require()),而更多地依赖于ctx,这可以被认为是一种反模式。

例如,要从 ctx 添加对数据库的引用:

app.context.db = db();

app.use(async ctx => {

console.log(ctx.db);

});

然后再callback中,我们针对response和request进行了二次封装。

再来看看这段代码:

app.context.db = db();

app.use(async ctx => {

console.log(ctx.db);

});

再使用use之前,通过app.context对context进行了修改。当使用use函数的时候,是不是直接进入了callback函数,此时的this.context已经是修改过的了。

测试

const Koa=require(‘./application.js’)

const app=new Koa()

app.use((ctx) => {

// 测试1

ctx.response.res.end(" hello my koa")

// 测试2

ctx.res.end(" hello my koa")

})

app.listen(3000,()=>{

console.log(‘3000’)

})

正常访问!

image.png

封装request.js


明确一个事实:request类的属性是通过getter和setter设置的。为什么会这样设置?这样设置的好处是可以方便的设置和获取到值。是不是有点懵逼!请听我细细道来。

先来看一下Koa中request类所绑定的属性,官方链接

我这里简单的列举几个:

request.header=

设置请求头对象。

request.headers

请求头对象。别名为 request.header.

request.headers=

设置请求头对象。别名为 request.header=

request.url

获取请求 URL.

request.url=

设置请求 URL, 对 url 重写有用。

request.originalUrl

获取请求原始URL。

  1. 这里对于每个属性都有设置和获取的功能,使用getter和setter可以很好的实现。

get url () {

return this.req.url

},

  1. 这里的每个属性是如何设置的,如果我们对request本身设置有效吗?

const request={

url:‘’,

header:{

}

}

const request={

set url (val) {

this.req.url = val

}

get url () {

return this.req.url

},

}

request.socket的getter

socket在这里指套接字。套接字的概念这里不赘述!

get socket () {

return this.req.socket

},

request.protocol的getter

返回请求协议,“https” 或 “http”。当 app.proxytrue 时支持 X-Forwarded-Proto

先判断套接字中是否存在encrypted(加密),如果加密,就是https,

X-Forwarded-Proto用来确定客户端与代理服务器或者负载均衡服务器之间的连接所采用的传输协议(HTTP 或 HTTPS)

X-Forwarded-Proto: https

X-Forwarded-Proto: http

get protocol () {

if (this.socket.encrypted) return ‘https’

if (!this.app.proxy) return ‘http’

const proto = this.get(‘X-Forwarded-Proto’)

return proto ? proto.split(/\s*,\s*/, 1)[0] : ‘http’

},

这里有一个get函数,主要时根据字段,从请求头中获取数据。

get (field) {

const req = this.req

switch (field = field.toLowerCase()) {

case ‘referer’:

case ‘referrer’:

return req.headers.referrer || req.headers.referer || ‘’

default:

return req.headers[field] || ‘’

}

},

request.host的getter

存在时获取主机(hostname:port)。当 app.proxytrue 时支持 X-Forwarded-Host,否则使用 Host

get host () {

const proxy = this.app.proxy

let host = proxy && this.get(‘X-Forwarded-Host’)

if (!host) {

if (this.req.httpVersionMajor >= 2) host = this.get(‘:authority’)

if (!host) host = this.get(‘Host’)

}

if (!host) return ‘’

return host.split(/\s*,\s*/, 1)[0]

},

总结

互联网大厂比较喜欢的人才特点:对技术有热情,强硬的技术基础实力;主动,善于团队协作,善于总结思考。无论是哪家公司,都很重视高并发高可用技术,重视基础,所以千万别小看任何知识。面试是一个双向选择的过程,不要抱着畏惧的心态去面试,不利于自己的发挥。同时看中的应该不止薪资,还要看你是不是真的喜欢这家公司,是不是能真的得到锻炼。其实我写了这么多,只是我自己的总结,并不一定适用于所有人,相信经过一些面试,大家都会有这些感触。

**另外本人还整理收藏了2021年多家公司面试知识点以及各种技术点整理 **

下面有部分截图希望能对大家有所帮助。

在这里插入图片描述

本文已被CODING开源项目:【一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频+实战项目源码】收录

需要这份系统化的资料的朋友,可以点击这里获取

‘’

default:

return req.headers[field] || ‘’

}

},

request.host的getter

存在时获取主机(hostname:port)。当 app.proxytrue 时支持 X-Forwarded-Host,否则使用 Host

get host () {

const proxy = this.app.proxy

let host = proxy && this.get(‘X-Forwarded-Host’)

if (!host) {

if (this.req.httpVersionMajor >= 2) host = this.get(‘:authority’)

if (!host) host = this.get(‘Host’)

}

if (!host) return ‘’

return host.split(/\s*,\s*/, 1)[0]

},

总结

互联网大厂比较喜欢的人才特点:对技术有热情,强硬的技术基础实力;主动,善于团队协作,善于总结思考。无论是哪家公司,都很重视高并发高可用技术,重视基础,所以千万别小看任何知识。面试是一个双向选择的过程,不要抱着畏惧的心态去面试,不利于自己的发挥。同时看中的应该不止薪资,还要看你是不是真的喜欢这家公司,是不是能真的得到锻炼。其实我写了这么多,只是我自己的总结,并不一定适用于所有人,相信经过一些面试,大家都会有这些感触。

**另外本人还整理收藏了2021年多家公司面试知识点以及各种技术点整理 **

下面有部分截图希望能对大家有所帮助。

[外链图片转存中…(img-6SaXYo1N-1715094215861)]

本文已被CODING开源项目:【一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频+实战项目源码】收录

需要这份系统化的资料的朋友,可以点击这里获取

  • 5
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值