苍穹外卖总结2(面试题)

14、Spring Cache

Spring Cache 是一个框架,实现了基于注解的缓存功能,只需要简单地加一个注解,就能实现缓存功能。在项目中用来缓存套餐

使用步骤

1. 导入依赖

使用Redis作为缓存技术,我需要到导入redis的依赖和springcache的依赖

<dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
​
<dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-cache</artifactId>
</dependency>

2. 在启动类上加入@EnableCaching注解,开启缓存注解功能

@SpringBootApplication
@EnableTransactionManagement
@Slf4j
@EnableCaching
public class SkyApplication {
    public static void main(String[] args) {
        SpringApplication.run(SkyApplication.class, args);
    }
}

3. 在相关的接口上添加注解

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

@CachePut
/**
* CachePut:将方法返回值放入缓存
* value:缓存的名称,每个缓存名称下面可以有多个key
* key:缓存的key
*/
@PostMapping
@CachePut(value = "userCache", key = "#user.id")//key的生成:userCache::1
public User save(@RequestBody User user){
    userMapper.insert(user);
    return user;
}
@Cacheable
/**
* Cacheable:在方法执行前spring先查看缓存中是否有数据,如果有数据,则直接返回缓存数据;若没有数据调用方法并将方法返回值放到缓存中
* value:缓存的名称,每个缓存名称下面可以有多个key
* key:缓存的key
*/
@GetMapping
@Cacheable(cacheNames = "userCache",key="#id")
public User getById(Long id){
    User user = userMapper.getById(id);
    return user;
}
@CacheEvict
@DeleteMapping
@CacheEvict(cacheNames = "userCache",key = "#id")//删除某个key对应的缓存数据
public void deleteById(Long id){
    userMapper.deleteById(id);
}
​
@DeleteMapping("/delAll")
@CacheEvict(cacheNames = "userCache",allEntries = true)//删除userCache下所有的缓存数据
public void deleteAll(){
    userMapper.deleteAll();
}

15、HttpClient

HttpClient作用:

  • 发送HTTP请求

  • 接收响应数据

为什么要在Java程序中发送Http请求?有哪些应用场景呢?

HttpClient应用场景:当我们在使用扫描支付、查看地图、获取验证码、查看天气等功能时

其实,应用程序本身并未实现这些功能,都是在应用程序里访问提供这些功能的服务,访问这些服务需要发送HTTP请求,并且接收响应数据,可通过HttpClient来实现。

HttpClient的核心API:

  • HttpClient:Http客户端对象类型,使用该类型对象可发起Http请求。

  • HttpClients:可认为是构建器,可创建HttpClient对象。

  • CloseableHttpClient:实现类,实现了HttpClient接口。

  • HttpGet:Get方式请求类型。

  • HttpPost:Post方式请求类型。

使用步骤(以GET方式请求为例)
  1. 导入依赖

<dependency>
    <groupId>org.apache.httpcomponents</groupId>
    <artifactId>httpclient</artifactId>
    <version>4.5.13</version>
</dependency>
  1. 创建HttpClient对象

  2. 创建Http请求对象

  3. 调用HttpClient的execute方法发送请求,接受结果

  4. 解析结果

  5. 关闭资源

在访问http://localhost:8080/user/shop/status请求时,需要提前启动项目。

测试结果:

自定义HttpClient工具类
/**
 * Http工具类
 */
public class HttpClientUtil {
​
    static final  int TIMEOUT_MSEC = 5 * 1000;
​
    /**
     * 发送GET方式请求
     * @param url
     * @param paramMap
     * @return
     */
    public static String doGet(String url,Map<String,String> paramMap){
        // 创建Httpclient对象
        CloseableHttpClient httpClient = HttpClients.createDefault();
​
        String result = "";
        CloseableHttpResponse response = null;
​
        try{
            URIBuilder builder = new URIBuilder(url);
            if(paramMap != null){
                for (String key : paramMap.keySet()) {
                    builder.addParameter(key,paramMap.get(key));
                }
            }
            URI uri = builder.build();
​
            //创建GET请求
            HttpGet httpGet = new HttpGet(uri);
​
            //发送请求
            response = httpClient.execute(httpGet);
​
            //判断响应状态
            if(response.getStatusLine().getStatusCode() == 200){
                result = EntityUtils.toString(response.getEntity(),"UTF-8");
            }
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            try {
                response.close();
                httpClient.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
​
        return result;
    }
​
    /**
     * 发送POST方式请求
     * @param url
     * @param paramMap
     * @return
     * @throws IOException
     */
    public static String doPost(String url, Map<String, String> paramMap) throws IOException {
        // 创建Httpclient对象
        CloseableHttpClient httpClient = HttpClients.createDefault();
        CloseableHttpResponse response = null;
        String resultString = "";
​
        try {
            // 创建Http Post请求
            HttpPost httpPost = new HttpPost(url);
​
            // 创建参数列表
            if (paramMap != null) {
                List<NameValuePair> paramList = new ArrayList();
                for (Map.Entry<String, String> param : paramMap.entrySet()) {
                    paramList.add(new BasicNameValuePair(param.getKey(), param.getValue()));
                }
                // 模拟表单
                UrlEncodedFormEntity entity = new UrlEncodedFormEntity(paramList);
                httpPost.setEntity(entity);
            }
​
            httpPost.setConfig(builderRequestConfig());
​
            // 执行http请求
            response = httpClient.execute(httpPost);
​
            resultString = EntityUtils.toString(response.getEntity(), "UTF-8");
        } catch (Exception e) {
            throw e;
        } finally {
            try {
                response.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
​
        return resultString;
    }
​
    /**
     * 发送POST方式请求
     * @param url
     * @param paramMap
     * @return
     * @throws IOException
     */
    public static String doPost4Json(String url, Map<String, String> paramMap) throws IOException {
        // 创建Httpclient对象
        CloseableHttpClient httpClient = HttpClients.createDefault();
        CloseableHttpResponse response = null;
        String resultString = "";
​
        try {
            // 创建Http Post请求
            HttpPost httpPost = new HttpPost(url);
​
            if (paramMap != null) {
                //构造json格式数据
                JSONObject jsonObject = new JSONObject();
                for (Map.Entry<String, String> param : paramMap.entrySet()) {
                    jsonObject.put(param.getKey(),param.getValue());
                }
                StringEntity entity = new StringEntity(jsonObject.toString(),"utf-8");
                //设置请求编码
                entity.setContentEncoding("utf-8");
                //设置数据类型
                entity.setContentType("application/json");
                httpPost.setEntity(entity);
            }
​
            httpPost.setConfig(builderRequestConfig());
​
            // 执行http请求
            response = httpClient.execute(httpPost);
​
            resultString = EntityUtils.toString(response.getEntity(), "UTF-8");
        } catch (Exception e) {
            throw e;
        } finally {
            try {
                response.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
​
        return resultString;
    }
    private static RequestConfig builderRequestConfig() {
        return RequestConfig.custom()
                .setConnectTimeout(TIMEOUT_MSEC)
                .setConnectionRequestTimeout(TIMEOUT_MSEC)
                .setSocketTimeout(TIMEOUT_MSEC).build();
    }
​
}
HttpClient应用

苍穹外卖项目中HttpClient的应用

/**
* 调用微信接口服务,获取微信用户的openid
* @param code
* @return
*/
private String getOpenid(String code){
    //调用微信接口服务,获得当前微信用户的openid
    Map<String, String> map = new HashMap<>();
    map.put("appid",weChatProperties.getAppid());
    map.put("secret",weChatProperties.getSecret());
    map.put("js_code",code);
    map.put("grant_type","authorization_code");
    String json = HttpClientUtil.doGet("https://api.weixin.qq.com/sns/jscode2session", map); 
    // HttpClientUtil工具类已写好 封装好了相关HttpClient函数
​
    JSONObject jsonObject = JSON.parseObject(json);
    String openid = jsonObject.getString("openid");
    return openid;
}

16、微信登录

微信登录的核心是通过微信小程序提供的临时凭证code换取永久凭证openid的过程

  1. 小程序端,调用wx.login()获取code,就是授权码。

  2. 小程序端,调用wx.request()发送请求并携带code,请求开发者服务器(自己编写的后端服务)。

  3. 开发者服务端,通过HttpClient向微信接口服务发送请求,并携带appId+appsecret+code三个参数。

  4. 开发者服务端,接收微信接口服务返回的opendId。判断该用户是否新用户

  5. 开发者服务端,将openid等数据返回给小程序端,方便后绪请求身份校验。

  6. 后面的验证和前面用户验证一样了

17、Spring Task

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

  • 定位:定时任务框架

  • 作用:定时自动执行某段Java代码

  • cron表达式: 其实就是一个字符串,通过cron表达式可以定义任务触发的时间

Spring Task使用步骤
  1. 导入依赖spring-context(在本项目中包含在spring-boot-starter)

  2. 启动类添加注解@EnableScheduling(用于开启任务调度)

  3. 自定义启动类(在其中添加方法用于实现基础功能)

在本项目中学习SpringTask用来解决:
  • 用户下单后,一直没有支付。超过我们设置的支付时限15分钟

    • 解决方法:定时任务每分钟检查一次

  • 一些订单用户已经收到,订单一直处于“派送中”。我们需要将这些订单改为“已完成”

    • 解决方法:定时任务每天凌晨检查一次

使用步骤

1. 启动类添加注解

@SpringBootApplication
@EnableTransactionManagement
@Slf4j
@EnableCaching
@EnableScheduling//用于开启任务调度
public class SkyApplication {
    public static void main(String[] args) {
        SpringApplication.run(SkyApplication.class, args);
        log.info("server started");
    }
}

2. 自定义定时任务类

/**
 * 自定义定时任务,实现订单状态定时处理
 */
@Component
@Slf4j
public class OrderTask {
    /**
     * 定时任务 每隔5秒触发一次
     */
    @Scheduled(cron = "0/5 * * * * ?")
    public void executeTask(){
        log.info("定时任务开始执行:{}",new Date());
    }
}

3. 测试

启动服务,查看日志

每隔5秒执行一次。

18、WebSocket

一种新的网络协议。类似于电话通信。实现的功能:客户催单、来单提醒

实现步骤

1. 导入maven坐标

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-websocket</artifactId>
</dependency>

2. 定义WebSocket服务端组件(资料中已提供)

/**
 * 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();
            }
        }
    }
​
}

3. 定义配置类,注册WebSocket的服务端组件(从资料中直接导入即可)

/**
 * WebSocket配置类,用于注册WebSocket的Bean
 */
@Configuration
public class WebSocketConfiguration {
​
    @Bean
    public ServerEndpointExporter serverEndpointExporter() {
        return new ServerEndpointExporter();
    }
​
}

4. 定义定时任务类,定时向客户端推送数据(从资料中直接导入即可)

@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()));
    }
}

19、Apache ECharts

  • 营业额统计

  • 用户统计

  • 订单统计

  • 销量排名Top10

20、Apache POI

实现导出excel报表

依赖

<dependency>
    <groupId>org.apache.poi</groupId>
    <artifactId>poi</artifactId>
    <version>3.16</version>
</dependency>
<dependency>
    <groupId>org.apache.poi</groupId>
    <artifactId>poi-ooxml</artifactId>
    <version>3.16</version>
</dependency>
代码开发

1. 基于POI向Excel文件写入数据

public class POITest {
​
    /**
     * 基于POI向Excel文件写入数据
     * @throws Exception
     */
    public static void write() throws Exception{
        //在内存中创建一个Excel文件对象
        XSSFWorkbook excel = new XSSFWorkbook();
        //创建Sheet页
        XSSFSheet sheet = excel.createSheet("itcast");
​
        //在Sheet页中创建行,0表示第1行
        XSSFRow row1 = sheet.createRow(0);
        //创建单元格并在单元格中设置值,单元格编号也是从0开始,1表示第2个单元格
        row1.createCell(1).setCellValue("姓名");
        row1.createCell(2).setCellValue("城市");
​
        XSSFRow row2 = sheet.createRow(1);
        row2.createCell(1).setCellValue("张三");
        row2.createCell(2).setCellValue("北京");
​
        XSSFRow row3 = sheet.createRow(2);
        row3.createCell(1).setCellValue("李四");
        row3.createCell(2).setCellValue("上海");
​
        FileOutputStream out = new FileOutputStream(new File("D:\\itcast.xlsx"));
        //通过输出流将内存中的Excel文件写入到磁盘上
        excel.write(out);
​
        //关闭资源
        out.flush();
        out.close();
        excel.close();
    }
    public static void main(String[] args) throws Exception {
        write();
    }
}

2. 实现效果

在D盘中生成itcast.xlsx文件,创建名称为itcast的Sheet页,同时将内容成功写入。

1. 基于POI读取Excel文件

package com.sky.test;
​
public class POITest {
    /**
     * 基于POI读取Excel文件
     * @throws Exception
     */
    public static void read() throws Exception{
        FileInputStream in = new FileInputStream(new File("D:\\itcast.xlsx"));
        //通过输入流读取指定的Excel文件
        XSSFWorkbook excel = new XSSFWorkbook(in);
        //获取Excel文件的第1个Sheet页
        XSSFSheet sheet = excel.getSheetAt(0);
​
        //获取Sheet页中的最后一行的行号
        int lastRowNum = sheet.getLastRowNum();
​
        for (int i = 0; i <= lastRowNum; i++) {
            //获取Sheet页中的行
            XSSFRow titleRow = sheet.getRow(i);
            //获取行的第2个单元格
            XSSFCell cell1 = titleRow.getCell(1);
            //获取单元格中的文本内容
            String cellValue1 = cell1.getStringCellValue();
            //获取行的第3个单元格
            XSSFCell cell2 = titleRow.getCell(2);
            //获取单元格中的文本内容
            String cellValue2 = cell2.getStringCellValue();
​
            System.out.println(cellValue1 + " " +cellValue2);
        }
​
        //关闭资源
        in.close();
        excel.close();
    }
​
    public static void main(String[] args) throws Exception {
        read();
    }
}

2. 实现效果

将itcast.xlsx文件中的数据进行读取

项目是如何进行异常处理的

在我们的项目中,异常处理都是通过spring的全局异常处理器来实现的,核心是两个注解:

一个是@RestControllerAdvice,表示该类为全局异常处理类 一个是@ExceptionHandler,表明方法处理哪些异常

/**
 * 全局异常处理器,处理项目中抛出的业务异常
 */
@RestControllerAdvice//定义类为异常处理类
@Slf4j
public class GlobalExceptionHandler {
​
    /**
     * 捕获业务异常
     * @param ex
     * @return
     * @ExceptionHandler(Exception.class)//标明方法处理哪些异常
     */
    @ExceptionHandler
    public Result exceptionHandler(BaseException ex){
        log.error("异常信息:{}", ex.getMessage());
        return Result.error(ex.getMessage());
    }
​
    /**
    * 如果出现SQLIntegrityConstraintViolationException
    * 就会调用该方法
    */
    @ExceptionHandler
    public Result exceptionHandler(SQLIntegrityConstraintViolationException ex){
        //Duplicate entry 'likeyou2' for key 'employee.idx_username'
        String msg = ex.getMessage();
        if(msg.contains("Duplicate entry")){
            String[] split = msg.split(" ");
            String username = split[2];
            String ans = username+ MessageConstant.ALREADY_EXISTS;
            return Result.error(ans);
        }else{
            return Result.error(MessageConstant.UNKNOWN_ERROR);
        }
    }
}

项目是如何进行参数校验的

使用Validation

  • @Null 可以标注在任意类型元素上,被标注的元素必须为null
  • @NotEmpty 可以标注 在字符串,集合,数组,map上,被标注的元素必须不能为 null ,也不能是空串
  • @Range 标注在字符串和数值的大小必须在指定的范围内,对于null无效
  • @Digits(integer(数值的位数) =3 , fraction(小数的位数)=2)
  • @size(min=,max=) 可以标注在字符串,数组,集合,map用于控制长度
  • @Email 邮箱
  • @URL 合法的地址

项目如何设置时间格式的?

法1:在实体类上添加@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")

法2:使用对象映射器

这下面是我写在简历上面的,我这个写的不好看到的,能不能给提个建议?

  1. 使用JWT令牌技术,用自定义的拦截器完成用户认证,通过ThreadLocal优化逻辑
  2. 使用Nginx部署反向代理和负载均衡
  3. 使用Swagger实现接口文档的自动生成
  4. 使用AOP思想,实现公共字段自动填充
  5. 使用Redis将店铺营业状态和菜品套餐进行缓存,提高开发效率,其中使用SpringCache简化开发
  6. 通过HttpClient向微信接口服务发送请求,实现微信登陆
  7. 使用Spring Task定时任务框架,实现订单功能代码的完善
  8. 使用WebSocket实现来单提醒和客户催单业务
  9. 基于Apache ECharts在工作台展示指定时间段内的数据统计
  10. 使用Apache POI导出excel表格
苍穹外卖项目可以使用Postman进行API接口的测试和调试。Postman是一款常用的API开发工具,它可以帮助开发人员发送HTTP请求并查看响应结果,方便进行接口的测试和调试。 在苍穹外卖项目中,可以使用Postman发送各种类型的HTTP请求,比如GET、POST、PUT、DELETE等,来模拟用户操作和测试接口功能。通过Postman,可以验证接口的正确性、查看接口返回的数据、调试接口的参数等。 为了使用Postman进行苍穹外卖项目的接口测试,您需要以下步骤: 1. 下载并安装Postman:您可以从Postman官网(https://www.postman.com/)上下载并安装适合您的操作系统的版本。 2. 打开Postman并创建一个新的请求:打开Postman应用,在界面上选择"New"来创建一个新的请求。 3. 输入接口URL和选择请求方法:在新建请求的界面中,输入苍穹外卖项目的接口URL,并选择适当的请求方法,比如GET或POST。 4. 添加请求参数和请求头:根据需要,您可以添加请求参数和请求头,以便于模拟不同的请求情况。 5. 发送请求并查看响应:点击发送按钮,Postman会向服务器发送请求,并在界面上显示响应结果。您可以查看接口返回的数据、响应状态码等信息。 通过以上步骤,您可以使用Postman进行苍穹外卖项目的接口测试。这样可以帮助您确保接口的正确性和稳定性,提高项目的质量和用户体验。
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值