diff --git a/neutrino-core/src/main/java/fun/asgc/neutrino/core/util/BeanManager.java b/neutrino-core/src/main/java/fun/asgc/neutrino/core/util/BeanManager.java index e4bec979..be64545f 100644 --- a/neutrino-core/src/main/java/fun/asgc/neutrino/core/util/BeanManager.java +++ b/neutrino-core/src/main/java/fun/asgc/neutrino/core/util/BeanManager.java @@ -25,6 +25,8 @@ package fun.asgc.neutrino.core.util; import fun.asgc.neutrino.core.annotation.Component; import fun.asgc.neutrino.core.bean.BeanFactory; import fun.asgc.neutrino.core.bean.BeanFactoryAware; +import fun.asgc.neutrino.core.bean.BeanIdentity; +import fun.asgc.neutrino.core.exception.BeanException; import lombok.extern.slf4j.Slf4j; import java.util.List; @@ -58,6 +60,20 @@ public class BeanManager implements BeanFactoryAware { return beanFactory.getBean(clazz); } + /** + * 尝试根据Bean身份标识获取bean + * @param identity + * @param + * @return + * @throws BeanException + */ + public static T getBean(BeanIdentity identity) { + if (null == beanFactory) { + throw new RuntimeException("BeanFactory尚未初始化"); + } + return beanFactory.getBean(identity); + } + /** * 根据超类获取bean集合 * @param superClass diff --git a/neutrino-core/src/main/java/fun/asgc/neutrino/core/web/HttpRequestHandler.java b/neutrino-core/src/main/java/fun/asgc/neutrino/core/web/HttpRequestHandler.java index 4026be5e..17938356 100644 --- a/neutrino-core/src/main/java/fun/asgc/neutrino/core/web/HttpRequestHandler.java +++ b/neutrino-core/src/main/java/fun/asgc/neutrino/core/web/HttpRequestHandler.java @@ -21,19 +21,30 @@ */ package fun.asgc.neutrino.core.web; +import com.alibaba.fastjson.JSONObject; import fun.asgc.neutrino.core.annotation.Autowired; import fun.asgc.neutrino.core.annotation.Component; import fun.asgc.neutrino.core.annotation.NonIntercept; import fun.asgc.neutrino.core.context.ApplicationConfig; +import fun.asgc.neutrino.core.util.*; import fun.asgc.neutrino.core.web.router.DefaultHttpRouter; +import fun.asgc.neutrino.core.web.router.HttpRouteParam; +import fun.asgc.neutrino.core.web.router.HttpRouteResult; +import fun.asgc.neutrino.core.web.router.HttpRouterType; +import io.netty.buffer.Unpooled; +import io.netty.channel.ChannelFutureListener; import io.netty.channel.ChannelHandlerContext; -import io.netty.handler.codec.http.FullHttpRequest; +import io.netty.handler.codec.http.*; +import lombok.extern.slf4j.Slf4j; + +import java.lang.reflect.InvocationTargetException; /** * * @author: aoshiguchen * @date: 2022/7/15 */ +@Slf4j @NonIntercept @Component public class HttpRequestHandler { @@ -41,10 +52,66 @@ public class HttpRequestHandler { private ApplicationConfig applicationConfig; @Autowired private DefaultHttpRouter defaultHttpRouter; + private static volatile String httpContextPath; public void handle(ChannelHandlerContext context, FullHttpRequest request) { - // TODO - System.out.println("hello"); + String routePath = getRoutePath(request.uri()); + HttpMethod httpMethod = HttpMethod.of(request.method().name()); + HttpRouteResult httpRouteResult = defaultHttpRouter.route(new HttpRouteParam().setMethod(httpMethod).setUrl(routePath)); + if (null == httpRouteResult) { + HttpServerUtil.send404Response(context, request.uri()); + return; + } + if (HttpRouterType.METHOD == httpRouteResult.getType()) { + try { + Object invokeResult = httpRouteResult.getMethod().invoke(httpRouteResult.getInstance()); + String res = String.valueOf(invokeResult); + if (!TypeUtil.isNormalBasicType(invokeResult.getClass())) { + res = JSONObject.toJSONString(invokeResult); + } + FullHttpResponse fullHttpResponse = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.OK, Unpooled.wrappedBuffer(res.getBytes())); + fullHttpResponse.headers().add(HttpHeaderNames.CONTENT_TYPE, HttpHeaderValues.APPLICATION_JSON); + context.writeAndFlush(fullHttpResponse).addListener(ChannelFutureListener.CLOSE); + return; + } catch (Exception e) { + // TODO + } + } else { + // TODO + HttpServerUtil.send404Response(context, request.uri()); + return; + } } + private String getRoutePath(String url) { + String httpContextPath = getHttpContextPath(); + if (StringUtil.isEmpty(httpContextPath)) { + return url; + } + String res = url.substring(httpContextPath.length()); + if (StringUtil.isEmpty(res)) { + return "/"; + } + return res; + } + + private String getHttpContextPath() { + return LockUtil.doubleCheckProcessForNoException( + () -> null == httpContextPath, + this, + () -> { + httpContextPath = applicationConfig.getHttp().getContextPath(); + if (StringUtil.isEmpty(httpContextPath)) { + httpContextPath = "/"; + } + if (!httpContextPath.startsWith("/")) { + httpContextPath = "/" + httpContextPath; + } + if (httpContextPath.endsWith("/")) { + httpContextPath = httpContextPath.substring(0, httpContextPath.length() - 1); + } + }, + () -> httpContextPath + ); + } } diff --git a/neutrino-core/src/main/java/fun/asgc/neutrino/core/web/router/DefaultHttpRouter.java b/neutrino-core/src/main/java/fun/asgc/neutrino/core/web/router/DefaultHttpRouter.java index f0f32365..135774a7 100644 --- a/neutrino-core/src/main/java/fun/asgc/neutrino/core/web/router/DefaultHttpRouter.java +++ b/neutrino-core/src/main/java/fun/asgc/neutrino/core/web/router/DefaultHttpRouter.java @@ -21,14 +21,27 @@ */ package fun.asgc.neutrino.core.web.router; +import com.google.common.collect.Sets; import fun.asgc.neutrino.core.annotation.*; import fun.asgc.neutrino.core.bean.BeanWrapper; import fun.asgc.neutrino.core.bean.SimpleBeanFactory; +import fun.asgc.neutrino.core.util.ArrayUtil; +import fun.asgc.neutrino.core.util.CollectionUtil; +import fun.asgc.neutrino.core.util.ReflectUtil; +import fun.asgc.neutrino.core.web.HttpMethod; +import fun.asgc.neutrino.core.web.annotation.GetMapping; +import fun.asgc.neutrino.core.web.annotation.PostMapping; +import fun.asgc.neutrino.core.web.annotation.RequestMapping; +import fun.asgc.neutrino.core.web.annotation.RestController; import lombok.extern.slf4j.Slf4j; +import java.lang.reflect.Method; import java.util.List; import java.util.Map; +import java.util.Set; import java.util.concurrent.ConcurrentHashMap; +import java.util.stream.Collectors; +import java.util.stream.Stream; /** * 默认的http路由器 @@ -50,7 +63,125 @@ public class DefaultHttpRouter implements HttpRouter { public void init() { log.debug("Http路由器初始化..."); List beanWrapperList = webApplicationBeanFactory.beanWrapperList(); - // TODO 路由初始化 + beanWrapperList.forEach(beanWrapper -> { + RestController restController = beanWrapper.getType().getAnnotation(RestController.class); + if (null == restController) { + return; + } + Set methods = null; + Set paths = null; + if (beanWrapper.getType().isAnnotationPresent(GetMapping.class)) { + GetMapping getMapping = beanWrapper.getType().getAnnotation(GetMapping.class); + methods = Sets.newHashSet(HttpMethod.GET); + if (ArrayUtil.notEmpty(getMapping.value())) { + paths = Stream.of(getMapping.value()).collect(Collectors.toSet()); + } + } else if (beanWrapper.getType().isAnnotationPresent(PostMapping.class)) { + PostMapping postMapping = beanWrapper.getType().getAnnotation(PostMapping.class); + methods = Sets.newHashSet(HttpMethod.POST); + if (ArrayUtil.notEmpty(postMapping.value())) { + paths = Stream.of(postMapping.value()).collect(Collectors.toSet()); + } + } else if (beanWrapper.getType().isAnnotationPresent(RequestMapping.class)) { + RequestMapping requestMapping = beanWrapper.getType().getAnnotation(RequestMapping.class); + if (ArrayUtil.notEmpty(requestMapping.value())) { + paths = Stream.of(requestMapping.value()).collect(Collectors.toSet()); + } + if (ArrayUtil.notEmpty(requestMapping.method())) { + methods = Stream.of(requestMapping.method()).collect(Collectors.toSet()); + } + } + + if (CollectionUtil.isEmpty(methods)){ + methods = Stream.of(HttpMethod.values()).collect(Collectors.toSet()); + } + if (CollectionUtil.isEmpty(paths)) { + paths = Sets.newHashSet(""); + } + paths = paths.stream().map(p -> { + if (p.endsWith("/")) { + return p.substring(0, p.length() - 1); + } + return p; + }).collect(Collectors.toSet()); + methodScan(beanWrapper, methods, paths); + }); + } + + private void methodScan(BeanWrapper beanWrapper, Set httpMethods, Set paths) { + Set methods = ReflectUtil.getDeclaredMethods(beanWrapper.getType()); + methods.forEach(method -> { + Set realityHttpMethods = null; + Set subPaths = null; + if (method.isAnnotationPresent(GetMapping.class)) { + GetMapping getMapping = method.getAnnotation(GetMapping.class); + if (!httpMethods.contains(HttpMethod.GET)) { + throw new RuntimeException(String.format("Controller[type:%s, name:%s] method:%s 方法上的GetMapping注解与类的声明发生冲突!", beanWrapper.getType().getName(), beanWrapper.getName(), method.toString())); + } + realityHttpMethods = Sets.newHashSet(HttpMethod.GET); + if (ArrayUtil.notEmpty(getMapping.value())) { + subPaths = Stream.of(getMapping.value()).collect(Collectors.toSet()); + } + } else if (method.isAnnotationPresent(PostMapping.class)) { + PostMapping postMapping = method.getAnnotation(PostMapping.class); + if (!httpMethods.contains(HttpMethod.POST)) { + throw new RuntimeException(String.format("Controller[type:%s, name:%s] method:%s 方法上的PostMapping注解与类的声明发生冲突!", beanWrapper.getType().getName(), beanWrapper.getName(), method.toString())); + } + realityHttpMethods = Sets.newHashSet(HttpMethod.POST); + if (ArrayUtil.notEmpty(postMapping.value())) { + subPaths = Stream.of(postMapping.value()).collect(Collectors.toSet()); + } + } else if (method.isAnnotationPresent(RequestMapping.class)) { + RequestMapping requestMapping = method.getAnnotation(RequestMapping.class); + if (ArrayUtil.notEmpty(requestMapping.method())) { + realityHttpMethods = Stream.of(requestMapping.method()).collect(Collectors.toSet()); + } + if (!httpMethods.containsAll(realityHttpMethods)) { + throw new RuntimeException(String.format("Controller[type:%s, name:%s] method:%s 方法上的RequestMapping注解与类的声明发生冲突!", beanWrapper.getType().getName(), beanWrapper.getName(), method.toString())); + } + if (ArrayUtil.notEmpty(requestMapping.value())) { + subPaths = Stream.of(requestMapping.value()).collect(Collectors.toSet()); + } + } else { + return; + } + if (CollectionUtil.isEmpty(realityHttpMethods)) { + realityHttpMethods = Sets.newHashSet(httpMethods); + } + if (CollectionUtil.isEmpty(subPaths)) { + subPaths = Sets.newHashSet("/"); + } + subPaths = subPaths.stream().map(p -> { + if (!p.startsWith("/")) { + return "/" + p; + } + return p; + }).collect(Collectors.toSet()); + addRoute(beanWrapper, method, realityHttpMethods, paths, subPaths); + }); + } + + private void addRoute(BeanWrapper beanWrapper, Method method, Set httpMethods, Set paths, Set subPaths) { + for (HttpMethod httpMethod : httpMethods) { + for (String path : paths) { + for (String subPath : subPaths) { + addRoute(beanWrapper, method, httpMethod, path + subPath); + } + } + } + } + + private synchronized void addRoute(BeanWrapper beanWrapper, Method method, HttpMethod httpMethod, String path) { + log.info("addRoute[{}#{}] httpMethod:{} path:{}", beanWrapper.getType().getName(), method.getName(), httpMethod.name(), path); + HttpRouteIdentity identity = new HttpRouteIdentity(httpMethod, path); + if (routeCache.containsKey(identity)) { + throw new RuntimeException(String.format("Controller[type:%s, name:%s] method:%s 路由存在重复!", beanWrapper.getType().getName(), beanWrapper.getName(), method.getName())); + } + routeCache.put(identity, new HttpRouteInfo() + .setType(HttpRouterType.METHOD) + .setMethod(method) + .setBeanIdentity(beanWrapper.getIdentity()) + ); } @Override @@ -75,5 +206,4 @@ public class DefaultHttpRouter implements HttpRouter { public void destroy() { log.debug("Http路由器销毁..."); } - } diff --git a/neutrino-core/src/main/java/fun/asgc/neutrino/core/web/router/HttpRouteIdentity.java b/neutrino-core/src/main/java/fun/asgc/neutrino/core/web/router/HttpRouteIdentity.java index 8549b501..f4b515b2 100644 --- a/neutrino-core/src/main/java/fun/asgc/neutrino/core/web/router/HttpRouteIdentity.java +++ b/neutrino-core/src/main/java/fun/asgc/neutrino/core/web/router/HttpRouteIdentity.java @@ -55,7 +55,7 @@ public class HttpRouteIdentity implements Identity { Assert.notEmpty(url, "url不能为空!"); this.method = method; this.url = url; - this.identityHashCode = System.identityHashCode(url); + this.identityHashCode = System.identityHashCode(method); } public HttpMethod getMethod() { diff --git a/neutrino-core/src/main/java/fun/asgc/neutrino/core/web/router/HttpRouteInfo.java b/neutrino-core/src/main/java/fun/asgc/neutrino/core/web/router/HttpRouteInfo.java index de251bcd..03fa6727 100644 --- a/neutrino-core/src/main/java/fun/asgc/neutrino/core/web/router/HttpRouteInfo.java +++ b/neutrino-core/src/main/java/fun/asgc/neutrino/core/web/router/HttpRouteInfo.java @@ -23,6 +23,7 @@ package fun.asgc.neutrino.core.web.router; import fun.asgc.neutrino.core.bean.BeanIdentity; import lombok.Data; +import lombok.experimental.Accessors; import java.lang.reflect.Method; @@ -31,6 +32,7 @@ import java.lang.reflect.Method; * @author: aoshiguchen * @date: 2022/7/16 */ +@Accessors(chain = true) @Data public class HttpRouteInfo { /** diff --git a/neutrino-core/src/main/java/fun/asgc/neutrino/core/web/router/HttpRouteParam.java b/neutrino-core/src/main/java/fun/asgc/neutrino/core/web/router/HttpRouteParam.java index b59c94bb..210617fb 100644 --- a/neutrino-core/src/main/java/fun/asgc/neutrino/core/web/router/HttpRouteParam.java +++ b/neutrino-core/src/main/java/fun/asgc/neutrino/core/web/router/HttpRouteParam.java @@ -23,12 +23,14 @@ package fun.asgc.neutrino.core.web.router; import fun.asgc.neutrino.core.web.HttpMethod; import lombok.Data; +import lombok.experimental.Accessors; /** * http路由参数 * @author: aoshiguchen * @date: 2022/7/16 */ +@Accessors(chain = true) @Data public class HttpRouteParam { private HttpMethod method; diff --git a/neutrino-proxy-server/src/main/java/fun/asgc/neutrino/proxy/server/controller/Test1Controller.java b/neutrino-proxy-server/src/main/java/fun/asgc/neutrino/proxy/server/controller/Test1Controller.java new file mode 100644 index 00000000..c99f8d6a --- /dev/null +++ b/neutrino-proxy-server/src/main/java/fun/asgc/neutrino/proxy/server/controller/Test1Controller.java @@ -0,0 +1,46 @@ +/** + * Copyright (c) 2022 aoshiguchen + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +package fun.asgc.neutrino.proxy.server.controller; + +import fun.asgc.neutrino.core.web.annotation.GetMapping; +import fun.asgc.neutrino.core.web.annotation.RequestMapping; +import fun.asgc.neutrino.core.web.annotation.RestController; + +import java.text.SimpleDateFormat; +import java.util.Date; + +/** + * + * @author: aoshiguchen + * @date: 2022/7/17 + */ +@RestController +@RequestMapping("/test1") +public class Test1Controller { + private static final SimpleDateFormat SDF = new SimpleDateFormat( "yyyy-MM-dd :HH:mm:ss"); + + @GetMapping("hello") + public String hello() { + return "hello 现在时间是:" + SDF.format(new Date()); + } + +}