如何使用Java自带包快速实现一个HTTP服务?

前言

JDK中自带的一个包com.sun.net.httpserver,使用他可以简单快速的创建一个http服务,支持http和https,但也只是简单,很多功能还需要自己写。

下面来做一个演示。

fun main() {
    var httpServer = HttpServer.create()
    httpServer.bind(InetSocketAddress(8080), 0);
    httpServer.createContext("/", object : HttpHandler {
        override fun handle(exchange: HttpExchange) {
            println(exchange.requestURI.query)
            var data = "Hello http server".toByteArray()
            exchange.sendResponseHeaders(200, data.size.toLong());
            exchange.responseBody.write(data)
            exchange.responseBody.flush()
        }
    })
    httpServer.setExecutor(null);
    httpServer.start()
}
复制代码

这几行就创建了一个Http,并且实现了/路径的访问

1641380662(1).png

HttpExchange是用来做输入输出的,不过像参数解析这些还得自己手写,可以通过下面语句获取url中整个的参数,注意的是getQuery方法是获取的被解码后的字符,如果要获取未被解码的字符需要使用getRawQuery。

var query = exchange.requestURI.query
复制代码

比如访问http://localhost:8080/ass/sdf?id=1&data=2,那么拿到的值就是id=1&data=2,下面做一下解析。

当然这个还不严谨,特殊编码没有处理。

fun String.toQueryMap(): Map<String, String> {
    var mutableMapOf = mutableMapOf<String, String>()
    var split = this.split("&")
    split.forEach {
        var item = it.split("=")
        mutableMapOf[item[0]] = item[1]
    }
    return mutableMapOf
}
fun main() {
    var httpServer = HttpServer.create()
    httpServer.bind(InetSocketAddress(8080), 0);
    httpServer.createContext("/", object : HttpHandler {
        override fun handle(exchange: HttpExchange) {
            var query = exchange.requestURI.query
            var querymap = query.toQueryMap().toString()
            var data = querymap.toByteArray()
            exchange.sendResponseHeaders(200, data.size.toLong());
            exchange.responseBody.write(data)
            exchange.responseBody.flush()
        }
    })
    httpServer.setExecutor(null);
    httpServer.start()
}
复制代码

在往下实现一个类似SpringBoot的@Controller注解。

定义请求参数注解

@Target(AnnotationTarget.VALUE_PARAMETER)
annotation class RequestParam(val name: String = "")
复制代码

定义Get请求映射地址

@Target(AnnotationTarget.FUNCTION)
annotation class GetMapping(
    val name: String = ""
)
复制代码

定义控制器

annotation class Controller(){
}
复制代码

控制器实现

@Controller
class IndexController {

    @GetMapping("/echo")
    fun index(@RequestParam("data") string: String): String {
        return "$string"
    }

    @GetMapping("/sendMessage")
    fun sendMessage(@RequestParam("msg") string: String): String {
        return "sendMessage ${string}"
    }
}
复制代码

核心实现

import com.sun.net.httpserver.HttpExchange
import com.sun.net.httpserver.HttpHandler
import com.sun.net.httpserver.HttpServer
import jdk.internal.org.objectweb.asm.ClassReader
import jdk.internal.org.objectweb.asm.tree.ClassNode
import java.io.File
import java.io.IOException
import java.lang.reflect.Method
import java.net.InetSocketAddress
import java.nio.file.*
import java.nio.file.attribute.BasicFileAttributes


var beans = mutableMapOf<String, Any>()

var urlMapping = mutableMapOf<String, UrlHandler>()

class Test {
}

class UrlHandler(var obj: Any, var method: Method) {
    fun invoke(vararg args: Any): String {
        return method.invoke(obj, *args) as String
    }
}

fun String.toQueryMap(): MutableMap<String, String> {
    var mutableMapOf = mutableMapOf<String, String>()

    try {
        var split = this.split("&")
        split.forEach {
            var item = it.split("=")
            mutableMapOf[item[0]] = item[1]
        }
    } catch (e: Exception) {
        e.printStackTrace()
    }
    return mutableMapOf
}

fun scanClassPath(): List<String> {
    var directory = ClassLoader.getSystemResource(".").file
    var classFileList = mutableListOf<String>()
    Files.walkFileTree(Paths.get(directory), object : SimpleFileVisitor<Path>() {
        override fun visitFile(file: Path, attrs: BasicFileAttributes): FileVisitResult {
            if (file.toString().endsWith(".class")) {
                classFileList.add(file.toString())
            }
            return FileVisitResult.CONTINUE
        }

        override fun postVisitDirectory(dir: Path, exc: IOException?): FileVisitResult {
            return FileVisitResult.CONTINUE
        }
    })
    return classFileList
}

fun doScan(files: List<String>, controllers: MutableList<String>) {
    files.forEach {
        var classReader = ClassReader(File(it).inputStream())
        var classNode = ClassNode()
        classReader.accept(classNode, ClassReader.SKIP_CODE)
        var controllerDesc = "L" + Controller::class.java.simpleName + ";"
        var hasControllerAnnotation =
            classNode.visibleAnnotations.stream().filter { it.desc == controllerDesc }.count() > 0
        if (hasControllerAnnotation) {
            controllers.add(classNode.name.replace("/", "."))
        }
    }
}

fun getBean(clazz: Class<*>): Any? {
    if (beans.containsKey(clazz.name)) {
        return beans[clazz.name]
    }
    var newInstance = clazz.newInstance()
    beans[clazz.name] = newInstance
    clazz.declaredMethods.forEach {
        var mapping = it.getDeclaredAnnotation(GetMapping::class.java)
        if (mapping != null) {
            var url = mapping.name
            urlMapping[url] = UrlHandler(newInstance, it)
        }
    }
    return beans[clazz.name]
}

fun getHandler(url: String): UrlHandler? {
    var indexOf = url.indexOf("?")
    if (indexOf != -1) {
        var requestPath = url.subSequence(0, indexOf)
        return urlMapping.get(requestPath);
    }
    return urlMapping.get(url);
}

fun dispatcher(exchange: HttpExchange): String {
    var query = exchange.requestURI.query
    var querymap = mutableMapOf<String, String>()
    query?.apply {
        querymap = query.toQueryMap()
    }
    var handler = getHandler(exchange.requestURI.toString())

    if (handler != null) {
        var method = handler.method
        if (method.parameterCount >= 1) {
            var args = mutableListOf<String>()
            var parameters = method.parameters
            parameters.forEach {
                var requestParam = it.getDeclaredAnnotation(RequestParam::class.java)
                var param = querymap.getOrDefault(requestParam.name,"")
                args.add(param)
            }
            try {
                return handler.invoke(*args.toTypedArray())
            } catch (e: Exception) {
                e.printStackTrace()
            }
        }
        return handler.invoke()
    }

    return "404";
}

fun main() {
    var controllers = mutableListOf<String>()
    var classFileList = scanClassPath()

    doScan(classFileList, controllers)

    controllers.forEach { getBean(Class.forName(it)) }


    var httpServer = HttpServer.create()
    httpServer.bind(InetSocketAddress(8080), 0);

    httpServer.createContext("/", object : HttpHandler {
        override fun handle(exchange: HttpExchange) {
            var dispatcher = dispatcher(exchange)
            exchange.sendResponseHeaders(200, dispatcher.length.toLong());
            exchange.responseBody.write(dispatcher.toByteArray())
            exchange.responseBody.flush()
        }
    })
    httpServer.setExecutor(null);
    httpServer.start()
}
复制代码

测试

1641387035(1).png

1641387121(1).png

Supongo que te gusta

Origin juejin.im/post/7049708833035255815
Recomendado
Clasificación