使用Java内置的Http Server构建Web应用
一、概述
使用Java技术构建Web应用时, 我们通常离不开tomcat和jetty之类的servlet容器,这些Web服务器功能强大,性能强劲,深受欢迎,是运行大型Web应用的必备神器。
虽然Java的设计初衷就是用来开发大型应用的,然而有时候我们开发的程序只是简单的小型应用,对于功能的需求和性能的要求并不高, 可能仅仅就几百行甚至几十行代码,这个时候使用tomcat之类的Web服务器去运行就显得有点大材小用了。 比如说只是将数据库中的数据读出来转换成JSON,以Web服务的形式吐给调用方这样的阉割型Web应用。 如下图所示

二、最简单的Java Http服务器
其实在jdk中已经内置了用于此类简单Web应用构建需求的类库了,sun公司提供的 com.sun.net.httpserver 包就是用来帮助我们解决这类问题的
import com.sun.net.httpserver.HttpExchange;
import com.sun.net.httpserver.HttpHandler;
import com.sun.net.httpserver.HttpServer; import java.io.IOException;
import java.io.OutputStream;
import java.net.InetSocketAddress; public class Main { public static void main(String[] arg) throws Exception {
HttpServer server = HttpServer.create(new InetSocketAddress(8001), 0);
server.createContext("/test", new TestHandler());
server.start();
} static class TestHandler implements HttpHandler{
@Override
public void handle(HttpExchange exchange) throws IOException {
String response = "hello world";
exchange.sendResponseHeaders(200, 0);
OutputStream os = exchange.getResponseBody();
os.write(response.getBytes());
os.close();
}
}
}
如上代码清单所示, 仅仅几行代码就可以构建一个五脏俱全的Web应用了。执行代码,在浏览器地址栏里代开链接
http://localhost:8001/test
就能运行这个段程序,输入的结果为helloworld
三、获得外部数据
那在这个程序中如何获取到外部传递过来的数据呢?比如说URL上的查询字符串,POST提交的数据等,其实也很简单
import com.sun.net.httpserver.HttpExchange;
import com.sun.net.httpserver.HttpHandler;
import com.sun.net.httpserver.HttpServer;
import org.apache.commons.io.IOUtils; import java.io.IOException;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.net.InetSocketAddress;
import java.net.URLDecoder;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map; public class Main { public static void main(String[] arg) throws Exception {
HttpServer server = HttpServer.create(new InetSocketAddress(8001), 0);
server.createContext("/test", new TestHandler());
server.start();
} static class TestHandler implements HttpHandler{
@Override
public void handle(HttpExchange exchange) {
String response = "hello world"; try{
//获得查询字符串(get)
String queryString = exchange.getRequestURI().getQuery();
Map<String,String> queryStringInfo = formData2Dic(queryString);
//获得表单提交数据(post)
String postString = IOUtils.toString(exchange.getRequestBody());
Map<String,String> postInfo = formData2Dic(postString); exchange.sendResponseHeaders(200,0);
OutputStream os = exchange.getResponseBody();
os.write(response.getBytes());
os.close();
}catch (IOException ie) { } catch (Exception e) { }
}
} public static Map<String,String> formData2Dic(String formData ) {
Map<String,String> result = new HashMap<>();
if(formData== null || formData.trim().length() == 0) {
return result;
}
final String[] items = formData.split("&");
Arrays.stream(items).forEach(item ->{
final String[] keyAndVal = item.split("=");
if( keyAndVal.length == 2) {
try{
final String key = URLDecoder.decode( keyAndVal[0],"utf8");
final String val = URLDecoder.decode( keyAndVal[1],"utf8");
result.put(key,val);
}catch (UnsupportedEncodingException e) {}
}
});
return result;
}
}
上面的代码清单标识了实现的方法。
注意,要保证上面代码编译通过, 需要引入commons-io.jar,此包中提供将InputStream转换成String的方法。
四、并发处理
com.sun.net.httpserver似乎默认不支持同时处理多个请求,一旦有并行的请求涌入,需要排队等待程序处理,导致Web程序响应卡顿。自定义实现的方法也很简单,为每个请求开一个新的线程处理即可, 如下代码清单所示
import com.sun.net.httpserver.HttpExchange;
import com.sun.net.httpserver.HttpHandler;
import com.sun.net.httpserver.HttpServer;
import org.apache.commons.io.IOUtils; import java.io.IOException;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.net.InetSocketAddress;
import java.net.URLDecoder;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map; public class Main { public static void main(String[] arg) throws Exception {
HttpServer server = HttpServer.create(new InetSocketAddress(8001), 0);
server.createContext("/test", new TestHandler());
server.start();
} static class TestHandler implements HttpHandler{
@Override
public void handle(HttpExchange exchange) {
new Thread(new Runnable() {
@Override
public void run() {
try{
String response = "hello world";
//获得查询字符串(get)
String queryString = exchange.getRequestURI().getQuery();
Map<String,String> queryStringInfo = formData2Dic(queryString);
//获得表单提交数据(post)
String postString = IOUtils.toString(exchange.getRequestBody());
Map<String,String> postInfo = formData2Dic(postString); exchange.sendResponseHeaders(200,0);
OutputStream os = exchange.getResponseBody();
os.write(response.getBytes());
os.close();
}catch (IOException ie) {
ie.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
}
}).start();
}
} public static Map<String,String> formData2Dic(String formData ) {
Map<String,String> result = new HashMap<>();
if(formData== null || formData.trim().length() == 0) {
return result;
}
final String[] items = formData.split("&");
Arrays.stream(items).forEach(item ->{
final String[] keyAndVal = item.split("=");
if( keyAndVal.length == 2) {
try{
final String key = URLDecoder.decode( keyAndVal[0],"utf8");
final String val = URLDecoder.decode( keyAndVal[1],"utf8");
result.put(key,val);
}catch (UnsupportedEncodingException e) {}
}
});
return result;
}
}
五、优点
Java内置Web服务器在功能、性能、稳定等方面是无法和tomcat和jetty之类的专业Web服务器相比的, 它的优点主要是开发和部署方便简单, 把程序代码编译成jar包后,丢到装有jvm的服务器上, 直接运行就可以了,省去了安装相关的软件、依赖, 配置复杂的环境等工作量。
但是, 在一些各方面要求都比较高的生产环境下,还是建议使用专门的Web服务器,毕竟它们久经考验,能满足所有功能需求,并且出问题的几率低。
使用Java内置的Http Server构建Web应用的更多相关文章
- Java 性能分析工具 , 第 2 部分:Java 内置监控工具
引言 本文为 Java 性能分析工具系列文章第二篇,第一篇:操作系统工具.在本文中将介绍如何使用 Java 内置监控工具更加深入的了解 Java 应用程序和 JVM 本身.在 JDK 中有许多内置的工 ...
- 【子非鱼】冒泡排序过程呈现之java内置GUI表示
自己玩玩写写,排序的过程多么有趣,特别是把看着电脑吧一堆乱七八糟的数据排成有序组合的时候,看起来贼舒服,特别是强迫症患者.好了,话不多说上代码,也算是自己记录一下吧,没有什么技术含量但个人感觉比较有趣 ...
- 深入理解Java内置锁和显式锁
synchronized and Reentrantlock 多线程编程中,当代码需要同步时我们会用到锁.Java为我们提供了内置锁(synchronized)和显式锁(ReentrantLock)两 ...
- java内置线程池ThreadPoolExecutor源码学习记录
背景 公司业务性能优化,使用java自带的Executors.newFixedThreadPool()方法生成线程池.但是其内部定义的LinkedBlockingQueue容量是Integer.MAX ...
- Java内置包装类
Java内置包装类有Object.Integer.Float.Double.Number.Charcter.Boolean.Byte.System. Number,是抽象类,也是超类(父类).Numb ...
- java注解学习(1)注解的作用和三个常用java内置注解
今天,记录一下自己学习的关于注解方面的知识. Annotation是从JDK5.0开始引入的新技术 Annotation的作用: -不是程序本身,可以对程序做出解释(这一点和注释没什么区别) -可以被 ...
- 不使用java内置函数,将String字符串转换为int类型
package com.test; public class AtoiTest { public static void main(String[] args) throws Exception { ...
- 设计模式 - 观察者模式(Observer Pattern) Java内置 用法
观察者模式(Observer Pattern) Java内置 用法 本文地址: http://blog.csdn.net/caroline_wendy/article/details/26601659 ...
- 【开发者笔记】冒泡排序过程呈现之java内置GUI表示
自己玩玩写写,排序的过程多么有趣,特别是把看着电脑吧一堆乱七八糟的数据排成有序组合的时候,看起来贼舒服,特别是强迫症患者.好了,话不多说上代码,也算是自己记录一下吧,没有什么技术含量但个人感觉比较有趣 ...
随机推荐
- 老生常谈之Block
前面有一篇介绍Block的博客,主要介绍了Block的简单使用技巧.这篇博客主要更加深入地了解一下Block.包括:Block的实现.__Block的原理以及Block的存储域三方面. Block的实 ...
- 数据分析与展示——Pandas数据特征分析
Pandas数据特征分析 数据的排序 将一组数据通过摘要(有损地提取数据特征的过程)的方式,可以获得基本统计(含排序).分布/累计统计.数据特征(相关性.周期性等).数据挖掘(形成知识). .sort ...
- Python学习日记:day4
列表 li=['alex',[1,2,3] ,'wusir','egon','女神','taibai']#列表 l1 = li[0] print(l1)#alex l2 = li[1] print ( ...
- [经验分享]Linux网络连接-VMware+CentOS 7
VMware虚拟机中安装CentOS,进行网络连接,分为两步,内网连接,与外网连接. 前提: 当你正确安装VMware后,网络适配器会增加2个新的网卡:(可在设备管理器->网络适配器中查看) 第 ...
- Windows 刷新DNS缓存
释放IP配置信息 ipconfig /release 刷新DNS ipconfig /flushdns 更新网卡适配器 ipconfig /renew
- centos7 yum 安装 redis
//从中国科学技术大学开源镜像站 wget http://mirrors.ustc.edu.cn/epel/7/x86_64/Packages/e/epel-release-7-11.noarch.r ...
- sourcetree跳过注册的方法
当前只有Win的版本,Mac自行百度(笑) 很多人用git命令行不熟练,那么可以尝试使用sourcetree进行操作. 然鹅~~sourcetree又一个比较严肃的问题就是,很多人不会跳过注册或者操作 ...
- DES加解密、JavaScript、Java
JavaScript代码 DES.js /** * Created by Andy on 2017/11/30. */ /** * DES加密/解密 * @Copyright Copyright ...
- K:枚举的线程安全性及其序列化问题
枚举是如何保证线程安全的且其在序列化和反序列化的操作中是单例的? 要想看源码,首先得有一个类吧,那么枚举类型到底是什么类呢?是enum吗?答案很明显不是,enum就和class一样,只是一个关 ...
- Odwiedziny[POI 2015]
题目描述 给定一棵n个点的树,树上每条边的长度都为1,第i个点的权值为a[i]. Byteasar想要走遍这整棵树,他会按照某个1到n的全排列b走n-1次,第i次他会从b[i]点走到b[i+1]点,并 ...