跟我动手搭框架三之Web容器实现
本篇主要对Web的实现做说明,在参考文章的同事,可以把code clone下来,看,代码中有很多需要优化的地址,我已经用TODO标记处理啊,小编会不断的进行优化和分析,演示SmileBootDemo也可以git clone,debug学习
目录
-
- 核心描述类介绍
-
- Smile启动核心实现
-
- Http请求多线程异步实现
-
- 下一篇主要介绍内容
-
- 扩展
1. 核心描述类,主要保存处理方法及参数类型
其实所有方法的执行,都离不开ioc的实现,IOC主要将组件(被@SmileComponent标记过的都为组件)保存为BeanDefinition的形式,而组件中的method,主要保存为WebDefinition的形式,而method的参数名称,参数类型,参数位置索引,主要保存在ParamterDefinition,对于请求的处理,就是根据url找到对应的method,然后根据ParamterDefinition将请求参数,转换成参数原本类型,然后处理
描述类 | 说明 | 存放位置 |
---|---|---|
BeanDefinition | 保存组件Class字节码及实例化对象 | Map |
2. Smile启动核心
2.1 SmileApplicationContext 扫描所有组件,并check 是否需要代理,最终生成IOC容器
Map<String, BeanDefinition> registeredBeans = new ConcurrentHashMap<>();
2.2 SmileApplicationContext生成IOC容器之后,加载是否有实现ExtApplicationContext扩展类
scanExtContext()
方法WebApplicationContext此时会执行,并扫描被@ResController注解修饰的路由类,然后
扫描其Methods,获取方法中有@GetMapping和@PostMapping 的方法生成
WebDefinition
和ParamterDefinition
Consumer<Map.Entry<String, BeanDefinition>> entryConsumer = entry -> { BeanDefinition beanDefinition = entry.getValue(); Class<?> controllerClass = beanDefinition.getClazz(); RestController annotation = controllerClass.getAnnotation(RestController.class); String oneUrl = annotation.value(); Method[] methods = controllerClass.getMethods(); for (Method method : methods) { boolean isGet = method.isAnnotationPresent(GetMapping.class); if (isGet) { bindGetMethod(oneUrl, method, beanDefinition); } boolean isPost = method.isAnnotationPresent(PostMapping.class); if (isPost) { bindPostMethod(oneUrl, method, beanDefinition); } } }; definitionMap.entrySet().forEach(entryConsumer);
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
/** * 绑定get请求 * * @param oneUrl 一级url * @param method 方法 * @param beanDefinition bean描述 */ public void bindGetMethod(String oneUrl, Method method, BeanDefinition beanDefinition) { Object controllerInstance = beanDefinition.getInstance(); Package aPackage = beanDefinition.getClazz().getPackage(); GetMapping getMapping = method.getAnnotation(GetMapping.class); String twoUrl = getMapping.value(); String[] parameterNames = WebTools.getParameterNames(method); if (StringTools.isEmpty(twoUrl)) { throw new BindUrlHanderException("[ " + aPackage.getName() + " ]:绑定url异常,请检查,请填写需要绑定的url地址"); } String realUrl = WebTools.checkUrl(oneUrl, twoUrl); String methodPath = method.toGenericString(); logger.info("Mapped url:[{}],produces:[{}],consumes:[{}],paramter:{},onto:{}", realUrl, getMapping.produces(), getMapping.consumes(), parameterNames, methodPath); webHandlerMaps.put(realUrl, new WebDefinition(realUrl, RequestMethod.GET, getMapping.consumes(), getMapping.produces(), controllerInstance, method, parameterNames)); }
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
3. HTTP处理实现核心
3.1将每个请求生成一个MessageRequest及MessageResponse,作为一个任务交给线程池去异步执行
MessageRequest messageRequest = new MessageRequest(randomUUID, requestMethod, requestParams, webDefinition, headerMaps); MessageResponse messageResponse = new MessageResponse(); SmileTaskChoice smileTaskChoice = new DefaultTaskProcessChoice(messageRequest, messageResponse, false); SmileMessageExecutor.submit(smileTaskChoice.choice(), ctx, req, messageRequest, messageResponse);
- 1
- 2
- 3
- 4
3.2 SmileTaskChoice 是执行策略,当为ture时候,为rpc默认,可以定义自己的rpc远程调用的方式实现结果返回
public class DefaultTaskProcessChoice implements SmileTaskChoice { private boolean isRpc = false; private MessageRequest messageRequest; private MessageResponse messageResponse; /** * * @param request * @param response * @param isRpc 本地方法:false rpc调用:true */ public DefaultTaskProcessChoice(final MessageRequest request, final MessageResponse response, boolean isRpc) { this.messageRequest = request; this.messageResponse = response; this.isRpc = isRpc; } @Override public Callable choice() { Callable callTask =null; if (!isRpc) { callTask = new LocalMessageTask(messageRequest, messageResponse); }else { callTask=new RpcProcessTask(messageRequest,messageResponse); } return callTask; } }
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
3.3 SmileTask中
主要利用反射,将请求 URL
获取到 WebDefinition
,拿到执行方法,将请求参数,绑定到方法,作为实参,传递
Object invokeResult = method.invoke(controller, args);
并通过Netty 连接通道Channel把处理结果返回给客户端
3.4 SmileMessageExecutor.submit 方法中监听任务是否成功处理,成功并通过Netty 连接通道Channel把处理结果返回给客户端
public static void submit(Callable<Boolean> task, final ChannelHandlerContext ctx, HttpRequest metaRequest, final MessageRequest request, final MessageResponse response) { /** * SmileThreadFactory 目的构建自己的线程名,并通过线程组进行统一管理 * SmileThreadPoolExecutor 构建自己的线程池,对任务进行,细微管理 */ if (threadPoolExecutor == null) { SmileThreadPoolExecutor smileThreadPoolExecutor = new SmileThreadPoolExecutor(new SmileThreadFactory("Smile")); ThreadPoolExecutor executorService = (ThreadPoolExecutor) smileThreadPoolExecutor.getExecutory(); threadPoolExecutor = MoreExecutors.listeningDecorator(executorService); } /** * 处理完成任务如果任务完成就,渲染出去 */ ListenableFuture<Boolean> listenableFuture = threadPoolExecutor.submit(task); Futures.addCallback(listenableFuture, new FutureCallback<Boolean>() { @Override public void onSuccess(Boolean result) { if (result){ NettyResponse.writeResponseAndListener(ctx.channel(), request, response, new ChannelFutureListener() { @Override public void operationComplete(ChannelFuture channelFuture) throws Exception { channelFuture.channel().close(); logger.info("Smile Server Send message-id:{}" , request.getMessageId()); } }); } } @Override public void onFailure(Throwable t) { t.printStackTrace(); } }, threadPoolExecutor); }
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
4. 下一篇主要介绍内容
框架的实现方案属于传统的MVC机构,只不过吧视图层V取消掉了,这也是趋势,前后分离,后端只做关心数据处理,
而传统的MVC架构,核心为Servlet,SpringMVC核心为DispatchServlet,是对原始Java Servlet的一个封装,关于这点可以看小编的另一篇文章手写一个轻量级的网关API,当然使用Netty也是如此,我们的入口就是HttpDispatchServerHandler
核心方法就是messageReceivedDispatch
而只知道,这些是远远不够的,Netty是由JBOSS提供的一个java开源框架。Netty提供异步的、事件驱动的网络应用程序框架和工具,我会新开二篇,专门介绍IO模型重点介绍IO multiplexing(IO多路复用)
和Netty
如何工作 ! 包括如何实现,心跳检测
主要会介绍下面写模块
- Bootstrap or ServerBootstrap
- EventLoop
- EventLoopGroup
- ChannelPipeline
- Future or ChannelFuture
- ChannelInitializer
- ChannelHandler
- ByteToMessageDecoder
- MessageToByteEncoder
public void messageReceivedDispatch(ChannelHandlerContext ctx, HttpObject msg) throws Exception {
String dispatchUrl = "";
Map<String, Object> headerMaps = new ConcurrentHashMap<>();
if (msg instanceof HttpRequest) {
HttpRequest req = this.request = (HttpRequest) msg;
HttpHeaders headers = req.headers();
headers.entries().stream().forEach(x -> {
headerMaps.put(x.getKey(), x.getValue());
});
String contentType = request.headers().get("Content-Type");
String methodName = request.getMethod().name();
dispatchUrl = req.getUri();
String randomUUID = UUID.randomUUID().toString().replaceAll("-", "");
Map<String, Object> requestParams = new ConcurrentHashMap<>();
// 处理get请求
if (methodName.equalsIgnoreCase("GET")) {
boolean contains = dispatchUrl.contains("?");
if (contains){
String queryContent = dispatchUrl.substring(dispatchUrl.indexOf("?") + 1);
Map<String, Object> queryParameterFromContent = URLTools.getQueryParameterFromContent(queryContent);
queryParameterFromContent.entrySet().forEach(entry -> {
requestParams.put(entry.getKey(), entry.getValue());
});
}
}
// 处理POST请求
if (methodName.equalsIgnoreCase("POST")) {
if (StringTools.endsWithIgnoreCase(contentType, "application/json")) {
FullHttpRequest request1 = (FullHttpRequest) msg;
ByteBuf jsonBuf = request1.content();
String jsonStr = jsonBuf.toString(CharsetUtil.UTF_8).replaceAll("\\\\s*|\\t|\\r|\\n", "");
if (!StringTools.isEmpty(jsonStr)) {
requestParams.put("BODY", jsonStr);
}
} else {
HttpPostRequestDecoder decoder = new HttpPostRequestDecoder(
new DefaultHttpDataFactory(false), req);
List<InterfaceHttpData> postData = decoder.getBodyHttpDatas(); //
for (InterfaceHttpData data : postData) {
if (data.getHttpDataType() == InterfaceHttpData.HttpDataType.Attribute) {
MemoryAttribute attribute = (MemoryAttribute) data;
requestParams.put(attribute.getName(), attribute.getValue());
}
}
}
}
if (StringTools.contains(dispatchUrl,"?")){
dispatchUrl = dispatchUrl.substring(0, dispatchUrl.indexOf("?"));
}
RequestMethod requestMethod = WebTools.getRequestMethod(methodName);
WebDefinition webDefinition = WebContextTools.getWebDefinitionByUrl(dispatchUrl, requestMethod);
if (webDefinition instanceof Web404Definition) {
NettyResponse.writeResponse(ctx.channel(), "Not Found", HttpResponseStatus.NOT_FOUND);
return;
}
if (webDefinition instanceof Web405Definition) {
NettyResponse.writeResponse(ctx.channel(), "Method Not Allowed", HttpResponseStatus.METHOD_NOT_ALLOWED);
return;
}
String consumes = webDefinition.getConsumes();
if (StringTools.isNotEmpty(contentType)){
if (StringTools.isNotEmpty(consumes)&(!contentType.equalsIgnoreCase(consumes))){
NettyResponse.writeResponse(ctx.channel(), "Bad Request (The content-type don't match)", HttpResponseStatus.BAD_REQUEST);
return;
}
}
/**
* //TODO 异步处理url获取处理的 bean
*/
MessageRequest messageRequest = new MessageRequest(randomUUID, requestMethod, requestParams, webDefinition, headerMaps);
MessageResponse messageResponse = new MessageResponse();
/**
* //TODO 根据启动配置,当如果是rpc服务就要使用MessageProcessTask
* 如果是本地服务使用LocalMessageTask
*
* 此时MessageRequest和MessageResponse都是final 修饰,目的是保证始终是对当前的MessageResponse
*/
SmileTaskChoice smileTaskChoice = new DefaultTaskProcessChoice(messageRequest, messageResponse, false);
/**
* //TODO 交给线程处理异步处理响应
*/
SmileMessageExecutor.submit(smileTaskChoice.choice(), ctx, req, messageRequest, messageResponse);
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
- 84
扩展
再次声明小编也是一个菜鸟,是一只具有学习精神,并热爱编程的菜鸟, 所有的文章都是经过参考很多优秀博文,给我带来的进步,小编,希望将学习到的所有知识点,也分享给大家 ! 小编会在这里列出,参考到的优秀博文,尊重每位知识传播者的劳动果实.
如果您发现小编文章中,有错误,请及时指出,并通知小编改正,小编在此谢过.
欢迎继续关注小编~ 小编努力coding…
参考
Smart Framework 设计动力来源
segmentfault-Netty 源码分析 Netty强化学习
SpringIOC源码分析 描述类灵感来源
文章来源: springlearn.blog.csdn.net,作者:西魏陶渊明,版权归原作者所有,如需转载,请联系作者。
原文链接:springlearn.blog.csdn.net/article/details/78858091
- 点赞
- 收藏
- 关注作者
评论(0)