前言
文本已收录至我的 GitHub 仓库,欢送 Star:https://github.com/bin392328206 > 种一棵树最好的工夫是十年前,其次是当初
six-finger-web
一个 Web 后端框架的轮子从解决 Http 申请【基于 Netty 的申请级 Web 服务器】到 mvc【接口封装转发)】,再到 ioc【依赖注入】,aop【切面】,再到 rpc【近程过程调用】最初到 orm【数据库操作】全副本人撸一个(繁难)的轮子。
github
为啥要写这个轮子
其实是这样的,小六六本人平时呢?有时候喜爱看看人家的源码比方 Spring, 然而小六六的程度可能不怎么样,每次看都看得昏头昏脑,而后就感觉外面的细节太难了,而后我就只能观其总体的思维,而后我就想我如果能够依据各位前辈的一些思考,本人撸一个简略的轮子进去,那我前面去了解作者的思维是不是简略点呢?于是呢 six-finger-web 就面世了,它其实就是我的一个学习过程,而后我把它开源进去,心愿能帮忙那些对于学习源码有艰难的同学。还有就是能够锤炼一下本人的编码能力,因为平时咱们总是 crud 用的 Java api 都是那些,长此以往,很多框架类的 api 咱们基本就不纯熟了,所以借此机会,锤炼一下。
特点
- 内置由 Netty 编写 HTTP 服务器,无需额定依赖 Tomcat 之类的 web 服务(刚好小六六把 Netty 系列写完,顺便用下)
- 代码简略易懂(小六六本人写不出框架大佬那种高类聚,低耦合的代码),能力略微强一点看代码就能懂,弱点的也没关系,小六六有配套的从 0 搭建教程。
- 反对 MVC 相干的注解确保和 SpringMVC 的用法相似
- 反对 Spring IOC 和 Aop 相干性能
- 反对相似于 Mybatis 相干性能
- 反对相似于 Dubbo 的 rpc 相干性能
- 对于数据返回,只反对 Json 格局
絮叨
此教程只适宜初中级程度,因为作者自身程度不高,不喜勿喷,明天是文章的第一篇,所以先写的是 由 Netty 搭建一个 http 服务器
应用 Netty 实现 HTTP 服务器
Netty 是一个异步事件驱动的网络应用程序框架用于疾速开发可保护的高性能协定服务器和客户端。Netty 通过精心设计,具备丰盛的协定,如 FTP,SMTP,HTTP 以及各种二进制和基于文本的传统协定。
Java 程序员在开发 web 利用的时候, 咱们习惯于基于 servlet 标准,来做后端开发,就比方咱们的 SpringMVC 其本质也是一个 servlet, 至于 spring Webfux,我不晓得有多少公司应用了,然而目前为止 2020,咱们公司是没有应用的,这次呢咱们就试试用 Netty 来实现一下,其实这个很简略,以前的我写 Netty 系列的时候,我曾经写过了,大家能够去找找 https://github.com/bin3923282…
首先是创立我的项目
因为咱们这个是 six-finger-web 的第一篇,所以我尽量把点点滴滴做到
首先创立一个 maven 我的项目,如果这个都不会的话,小六六倡议先学习根底再来,在文章很多的中央,一些根底的小六六是默认你懂,如果有啥不懂的能够上 github 上找我联系方式,我如果有空会给大家解答的
- 创立 pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion>
<groupId>com.xiaoliuliu</groupId> <artifactId>six-finger-web</artifactId> <packaging>jar</packaging> <version>1.0-SNAPSHOT</version>
<dependencies> <!-- 为了代码简洁引入 lombok, 不须要再写 setter 和 getter(能够不引入)-->
<dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>1.18.12</version> <scope>provided</scope> </dependency> <!-- 动静代理相干 -->
<dependency> <groupId>cglib</groupId> <artifactId>cglib</artifactId> <version>3.1</version> </dependency>
<!-- Netty 相干 -->
<dependency> <groupId>io.netty</groupId> <artifactId>netty-all</artifactId> <version>4.1.51.Final</version> </dependency>
</dependencies></project>
HttpServer
Netty 编写 HTTP 服务器主类
package com.xiaoliuliu.six.finger.web.server;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.logging.LogLevel;
import io.netty.handler.logging.LoggingHandler;
import java.net.InetSocketAddress;
/**
* @author 小六六
* @version 1.0 * @date 2020/10/13 11:41 * Netty 编写 HTTP 服务器
* 主类
*/public class HttpServer {
/** * @Des 端口 http 申请的端口
* @Author 小六六
* @Date 2020/10/13 11:42 * @Param * @Return */ int port;
/** * @Des 构造方法
* @Author 小六六
* @Date 2020/10/13 11:42 * @Param * @Return */ public HttpServer(int port) {this.port = port;}
/** * @Des 服务的启动办法
* @Author 小六六
* @Date 2020/10/13 11:43 * @Param * @Return */ public void start() throws Exception { // 启动疏导类
ServerBootstrap bootstrap = new ServerBootstrap(); NioEventLoopGroup boss = new NioEventLoopGroup(); NioEventLoopGroup work = new NioEventLoopGroup();
bootstrap.group(boss, work) .handler(new LoggingHandler(LogLevel.DEBUG)) .channel(NioServerSocketChannel.class) .childHandler(new HttpServerInitializer());
ChannelFuture cf = bootstrap.bind(new InetSocketAddress(port)).sync(); System.out.println("server start up on port :" + port); cf.channel().closeFuture().sync();}}
HttpServerInitializer
package com.xiaoliuliu.six.finger.web.server;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.socket.SocketChannel;
import io.netty.handler.codec.http.HttpObjectAggregator;
import io.netty.handler.codec.http.HttpServerCodec;
/**
* @author 小六六
* @version 1.0 * @date 2020/10/13 11:57 * 用于配置 pipeline 的解决链
*/public class HttpServerInitializer extends ChannelInitializer<SocketChannel> {@Override protected void initChannel(SocketChannel socketChannel) throws Exception {ChannelPipeline pipeline = socketChannel.pipeline(); // http 编解码
pipeline.addLast(new HttpServerCodec()); // http 音讯聚合器
pipeline.addLast("httpAggregator",new HttpObjectAggregator(512*1024)); // 申请处理器
pipeline.addLast(new HttpRequestHandler()); }}
HttpRequestHandler
package com.xiaoliuliu.six.finger.web.server;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.handler.codec.http.*;
import io.netty.util.AsciiString;
import io.netty.util.CharsetUtil;
import java.util.HashMap;
import java.util.Map;
import static io.netty.handler.codec.http.HttpUtil.is100ContinueExpected;
/**
* @author 小六六
* @version 1.0 * @date 2020/10/13 12:01 * 外围解决 http 申请的类,包含 url 的匹配外围办法都是在 channelRead0 办法
*/public class HttpRequestHandler extends SimpleChannelInboundHandler<FullHttpRequest> {private static final String FAVICON_ICO = "/favicon.ico"; private static final AsciiString CONNECTION = AsciiString.cached("Connection"); private static final AsciiString KEEP_ALIVE = AsciiString.cached("keep-alive");
@Override public void channelReadComplete(ChannelHandlerContext ctx) {ctx.flush(); }
@Override protected void channelRead0(ChannelHandlerContext ctx, FullHttpRequest req) throws Exception {System.out.println("取得的参数:"+req);
if (is100ContinueExpected(req)) {ctx.write(new DefaultFullHttpResponse( HttpVersion.HTTP_1_1, HttpResponseStatus.CONTINUE)); } // 获取申请的 uri
String uri = req.uri(); Map<String,String> resMap = new HashMap<>(); resMap.put("method",req.method().name()); resMap.put("uri",uri); String msg = "<html><head><title> 小六六揭示你 </title></head><body> 你申请 uri 为:" + uri+"</body></html>";
// 创立 http 响应
DefaultFullHttpResponse response = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.OK, Unpooled.copiedBuffer(msg, CharsetUtil.UTF_8));
// 设置头信息
response.headers().set(HttpHeaderNames.CONTENT_TYPE, "text/html; charset=UTF-8");
// 把音讯输入到浏览器
ctx.writeAndFlush(response).addListener(ChannelFutureListener.CLOSE);
}
@Override public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {cause.printStackTrace(); ctx.close();}}
ApplicationServer 测试类
package com.xiaoliuliu.six.finger.web.demo.server;
import com.xiaoliuliu.six.finger.web.server.HttpServer;
/**
* @author 小六六
* @version 1.0 * @date 2020/10/13 14:26 * 这个类 用于 搭建 Netty web 服务器的测试类,只实用于搭建教程的第一篇文章
*/public class ApplicationServer {public static void main(String[] args) throws Exception {HttpServer server = new HttpServer(8081);// 8081 为启动端口
server.start();}}
测试后果
在浏览器上输出
http://localhost:8081/xiaoliuliu
咱们看看输入
而后咱们来看看控制台
发现多了一次申请,这个是什么起因呢?
这是因为 HttpRequestDecoder 把申请拆分成 HttpRequest 和 HttpContent 两局部,
所以咱们要过滤哪个 /favicon.ico 的申请,所以改改代码
if("/favicon.ico".equals(uri)) {System.out.println("申请了 favicon.ico, 不做响应");
return; }```
## 结尾
好了,明天咱们用几十行代码实现了一个简略的 Http 服务器,很多的细节咱们一一解说,然而我的正文基本上都写了,如果你有看不懂的中央,欢送你来找我,我有空会给大家解答的,而后下一章就是咱们要写的 SpringMVC 相干的代码了。![](//p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/14aafd3e9f1c4db8b62fe1e5baaaac4b~tplv-k3u1fbpfcp-zoom-1.image)
## 日常求赞
> 好了各位,以上就是这篇文章的全部内容了,能看到这里的人呀,都是 ** 真粉 **。> 创作不易,各位的反对和认可,就是我创作的最大能源,咱们下篇文章