随笔

Implement Java Web Starter

最开始接触编程时,Java 就是一个超级火热的语言,虽然其中有一些是因为 Android 而带来的,但是服务端领域基本都是 Java,当时比较流行的一种组合是叫做SSH(Spring + Struts + Hibernate),但是项目配置起来非常繁复,Hibernate 记得好像 Bean 啥的都是配置在 XML 里面,非常繁杂冗余。

今天忽然心血来潮就想再次尝试下 Java 现在的写法如何,就来实现一个简单的 Web 程序,主要实现接口请求,从 MySQL 拿数据返回,以及授权拦截等核心功能。

创建项目

现在比较流行的是 Spring Boot,所以直接去 https://start.spring.io/ 生成一个基础版项目,注意选择 Java 版本时看下机器安装的 JDK 版本,然后 Project 选 Maven 管理依赖。

spring initializr

然后一个最简单的 Web 就跑起来了,因为没有配置路径,所以访问任何地址都是一个报错页面
first run

default error

增加路由

直接开始创建 controller,试试活
product controller

view product controller

使用数据库

既然接口能通了,那就直接开始上数据库,让项目真实的跑起来

安装依赖包

setup package

配置数据库连接

修改 application.properties 文件

# MySQL Database configuration
spring.datasource.url=jdbc:mysql://localhost:3306/java-demo?useSSL=false&serverTimezone=UTC
spring.datasource.username=root
spring.datasource.password=lilonghe
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver

# JPA configuration
spring.jpa.hibernate.ddl-auto=update
spring.jpa.show-sql=true
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQLDialect

配置 Model

package com.lilonghe.demo.model;

import jakarta.persistence.Entity;
import jakarta.persistence.Id;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.time.LocalDateTime;

@Data
@NoArgsConstructor
@AllArgsConstructor
@Entity
public class Product {
    @Id
    private Integer id;
    private String name;
    private String description;
    private LocalDateTime createTime;
    private LocalDateTime updateTime;
    private LocalDateTime deleteTime;
}

配置 Service

package com.lilonghe.demo.repository;

import com.lilonghe.demo.model.Product;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;

@Repository
public interface ProductRepository extends JpaRepository<Product, Integer> {
}

修改 Controller

@GetMapping
public List<Product> list() {
    return productRepository.findAll();
}

db data

非常简单顺滑的就完成了读取数据库,然后返回 JSON 的一个 API,仅仅只是配置了下连接字符串,创建了一个 Model,比以前的体验好了太多太多。

包装返回数据

创建一个 Response 类,在返回时包装下返回格式。现在的注解也超好用,比如这里只声明了字段,没有声明 Getter 和 Setter 以及构造方法,但是通过注解,就可以帮你做到在运行时自动生成相应的方法。

package com.lilonghe.demo.utils;

import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import lombok.AllArgsConstructor;
import lombok.Data;

@Data
@AllArgsConstructor
@JsonSerialize
public class APIResponse<T> {
    private int code;
    private String message;
    private T data;
}
@GetMapping
public APIResponse<List<Product>> list() {
    return new APIResponse<>(200, null, productRepository.findAll());
}

授权拦截

普通的 Web 程序必然要有登录鉴权,这里也一起给加上,继承 HandlerInterceptor 去重载 preHandle 方法,去检查 Cookie 里是否存在 token,这里为了省事就不对 token 有效性做验证了。

AuthInterceptor

login/logout

异常处理

处理了一个兜底错误,以及一个 404 错误,如果有其他错误直接扩展即可

package com.lilonghe.demo.exception;

import com.lilonghe.demo.utils.APIResponse;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.servlet.resource.NoResourceFoundException;

@RestControllerAdvice
public class ControllerException {

    @ExceptionHandler(Exception.class)
    @ResponseBody
    public APIResponse<String> globalExceptionHandler(Exception ex) {
        return new APIResponse<>(500, "系统异常,请稍后重试", null);
    }

    @ExceptionHandler(NoResourceFoundException.class)
    @ResponseBody
    public APIResponse<String> notFoundExceptionHandler(Exception ex) {
        return new APIResponse<>(404, "未找到相关资源", null);
    }
}

总结

到此为止,一个非常典型的 Web 系统该有的功能(登入登出,普通接口,连接数据库,异常处理)就都有了,剩下的无非就是业务逻辑的实现。

总体使用起来很顺滑,尤其是搭配 ChatGPT 更是上手很快,也可能是因为自身本来就对这些语言或者框架都有一定的认知。

Java 相比前端开发领域,最主要的区别就是 OOP,以及依赖注入,控制反转等一些列概念,但是架子搭起来后,真正业务写起来其实并不会很复杂,而且有一些列非常成熟的工具或者库可以使用。

当时流行的 MVC 分层架构其实到现在一直都在用,虽然有些演变,但主体其实一直没变,大部分是多一层或少一层的区别。

另外就从今天实现的项目来说,上手难度上其实比许多前端项目都要更简单一些,当然这是以我个人角度来讲,毕竟每个人都有属于自己的看法。

https://github.com/lilonghe/java-hello

本文链接:https://note.lilonghe.net/post/implement-java-web-starter.html

-- EOF --