一、nginx
1.1、反向代理
在 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、负载均衡
# 服务器1
server {
listen 9001;
server_name localhost;
default_type text/html;
location / {
return 200 '<h1>server:9001</h1>';
}
}
# 服务器2
server {
listen 9002;
server_name localhost;
default_type text/html;
location / {
return 200 '<h1>server:9002</h1>';
}
}
# 服务器3
server {
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、前后端分离开发流程
三、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相关配置
@Bean public 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、常用注解
四、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();
}
五、公共字段的抽取
在一个项目中,总有些公共的字段需要重复的进行填充,例如修改时间,修改人这类的数据
-
解决办法–通过切面来进行统一拦截
- 自定义一个注解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 INSERT OperationType 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注解
六、数据库自动生成主键和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
- 依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-cache</artifactId>
</dependency>
- 注解
- @CachePut
//如果使用spring cache 缓存数据,key的生成:userCache::key
@CachePut(cacheNames = "userCache",key = "#user.id")
// key = "#result.id")从结果中获取id
// key = "#p0.id")取第一个参数的id
// key = "#a0.id")取第一个参数的id
public 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个域,由空格分隔开,每个域代表一个含义
每个域的含义分别为:秒、分钟、小时、日、月、周、年(可选)
注意周和日不能同时出现
9.1、使用步骤
- 导入maven坐标spring-context (已存在)
- 启动类添加注解@Enablescheduling开启任务调度
- 自定义定时任务类
十、WebSocket
WebSocket是基于TCP的一种新的网络协议。它实现了浏览器与服务器全双工通信——浏览器和服务器只需要完成一次握手,两者之间就可以创建持久性的连接,并进行双向数据传输。
- 与http协议区别
- http是短连接
- WebSocket是长连接
- Http通信是单向的,基于请求响应模式
- WebSocket支持双向通信
- Http和WebSocket底层都是TCP连接
- 应用场景
- 视频弹幕
- 网页聊天
- 体育实况更新
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(); /** * 连接建立成功调用的方法 */ @OnOpen public void onOpen(Session session, @PathParam("sid") String sid) { System.out.println("客户端:" + sid + "建立连接"); sessionMap.put(sid, session); } /** * 收到客户端消息后调用的方法 * * @param message 客户端发送过来的消息 */ @OnMessage public void onMessage(String message, @PathParam("sid") String sid) { System.out.println("收到来自客户端:" + sid + "的信息:" + message); } /** * 连接关闭调用的方法 * * @param sid */ @OnClose public 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 { @Bean public 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 { @Autowired private 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模板文件(一般是由开发者自行设计)
- 查询近30天的运营数据
- 将查询到的运营数据写入模板文件
- 通过输出流将Excel文件下载到客户端浏览器