IDEA實(shí)現(xiàn)純java項(xiàng)目并打包jar的步驟(不使用Maven,Spring)
前言
在工作的過程中,我們一直有用
Maven,Spring,SpringBoot等去構(gòu)建我們的java后端項(xiàng)目
又因?yàn)橹爸皇鞘褂?,?duì)其原理不是很了解
然后突然想到,如果要構(gòu)建一個(gè)不用Maven,Spring要怎么實(shí)現(xiàn)?
最近工作稍微清閑一點(diǎn),于是有時(shí)間研究一下,最終有了這篇文章
因此,本文章最終會(huì)教你實(shí)現(xiàn)如下
? 使用JDBC連接MySql
? 使用純java實(shí)現(xiàn)GET,POST請(qǐng)求供外部接口調(diào)用
? 實(shí)現(xiàn)純java項(xiàng)目實(shí)現(xiàn)簡單的定時(shí)任務(wù)
? 使用純Java啟動(dòng)我們的項(xiàng)目
? 使用IDEA打包jar包
要素
正常來說,我們肯定是應(yīng)該使用Maven,Spring等去構(gòu)建我們的項(xiàng)目,但是這篇文章的意義主要是為了了解純java是如何實(shí)現(xiàn)一個(gè)后端系統(tǒng)的,為了后面的Maven,Spring構(gòu)建有更深的了解
要實(shí)現(xiàn)一個(gè)最簡單后端系統(tǒng),我們首先需要可以聯(lián)通我們的數(shù)據(jù)庫,然后去進(jìn)行增刪改查,實(shí)現(xiàn)具體的邏輯,此外還可以添加定時(shí)任務(wù)進(jìn)行點(diǎn)綴處理,當(dāng)然如果自己的項(xiàng)目不涉及定時(shí)任務(wù),這個(gè)也是可以省略
最終我實(shí)現(xiàn)的項(xiàng)目結(jié)構(gòu)如下
PureJavaDemo // 項(xiàng)目名稱 ├── lib // 依賴文件夾 ├── mysql-connector-java-8.0.15.jar // mysql驅(qū)動(dòng)依賴文件包 ├── src ├── com ├── demo ├── bean ├── vo ├── ResponseResult.java // 統(tǒng)一響應(yīng)實(shí)體類 ├── User.java // 表User的對(duì)象實(shí)體 ├── config // 配置相關(guān)代碼 ├── router // 路由相關(guān)代碼 ├── RouteHandler.java // 路由處理器 ├── RouteRegistry.java // 路由注冊器 ├── DatabaseConnection.java // mysql數(shù)據(jù)庫連接配置類 ├── controller // 路由控制層 ├── RequestRouter.java // 請(qǐng)求路由器 ├── UserRouteHandler.java // 表User相關(guān)邏輯請(qǐng)求路由 ├── dao // 數(shù)據(jù)庫不同表增刪改查dao層 ├── UserDao // User表增刪改查 ├── schedule // 定時(shí)任務(wù)相關(guān)邏輯代碼 ├── BaseTask.java // 定時(shí)任務(wù)基類 ├── TaskScheduler.java // 定時(shí)任務(wù)管理器 ├── UserCleanupTask.java // 用戶清除定時(shí)任務(wù) ├── service // 具體邏輯實(shí)現(xiàn) ├── UserService.java // User相關(guān)邏輯具體實(shí)現(xiàn) ├── util // 工具類 ├── JsonUtil.java // JSON字符處理工具類 ├── TimeUtil.java // 時(shí)間處理工具類 ├── Main.java // 項(xiàng)目啟動(dòng)類
我實(shí)際項(xiàng)目截圖如下

在我實(shí)際的純java項(xiàng)目中,我只引入了一個(gè)
mysql依賴包,實(shí)際上也可以如Maven,Spring一樣引入對(duì)應(yīng)pom.xml里面的相關(guān)依賴來精簡自己的項(xiàng)目邏輯
搭建項(xiàng)目
首先我們使用IDEA新建我們的項(xiàng)目
?? File -> New -> Project… 打開創(chuàng)建模塊面板

?? 選擇java 創(chuàng)建java項(xiàng)目,直接點(diǎn)擊Next,別的什么都不要?jiǎng)?/p>

?? 一路Next,輸入項(xiàng)目名稱,然后點(diǎn)擊Finish即可

??然后會(huì)創(chuàng)建一個(gè)默認(rèn)什么都沒有的項(xiàng)目

?? 接著新建一個(gè)文件夾,取名lib,名稱任意,一般叫這個(gè);找mysql的驅(qū)動(dòng)依賴文件,這里可以從我們的
本地Maven庫拿,或者從網(wǎng)上搜索獲取mysql依賴,其他依賴同理,這里我演示如何從本地獲取,前提是有下載過
我們找到我們其他Maven,Spring項(xiàng)目,看IDEA的MAVEN配置存放路徑
?? File -> Setting -> 搜索Maven

?? 然后復(fù)制路徑打開文件夾,再看看我們的mysql的依賴層級(jí),任意選擇一個(gè)版本,按照自己需求,然后復(fù)制里面的jar格式的文件到自己項(xiàng)目的lib文件夾里面

<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.15</version>
</dependency>


?? OK之后,我們需要把把依賴文件添加到項(xiàng)目里面,這里指的是讓項(xiàng)目識(shí)別到依賴
File -> Project Structure -> Libraries打開這里之后,點(diǎn)擊+號(hào)選擇依賴引入,引入之后會(huì)重新在這里,然后Apply應(yīng)用就OK了

?? 到這里搭建基本好了,然后按照我給出的項(xiàng)目進(jìn)行建立對(duì)應(yīng)的java文件代碼
邏輯
那么接下來就需要正式實(shí)現(xiàn)我們的純java項(xiàng)目,我這邊以一個(gè)表為例:
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 '創(chuàng)建時(shí)間', `update_time` datetime DEFAULT NULL COMMENT '更新時(shí)間', `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;
我會(huì)實(shí)現(xiàn)查詢接口,等項(xiàng)目啟動(dòng)之后使用POSTMAN進(jìn)行調(diào)用
數(shù)據(jù)庫連接
首先我們使用JDBC,連接我們的數(shù)據(jù)庫
DatabaseConnection.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();
}
}
}
}
表增刪改查
這個(gè)是映射User表的增刪改查,通過傳遞數(shù)據(jù)庫的連接實(shí)現(xiàn)增刪改查,其他表的可以按照這個(gè)相同處理,同樣要實(shí)現(xiàn)增刪改查,需要把實(shí)體建立出來
User.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
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;
}
// 根據(jù)用戶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();
}
}
// 根據(jù)用戶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對(duì)象
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處理邏輯
在這里我使用了簡單的統(tǒng)一響應(yīng)類,去返回響應(yīng)結(jié)果,相關(guān)代碼是
ResponseResult.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
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);
}
}
}
工具類實(shí)現(xiàn)
因?yàn)闉榱吮M可能減少引入依賴,我這邊添加了工具類進(jìn)行補(bǔ)充,實(shí)際可以添加更多依賴代替
JsonUtil.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
package com.demo.util;
public class TimeUtil {
/**
* 計(jì)算距離下一個(gè)指定時(shí)間點(diǎn)的延遲時(shí)間
* @param hour 小時(shí) (0-23)
* @param minute 分鐘 (0-59)
* @return 延遲毫秒數(shù)
*/
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; // 如果今天的時(shí)間已過,則計(jì)算明天的時(shí)間
}
return next - now;
}
}路由邏輯
這里路由指我們需要提供給外部訪問的接口路徑
RouteHandler.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
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
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();
// 注冊用戶相關(guān)路由
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
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);
}
}
// 添加更多用戶相關(guān)的方法
// public ResponseResult createUser(Map<String, String> params) { ... }
}
定時(shí)任務(wù)
如果不加上定時(shí)任務(wù),純java項(xiàng)目是可以更精簡一點(diǎn),屬于可有可無的,但是正常實(shí)際項(xiàng)目都會(huì)有定時(shí)功能的需求,因此還是加上
BaseTask.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 + "] 任務(wù)開始執(zhí)行...");
executeTask();
System.out.println("[" + taskName + "] 任務(wù)執(zhí)行完成");
} catch (Exception e) {
System.err.println("[" + taskName + "] 任務(wù)執(zhí)行失敗: " + e.getMessage());
e.printStackTrace();
}
}
protected abstract void executeTask() throws Exception;
public String getTaskName() {
return taskName;
}
}
TaskScheduler.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() {
// 私有構(gòu)造器
}
public static TaskScheduler getInstance() {
return instance;
}
/**
* 安排定時(shí)任務(wù)
* @param task 要執(zhí)行的任務(wù)
* @param initialDelay 首次執(zhí)行延遲時(shí)間
* @param period 執(zhí)行周期
* @param unit 時(shí)間單位
* @return 定時(shí)任務(wù)控制器
*/
public ScheduledFuture<?> scheduleAtFixedRate(Runnable task,
long initialDelay,
long period,
TimeUnit unit) {
return scheduler.scheduleAtFixedRate(task, initialDelay, period, unit);
}
/**
* 安排延遲執(zhí)行任務(wù)
* @param task 要執(zhí)行的任務(wù)
* @param delay 延遲時(shí)間
* @param unit 時(shí)間單位
* @return 定時(shí)任務(wù)控制器
*/
public ScheduledFuture<?> schedule(Runnable task,
long delay,
TimeUnit unit) {
return scheduler.schedule(task, delay, unit);
}
/**
* 關(guān)閉定時(shí)任務(wù)管理器
*/
public void shutdown() {
scheduler.shutdown();
try {
if (!scheduler.awaitTermination(60, TimeUnit.SECONDS)) {
scheduler.shutdownNow();
}
} catch (InterruptedException e) {
scheduler.shutdownNow();
Thread.currentThread().interrupt();
}
}
}
UserCleanupTask.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("用戶清理任務(wù)");
}
@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 + " 個(gè)超過 "
+ INACTIVE_DAYS + " 天未激活的用戶");
}
}
}
啟動(dòng)類
啟動(dòng)類代表將整個(gè)項(xiàng)目進(jìn)行啟動(dòng),可以和我們的Maven項(xiàng)目,Spring項(xiàng)目一樣,也是項(xiàng)目的入口
Main.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);
// 初始化定時(shí)任務(wù)
initScheduledTasks();
// 添加關(guān)閉鉤子,確保關(guān)閉
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分鐘執(zhí)行一次的用戶清理任務(wù)
ScheduledFuture<?> userCleanupTask = scheduler.scheduleAtFixedRate(
new UserCleanupTask(),
0, // 初始延遲(立即開始)
10, // 每10分鐘
TimeUnit.MINUTES
);
// 每天凌晨2點(diǎn)執(zhí)行的統(tǒng)計(jì)任務(wù)
scheduler.scheduleAtFixedRate(
() -> System.out.println("每日統(tǒng)計(jì)任務(wù)執(zhí)行中..."),
TimeUtil.calculateInitialDelay(2, 0), // 凌晨2點(diǎn)
24, // 每天執(zhí)行一次
TimeUnit.HOURS
);
System.out.println("定時(shí)任務(wù)初始化完成");
}
}
打包Jar并啟動(dòng)
啟動(dòng)
如上我們整個(gè)項(xiàng)目就處理完畢了,如果要求啟動(dòng)項(xiàng)目,那么要求我們的端口不能被占用,否則會(huì)報(bào)錯(cuò):
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)
啟動(dòng)點(diǎn)擊
Main.java右鍵選擇Run Mian.main()或者Debug Mian.main()
啟動(dòng)之后控制臺(tái)會(huì)打印一些數(shù)據(jù)
如我的項(xiàng)目會(huì)是這些
? Server started on port 8082
[用戶清理任務(wù)] 任務(wù)開始執(zhí)行…
定時(shí)任務(wù)初始化完成
清理了 0 個(gè)超過 30 天未激活的用戶
[用戶清理任務(wù)] 任務(wù)執(zhí)行完成

在我的項(xiàng)目示例中我有兩個(gè)外部調(diào)用接口
/user/selectAllUsers/user/selectUserByUserId
此時(shí)我的表數(shù)據(jù)有這些:

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');
此時(shí)調(diào)用接口:
http://localhost:8082/user/selectAllUsers
響應(yīng)數(shù)據(jù)如下:

{
"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"
}
]
}
成功啟動(dòng)并且請(qǐng)求到了數(shù)據(jù)庫表數(shù)據(jù)
打包jar
如上我們已經(jīng)驗(yàn)證了項(xiàng)目正常啟動(dòng),我們我們需要將其打包jar,使其可以在本機(jī)或者服務(wù)器正常部署使用
File -> Project Structure -> Artifacts打開到這個(gè)界面,然后點(diǎn)擊+號(hào),選擇jar下的from modules with dependencies

Module會(huì)自動(dòng)選擇當(dāng)前項(xiàng)目,Main Class需要選擇Main.java,并且JAR files from dependencies需要選擇為copy to the output directory and link via mainfest
點(diǎn)擊OK然后Apply


就會(huì)多出一個(gè)項(xiàng)目名稱的jar包在這里
然后點(diǎn)擊Build -> Build Artifacts再在新界面選擇點(diǎn)擊Build



等待打包完成,這個(gè)時(shí)候會(huì)在我們的
out文件夾下的artifacts文件夾會(huì)看到我們剛剛打包后的jar包
關(guān)閉我們的項(xiàng)目,然后啟動(dòng)該jar包java -jar PureJavaDemo.jar


可以看到如上截圖成功執(zhí)行了jar,往user表加些數(shù)據(jù)再次請(qǐng)求postman驗(yàn)證是否可用

{
"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"
}
]
}
可以看到正常的訪問,但是有一點(diǎn)需要注意,
打包之后的jar需要和依賴放在同一個(gè)文件夾下
項(xiàng)目GIT
優(yōu)缺點(diǎn)
綜上,我們已經(jīng)很清晰明了的知道java是怎么去運(yùn)作我們的項(xiàng)目了,但是還是那句話,這個(gè)只是讓你更好的學(xué)習(xí)和了解java,真正要開發(fā)一個(gè)項(xiàng)目還是選擇Maven,Spring框架,如下我列舉了一些純java項(xiàng)目優(yōu)缺點(diǎn)
優(yōu)點(diǎn)
?? 輕量: 啟動(dòng)快,內(nèi)存占用第,無框架開銷
?? 自主掌控: 自主實(shí)現(xiàn)邏輯,可以深度理解底層機(jī)制(HTTP/TCP等)
??學(xué)習(xí)價(jià)值高: 對(duì)于掌握基礎(chǔ)友好
缺點(diǎn)
??開發(fā)效率低: 手動(dòng)實(shí)現(xiàn)路由,依賴注入等,重復(fù)造輪子,缺少標(biāo)準(zhǔn)項(xiàng)目結(jié)構(gòu)
??維護(hù)成本高: 自行管理依賴版本,缺少自動(dòng)化測試支持,擴(kuò)展性差
??團(tuán)隊(duì)協(xié)作難: 無統(tǒng)一的開發(fā)規(guī)范,項(xiàng)目結(jié)構(gòu)依賴個(gè)人風(fēng)格
結(jié)語
到此這篇關(guān)于IDEA實(shí)現(xiàn)純java項(xiàng)目并打包jar(不使用Maven,Spring)的文章就介紹到這了,更多相關(guān)DEA純java項(xiàng)目打包jar內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Java的Hibernate框架中用于操作數(shù)據(jù)庫的HQL語句講解
這篇文章主要介紹了Java的Hibernate框架中用于操作數(shù)據(jù)庫的HQL語句講解,Hibernate是Java的SSH三大web開發(fā)框架之一,需要的朋友可以參考下2016-01-01
macOS下Spring Boot開發(fā)環(huán)境搭建教程
這篇文章主要為大家詳細(xì)介紹了macOS下Spring Boot開發(fā)環(huán)境搭建教程,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2018-01-01
mybatis-plus 攔截器敏感字段加解密的實(shí)現(xiàn)
數(shù)據(jù)庫在保存數(shù)據(jù)時(shí),對(duì)于某些敏感數(shù)據(jù)需要脫敏或者加密處理,本文主要介紹了mybatis-plus 攔截器敏感字段加解密的實(shí)現(xiàn),感興趣的可以了解一下2021-11-11
Spring Boot 實(shí)例代碼之通過接口安全退出
這篇文章主要介紹了Spring Boot 實(shí)例代碼之通過接口安全退出的相關(guān)資料,需要的朋友可以參考下2017-09-09
SpringBoot+Vue前后端分離實(shí)現(xiàn)請(qǐng)求api跨域問題
這篇文章主要介紹了SpringBoot+Vue前后端分離實(shí)現(xiàn)請(qǐng)求api跨域問題,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-06-06

