Java轻量级代码工程

一、背景简介

最近2年,很多公司接项目开始不挑剔了,只要核算下来有收益不会亏,就会库库的做各种没头没脑的项目,都是抱着多撑一天是一天的躺平心态。

如果项目和业务稳定,自然也有动力卷架构卷设计。

然而2024年对于很多研发团队的打工人来说,普遍都是处在随时卷铺盖走人的状态中,自然就是怎么省事怎么来,做项目时间和成本是最重视的维度。

抛开什么AI和各种工具加持,节省编程时间最有效的办法就是:拿现成的,最好只改个端口号。

二、工程结构

2024年上半年各种不大不小的项目写下来,其实从代码工程上看都是差不多的,服务端无非就是前和后两个代码工程,集成基础框架就可以增删改查了。

为了后续其它项目使用相同的基础工程,在第一个项目中,预留了代码工程的初期基础设计,方便那什么循环利用,在这篇文章简单分享一下。

基础的代码工程就这几块,实际上就是前后台两个单体服务,不涉及微服务和复杂架构,就是拿来应付各种奇怪的内部项目的快速开发;两个门面服务分别依赖framesharedthird三层代码包,然后运行启动类即可。

既然是单体服务,自然也不涉及数据库的拆分,能安稳运行就可以了。

xml 复制代码
<modules>
  <!-- 门面管理 -->
  <module>bm-facade</module>
  <!-- 后台管理 -->
  <module>bm-admin</module>
  <!-- 业务共享层 -->
  <module>bm-shared</module>
  <!-- 框架管理 -->
  <module>bm-frame</module>
  <!-- 第三方接入 -->
  <module>bm-third</module>
</modules>

三、模块化功能

3.1 框架模块

主要管理代码工程核心组件依赖,比如SpringBoot框架,MySQL数据库,MybatisPlus持久层,Swagger接口等,并且添加了一个公共过滤器。

java 复制代码
@Slf4j
@Component
public class CommonFilter extends OncePerRequestFilter implements Ordered {

    @Override
    protected void doFilterInternal(HttpServletRequest request,
                                    HttpServletResponse response,
                                    FilterChain chain)
    throws ServletException, IOException {

        log.info("CommonFilter...doFilterInternal");

        // 请求向下转发
        chain.doFilter(request, response);
    }

    @Override
    public int getOrder() {
        return 999 ;
    }
}

对于一些必要的配置类和二次封装,也放在了这一层,另外为了简化工程的复杂性,使用AOP的方式包装了日志记录和令牌核验;

AOP切面编程就不说了,这个在项目中经常使用;Token令牌的封装方式可以自行扩展,权限校验拿数据的记录对比一下即可。

java 复制代码
@Component
public class BaseToken {

  @Getter
  @Setter
  private Long userId;

  @Getter
  @Setter
  private String userName;

  @Getter
  @Setter
  private String appName;

  @Getter
  @Setter
  private Long expireTime;

  private static final byte[] AES_KEY = Arrays.copyOf(MD5.create().digest(BaseToken.class.getPackage().getName()), 16);

  @Getter
  private final String currentType = BaseToken.class.getTypeName();

  public void expireTime(int effective) {
    expireTime = System.currentTimeMillis()/1000 + effective ;
  }

  public boolean expired() {
    return expireTime < (System.currentTimeMillis() / 1000);
  }

  public String toToken() {
    String data = JSONUtil.toJsonStr(this);
    return SecureUtil.aes(AES_KEY).encryptBase64(data);
  }

  public static BaseToken parseToken(String token) {
    String dataStr = SecureUtil.aes(AES_KEY).decryptStr(token);
    return JSONUtil.toBean(dataStr, BaseToken.class);
  }

  public boolean checkPermission (String... permissionCode) {
    AuthCheck authService = SpringUtil.getBean(AuthCheck.class);
    return authService.hasPermission(this.getUserId(), permissionCode);
  }

}

使用AOP切面编程拦截要鉴权的方法即可。

java 复制代码
@Component
@Aspect
@RequiredArgsConstructor
public class AuthAop {

    /**
     * 切入点
     */
    @Pointcut("@annotation(com.butte.mound.frame.security.Auth)")
    public void logPointCut() {

    }

    @Around("logPointCut()")
    public Object around (ProceedingJoinPoint point) throws Throwable {
        try{
            // 检查权限
            checkAuth(point);
            // 执行方法
            Object result = point.proceed();
            return result;
        } catch (Exception e){
            e.printStackTrace();
            throw e ;
        }
    }

    private void checkAuth (ProceedingJoinPoint point){
        try{
            MethodSignature signature = (MethodSignature) point.getSignature();
            Method method = signature.getMethod();
            Auth auth = method.getAnnotation(Auth.class);
            BaseToken token = TokenContext.getToken();
            token.checkPermission(auth.authCode());
        } catch (Exception e){
            e.printStackTrace();
        }
    }
}

3.2 业务共享模块

主要管理业务代码,数据库相关的持久层文件,如果在服务层前后台业务差异过大的话,也可以考虑分2层管理即可,在体量小的项目中,持久层代码几乎差异不大,最多只是一些自定义查询。

3.3 前后台模块

前后台两个模块,主要就是API接入层,通常就是用户访问和系统管理的两个服务,在逻辑上并没有什么本质差异,为了方便不同角色的请求管理,在两个工程内各添加了一个拦截器。

java 复制代码
@Slf4j
@Component
public class FacadeInterceptor implements HandlerInterceptor {

  @Override
  public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {

    log.info("req uri : {}",request.getRequestURI());

    String facadeToken = "令牌";

    BaseToken token = BaseToken.parseToken(facadeToken);
    TokenContext.setToken(token);

    return Boolean.TRUE ;
  }
}

3.4 第三方模块

至于常见的第三方API对接,比如短信服务,OSS文件存储,第三方登录等,如果考虑多个项目多次复用的话,可以单独拎一层出来,简单封装一层工具类,方便之后随时替换,当然也可以在业务工程中直接调用第三方的封装类。

四、源码地址

java 复制代码
文档仓库:
https://gitee.com/cicadasmile/butte-java-note

源码仓库:
https://gitee.com/cicadasmile/butte-mound
相关推荐
sinat_26229211几秒前
Java面试实战:电商场景下的Spring Cloud微服务架构与缓存技术剖析
java·redis·spring cloud·微服务·消息队列·缓存技术·监控运维
大阔几秒前
安卓开发中XML布局的实用技巧 —— 移动应用开发(安卓)
java
用户484695206971 分钟前
ArrayList 源码分析
java
大阔2 分钟前
如何使用Intent在安卓Activity间传输数据 —— 移动应用开发(安卓)
java
AronTing3 分钟前
缓存与数据库一致性深度解析与解决方案
java·后端·面试
AronTing4 分钟前
分布式缓存与本地缓存协同优化实践
java·后端·面试
带刺的坐椅7 分钟前
Java AI(智能体)编排开发就用 Solon Flow
java·ai·openai·solon·solon-flow
编码七号14 分钟前
【axios取消请求】如何在token过期后取消未响应的请求
java·前端·javascript
居然是阿宋30 分钟前
Kotlin函数体详解:表达式函数体 vs 代码块函数体——使用场景与最佳实践
java·开发语言·kotlin