实现简单的Http服务器+SpringMvc,集成到Spring

实现简单的Http服务器+SpringMvc,集成到Spring
1、Http协议
1.1、HTTP 协议请求格式
复制代码
方法 + 空格 + URL + 空格 + 版本 + 回车符 + 换行符

头部域名称:头部域值                 + 回车符 + 换行符

...

头部域名称:头部域值                 + 回车符 + 换行符

回车符 + 换行符

请求数据

利用Tcp接收一条Http请求数据如下:

java 复制代码
GET /task/findAll?a=b HTTP/1.1
Host: localhost:8081
Connection: keep-alive
sec-ch-ua: "Google Chrome";v="119", "Chromium";v="119", "Not?A_Brand";v="24"
sec-ch-ua-mobile: ?0
sec-ch-ua-platform: "Windows"
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/119.0.0.0 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7
Sec-Fetch-Site: none
Sec-Fetch-Mode: navigate
Sec-Fetch-User: ?1
Sec-Fetch-Dest: document
Accept-Encoding: gzip, deflate, br
Accept-Language: zh-CN,zh;q=0.9

完全符合上面的http协议的格式,反过来,可以根据上面的协议格式,解析Http的参数和需要的字段。

解析完后的实体如下:

复制代码
{
    "headers": {
        "Accept": " text/html,application/xhtml xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7",
        "Connection": " keep-alive",
        "User-Agent": " Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/119.0.0.0 Safari/537.36",
        "Sec-Fetch-Site": " none",
        "Sec-Fetch-Dest": " document",
        "Host": " localhost",
        "Accept-Encoding": " gzip, deflate, br",
        "content-Type": "application/x-www-form-urlencoded",
        "Sec-Fetch-Mode": " navigate",
        "sec-ch-ua": " \"Google Chrome\";v=\"119\", \"Chromium\";v=\"119\", \"Not?A_Brand\";v=\"24\"",
        "sec-ch-ua-mobile": " ?0",
        "Cache-Control": " max-age=0",
        "Upgrade-Insecure-Requests": " 1",
        "sec-ch-ua-platform": " \"Windows\"",
        "Sec-Fetch-User": " ?1",
        "Accept-Language": " zh-CN,zh;q=0.9"
    },
    "methodName": "GET",
    "protocol": "HTTP",
    "queryString": {
        "a": "b"
    },
    "uri": "/task/findAll",
    "version": "1.1"
}
1.2、http协议相应格式
复制代码
版本 + 空格 + 状态码 + 空格 + 原因短句 + 回车符 + 换行符

头部域名称:头部域值                             + 回车符 + 换行符

...

头部域名称:头部域值                             + 回车符 + 换行符

回车符 + 换行符

相应正文
1.3、请求及相应
复制代码
public class Test {

    public static void main(String[] args) {
        HttpInvoke httpInvoke = new HttpInvoke() {
            @Override
            public Object invoke(RequestEntity entity) {
                return "hello";
            }
        };

        BootstrapServer bootstrapServer = new BootstrapServer(8081, httpInvoke);
    }
}
// HttpInvoke作为请求的回调,这里只做简单的回应"hello",启动8081

传送门(在httptest包下)

2、SpringMvc
2.1、扫描组件包

扫描组件包,将每个接口请求封装成MethodHandler

复制代码
Properties prop = PropertyUtils.getClassPathProperties(MVC_CLASSPATH_NAME);
packageScanner = new GenericPackagesScanner(ctx.getPackages());
// 1、找到被controller注解标记的类
Set<Class<?>> classes = packageScanner.getFullyQualifiedClassNameList();
Set<Class<?>> webSet = new HashSet<>();
Iterator<Class<?>> iterator = classes.iterator();
while (iterator.hasNext()) {
    Class<?> next = iterator.next();
    if(AnnotationUtils.containsAnyAnnotation(next, ControllerVax.class)) {
        webSet.add(next);
    }
}
// 2、解析处理方法
Iterator<Class<?>> controller = webSet.iterator();
List<MethodHandler> handlers = new ArrayList<>();
while (controller.hasNext()) {
    Class<?> next = controller.next();
    String commonUrl = AnnotationUtils.get(next, UrlMapping.class);
    List<Method> methods = ClassUtils.getMarkedMethod(next, UrlMapping.class);
    Iterator<Method> methodIt = methods.iterator();
    while (methodIt.hasNext()) {
        Method method = methodIt.next();
        String subUri = AnnotationUtils.get(method, UrlMapping.class);
        MethodHandler methodHandler = new MethodHandler();
        methodHandler.setMethod(method);
        methodHandler.setMethodURL(subUri);
        methodHandler.setClazz(next);
        methodHandler.setClazzUrl(commonUrl);
        methodHandler.setObj(ctx.getBean(next));
        methodHandler.setUrl(PathUtils.merge(commonUrl, subUri));
        methodHandler.setParameters(ClassUtils.getMethodParameters(method));
        if(handlers.contains(methodHandler)) {
        throw new MappingExistsException("method mapping exists");
        }
        handlers.add(methodHandler);
    }
}
2.2、匹配请求

遍历所有的MethodHandler,如果接口上的uri和请求中的uri一直,则将参数带进去执行,并将结果返回。

复制代码
Iterator<MethodHandler> iterator = handlers.iterator();
        while (iterator.hasNext()) {
            // 接口方法
            MethodHandler handler = iterator.next();
            if(StringUtils.equals(handler.getUrl(), entity.getUri())) {
                // controller实例
                Object bean = ctx.getBean(handler.getClazz());
                Method method = handler.getMethod();
                List<String> parameters = handler.getParameters();
                List<Parameter> parameterType = Arrays.asList(method.getParameters());
                Object[] parameterVal = new Object[parameters.size()];
                for (int i = 0; i < parameters.size(); i++) {
                    // 1、queryString
                    Map<String, String> queryString = entity.getQueryString();
                    if(queryString != null && queryString.size() > 0) {
                        parameterVal[i] = ObjectUtils.firstNotNull(queryString.get(parameters.get(i)));
                    }
                    // 2、body-json/form-data
                    Map<String, String> body = (Map<String, String>) entity.getBody();
                    if(body != null && body.size() > 0) {
                        parameterVal[i] = ObjectUtils.firstNotNull(body.get(parameters.get(i)));
                    }
                    if(!ObjectUtils.isSimpleType(parameterType.get(i).getType()) && StringUtils.equalsIgnore(entity.getContentType(), Constants.JSON_CONTENT_TYPE_VAL)) {
                        parameterVal[i] = JSON.parseObject(entity.getBody().toString(), parameterType.get(i).getType());
                    }
                }
                return ObjectUtils.invoke(bean, method, parameterVal);
            }
        }
3、Spring集成Mybatis

mybatis的代理对象,作为Spring组件,注入到容器中

复制代码
JdbcManager jdbcManager = new JdbcManager(getJdbcProperties());
packageScanner.setPackages(ctx.getPackages());
this.classes = packageScanner.getFullyQualifiedClassNameList();
Iterator<Class<?>> iterator = this.classes.iterator();
// 2、移除没有被注解标记的类
while (iterator.hasNext()) {
    Class<?> clazz = iterator.next();
    if(!AnnotationUtils.containsAnyAnnotation(clazz, GlobalConstants.MAPPER_ANNOTATIONS)) {
        iterator.remove();
        continue;
    }
    ctx.registry(clazz.getSimpleName(), ProxyUtils.getProxy(clazz, new MapperProxyFactory(jdbcManager)));
}
ctx.refresh(false);
4、运行结果





传送门

相关推荐
疯狂打码的少年8 分钟前
【计算机网络】子网掩码与子网划分计算(含CIDR表示法)
服务器·笔记·计算机网络·php
网硕互联的小客服1 小时前
怎样在CMD里用chkntfs /x C: 命令来取消磁盘自检?——原理与实践全解
运维·服务器·网络
布莱克6051 小时前
Linux 常用命令详解:从入门到实战
linux·运维·服务器
步行cgn1 小时前
Spring 启动报错:BeanFactory not initialized 的原因与解决
java·python·spring
步行cgn1 小时前
Spring 报错:No bean class specified on bean definition 的原因与解决
java·后端·spring
重生之我是Java开发战士2 小时前
【Spring Cloud】Nacos注册中心
后端·spring·spring cloud
Wang's Blog2 小时前
Java框架快速入门:Spring Security+OAuth2之用户注册与唯一性校验实现
java·数据库·spring
学习路上_write2 小时前
Linux黑马命令学习
linux·运维·服务器
那年窗外下的雪.2 小时前
AIDC 学习日志|第 22 天|All-Active/Single-Active 故障演练设计
服务器·网络·学习·算法·哈希算法
Wang's Blog2 小时前
Java框架快速入门: Spring Security+OAuth2之多因子认证与TOTP实战
java·spring·ui