IDEA实现纯java项目并打包jar(不使用Maven,Spring)

前言

在工作的过程中,我们一直有用MavenSpring,SpringBoot等去构建我们的java后端项目

又因为之前只是使用,对其原理不是很了解

然后突然想到,如果要构建一个不用MavenSpring要怎么实现?

最近工作稍微清闲一点,于是有时间研究一下,最终有了这篇文章

因此,本文章最终会教你实现如下

✨ 使用JDBC连接MySql

✨ 使用纯java实现GET,POST请求供外部接口调用

✨ 实现纯java项目实现简单的定时任务

✨ 使用纯Java启动我们的项目

✨ 使用IDEA打包jar包

要素

正常来说,我们肯定是应该使用Maven,Spring等去构建我们的项目,但是这篇文章的意义主要是为了了解纯java是如何实现一个后端系统的,为了后面的Maven,Spring构建有更深的了解

要实现一个最简单后端系统,我们首先需要可以联通我们的数据库,然后去进行增删改查,实现具体的逻辑,此外还可以添加定时任务进行点缀处理,当然如果自己的项目不涉及定时任务,这个也是可以省略

最终我实现的项目结构如下

bash 复制代码
PureJavaDemo  // 项目名称
├── lib // 依赖文件夹
	├── mysql-connector-java-8.0.15.jar  // mysql驱动依赖文件包
├── src
	├── com
		├── demo
			├── bean
				├── vo
					├── ResponseResult.java  // 统一响应实体类
				├── User.java // 表User的对象实体
			├── config // 配置相关代码
				├── router  // 路由相关代码
					├── RouteHandler.java  // 路由处理器
					├── RouteRegistry.java // 路由注册器
				├── DatabaseConnection.java // mysql数据库连接配置类
			├── controller // 路由控制层
				├── RequestRouter.java // 请求路由器
				├── UserRouteHandler.java // 表User相关逻辑请求路由
			├── dao // 数据库不同表增删改查dao层
				├── UserDao // User表增删改查
			├── schedule // 定时任务相关逻辑代码
				├── BaseTask.java // 定时任务基类
				├── TaskScheduler.java // 定时任务管理器
				├── UserCleanupTask.java // 用户清除定时任务
			├── service // 具体逻辑实现
				├── UserService.java // User相关逻辑具体实现
			├── util // 工具类
				├── JsonUtil.java // JSON字符处理工具类
				├── TimeUtil.java // 时间处理工具类				
			├── Main.java	// 项目启动类		

我实际项目截图如下

在我实际的纯java项目中,我只引入了一个mysql依赖包,实际上也可以如MavenSpring一样引入对应pom.xml里面的相关依赖来精简自己的项目逻辑

搭建项目

首先我们使用IDEA新建我们的项目

🎈 File -> New -> Project... 打开创建模块面板

🎈 选择java 创建java项目,直接点击Next,别的什么都不要动

🎈 一路Next,输入项目名称,然后点击Finish即可

🎈然后会创建一个默认什么都没有的项目

🎈 接着新建一个文件夹,取名lib,名称任意,一般叫这个;找mysql的驱动依赖文件,这里可以从我们的本地Maven库拿,或者从网上搜索获取mysql依赖,其他依赖同理,这里我演示如何从本地获取,前提是有下载过

我们找到我们其他Maven,Spring项目,看IDEA的MAVEN配置存放路径

🎈 File -> Setting -> 搜索Maven

🎈 然后复制路径打开文件夹,再看看我们的mysql的依赖层级,任意选择一个版本,按照自己需求,然后复制里面的jar格式的文件到自己项目的lib文件夹里面

xml 复制代码
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>8.0.15</version>
        </dependency>

🎈 OK之后,我们需要把把依赖文件添加到项目里面,这里指的是让项目识别到依赖
File -> Project Structure -> Libraries打开这里之后,点击+号选择依赖引入,引入之后会重新在这里,然后Apply应用就OK了

🎈 到这里搭建基本好了,然后按照我给出的项目进行建立对应的java文件代码

逻辑

那么接下来就需要正式实现我们的纯java项目,我这边以一个表为例:

sql 复制代码
CREATE TABLE `user` (
  `user_id` varchar(20) NOT NULL COMMENT '用户id',
  `avatar` longtext COMMENT '头像',
  `user_name` varchar(50) NOT NULL COMMENT '用户名',
  `password` varchar(50) NOT NULL COMMENT '密码',
  `create_time` datetime DEFAULT NULL COMMENT '创建时间',
  `update_time` datetime DEFAULT NULL COMMENT '更新时间',
  `is_admin` varchar(1) DEFAULT 'N' COMMENT '是否为管理员',
  `status` varchar(1) DEFAULT 'Y' COMMENT '是否生效',
  `silence` varchar(1) DEFAULT 'N' COMMENT '是否禁言',
  PRIMARY KEY (`user_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

我会实现查询接口,等项目启动之后使用POSTMAN进行调用

数据库连接

首先我们使用JDBC,连接我们的数据库

DatabaseConnection.java

java 复制代码
package com.demo.config;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;

public class DatabaseConnection {
  private static final String URL = "jdbc:mysql://localhost:3306/chat?useUnicode=true&characterEncoding=UTF-8&useSSL=false&serverTimezone=UTC";
  private static final String USER = "root";
  private static final String PASSWORD = "root";

  static {
    try {
      Class.forName("com.mysql.cj.jdbc.Driver");
      //Class.forName("com.mysql.cj.jdbc.Driver", true, Thread.currentThread().getContextClassLoader());
    } catch (ClassNotFoundException e) {
      throw new RuntimeException("MySQL JDBC Driver not found", e);
    }
  }

  public static Connection getConnection() throws SQLException {
    return DriverManager.getConnection(URL, USER, PASSWORD);
  }

  public static void closeConnection(Connection conn) {
    if (conn != null) {
      try {
        conn.close();
      } catch (SQLException e) {
        e.printStackTrace();
      }
    }
  }
}

表增删改查

这个是映射User表的增删改查,通过传递数据库的连接实现增删改查,其他表的可以按照这个相同处理,同样要实现增删改查,需要把实体建立出来

User.java

java 复制代码
package com.demo.bean;

import java.util.Date;

public class User {
  private String userId;
  private String avatar;
  private String userName;
  private String password;
  private Date createTime;
  private Date updateTime;
  private String isAdmin;
  private String status;
  private String silence;

  // Getters and Setters
  public String getUserId() { return userId; }
  public void setUserId(String userId) { this.userId = userId; }
  public String getAvatar() { return avatar; }
  public void setAvatar(String avatar) { this.avatar = avatar; }
  public String getUserName() { return userName; }
  public void setUserName(String userName) { this.userName = userName; }
  public String getPassword() { return password; }
  public void setPassword(String password) { this.password = password; }
  public Date getCreateTime() { return createTime; }
  public void setCreateTime(Date createTime) { this.createTime = createTime; }
  public Date getUpdateTime() { return updateTime; }
  public void setUpdateTime(Date updateTime) { this.updateTime = updateTime; }
  public String getIsAdmin() { return isAdmin; }
  public void setIsAdmin(String isAdmin) { this.isAdmin = isAdmin; }
  public String getStatus() { return status; }
  public void setStatus(String status) { this.status = status; }
  public String getSilence() { return silence; }
  public void setSilence(String silence) { this.silence = silence; }

  @Override
  public String toString() {
    return "User{" +
        "userId='" + userId + '\'' +
        ", userName='" + userName + '\'' +
        ", avatar='" + avatar + '\'' +
        ", password='" + password + '\'' +
        ", createTime='" + createTime + "\'" +
        ", updateTime='" + updateTime + "\'" +
        ", isAdmin='" + isAdmin + "\'" +
        ", status='" + status + "\'" +
        ", silence='" + silence + "\'" +
        '}';
  }
}

UserDao.java

java 复制代码
package com.demo.dao;
import com.demo.bean.User;
import java.sql.*;
import java.util.ArrayList;
import java.util.List;

public class UserDao {

  // 查询所有用户
  public List<User> selectAllUsers(Connection conn) throws SQLException {
    List<User> users = new ArrayList<>();
    String sql = "SELECT * FROM user";
    try (PreparedStatement stmt = conn.prepareStatement(sql);
        ResultSet rs = stmt.executeQuery()) {
      while (rs.next()) {
        users.add(mapResultSetToUser(rs));
      }
    }
    return users;
  }

  // 根据用户ID查询用户
  public User selectUserByUserId(Connection conn, String userId) throws SQLException {
    String sql = "SELECT * FROM user WHERE user_id = ?";
    try (PreparedStatement stmt = conn.prepareStatement(sql)) {
      stmt.setString(1, userId);
      try (ResultSet rs = stmt.executeQuery()) {
        if (rs.next()) {
          return mapResultSetToUser(rs);
        }
      }
    }
    return null;
  }

  // 新增用户
  public int insertUser(Connection conn, User user) throws SQLException {
    String sql = "INSERT INTO user (" +
        "user_id, avatar, user_name, password, " +
        "create_time, update_time, is_admin, status, silence" +
        ") VALUES (?, ?, ?, ?, NOW(), NOW(), ?, ?, ?)";

    try (PreparedStatement stmt = conn.prepareStatement(sql)) {
      stmt.setString(1, user.getUserId());
      stmt.setString(2, user.getAvatar());
      stmt.setString(3, user.getUserName());
      stmt.setString(4, user.getPassword());
      stmt.setString(5, user.getIsAdmin());
      stmt.setString(6, user.getStatus());
      stmt.setString(7, user.getSilence());

      return stmt.executeUpdate();
    }
  }

  // 根据用户ID删除用户
  public int deleteUserById(Connection conn, String userId) throws SQLException {
    String sql = "DELETE FROM user WHERE user_id = ?";
    try (PreparedStatement stmt = conn.prepareStatement(sql)) {
      stmt.setString(1, userId);
      return stmt.executeUpdate();
    }
  }

  // 更新用户信息
  public int updateUser(Connection conn, User user) throws SQLException {
    String sql = "UPDATE user SET " +
        "avatar = ?, " +
        "user_name = ?, " +
        "password = ?, " +
        "update_time = NOW(), " +
        "is_admin = ?, " +
        "status = ?, " +
        "silence = ? " +
        "WHERE user_id = ?";

    try (PreparedStatement stmt = conn.prepareStatement(sql)) {
      stmt.setString(1, user.getAvatar());
      stmt.setString(2, user.getUserName());
      stmt.setString(3, user.getPassword());
      stmt.setString(4, user.getIsAdmin());
      stmt.setString(5, user.getStatus());
      stmt.setString(6, user.getSilence());
      stmt.setString(7, user.getUserId());

      return stmt.executeUpdate();
    }
  }

  // 清理不活跃用户
  public int deleteInactiveUsers(Connection conn, int inactiveDays) throws SQLException {
    String sql = "DELETE FROM user WHERE status = 'N' AND create_time < DATE_SUB(NOW(), INTERVAL ? DAY)";
    try (PreparedStatement stmt = conn.prepareStatement(sql)) {
      stmt.setInt(1, inactiveDays);
      return stmt.executeUpdate();
    }
  }

  // 辅助方法:将ResultSet映射到User对象
  private User mapResultSetToUser(ResultSet rs) throws SQLException {
    User user = new User();
    user.setUserId(rs.getString("user_id"));
    user.setUserName(rs.getString("user_name"));
    user.setAvatar(rs.getString("avatar"));
    user.setPassword(rs.getString("password"));
    user.setCreateTime(rs.getTimestamp("create_time"));
    user.setUpdateTime(rs.getTimestamp("update_time"));
    user.setIsAdmin(rs.getString("is_admin"));
    user.setStatus(rs.getString("status"));
    user.setSilence(rs.getString("silence"));
    return user;
  }
}

简单User处理逻辑

在这里我使用了简单的统一响应类,去返回响应结果,相关代码是

ResponseResult.java

java 复制代码
package com.demo.bean.vo;

public class ResponseResult {
  private int code;
  private String message;
  private Object data;

  public ResponseResult(int code, String message, Object data) {
    this.code = code;
    this.message = message;
    this.data = data;
  }

  public int getCode() { return code; }
  public String getMessage() { return message; }
  public Object getData() { return data; }
}

UserService.java

java 复制代码
package com.demo.service;
import com.demo.bean.User;
import com.demo.bean.vo.ResponseResult;
import com.demo.config.DatabaseConnection;
import com.demo.dao.UserDao;
import java.sql.Connection;
import java.util.List;

public class UserService {
  private final UserDao userDao = new UserDao();

  public ResponseResult getAllUsers() {
    try (Connection conn = DatabaseConnection.getConnection()) {
      List<User> users = userDao.selectAllUsers(conn);
      return new ResponseResult(200, "Success", users);
    } catch (Exception e) {
      return new ResponseResult(500, "Error: " + e.getMessage(), null);
    }
  }

  public ResponseResult getUserById(String userId) {
    try (Connection conn = DatabaseConnection.getConnection()) {
      User user = userDao.selectUserByUserId(conn, userId);
      if (user != null) {
        return new ResponseResult(200, "Success", user);
      }
      return new ResponseResult(404, "User not found", null);
    } catch (Exception e) {
      return new ResponseResult(500, "Error: " + e.getMessage(), null);
    }
  }
}

工具类实现

因为为了尽可能减少引入依赖,我这边添加了工具类进行补充,实际可以添加更多依赖代替

JsonUtil.java

java 复制代码
package com.demo.util;

import java.lang.reflect.Field;
import java.text.SimpleDateFormat;
import java.util.Collection;
import java.util.Date;

public class JsonUtil {
  private static final SimpleDateFormat DATE_FORMAT = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");

  public static String toJson(Object obj) {
    if (obj == null) return "null";

    if (obj instanceof Collection) {
      return collectionToJson((Collection<?>) obj);
    } else if (obj instanceof String) {
      return "\"" + escapeJson((String) obj) + "\"";
    } else if (obj instanceof Number || obj instanceof Boolean) {
      return obj.toString();
    } else if (obj instanceof Date) {
      return "\"" + DATE_FORMAT.format((Date) obj) + "\"";
    } else {
      return objectToJson(obj);
    }
  }

  private static String objectToJson(Object obj) {
    StringBuilder sb = new StringBuilder("{");
    Field[] fields = obj.getClass().getDeclaredFields();
    boolean firstField = true;

    for (Field field : fields) {
      field.setAccessible(true);
      try {
        Object value = field.get(obj);
        if (value == null) continue;

        if (!firstField) {
          sb.append(",");
        }
        firstField = false;

        sb.append("\"").append(field.getName()).append("\":");

        if (value instanceof String || value instanceof Date) {
          sb.append("\"").append(escapeJson(value.toString())).append("\"");
        } else if (value instanceof Number || value instanceof Boolean) {
          sb.append(value);
        } else {
          sb.append(toJson(value));
        }

      } catch (IllegalAccessException e) {
        // 忽略无法访问的字段
      }
    }

    return sb.append("}").toString();
  }

  private static String collectionToJson(Collection<?> collection) {
    StringBuilder sb = new StringBuilder("[");
    boolean firstElement = true;

    for (Object item : collection) {
      if (!firstElement) {
        sb.append(",");
      }
      firstElement = false;
      sb.append(toJson(item));
    }

    return sb.append("]").toString();
  }

  private static String escapeJson(String input) {
    if (input == null) return "";
    return input.replace("\\", "\\\\")
        .replace("\"", "\\\"")
        .replace("\b", "\\b")
        .replace("\f", "\\f")
        .replace("\n", "\\n")
        .replace("\r", "\\r")
        .replace("\t", "\\t");
  }
}

TimeUtil.java

java 复制代码
package com.demo.util;

public class TimeUtil {
  /**
   * 计算距离下一个指定时间点的延迟时间
   * @param hour 小时 (0-23)
   * @param minute 分钟 (0-59)
   * @return 延迟毫秒数
   */
  public static long calculateInitialDelay(int hour, int minute) {
    long now = System.currentTimeMillis();
    long next = java.time.LocalDate.now()
        .atTime(hour, minute)
        .atZone(java.time.ZoneId.systemDefault())
        .toInstant()
        .toEpochMilli();

    if (now > next) {
      next += 24 * 60 * 60 * 1000; // 如果今天的时间已过,则计算明天的时间
    }

    return next - now;
  }
}

路由逻辑

这里路由指我们需要提供给外部访问的接口路径

RouteHandler.java

java 复制代码
package com.demo.config.router;

import com.demo.bean.vo.ResponseResult;

import java.util.Map;

public interface RouteHandler {
  ResponseResult handle(Map<String, String> params) throws Exception;
}

RouteRegistry.java

java 复制代码
package com.demo.config.router;

import com.sun.net.httpserver.HttpExchange;

import java.util.HashMap;
import java.util.Map;

public class RouteRegistry {
  private final Map<String, Map<String, RouteHandler>> routes = new HashMap<>();

  public void register(String method, String path, RouteHandler handler) {
    routes.computeIfAbsent(method, k -> new HashMap<>()).put(path, handler);
  }

  public RouteHandler findHandler(HttpExchange exchange) {
    String method = exchange.getRequestMethod();
    String path = exchange.getRequestURI().getPath();

    Map<String, RouteHandler> methodRoutes = routes.get(method);
    if (methodRoutes == null) {
      return null;
    }

    // 精确匹配
    RouteHandler handler = methodRoutes.get(path);
    if (handler != null) {
      return handler;
    }

    // 通配符匹配 (可选)
    for (Map.Entry<String, RouteHandler> entry : methodRoutes.entrySet()) {
      if (path.matches(entry.getKey().replace("*", ".*"))) {
        return entry.getValue();
      }
    }

    return null;
  }
}

RequestRouter.java

java 复制代码
package com.demo.controller;
import com.demo.bean.vo.ResponseResult;
import com.demo.config.router.RouteHandler;
import com.demo.config.router.RouteRegistry;
import com.demo.util.JsonUtil;
import com.sun.net.httpserver.HttpExchange;
import com.sun.net.httpserver.HttpHandler;

import java.io.IOException;
import java.io.OutputStream;
import java.net.URI;
import java.util.HashMap;
import java.util.Map;

public class RequestRouter implements HttpHandler {
  private final RouteRegistry routeRegistry;

  public RequestRouter() {
    this.routeRegistry = new RouteRegistry();
    registerRoutes();
  }

  private void registerRoutes() {
    UserRouteHandler userHandler = new UserRouteHandler();

    // 注册用户相关路由
    routeRegistry.register("GET", "/user/selectAllUsers", userHandler::getAllUsers);
    routeRegistry.register("GET", "/user/selectUserByUserId", userHandler::getUserById);

    // 示例:注册其他路由
    // routeRegistry.register("POST", "/product/create", productHandler::createProduct);
  }

  @Override
  public void handle(HttpExchange exchange) throws IOException {
    URI uri = exchange.getRequestURI();
    Map<String, String> params = parseQueryParams(uri.getQuery());

    ResponseResult result;
    try {
      RouteHandler handler = routeRegistry.findHandler(exchange);

      if (handler != null) {
        result = handler.handle(params);
      } else {
        result = new ResponseResult(404, "Endpoint not found: " + uri.getPath(), null);
      }
    } catch (Exception e) {
      result = new ResponseResult(500, "Internal error: " + e.getMessage(), null);
      e.printStackTrace();
    }

    sendResponse(exchange, result);
  }

  private void sendResponse(HttpExchange exchange, ResponseResult result) throws IOException {
    String response = JsonUtil.toJson(result);
    exchange.getResponseHeaders().add("Content-Type", "application/json");
    exchange.sendResponseHeaders(result.getCode(), response.getBytes().length);

    try (OutputStream os = exchange.getResponseBody()) {
      os.write(response.getBytes());
    }
  }

  private Map<String, String> parseQueryParams(String query) {
    Map<String, String> params = new HashMap<>();
    if (query != null) {
      for (String param : query.split("&")) {
        String[] pair = param.split("=");
        if (pair.length > 1) {
          params.put(pair[0], pair[1]);
        }
      }
    }
    return params;
  }
}

UserRouteHandler.java

java 复制代码
package com.demo.controller;


import com.demo.bean.vo.ResponseResult;
import com.demo.service.UserService;

import java.util.Map;

public class UserRouteHandler {
  private final UserService userService = new UserService();

  public ResponseResult getAllUsers(Map<String, String> params) throws Exception {
    return userService.getAllUsers();
  }

  public ResponseResult getUserById(Map<String, String> params) throws Exception {
    String userId = params.getOrDefault("userId", "");
    if (!userId.isEmpty()) {
      return userService.getUserById(userId);
    } else {
      return new ResponseResult(400, "Missing userId parameter", null);
    }
  }

  // 添加更多用户相关的方法
  // public ResponseResult createUser(Map<String, String> params) { ... }
}

定时任务

如果不加上定时任务,纯java项目是可以更精简一点,属于可有可无的,但是正常实际项目都会有定时功能的需求,因此还是加上

BaseTask.java

java 复制代码
package com.demo.schedule;

public abstract class BaseTask implements Runnable {
  protected final String taskName;

  public BaseTask(String taskName) {
    this.taskName = taskName;
  }

  @Override
  public void run() {
    try {
      System.out.println("[" + taskName + "] 任务开始执行...");
      executeTask();
      System.out.println("[" + taskName + "] 任务执行完成");
    } catch (Exception e) {
      System.err.println("[" + taskName + "] 任务执行失败: " + e.getMessage());
      e.printStackTrace();
    }
  }

  protected abstract void executeTask() throws Exception;

  public String getTaskName() {
    return taskName;
  }
}

TaskScheduler.java

java 复制代码
package com.demo.schedule;

import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;

public class TaskScheduler {
  private static final ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(3);
  private static final TaskScheduler instance = new TaskScheduler();

  private TaskScheduler() {
    // 私有构造器
  }

  public static TaskScheduler getInstance() {
    return instance;
  }

  /**
   * 安排定时任务
   * @param task 要执行的任务
   * @param initialDelay 首次执行延迟时间
   * @param period 执行周期
   * @param unit 时间单位
   * @return 定时任务控制器
   */
  public ScheduledFuture<?> scheduleAtFixedRate(Runnable task,
      long initialDelay,
      long period,
      TimeUnit unit) {
    return scheduler.scheduleAtFixedRate(task, initialDelay, period, unit);
  }

  /**
   * 安排延迟执行任务
   * @param task 要执行的任务
   * @param delay 延迟时间
   * @param unit 时间单位
   * @return 定时任务控制器
   */
  public ScheduledFuture<?> schedule(Runnable task,
      long delay,
      TimeUnit unit) {
    return scheduler.schedule(task, delay, unit);
  }

  /**
   * 关闭定时任务管理器
   */
  public void shutdown() {
    scheduler.shutdown();
    try {
      if (!scheduler.awaitTermination(60, TimeUnit.SECONDS)) {
        scheduler.shutdownNow();
      }
    } catch (InterruptedException e) {
      scheduler.shutdownNow();
      Thread.currentThread().interrupt();
    }
  }
}

UserCleanupTask.java

java 复制代码
package com.demo.schedule;
import com.demo.dao.UserDao;
import com.demo.config.DatabaseConnection;
import java.sql.Connection;

public class UserCleanupTask extends BaseTask {
  // 清理超过30天的未激活用户
  private static final int INACTIVE_DAYS = 30;

  public UserCleanupTask() {
    super("用户清理任务");
  }

  @Override
  protected void executeTask() throws Exception {
    try (Connection conn = DatabaseConnection.getConnection()) {
      UserDao userDao = new UserDao();
      int deletedCount = userDao.deleteInactiveUsers(conn, INACTIVE_DAYS);

      System.out.println("清理了 " + deletedCount + " 个超过 "
          + INACTIVE_DAYS + " 天未激活的用户");
    }
  }
}

启动类

启动类代表将整个项目进行启动,可以和我们的Maven项目,Spring项目一样,也是项目的入口

Main.java

java 复制代码
package com.demo;

import com.demo.controller.RequestRouter;
import com.demo.schedule.TaskScheduler;
import com.demo.schedule.UserCleanupTask;
import com.demo.util.TimeUtil;
import com.sun.net.httpserver.HttpServer;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;

public class Main {
  public static void main(String[] args) throws IOException {
    int port = 8082;
    HttpServer server = HttpServer.create(new InetSocketAddress(port), 0);
    server.createContext("/", new RequestRouter());
    server.setExecutor(null);
    server.start();
    System.out.println("✅ Server started on port " + port);

    // 初始化定时任务
    initScheduledTasks();

    // 添加关闭钩子,确保关闭
    Runtime.getRuntime().addShutdownHook(new Thread(() -> {
      System.out.println("Shutting down application...");
      server.stop(0);
      TaskScheduler.getInstance().shutdown();
      System.out.println("Application shutdown complete");
    }));
  }

  private static void initScheduledTasks() {
    TaskScheduler scheduler = TaskScheduler.getInstance();

    // 每10分钟执行一次的用户清理任务
    ScheduledFuture<?> userCleanupTask = scheduler.scheduleAtFixedRate(
        new UserCleanupTask(),
        0,   // 初始延迟(立即开始)
        10,  // 每10分钟
        TimeUnit.MINUTES
    );

    // 每天凌晨2点执行的统计任务
    scheduler.scheduleAtFixedRate(
        () -> System.out.println("每日统计任务执行中..."),
        TimeUtil.calculateInitialDelay(2, 0), // 凌晨2点
        24, // 每天执行一次
        TimeUnit.HOURS
    );

    System.out.println("定时任务初始化完成");
  }
}

打包Jar并启动

启动

如上我们整个项目就处理完毕了,如果要求启动项目,那么要求我们的端口不能被占用,否则会报错:

Exception in thread "main" java.net.BindException: Address already in use: bind

at sun.nio.ch.Net.bind0(Native Method)

at sun.nio.ch.Net.bind(Net.java:433)

at sun.nio.ch.Net.bind(Net.java:425)

at sun.nio.ch.ServerSocketChannelImpl.bind(ServerSocketChannelImpl.java:223)

at sun.nio.ch.ServerSocketAdaptor.bind(ServerSocketAdaptor.java:74)

at sun.net.httpserver.ServerImpl.(ServerImpl.java:100)

at sun.net.httpserver.HttpServerImpl.(HttpServerImpl.java:50)

at sun.net.httpserver.DefaultHttpServerProvider.createHttpServer(DefaultHttpServerProvider.java:35)

at com.sun.net.httpserver.HttpServer.create(HttpServer.java:130)

at com.demo.Main.main(Main.java:10)
启动点击Main.java 右键选择Run Mian.main()或者Debug Mian.main()

启动之后控制台会打印一些数据

如我的项目会是这些

✅ Server started on port 8082

用户清理任务\] 任务开始执行... 定时任务初始化完成 清理了 0 个超过 30 天未激活的用户 \[用户清理任务\] 任务执行完成

在我的项目示例中我有两个外部调用接口
/user/selectAllUsers
/user/selectUserByUserId

此时我的表数据有这些:

sql 复制代码
INSERT INTO chat.`user` (user_id, avatar, user_name, password, create_time, update_time, is_admin, status, silence) VALUES('11', '11', '11', '11', '2025-07-14 00:00:00', '2025-07-14 00:00:00', 'N', 'Y', 'N');

此时调用接口:

http://localhost:8082/user/selectAllUsers

响应数据如下:

bash 复制代码
{
    "code": 200,
    "message": "Success",
    "data": [
        {
            "userId": "11",
            "avatar": "11",
            "userName": "11",
            "password": "11",
            "createTime": "2025-07-14 08:00:00.0",
            "updateTime": "2025-07-14 08:00:00.0",
            "isAdmin": "N",
            "status": "Y",
            "silence": "N"
        }
    ]
}

成功启动并且请求到了数据库表数据

打包jar

如上我们已经验证了项目正常启动,我们我们需要将其打包jar,使其可以在本机或者服务器正常部署使用
File -> Project Structure -> Artifacts打开到这个界面,然后点击+号,选择jar下的from modules with dependencies

Module会自动选择当前项目,Main Class需要选择Main.java,并且JAR files from dependencies 需要选择为copy to the output directory and link via mainfest

点击OK然后Apply


就会多出一个项目名称的jar包在这里

然后点击Build -> Build Artifacts再在新界面选择点击Build

等待打包完成,这个时候会在我们的out文件夹下的artifacts文件夹会看到我们刚刚打包后的jar包

关闭我们的项目,然后启动该jar包
java -jar PureJavaDemo.jar

可以看到如上截图成功执行了jar,往user表加些数据再次请求postman验证是否可用

bash 复制代码
{
    "code": 200,
    "message": "Success",
    "data": [
        {
            "userId": "11",
            "avatar": "11",
            "userName": "11",
            "password": "11",
            "createTime": "2025-07-14 08:00:00.0",
            "updateTime": "2025-07-14 08:00:00.0",
            "isAdmin": "N",
            "status": "Y",
            "silence": "N"
        },
        {
            "userId": "22",
            "avatar": "22",
            "userName": "22",
            "password": "22",
            "createTime": "2025-07-14 08:00:00.0",
            "updateTime": "2025-07-14 08:00:00.0",
            "isAdmin": "N",
            "status": "Y",
            "silence": "N"
        }
    ]
}

可以看到正常的访问,但是有一点需要注意,打包之后的jar需要和依赖放在同一个文件夹下

项目GIT

纯java后端项目DEMO

优缺点

综上,我们已经很清晰明了的知道java是怎么去运作我们的项目了,但是还是那句话,这个只是让你更好的学习和了解java,真正要开发一个项目还是选择Maven,Spring框架,如下我列举了一些纯java项目优缺点
优点

🧡 轻量: 启动快,内存占用第,无框架开销

🧡 自主掌控: 自主实现逻辑,可以深度理解底层机制(HTTP/TCP等)

🧡学习价值高: 对于掌握基础友好
缺点

🖤开发效率低: 手动实现路由,依赖注入等,重复造轮子,缺少标准项目结构

🖤维护成本高: 自行管理依赖版本,缺少自动化测试支持,扩展性差

🖤团队协作难: 无统一的开发规范,项目结构依赖个人风格

结语

如上为实现纯java项目的过程和代码,如有遗漏后续补充

相关推荐
_殊途19 分钟前
《Java HashMap底层原理全解析(源码+性能+面试)》
java·数据结构·算法
椰椰椰耶1 小时前
【Spring】拦截器详解
java·后端·spring
没有bug.的程序员2 小时前
JAVA面试宝典 - 《MyBatis 进阶:插件开发与二级缓存》
java·面试·mybatis
没有羊的王K4 小时前
SSM框架学习——day1
java·学习
又菜又爱coding4 小时前
安装Keycloak并启动服务(macOS)
java·keycloak
不知道叫什么呀4 小时前
【C】vector和array的区别
java·c语言·开发语言·aigc
wan_da_ren5 小时前
JVM监控及诊断工具-GUI篇
java·开发语言·jvm·后端
cui_hao_nan5 小时前
JAVA并发——什么是Java的原子性、可见性和有序性
java·开发语言
best_virtuoso5 小时前
JAVA JVM垃圾收集
java·开发语言·jvm
lifallen5 小时前
Kafka 时间轮深度解析:如何O(1)处理定时任务
java·数据结构·分布式·后端·算法·kafka