Of course! Building an HTTP server in Java is a fantastic way to understand networking, web protocols, and concurrency. I'll guide you through it, starting from the simplest approach and moving to a more robust, production-ready one.

Overview of Approaches
-
The Simplest Way: Using
com.sun.net.httpserver(Java SE)- What it is: A built-in HTTP server library in the Java Standard Edition (since Java 6). It's not part of the official public API (
com.sun.*), but it's stable and widely used for simple tools, testing, and internal services. - Pros: No external dependencies, simple to get started.
- Cons: Not designed for high performance, limited features (no built-in routing, static file serving, etc.).
- What it is: A built-in HTTP server library in the Java Standard Edition (since Java 6). It's not part of the official public API (
-
The "Real" Way: Using a Framework (e.g., Spring Boot, SparkJava, Javalin)
- What it is: A web framework that runs on a full-featured web server (like Tomcat, Jetty, or Undertow). This is how 99.9% of real-world Java web applications are built.
- Pros: Extremely powerful, feature-rich (routing, templating, security, database integration), highly scalable, battle-tested.
- Cons: Requires adding a dependency (Maven/Gradle), has a steeper learning curve for the full framework.
-
The High-Performance Way: Using a Raw Server (e.g., Netty, Vert.x)
- What it is: Asynchronous, event-driven networking frameworks that give you maximum control and performance.
- Pros: Extremely high throughput and low latency.
- Cons: Very complex, requires a deep understanding of asynchronous programming.
Approach 1: The Simplest Way (com.sun.net.httpserver)
This is perfect for a quick utility, a simple API endpoint, or learning the fundamentals.
Key Concepts
HttpServer: The main class that represents the server.InetSocketAddress: Specifies the IP address and port the server will listen on.HttpContext: Defines a context path (e.g.,/api) and maps it to a handler.HttpHandler: An interface you implement to process incoming requests. Thehandle()method is called for every request to that context.
Example Code: A Simple "Hello World" Server
Let's create a server that listens on port 8000 and has two endpoints:
- Returns "Hello, World!"
/greet/{name}: Returns "Hello, {name}!"
import com.sun.net.httpserver.HttpServer;
import com.sun.net.httpserver.HttpHandler;
import com.sun.net.httpserver.HttpExchange;
import java.io.IOException;
import java.io.OutputStream;
import java.net.InetSocketAddress;
public class SimpleHttpServer {
public static void main(String[] args) throws IOException {
// 1. Create the HttpServer
// It will listen on port 8000 on any network interface (0.0.0.0)
HttpServer server = HttpServer.create(new InetSocketAddress(8000), 0);
// 2. Create a context for the root path "/"
// The handler for this context is a lambda expression that implements HttpHandler
server.createContext("/", new HttpHandler() {
@Override
public void handle(HttpExchange exchange) throws IOException {
String response = "Hello, World!";
sendResponse(exchange, 200, response);
}
});
// 3. Create a context for the greet path
// We will use a simple string replacement for path parameters
server.createContext("/greet/", new HttpHandler() {
@Override
public void handle(HttpExchange exchange) throws IOException {
// Get the request method
if (!"GET".equals(exchange.getRequestMethod())) {
sendResponse(exchange, 405, "Method Not Allowed");
return;
}
// The path will be something like "/greet/Alice"
String path = exchange.getRequestURI().getPath();
String name = path.substring("/greet/".length()); // Extract "Alice"
if (name.isEmpty()) {
sendResponse(exchange, 400, "Name is required");
return;
}
String response = String.format("Hello, %s!", name);
sendResponse(exchange, 200, response);
}
});
// 4. Start the server
server.setExecutor(null); // creates a default executor
server.start();
System.out.println("Server started on port 8000");
System.out.println("Try accessing: http://localhost:8000");
System.out.println("Try accessing: http://localhost:8000/greet/Alice");
}
/**
* Helper method to send an HTTP response.
* @param exchange The HttpExchange object.
* @param statusCode The HTTP status code (e.g., 200, 404).
* @param response The response body as a string.
* @throws IOException If an I/O error occurs.
*/
private static void sendResponse(HttpExchange exchange, int statusCode, String response) throws IOException {
exchange.sendResponseHeaders(statusCode, response.getBytes().length);
try (OutputStream os = exchange.getResponseBody()) {
os.write(response.getBytes());
}
}
}
How to Run:
- Save the code as
SimpleHttpServer.java. - Compile it:
javac SimpleHttpServer.java - Run it:
java SimpleHttpServer - Open your web browser or use
curlto test it:curl http://localhost:8000curl http://localhost:8000/greet/Bob
Approach 2: The "Real" Way (Using a Framework - SparkJava)
For any real application, you should use a framework. SparkJava is a fantastic choice for its simplicity and Java-like DSL (Domain Specific Language).
Setup (Maven)
First, add the SparkJava dependency to your pom.xml:
<dependencies>
<dependency>
<groupId>com.sparkjava</groupId>
<artifactId>spark-core</artifactId>
<version>2.9.4</version> <!-- Check for the latest version -->
</dependency>
</dependencies>
Example Code: A Simple REST API with SparkJava
This code achieves the same result as the com.sun.net.httpserver example but is much cleaner, more scalable, and includes automatic parsing of path parameters.
import static spark.Spark.*;
public class SparkServer {
public static void main(String[] args) {
// Configure the port (optional, default is 4567)
port(8000);
// --- Define Routes ---
// GET /hello
// .get() is a lambda: (request, response) -> { ... }
get("/hello", (req, res) -> "Hello, World!");
// GET /greet/:name
// Spark automatically extracts the 'name' parameter from the URL
get("/greet/:name", (req, res) -> {
String name = req.params(":name");
return String.format("Hello, %s!", name);
});
// Example of a POST request with a JSON body
post("/user", (req, res) -> {
// req.body() contains the raw request body
// In a real app, you'd use a library like Gson or Jackson to parse JSON
System.out.println("Received POST request with body: " + req.body());
res.status(201); // Created
return "User created successfully";
});
System.out.println("SparkJava server started on port 8000");
System.out.println("Try accessing: http://localhost:8000/hello");
System.out.println("Try accessing: http://localhost:8000/greet/Alice");
}
}
How to Run:
- Set up your Maven project.
- Save the code as
SparkServer.java. - Run it from your IDE or using Maven:
mvn compile exec:java -Dexec.mainClass="SparkServer" - Test with
curl:curl http://localhost:8000/hellocurl http://localhost:8000/greet/Bobcurl -X POST -d '{"name":"John"}' http://localhost:8000/user
Comparison and Recommendation
| Feature | com.sun.net.httpserver |
SparkJava (Framework) |
|---|---|---|
| Dependencies | None (built-in) | One Maven/Gradle dependency |
| Routing | Manual string parsing | Clean, expressive DSL (get, post, put, etc.) |
| Path Params | Manual substring extraction | Automatic (req.params(":name")) |
| JSON Handling | Manual parsing | Easy with plugins (e.g., Gson) |
| Static Files | Manual to implement | Built-in support |
| Performance | Low-Medium | High (runs on embedded Jetty/Tomcat) |
| Use Case | Quick scripts, tools, testing, learning | Production web applications, APIs, services |
| Ease of Use | Simple for basic cases | Very easy for complex apps |
Final Recommendation
- To learn the fundamentals of HTTP and Java networking: Start with
com.sun.net.httpserver. It forces you to deal with the low-level details ofHttpExchangeand headers. - To build any real, useful web service or API: Use a framework like SparkJava, Javalin, or the full Spring Boot. They will save you an enormous amount of time, provide robust features, and are built to scale. SparkJava is an excellent starting point due to its minimalism and Java-centric design.
