Java Web开发问题(二)

一、nginx

1.1、反向代理

image-20240316225117819

在 nginx.conf 配置文件中增加如下配置:

1     server {
2         listen       80;
3         server_name  www.123.com;
4 
5         location / {
6             proxy_pass http://127.0.0.1:8080;
7             index  index.html index.htm index.jsp;
8         }
9     }

1.2、负载均衡

	# 服务器1server {listen 9001;server_name localhost;default_type text/html;location / {return 200 '<h1>server:9001</h1>';}}# 服务器2server {listen 9002;server_name localhost;default_type text/html;location / {return 200 '<h1>server:9002</h1>';}}# 服务器3server {listen 9003;server_name localhost;default_type text/html;location / {return 200 '<h1>server:9003</h1>';}}# 代理服务器# 设置服务器组upstream backend {server localhost:9001;server localhost:9002;server localhost:9003;}server {listen 8080;server_name localhost;location / {# backend 就是服务器组的名称 上下保持一致proxy_pass http://backend/;}}
  • 策略

    默认

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

二、接口文档的定义

2.1、前后端分离开发流程

image-20240317085551741

三、Swagger

使用Swagger你只需要按照它的规范去定义接口及接口相关的信息,就可以做到生成接口文档,以及在线接口调试页面。

项目中可以使用Knife4j是javaMVC框架集成Swagger生成Api文档的增强解决方案

<dependency><groupId>com.github.xiaoymin</groupId><artifactId>knife4j-spring-boot-starter</artifactId><version>3.0.2</version>
</dependency>
  • 使用方式

    • 导入knife4j的maven坐标
    • 在配置类中加入knife4j相关配置
        @Beanpublic Docket docket() {ApiInfo apiInfo = new ApiInfoBuilder().title("项目接口文档").version("2.0").description("项目接口文档").build();Docket docket = new Docket(DocumentationType.SWAGGER_2).apiInfo(apiInfo).select().apis(RequestHandlerSelectors.basePackage("com.sky.controller")).paths(PathSelectors.any()).build();return docket;}
    
    • 设置静态资源映射,否则接口文档页面无法访问
        protected void addResourceHandlers(ResourceHandlerRegistry registry) {registry.addResourceHandler("/doc.html").addResourceLocations("classpath:/META-INF/resources/");registry.addResourceHandler("/webjars/**").addResourceLocations("classpath:/META-INF/resources/webjars/");}
    

3.1、常用注解

image-20240317093037140

四、ThreadLocal

ThreadLocal并不是一个Thread,而是Thread的局部变量。

ThreadLocal为每个线程提供单独一份存储空间,具有线程隔离的效果

只有在线程内才能获取到对应的值,线程外则不能访问。

  • 常用方法
    • set 设置当天线程的线程局部变量
    • get 返回当前线程所对应的线程局部变量的值
    • remove 移除当前线程的线程局部变量

例如我们需要知道当前操作此操作的人的信息就可以封装一个Base

public static ThreadLocal<Long> threadLocal = new ThreadLocal<>();public static void setCurrentId(Long id) {threadLocal.set(id);}public static Long getCurrentId() {return threadLocal.get();}public static void removeCurrentId() {threadLocal.remove();}

五、公共字段的抽取

在一个项目中,总有些公共的字段需要重复的进行填充,例如修改时间,修改人这类的数据

image-20240319170409777

  • 解决办法–通过切面来进行统一拦截

    • 自定义一个注解AutoFill
    package com.sky.annotation;import com.sky.enumeration.OperationType;import java.lang.annotation.ElementType;
    import java.lang.annotation.Retention;
    import java.lang.annotation.RetentionPolicy;
    import java.lang.annotation.Target;/*** 自定义注解,用于标识某个方法需要进行功能字段填充处理*/@Target(ElementType.METHOD)
    @Retention(RetentionPolicy.RUNTIME)
    public @interface AutoFill {//指定当前数据库操作类型 UPDATE INSERTOperationType value();
    }
    
    • 自定一个切面类AutoFillAspect,统一拦截加入AutoFill注解的方法,通过反射为公共字段赋值
    package com.sky.aspect;import com.sky.annotation.AutoFill;
    import com.sky.constant.AutoFillConstant;
    import com.sky.context.BaseContext;
    import com.sky.enumeration.OperationType;
    import lombok.extern.slf4j.Slf4j;
    import org.aspectj.lang.JoinPoint;import org.aspectj.lang.Signature;
    import org.aspectj.lang.annotation.Aspect;
    import org.aspectj.lang.annotation.Before;
    import org.aspectj.lang.annotation.Pointcut;import org.aspectj.lang.reflect.MethodSignature;
    import org.springframework.stereotype.Component;import java.lang.reflect.Method;
    import java.time.LocalDate;
    import java.time.LocalDateTime;/*** 自定义切面,实现公共字段的自动填充逻辑*/
    @Aspect
    @Component
    @Slf4j
    public class AutoFillAspect {/** 切入点* */@Pointcut("execution(* com.sky.mapper.*.*(..)) && @annotation(com.sky.annotation.AutoFill)")public void autoFillPointCut(){}/** 前置通知,在通知前进行公共字段的赋值* */@Before("autoFillPointCut()")public void autoFill(JoinPoint joinPoint){log.info("开始进行公共自动填充");//获取到当前被拦截的方法上的数据库操作类型MethodSignature signature = (MethodSignature)joinPoint.getSignature(); //方法签名对象AutoFill autoFill = signature.getMethod().getAnnotation(AutoFill.class); //获得方法上的注解对象OperationType operationType = autoFill.value(); //获得数据库操作类型//获取到当前被拦截的方法的参数--实体对象Object[] args = joinPoint.getArgs();if (args == null || args.length == 0  ){return;}//保证每次对象在第一位,所以取第一个Object entity = args[0];//准备赋值的数据LocalDateTime now = LocalDateTime.now();Long currentId = BaseContext.getCurrentId();//根据当前不同的操作类型,为对应的属性通过反射赋值if(operationType == OperationType.INSERT){//insert为四个公共字段赋值try {Method setCreateTime = entity.getClass().getDeclaredMethod(AutoFillConstant.SET_CREATE_TIME, LocalDateTime.class);Method setCreateUser = entity.getClass().getDeclaredMethod(AutoFillConstant.SET_CREATE_USER, Long.class);Method setUpdateTime = entity.getClass().getDeclaredMethod(AutoFillConstant.SET_UPDATE_TIME, LocalDateTime.class);Method setUpdateUser = entity.getClass().getDeclaredMethod(AutoFillConstant.SET_UPDATE_USER, Long.class);//通过反射为对象赋值setCreateTime.invoke(entity,now);setCreateUser.invoke(entity,currentId);setUpdateTime.invoke(entity,now);setUpdateUser.invoke(entity,currentId);} catch (Exception e) {throw new RuntimeException(e);}} else if (operationType == OperationType.UPDATE) {//insert为四个公共字段赋值try {Method setUpdateTime = entity.getClass().getDeclaredMethod(AutoFillConstant.SET_UPDATE_TIME, LocalDateTime.class);Method setUpdateUser = entity.getClass().getDeclaredMethod(AutoFillConstant.SET_UPDATE_USER, Long.class);//通过反射为对象赋值setUpdateTime.invoke(entity,now);setUpdateUser.invoke(entity,currentId);} catch (Exception e) {throw new RuntimeException(e);}}}}
    
    • 在Mapper的方法上加入AutoFill注解

    image-20240320102140407

六、数据库自动生成主键和Java对象的同步

在mybatis中使用

    <insert id="insert" useGeneratedKeys="true" keyProperty="id">

其中keyProperty对应需要返回的属性名

七、HttpClient

HttpClient是Apache Jakarta Common下的子项目,可以用来提供高效的、最新的、功能丰富的支持 HTTP协议的客户端编程工具包,并且它支持 HTTP协议最新的版本和建议。

八、Spring Cache

Spring Cache是一个框架,实现了基于注解的缓存功能,只需要简单地加一个注解,就能实现缓存功能。
Spring Cache提供了一层抽象,底层可以切换不同的缓存实现,例如

  • EHCache
  • Caffeine
  • Redis
  1. 依赖
        <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-cache</artifactId></dependency>
  1. 注解

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

  • @CachePut
    //如果使用spring cache 缓存数据,key的生成:userCache::key@CachePut(cacheNames = "userCache",key = "#user.id")
//  key = "#result.id")从结果中获取id
//  key = "#p0.id")取第一个参数的id
// key = "#a0.id")取第一个参数的idpublic User save(@RequestBody User user){userMapper.insert(user);return user;}
  • @Cacheable
    @Cacheable(cacheNames = "userCache",key = "#id")public User getById(Long id){User user = userMapper.getById(id);return user;}
  • @CacheEvict
 @CacheEvict(cacheNames = "userCache",allEntries = true)
//,key = "#id" 根据id删除
public void deleteAll(){userMapper.deleteAll();}

九、Spring Task

Spring Task是spring框架提供的任务调度工具,可以按照约定的时间自动执行某个代码逻辑。

cron表达式

cron表达式其实就是一个字符串,通过cron表达式可以定义任务触发的时间
构成规则:分为6或7个域,由空格分隔开,每个域代表一个含义
每个域的含义分别为:秒、分钟、小时、日、月、周、年(可选)

image-20240406165931131

注意周和日不能同时出现

9.1、使用步骤

  • 导入maven坐标spring-context (已存在)
  • 启动类添加注解@Enablescheduling开启任务调度
  • 自定义定时任务类

image-20240406170920611

十、WebSocket

WebSocket是基于TCP的一种新的网络协议。它实现了浏览器与服务器全双工通信——浏览器和服务器只需要完成一次握手,两者之间就可以创建持久性的连接,并进行双向数据传输。

  • 与http协议区别
    • http是短连接
    • WebSocket是长连接
    • Http通信是单向的,基于请求响应模式
    • WebSocket支持双向通信
    • Http和WebSocket底层都是TCP连接

image-20240407143321630

  • 应用场景
    • 视频弹幕
    • 网页聊天
    • 体育实况更新

10.1、使用步骤

  • 直接使用websocket.html页面作为WebSocket客户端

  • 导入WebSocket的maven坐标

            <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-websocket</artifactId></dependency>
    
  • 导入WebSocket服务端组件WebSocketServer,用于和客户端通信

    package com.sky.websocket;import org.springframework.stereotype.Component;
    import javax.websocket.OnClose;
    import javax.websocket.OnMessage;
    import javax.websocket.OnOpen;
    import javax.websocket.Session;
    import javax.websocket.server.PathParam;
    import javax.websocket.server.ServerEndpoint;
    import java.util.Collection;
    import java.util.HashMap;
    import java.util.Map;/*** WebSocket服务*/
    @Component
    @ServerEndpoint("/ws/{sid}")
    public class WebSocketServer {//存放会话对象private static Map<String, Session> sessionMap = new HashMap();/*** 连接建立成功调用的方法*/@OnOpenpublic void onOpen(Session session, @PathParam("sid") String sid) {System.out.println("客户端:" + sid + "建立连接");sessionMap.put(sid, session);}/*** 收到客户端消息后调用的方法** @param message 客户端发送过来的消息*/@OnMessagepublic void onMessage(String message, @PathParam("sid") String sid) {System.out.println("收到来自客户端:" + sid + "的信息:" + message);}/*** 连接关闭调用的方法** @param sid*/@OnClosepublic void onClose(@PathParam("sid") String sid) {System.out.println("连接断开:" + sid);sessionMap.remove(sid);}/*** 群发** @param message*/public void sendToAllClient(String message) {Collection<Session> sessions = sessionMap.values();for (Session session : sessions) {try {//服务器向客户端发送消息session.getBasicRemote().sendText(message);} catch (Exception e) {e.printStackTrace();}}}}
    
  • 导入配置类WebSocketConfiguration,注册WebSocket的服务端组件

    package com.sky.config;import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.web.socket.server.standard.ServerEndpointExporter;/*** WebSocket配置类,用于注册WebSocket的Bean*/
    @Configuration
    public class WebSocketConfiguration {@Beanpublic ServerEndpointExporter serverEndpointExporter() {return new ServerEndpointExporter();}}
  • 导入定时任务类WebSocketTask,定时向客户端推送数据

    package com.sky.task;import com.sky.websocket.WebSocketServer;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.scheduling.annotation.Scheduled;
    import org.springframework.stereotype.Component;
    import java.time.LocalDateTime;
    import java.time.format.DateTimeFormatter;@Component
    public class WebSocketTask {@Autowiredprivate WebSocketServer webSocketServer;/*** 通过WebSocket每隔5秒向客户端发送消息*/@Scheduled(cron = "0/5 * * * * ?")public void sendMessageToClient() {webSocketServer.sendToAllClient("这是来自服务端的消息:" + DateTimeFormatter.ofPattern("HH:mm:ss").format(LocalDateTime.now()));}
    }
    

十一、POI

Apache POI是一个处理Miscrosoft Office各种文件格式的开源项目。简单来说就是,我们可以使用POI在Java程序中对Miscrosoft Office各种文件进行读写操作。一般情况下,POI都是用于操作 Excel文件。

  • 应用场景
    • 银行网银系统导出交易明细
    • 各种业务系统导出Excel报表
    • 批量导入业务数据

11.1、使用步骤

  • 设计Excel模板文件(一般是由开发者自行设计)
    • image-20240409163524909
  • 查询近30天的运营数据
  • 将查询到的运营数据写入模板文件
  • 通过输出流将Excel文件下载到客户端浏览器

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/623102.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

数据结构之来链表——单链表

什么是单链表&#xff1a; 文字说明&#xff1a; 单链表顾名思义&#xff0c;就是指单项链表&#xff0c;即只有一个方向的链性线性表。 图解&#xff1a; 如下图所示&#xff0c;即为链表&#xff08;DATA为我们自己所定义的数据类型&#xff09;&#xff1a; 单链表的创建&am…

负荷预测 | Matlab基于TCN-GRU-Attention单变量时间序列多步预测

目录 效果一览基本介绍程序设计参考资料 效果一览 基本介绍 1.Matlab基于TCN-GRU-Attention单变量时间序列多步预测&#xff1b; 2.单变量时间序列数据集&#xff0c;采用前12个时刻预测未来96个时刻的数据&#xff1b; 3.excel数据方便替换&#xff0c;运行环境matlab2023及以…

MyBatis操作数据库(3)

其它查询操作 #{}和${} MyBatis参数赋值有两种方式, 咱们前面使用了#{}进行赋值, 接下来来看两者的区别: #{}和${}的使用 1.先看Integer类型的参数: Select("select username, password, age, gender, phone from userinfo where id #{id}") UserInfo queryByI…

护眼灯哪个品牌好?五大护眼灯品牌推荐

护眼灯哪个品牌好&#xff1f;在挑选对眼睛有益的台灯时&#xff0c;专业护眼台灯无疑是明智的选择。这类台灯不仅严格按照国家的标准制造&#xff0c;确保了安全与质量的可靠性&#xff0c;除此之外护眼台灯还采用了经过精心设计的发光结构&#xff0c;有效减少了光线对眼睛的…

MinIO + Prometheus + Grafana docker部署

文章目录 说明MinIO简介MinIO 容器化部署Prometheus服务地址配置方法一&#xff1a;先部署后修改方法二&#xff1a;部署时修改compose文件&#xff08;未验证&#xff09; MinIO Access Key配置Prometheus 容器化部署MinIO 生成抓取配置修改Prometheus配置文件Grafana 容器化部…

【Unity】游戏场景添加后处理特效PostProcessing

添加后处理特效PostProcessing 添加雾效果后处理何为后处理&#xff1f;添加后处理特效 添加雾效果 依次点击Window -> Rendering -> Lighting添加Lighting面板。 点击Lighting里面的Environment&#xff0c;找到Other Setting 将Fog选项勾选 更改下方的颜色 调整雾的浓…

Python教程:备份你的文件夹里面的数据

1.完全备份是最基本的备份类型&#xff0c;它涉及复制所有选定的数据到备份位置。无论文件是否自上次备份以来发生了变化&#xff0c;所有文件都会被复制。这种备份方式简单直接&#xff0c;确保了备份存储的数据总是最新的。 完全备份是通过递归复制源文件夹中的所有文件和子…

面试算法-175-将有序数组转换为二叉搜索树

题目 给你一个整数数组 nums &#xff0c;其中元素已经按 升序 排列&#xff0c;请你将其转换为一棵 平衡 二叉搜索树。 示例 1&#xff1a; 输入&#xff1a;nums [-10,-3,0,5,9] 输出&#xff1a;[0,-3,9,-10,null,5] 解释&#xff1a;[0,-10,5,null,-3,null,9] 也将被视…

链表里面头节点存在的目的

头节点存在的目的&#xff1a; 在单链表的使用中&#xff0c;头结点&#xff08;Header Node&#xff09;是一个常用的概念&#xff0c;特别是在进行链表操作时。头结点不是数据域中实际存储的数据节点&#xff0c;而是作为链表操作的辅助节点&#xff0c;它包含对第一个实际数…

面试经典150题——二叉树的最大深度

1. 题目描述 ​ 2. 题目分析与解析 这个题目有过一定基础的都应该知道&#xff0c;采用递归解决问题&#xff0c;因为要求一个二叉树的深度&#xff08;也就是高度&#xff09;&#xff0c;其实上就是根节点的左子树和右子树中高度最高的那个。因此这个问题就可以拆解为&…

【电控笔记6.1】稳定度判断

简要概括 现控:远离虚轴,稳定度越高 自控:相位裕度PM 增益裕度GM 开环传函 不稳定条件判断

微信小程序生成链接或二维码的对比

二维码 1.小程序样式码 获取参数直接options.xxx&#xff08;参数名&#xff09; 方法&#xff1a;微信公众平台 》工具》生成小程序二维码&#xff1b; 样式图&#xff1a;就一看就是小程序的二维码&#xff1b; 2.正方形二维码/链接 方法&#xff1a;微信公众平台》开…