java项目笔记(苍穹外卖)

MD5 加密方式

//进行md5加密
password = DigestUtils.md5DigestAsHex("123456".getBytes());

对于时间格式化的两种方式

  • 采用注解的方式

    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    

    缺点:只能对一个属性起作用,也就是有几个需要进行时间处理的变量,我们就需要添加几个注解

  • 自定义消息转换器

    可以对全局变量有效,因为这是我们自定义的配置类,会在全局生效

    WebMvcConfiguration.java
    
    @Override
        protected void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
            log.info("扩展消息转换器");
            //创建一个消息转换器对象
            MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter();
            //需要为消息转换器指定对象转换器    对象转换器课可以将java对象序列化为Json对象
            converter.setObjectMapper(new JacksonObjectMapper());
            //将自定义的消息转换器加入到容器中
            converters.add(0, converter);
     
        }
    
    import com.fasterxml.jackson.databind.DeserializationFeature;
    import com.fasterxml.jackson.databind.ObjectMapper;
    import com.fasterxml.jackson.databind.module.SimpleModule;
    import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateDeserializer;
    import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer;
    import com.fasterxml.jackson.datatype.jsr310.deser.LocalTimeDeserializer;
    import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateSerializer;
    import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer;
    import com.fasterxml.jackson.datatype.jsr310.ser.LocalTimeSerializer;
    
    import java.time.LocalDate;
    import java.time.LocalDateTime;
    import java.time.LocalTime;
    import java.time.format.DateTimeFormatter;
    
    import static com.fasterxml.jackson.databind.DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES;
    
    /**
     * 对象映射器:基于jackson将Java对象转为json,或者将json转为Java对象
     * 将JSON解析为Java对象的过程称为 [从JSON反序列化Java对象]
     * 从Java对象生成JSON的过程称为 [序列化Java对象到JSON]
     */
    public class JacksonObjectMapper extends ObjectMapper {
    
        public static final String DEFAULT_DATE_FORMAT = "yyyy-MM-dd";
        //public static final String DEFAULT_DATE_TIME_FORMAT = "yyyy-MM-dd HH:mm:ss";
        public static final String DEFAULT_DATE_TIME_FORMAT = "yyyy-MM-dd HH:mm";
        public static final String DEFAULT_TIME_FORMAT = "HH:mm:ss";
    
        public JacksonObjectMapper() {
            super();
            //收到未知属性时不报异常
            this.configure(FAIL_ON_UNKNOWN_PROPERTIES, false);
    
            //反序列化时,属性不存在的兼容处理
                      this.getDeserializationConfig().withoutFeatures(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);
    
            SimpleModule simpleModule = new SimpleModule()
                    .addDeserializer(LocalDateTime.class, new LocalDateTimeDeserializer(DateTimeFormatter.ofPattern(DEFAULT_DATE_TIME_FORMAT)))
                    .addDeserializer(LocalDate.class, new LocalDateDeserializer(DateTimeFormatter.ofPattern(DEFAULT_DATE_FORMAT)))
                    .addDeserializer(LocalTime.class, new LocalTimeDeserializer(DateTimeFormatter.ofPattern(DEFAULT_TIME_FORMAT)))
                    .addSerializer(LocalDateTime.class, new LocalDateTimeSerializer(DateTimeFormatter.ofPattern(DEFAULT_DATE_TIME_FORMAT)))
                    .addSerializer(LocalDate.class, new LocalDateSerializer(DateTimeFormatter.ofPattern(DEFAULT_DATE_FORMAT)))
                    .addSerializer(LocalTime.class, new LocalTimeSerializer(DateTimeFormatter.ofPattern(DEFAULT_TIME_FORMAT)));
    
            //注册功能模块 例如,可以添加自定义序列化器和反序列化器
            this.registerModule(simpleModule);
        }
    }
    

Swagger

Knife4j是为Java MVC框架集成Swagger生成Api文档的增强解决方案。

使用步骤

  1. 导入Knifeej的maven坐标:

    <dependency>
        <groupId>com.github.xiaoymin</groupId>
        <artifactId>knife4j-spring-boot-starter</artifactId>
        <version>3.0.2</version>
    </dependency>
    
  2. 在配置类中加入knife4j相关配置

    //WebMvcConfiguration.java中
    @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;
    }
    
  3. 设置静态资源映射,否则接口文档页面无法访问

    //WebMvcConfiguration.java中
    
    /**
    * 设置静态资源映射
    * @param registry
    */
    protected void addResourceHandlers(ResourceHandlerRegistry registry) {
        log.info("开始设置静态资源映射...");
        registry.addResourceHandler("/doc.html").addResourceLocations("classpath:/META-INF/resources/");
        registry.addResourceHandler("/webjars/**").addResourceLocations("classpath:/META-INF/resources/webjars/");
    }
    
  4. 访问文档接口:访问路径http://ip:port/doc.html

YAPI 与 swagger对比

  • Yapi 是在设计阶段使用的工具,用于管理和维护接口
  • Swagger是在开发阶段使用的框架,用于帮助后端开发人员做后端的接口测试

swagger常用注解

注解 说明
@Api 用在类上,例如controller类
@ApiModel 用在pojo上,例如entity,DTO,VO,表示对pojo类的说明
@ApiModelProperty 用在属性上,描述属性信息
@ApiOperation 用在方法上,例如controller的方法,说明方法的用途和作用

ThreadLocal

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

  • ThreadLocal为每个线程提供单独一份存储空间,具有线程隔离的效果,只有在线程内才能获取到对应的值,线程外则不能访问。

    (客户端每次发起请求,后端的Tomcat服务器都会分配一个单独的线程来处理请求)

ThreadLocal 常用方法

方法 说明
public void set(T value) 设置当前线程的线程局部变量值
public T get() 返回当前线程对应的线程局部变量的值
public void remove() 移除当前线程的线程局部变量

例如:

//BaseContext.java中
public class BaseContext {
    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();
    }
}

公共字段自动填充

例如:

  • create_time
  • create_user
  • update_time
  • update_user

这些字段在多处被执行相同的操作,导致代码冗余,不便于后期维护

实现思路

枚举,注解,AOP,反射

  1. 自定义注解 AutoFill,用于表示需要进行公共字段自动填充的方法
  2. 自定义切面类AutoFillAspect,统一拦截加入了AutoFill 注解的方法,通过反射为公共字段赋值。
  3. 在Mapper的方法上加入AutoFill注解

编码实现

  • 自定义注解AutoFill

    @Target(ElementType.METHOD)
    @Retention(RetentionPolicy.RUNTIME)
    public @interface AutoFill {
        //数据库操作类型:UPDATE INSERT
        OperationType value();
    }
    
  • 自定义切面AutoFillAspect

    @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) {
              //为四个公共字段赋值
              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) {
                  e.printStackTrace();
              }
      
          } else if (operationType == OperationType.UPDATE) {
              //为两个公共字段赋值
              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) {
                  e.printStackTrace();
              }
          }
          }
      }
    
    • 在Mapper方法上加入AutoFill注解

      //CategoryMapper.java
      @Insert("insert into category(type, name, sort, status, create_time, update_time, create_user, " + 
              "update_user) VALUES (#{type}, #{name}, #{sort}, #{status}, #{createTime}, #{updateTime}, " + 
              "#{createUser}, #{updateUser})")
      @AutoFill(OperationType.INSERT)
      void insert(Category category);
      
      @AutoFill(OperationType.UPDATE)
      void update(Category category);
      

    HttpClient

    HttpClient 可以用来提供高效的,最新的,功能丰富的HTTP协议的客户端编程工具,并且支持http协议最新的版本和协议

    • Maven坐标

      <dependency>
          <groupId>org.apache.httpcomponents</groupId>
          <artifactId>httpclient</artifactId>
          <version>4.5.13</version>
      </dependency>
      
    • 核心API:HttpClient、HttpClients、CloseableHttpClient、HttpGet、HttpPost

    • 发送请求步骤:

      • 创建HttpClient对象
      • 创建Http请求对象
      • 调用HttpClient的execute方法发送请求
  • Get请求

    //http客户端对象,可以发送http请求
    CloseableHttpClient httpClient = HttpClients.createDefault();
    
    //构造Get方式请求
    HttpGet httpGet = new HttpGet("http://localhost:8080/user/shop/status");
    //发送请求
    CloseableHttpResponse response = httpClient.execute(httpGet);
    
    //http响应码
    int statusCode = response.getStatusLine().getStatusCode();
    
    //http响应体
    HttpEntity entity = response.getEntity();
    //将响应体转为String字符串
    String body = EntityUtils.toString(entity);
    System.out.println(body);
    
    //关闭资源
    response.close();
    httpClient.close();
    
  • Post请求

    CloseableHttpClient httpClient = HttpClients.createDefault();
    //Post方式请求
    HttpPost httpPost = new HttpPost("http://localhost:8080/admin/employee/login");
    
    //构造json数据
    JSONObject jsonObject = new JSONObject();
    jsonObject.put("username","admin");
    jsonObject.put("password", "123456");
    
    //构造请求体
    StringEntity stringEntity = new StringEntity(jsonObject.toString());
    //设置请求编码
    stringEntity.setContentEncoding("utf-8");
    //设置数据类型
    stringEntity.setContentType("application/json");
    //设置当前Post请求的请求体
    httpPost.setEntity(stringEntity);
    
    //发送请求
    CloseableHttpResponse response = httpClient.execute(httpPost);
    
    //http响应码
    int statusCode = response.getStatusLine().getStatusCode();
    
    //http响应体
    HttpEntity entity = response.getEntity();
    //将响应体转为String字符串
    String body = EntityUtils.toString(entity);
    System.out.println(body);
    
    //关闭资源
    response.close();
    httpClient.close();
    

    Spring cache

    Spring cache 是一个框架,实现了基于注解的缓存功能,只需简单的加一个注解,就能实现缓存功能。

    Spring cache 提供了一层抽象,底层可以切换不同的缓存实现,例如:EHCache,Caffeine,Redis

    • Maven坐标为

      <dependency>
          <groupId>org.springframework.boot</groupId>
          <artifactId>spring-boot-starter-cache</artifactId>
          <version>2.7.3</version>
      </dependency>
      
    • 常用注解

      常用注解 说明
      @EnableCaching 开启缓存注解功能,通常加在启动类上
      @Cacheable 在方法执行前先查询缓存中是否有数据,如果有数据,则直接返回缓存数据;如果没有缓存数据,调用方法并将方法返回值放到缓存中
      @CachePut 将方法的返回值放到缓存中
      @CacheEvict 将一条或多条数据从缓存中删除
    • 用法

      • CachePut(cacheNames = "cachename", key = "#variable.property"),使用Spring Cache缓存数据时,key的生成规则为:cachename::{variable.property的值},其中key按照spEL(spring Expression Language)的规则来写,其中一种写法是目标变量.目标属性,这个"."叫做对象导航。
      • CacheEvict(cacheNames = "cachename", allEntries = true),使用allEntries = true即可删除cachename下的所有键值对。
    • 实现思路

      • 导入Spring Cache和Redis相关maven坐标。
      • 在启动类上加入@EnableCaching注解,开启缓存注解功能。
      • 在用户端接口SetmealController的 list 方法上加入@Cacheable注解。
      • 在管理端接口SetmealController的 save、delete、update、startOrStop等方法上加入CacheEvict注解。

Spring Task

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

应用场景

  • 信用卡每月还款提醒
  • 火车售票系统处理未支付订单
  • 入职纪念日为用户发送通知

只要是需要定时处理的场景都可以使用Spring task

cron表达式

  • cron表达式其实就是一个字符串,通过cron表达式可以定义任务触发的时间。
  • 构成规则:分为6或7个域,由空格分隔开,每个域代表一个含义。
  • 每个域的含义分别为:秒、分钟、小时、日、月、周、年(可选)。
  • 一般日和周不用同时指定,没指定的那个域可以使用?占位。
  • cron表达式在线生成器:https://cron.qqe2.com/。

Spring task使用步骤

  • 导入maven坐标 spring-context(Spring Task包含在spring-context中)。

  • 启动类添加注解 @EnableScheduling 开启任务调度。

  • 自定义定时任务类。

  • 具体处理逻辑,定义处理方法,方法没有返回值

    @Component
    @Slf4j
    public class OrderTask {
    
      @Autowired
      private OrderMapper orderMapper;
    
      @Scheduled(cron = "0 * * * * ?") //每分钟触发一次
      public void processTimeoutOrder() {
          LocalDateTime now = LocalDateTime.now();
          log.info("定时处理超时订单:{}", now);
    
          //处理15分钟前创建的未付款订单
          LocalDateTime time = now.minusMinutes(15);
          List<Orders> ordersList = orderMapper.getByStatusAndOrderTimeLT(Orders.PENDING_PAYMENT, time);
    
          //修改订单状态、取消时间、取消原因并批量更新
          if (ordersList != null && !ordersList.isEmpty()) {
              for (Orders orders : ordersList) {
                  orders.setStatus(Orders.CANCELLED);
                  orders.setCancelTime(now);
                  orders.setCancelReason("订单超时,自动取消");
              }
              orderMapper.updateBatch(ordersList);
          }
      }
    
      @Scheduled(cron = "0 0 1 * * ?") //每天凌晨1点触发一次
      public void processDeliveryOrder() {
          LocalDateTime now = LocalDateTime.now();
          log.info("定时处理处于派送中的订单:{}", now);
    
          //处理前一天创建的处于派送中的订单
          LocalDateTime time = now.minusHours(1);
          List<Orders> ordersList = orderMapper.getByStatusAndOrderTimeLT(Orders.DELIVERY_IN_PROGRESS, time);
    
          //修改订单状态并批量更新
          if (ordersList != null && !ordersList.isEmpty()) {
              for (Orders orders : ordersList) {
                  orders.setStatus(Orders.COMPLETED);
              }
              orderMapper.updateBatch(ordersList);
          }
      }
    }
    

WebSocket

websocket 是基于TCP的一种新的网络协议。它实现了浏览器与服务器全双工通宵。

浏览器和服务器只需要完成一次握手,两者之间就可以创建持久性的连接,并进行双向数据传输。

应用场景

  • 视频弹幕。
  • 网页聊天。
  • 体育实况更新。
  • 股票基金报价实时更新。

使用步骤

  • 编写html页面作为WebSocket客户端。

  • 导入WebSocket的maven坐标:

    <dependency>
    	<groupId>org.springframework.boot</groupId>
    	<artifactId>spring-boot-starter-websocket</artifactId>
    </dependency>
    
  • 编写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();
                }
            }
        }
    }
    
  • 编写WebSocket的配置类,注册WebSocket的服务端组件。

    @Configuration
    public class WebSocketConfiguration {
    
        @Bean
        public ServerEndpointExporter serverEndpointExporter() {
            return new ServerEndpointExporter();
        }
    
    }
    
  • 自定义与客户端通信的方法。

    @Autowired
    private WebSocketServer webSocketServer;
    
    public void paySuccess(String outTradeNo) {
        ...
        //通过WebSocket向客户端浏览器推送消息
        Map map = new HashMap();
        map.put("type", 1); //1表示来单提醒,2表示客户催单
        map.put("orderId", ordersDB.getId());
        map.put("content", "订单号:" + outTradeNo);
    
        String json = JSON.toJSONString(map);
        webSocketServer.sendToAllClient(json);
    }
    

Apache ECharts

  • Apache ECharts是一款基于Javascript的数据可视化图表库,提供直观,生动,可交互,可个性化定制的数据可视化图表。
  • 官网地址:https://echarts.apache.org/zh/index.html。
  • 使用Echarts,重点在于研究当前图表所需的数据格式。通常是需要后端提供符合格式要求的动态数据,然后响应给前端来展示图表。

java操作EXCEL

posted @ 2025-08-15 23:13  小郑[努力版]  阅读(20)  评论(0)    收藏  举报