本篇主要對Web的實(shí)現(xiàn)做說明,在參考文章的同事,可以把code clone下來,看,代碼中有很多需要優(yōu)化的地址,我已經(jīng)用TODO標(biāo)記處理啊,小編會(huì)不斷的進(jìn)行優(yōu)化和分析,演示SmileBootDemo也可以git clone,debug學(xué)習(xí)
目錄
- 核心描述類介紹
- Smile啟動(dòng)核心實(shí)現(xiàn)
- Http請求多線程異步實(shí)現(xiàn)
- 下一篇主要介紹內(nèi)容
-
- 擴(kuò)展
?
1. 核心描述類,主要保存處理方法及參數(shù)類型
其實(shí)所有方法的執(zhí)行,都離不開ioc的實(shí)現(xiàn),IOC主要將組件(被@SmileComponent標(biāo)記過的都為組件)保存為BeanDefinition的形式,而組件中的method,主要保存為WebDefinition的形式,而method的參數(shù)名稱,參數(shù)類型,參數(shù)位置索引,主要保存在ParamterDefinition,對于請求的處理,就是根據(jù)url找到對應(yīng)的method,然后根據(jù)ParamterDefinition將請求參數(shù),轉(zhuǎn)換成參數(shù)原本類型,然后處理
描述類 | 說明 | 存放位置 |
---|---|---|
BeanDefinition | 保存組件Class字節(jié)碼及實(shí)例化對象 | Map<String(beanName), BeanDefinition> registeredBeans |
WebDefinition | 保存Url及對應(yīng)的處理方法,及實(shí)例化對象,及參數(shù)類型 | Map<String(url), WebDefinition> webHandlerMaps = new ConcurrentHashMap<>(); |
ParamterDefinition | 保存參數(shù)位置索引及參數(shù)名稱,參數(shù)類型,參數(shù)注解 | WebDefinition.ParamterDefinition |
2. Smile啟動(dòng)核心
-
2.1 SmileApplicationContext 掃描所有組件,并check 是否需要代理,最終生成IOC容器
Map<String, BeanDefinition> registeredBeans = new ConcurrentHashMap<>();
-
2.2 SmileApplicationContext生成IOC容器之后,加載是否有實(shí)現(xiàn)ExtApplicationContext擴(kuò)展類
scanExtContext()
方法WebApplicationContext此時(shí)會(huì)執(zhí)行,并掃描被@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);
/** * 綁定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異常,請檢查,請?zhí)顚懶枰壎ǖ膗rl地址"); } 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)); }
?
3. HTTP處理實(shí)現(xiàn)核心
-
3.1將每個(gè)請求生成一個(gè)MessageRequest及MessageResponse,作為一個(gè)任務(wù)交給線程池去異步執(zhí)行
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);
-
3.2 SmileTaskChoice 是執(zhí)行策略,當(dāng)為ture時(shí)候,為rpc默認(rèn),可以定義自己的rpc遠(yuǎn)程調(diào)用的方式實(shí)現(xiàn)結(jié)果返回
public class DefaultTaskProcessChoice implements SmileTaskChoice { private boolean isRpc = false; private MessageRequest messageRequest; private MessageResponse messageResponse; /** * * @param request * @param response * @param isRpc 本地方法:false rpc調(diào)用: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; } }
?
3.3 SmileTask中
主要利用反射魔熏,將請求 URL
獲取到 WebDefinition
,拿到執(zhí)行方法,將請求參數(shù),綁定到方法,作為實(shí)參,傳遞
Object invokeResult = method.invoke(controller, args);
并通過Netty 連接通道Channel把處理結(jié)果返回給客戶端
-
3.4 SmileMessageExecutor.submit 方法中監(jiān)聽任務(wù)是否成功處理,成功并通過Netty 連接通道Channel把處理結(jié)果返回給客戶端
public static void submit(Callable<Boolean> task, final ChannelHandlerContext ctx, HttpRequest metaRequest, final MessageRequest request, final MessageResponse response) { /** * SmileThreadFactory 目的構(gòu)建自己的線程名,并通過線程組進(jìn)行統(tǒng)一管理 * SmileThreadPoolExecutor 構(gòu)建自己的線程池,對任務(wù)進(jìn)行,細(xì)微管理 */ if (threadPoolExecutor == null) { SmileThreadPoolExecutor smileThreadPoolExecutor = new SmileThreadPoolExecutor(new SmileThreadFactory("Smile")); ThreadPoolExecutor executorService = (ThreadPoolExecutor) smileThreadPoolExecutor.getExecutory(); threadPoolExecutor = MoreExecutors.listeningDecorator(executorService); } /** * 處理完成任務(wù)如果任務(wù)完成就,渲染出去 */ 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); }
?
4. 下一篇主要介紹內(nèi)容
框架的實(shí)現(xiàn)方案屬于傳統(tǒng)的MVC機(jī)構(gòu),只不過吧視圖層V取消掉了,這也是趨勢,前后分離,后端只做關(guān)心數(shù)據(jù)處理,
而傳統(tǒng)的MVC架構(gòu),核心為Servlet,SpringMVC核心為DispatchServlet,是對原始Java Servlet的一個(gè)封裝,關(guān)于這點(diǎn)可以看小編的另一篇文章手寫一個(gè)輕量級的網(wǎng)關(guān)API,當(dāng)然使用Netty也是如此,我們的入口就是HttpDispatchServerHandler
核心方法就是messageReceivedDispatch
而只知道,這些是遠(yuǎn)遠(yuǎn)不夠的,Netty是由JBOSS提供的一個(gè)java開源框架锈颗。Netty提供異步的淳衙、事件驅(qū)動(dòng)的網(wǎng)絡(luò)應(yīng)用程序框架和工具,我會(huì)新開二篇,專門介紹IO模型重點(diǎn)介紹IO multiplexing(IO多路復(fù)用)
和Netty
如何工作 ! 包括如何實(shí)現(xiàn),心跳檢測
主要會(huì)介紹下面寫模塊
- 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 根據(jù)啟動(dòng)配置,當(dāng)如果是rpc服務(wù)就要使用MessageProcessTask
* 如果是本地服務(wù)使用LocalMessageTask
*
* 此時(shí)MessageRequest和MessageResponse都是final 修飾,目的是保證始終是對當(dāng)前的MessageResponse
*/
SmileTaskChoice smileTaskChoice = new DefaultTaskProcessChoice(messageRequest, messageResponse, false);
/**
* //TODO 交給線程處理異步處理響應(yīng)
*/
SmileMessageExecutor.submit(smileTaskChoice.choice(), ctx, req, messageRequest, messageResponse);
}
}
擴(kuò)展
再次聲明小編也是一個(gè)菜鳥,是一只具有學(xué)習(xí)精神,并熱愛編程的菜鳥, 所有的文章都是經(jīng)過參考很多優(yōu)秀博文,給我?guī)淼倪M(jìn)步,小編,希望將學(xué)習(xí)到的所有知識(shí)點(diǎn),也分享給大家 ! 小編會(huì)在這里列出,參考到的優(yōu)秀博文,尊重每位知識(shí)傳播者的勞動(dòng)果實(shí).
如果您發(fā)現(xiàn)小編文章中,有錯(cuò)誤,請及時(shí)指出,并通知小編改正,小編在此謝過.
歡迎繼續(xù)關(guān)注小編~ 小編努力coding…
參考
Smart Framework 設(shè)計(jì)動(dòng)力來源
segmentfault-Netty 源碼分析 Netty強(qiáng)化學(xué)習(xí)
SpringIOC源碼分析 描述類靈感來源