一、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、负载均衡
# 服务器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、前后端分离开发流程
三、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、常用注解
四、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 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注解
六、数据库自动生成主键和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")取第一个参数的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个域,由空格分隔开,每个域代表一个含义
每个域的含义分别为:秒、分钟、小时、日、月、周、年(可选)
注意周和日不能同时出现
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();/*** 连接建立成功调用的方法*/@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模板文件(一般是由开发者自行设计)
- 查询近30天的运营数据
- 将查询到的运营数据写入模板文件
- 通过输出流将Excel文件下载到客户端浏览器