浅谈MVC设计模式

news2024/11/20 3:19:31

1 前言

1.1 内容概要

  1. 熟悉使用JSON工具,完成Java对象(Map)和Json字符串之间的相互转换(注意提供构造器和getter/setter方法)
  • 注意事项:不管使用的是什么JSON工具,都要提供类的无参构造方法和setter/getter方法
  1. 理解MVC设计模式
  2. 理解三层架构思想
  3. 掌握MVC设计模式和三层架构的代码风格

1.2 前置知识准备

  • JSON格式
  • Servlet开发(接收参数、响应数据等)

2 JSON

JSON(JavaScript Object Notation)是一种轻量级的数据交换格式,是存储和交换文本信息的一种语法,它与XML具有相同的特性,是一种数据存储格式,却比 XML 更小、更快、 更易于人编写和阅读、更易于生成和解析。

数据交换:就是数据交互,比如网站前台和后台之间的交互,我们可以在请求报文的请求体中使用JSON字符串携带信息,在响应报文的响应体中使用JSON字符串响应信息给客户端。

同时对之前key=value传输类型数据格式的一个补充,key=value最大的弊端是无法表示出数据和数据之间的关系来

2.1 和XML比较

这样的一个层级信息

  • 中国
    • 黑龙江
      • 哈尔滨
      • 大庆
    • 广东
      • 广州
      • 深圳
      • 珠海
    • 台湾
      • 台北
      • 高雄
    • 新疆
      • 乌鲁木齐
  1. 最外围要么是{} -> 对象、Map
    要么是[] -> 数组、集合类对象(比如List)
  2. key要有双引号
  3. key和value之间要有:
  4. value值的格式
    a. 对象或Map => {}
    b. 数组或集合类对象 => []
    c. 字符串 => “”
    d. 基本类型或对应的包装类 => 直接写
  5. 多个key之间使用逗号分隔开
{
 "name":"中国",
 "provinces":[
   {"name":"黑龙江","cities":["黑龙江","大庆"]},
   {"name":"广东","cities":["广州","深圳","珠海"]},
   {"name":"台湾","cities":["台北","高雄"]},
   {"name":"新疆","cities":["乌鲁木齐"]}
 ]
}

xml文件提供的信息

<?xml version="1.0"encoding="utf-8"?>
<country>
    <name>中国</name>
    <province>
        <name>黑龙江</name>
        <cities>
            <city>哈尔滨</city>
            <city>大庆</city>
        </cities>
    </province>
    <province>
        <name>广东</name>
        <cities>
            <city>广州</city>
            <city>深圳</city>
            <city>珠海</city>
        </cities>
    </province>
    <province>
        <name>台湾</name>
        <cities>
            <city>台北</city>
            <city>高雄</city>
        </cities>
    </province>
    <province>
        <name>新疆</name>
        <cities>
            <city>乌鲁木齐</city>
        </cities>
    </province>
</country>

使用JSON来提供信息

  • 前端中的JS对象

    var country = {name:"中国",province:[{name:"黑龙江", cities:["哈尔滨","大庆"]}]}
    
  • JSON字符串

    {
         "name":"中国",
         "province":[{"name":"黑龙江",”cities”:["哈尔滨","大庆"]},
                     {"name":"广东","cities":["广州","深圳","珠海"]},
                     {"name":"台湾","cities":["台北","高雄"]},
                     {"name":"新疆","cities":["乌鲁木齐"]}
                    ]
     }
    

我们在开发过程中主要使用的就是JSON字符串

相较于xml → 更小、更快、可读性更高、更易解析

2.2 常用Json解析

  • fastjson是阿里巴巴的开源JSON解析库
  • Gson是Google提供的JSON解析库
  • Jackson是SpringBoot默认序列化JSON解析库

性能方面,Jackson和FastJson差距很小,Jackson是SpringBoot默认的序列化库,也是最稳定的一个,FastJson由于频繁被曝出漏洞且作者没有那么多精力维护,所以默认序列方式还是选择Jackson最好。

分别对应的依赖

<!--Gson-->
<dependency>
    <groupId>com.google.code.gson</groupId>
    <artifactId>gson</artifactId>
    <version>2.8.9</version>
</dependency>
<!--fastjson-->
<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>fastjson</artifactId>
    <version>1.2.73</version>
</dependency>
<!--jackson-->
<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-databind</artifactId>
    <version>2.13.3</version>
</dependency>

我们使用一下Jackson来完成JSON转换

2.3 Jackson常规使用

首先实例化一个Jackson中用来做序列化的对象ObjectMapper

ObjectMapper objectMapper = new ObjectMapper();

然后使用ObjectMapper提供的方法完成转换

方法名参数返回值说明
writeValueAsString(Object object):StringObject:被转换的对象String:转换的结果将Object转换为JSONString
readValue(String content,Class<T> valueType):TString content:被转换的字符串;Class<T> valueType:指定接收返回值的类型泛型:在第二个参数被指定的类型将JSONString

接着构建一个场景:提供一个User对象,体现出这几项信息

姓名,密码,年龄,爱好(多个),房产(多个)

我们定义的User类如下

/**
 * @Data提供getter/setter方法
 */
@Data
public class User {
    String username;
    String password;
    Integer age;
    String[] hobbies;
    List<House> houseList; 
}
/**
 * @AllArgsConstructor 提供有参构造方法的同时提供无参构造方法
 * @NoArgsConstructor 提供无参构造方法
 */
@Data
@AllArgsConstructor
@NoArgsConstructor
public class House {
    String location;
    Double price;
}

接收初始化一个User实例,用来做接下来的转换

private static User user = new User();
static {
    user.setUsername("李雷");
    user.setPassword("123456");
    user.setAge(25);
    user.setHobbies(new String[]{"唱","跳","RAP","篮球"});
    List<House> houses = new ArrayList<>();
    houses.add(new House("软件新城C13二楼", 1_000_000.0));
    houses.add(new House("软件新城C13四楼", 1_250_000.0));
    user.setHouseList(houses);
}

接着使用的转换代码如下

public static void main(String[] args) throws JsonProcessingException {
    // 将user实例转换为JSON字符串
    String userJsonString = objectMapper.writeValueAsString(user);
    System.out.println("JSON:" + userJsonString);
	// 将JSON字符串转换为user实例
    User user = objectMapper.readValue(userJsonString, User.class);
    System.out.println("toString:" + user);
}

打印结果如下

JSON:{"username":"李雷","password":"123456","age":25,"hobbies":["唱","跳","RAP","篮球"],"houseList":[{"location":"软件新城C13二楼","price":1000000.0},{"location":"软件新城C13四楼","price":1250000.0}]}
toString:User(username=李雷, password=123456, age=25, hobbies=[,, RAP, 篮球], houseList=[House(location=软件新城C13二楼, price=1000000.0), House(location=软件新城C13四楼, price=1250000.0)])

注意:一定要增加上无参构造和getter/setter方法

2.4 指定日期格式

比如我们在User中增加一个成员变量Date birthday,我们重新完成转换

@Data
public class User {
    String username;
    String password;
    Integer age;
    String[] hobbies;
    List<House> houseList;
    Date birthday;
}
user.setBirthday(new Date());

转换

String userJsonString = objectMapper.writeValueAsString(user);
System.out.println("JSON:" + userJsonString);
{
    "username":"李雷",
    "password":"123456",
    "age":25,
    "hobbies":["唱","跳","RAP","篮球"],
    "houseList":[
        {"location":"软件新城C13二楼","price":1000000.0},
        {"location":"软件新城C13四楼","price":1250000.0}
    ],
    "birthday":1678762314011
}

大家可以看到的birthday的值是时间戳,我们通常看到的一些时间的话,通常看到是“2015-09-27”,“2009-03-26 15:26:32”之类的,也是针对于birthday这样的Date类型的成员变量,我们在值转换过程中可以指定格式

然后在初始化的时候增加以下代码

objectMapper.setDateFormat(new SimpleDateFormat("yyyy-MM-dd"));
{"username":"李雷","password":"123456","age":25,"hobbies":["唱","跳","RAP","篮球"],"houseList":[{"location":"软件新城C13二楼","price":1000000.0},{"location":"软件新城C13四楼","price":1250000.0}],"birthday":"2022-03-14"}

3 MVC设计模式

3.1 场景分析

先抛开MVC不谈,我们给大家来看一下这样一个jsp(里面的内容和html有些像)

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
</head>
<body>
    hello,来自${student.clazz}的${student.name}
</body>
</html>

当我们访问的时候,http://localhost:8080/student.jsp,然后我们看到的是这样的一个页面

在这里插入图片描述

出现${}位置的内容都是空白的,然后我们开发了如下Servlet

@WebServlet("/student")
public class StudentServlet extends HttpServlet {

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        String name = req.getParameter("name");
        Student student = StudentHolder.getStudentMap().get(name);

        req.setAttribute("student",student);
        req.getRequestDispatcher("/student.jsp").forward(req,resp);
    }
}

同时也提供了StudentHolder存储了一些学生数据

public class StudentHolder {

    private static Map<String, Student> studentMap = new HashMap<>();
    static {
        studentMap.put("lilei",new Student("李雷","三年二班"));
        studentMap.put("hanmeimei",new Student("韩梅梅","三年二班"));
        studentMap.put("liziming",new Student("李子明","三年六班"));
    }

    public static Map<String, Student> getStudentMap() {
        return studentMap;
    }
}

访问的请求:

  • http://localhost:8080/student?name=lilei
  • http://localhost:8080/student?name=hanmeimei
  • http://localhost:8080/student?name=liziming

在这里插入图片描述

分析上面的流程:

  1. 当我们发送的请求的URI为/student的时候,我们进入到StudentServlet中处理全部的业务
  2. 在Servlet的方法中,查询了student学生信息
  3. 将student信息放在Request域中,和转发的请求共享
  4. 在student.jsp中渲染Request域中的student信息

继续分析(和上面的1234对应来看):

  1. 这个Servlet可以称之为控制器 → Controller
  2. 查询student信息就是处理数据逻辑 → Model ;另外这部分工作是在Servlet(Controller)中进行的
  3. 将数据和jsp共享 → 在Controller中将Model封装的数据准备就绪
  4. Servlet(Controller)设置转发的jsp(视图View),并且在jsp(视图)中渲染出Model提供的信息

3.2 MVC介绍

三个核心部件:

  • 模型(Model)应用程序中用于处理应用程序数据逻辑的部分
  • 视图(View)应用程序中处理数据显示的部分,就是页面的展示,采集用户数据
  • 控制器(Controller)应用程序中处理用户交互的部分。接收用户端的请求,指的是Servlet的功能,根据界面传递过来不同的值进行不同的增删改查操作之后再跳转到不同的界面显示。做一个承上启下的作用。

强制性地使应用程序的输入、处理和输出分开。它们各自处理自己的任务。最典型的MVC就是JSP + Servlet + JavaBean的模式。

MVC其实说的就是一个事情:解耦。

可以这样子理解,当我们通过客户端向服务器发送请求,

  1. 我们请求的所有处理都是在控制器Controller中,在控制器中完成全部的请求处理
  2. 控制器Controller中,首先通过 模型Model 的处理获取我们最终要呈现的数据。举个例子,如果是学生信息查询,那么Model做的就是查询学生信息;如果做的是订单查询,Model查询的就是订单数据
  3. 控制器Controller中,做视图页面View 和 模型Model查询出来的数据的连接,最终在View中加载Model中封装的信息

对应流程图

在这里插入图片描述

3.3 前后端分离

前端技能3要素:HTML、CSS、JS

通过js向后端发起Ajax异步请求,然后请求到Server服务器,找到对应的控制器,由控制器和Model层和View做交互,最终服务器处理的结果以Json的形式交给JS,JS可以直接解析这个Json对象,在前端页面上呈现最终的效果

在这里插入图片描述

这时候大家可能会有疑问,这还是MVC吗?

是,服务器中承担的 MC + 0.5V ,还有前端也承担了一部分V,这样子前后端可以同步开发了,而前后端之间通信的载体是JSON

也就是我们在Servlet中完成开发之后,响应体中响应的是Json字符串

3.4 案例

3.4.1 需求

我们来开发一个这样的接口

请求相关信息

请求URL:http://localhost:8084/auth/account/check

请求方法:POST

请求参数:请求参数是JSON字符串

{"userAccount":"admin123"}

业务:传入的用户名信息,然后完成一些业务,需要在数据库user_t表中根据用户名查询id信息

  • 如果用户名长度小于6,响应一段JSON数据

  • {
        "data":null,
        "errmsg":"字符串长度至少6位",
        "errno":400
    }
    
  • 如果用户不存在,响应一段JSON数据

  • {
        "data":null,
        "errmsg":"用户不存在",
        "errno":502
    }
    
  • 如果用户存在,响应一段JSON数据

  • {
        "data":null,
        "errmsg":"用户存在",
        "errno":200
    }
    
DROP TABLE IF EXISTS `user_t`;
CREATE TABLE `user_t`  (
  `id` int(11) NOT NULL,
  `account` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NULL DEFAULT NULL,
  `password` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NULL DEFAULT NULL,
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_bin ROW_FORMAT = Dynamic;

-- ----------------------------
-- Records of user_t
-- ----------------------------
INSERT INTO `user_t` VALUES (3342, 'admin123', '123');
INSERT INTO `user_t` VALUES (3343, 'lilei123', '123456');
INSERT INTO `user_t` VALUES (3344, 'hanmeimei', '667890');

3.4.2 分析操作

获得请求URI,根据最后一个/的位置做截取,获得*位置的值,根据值的不同,调用Servlet中的不同方法

@WebServlet("/auth/account/*")
public class AuthServlet extends HttpServlet {

    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        //首先获得请求URL中的*的部分,也就是resource(operation)
        String operation = getOperation(request);
        switch (operation) {
            case "/check":
                check(request, response);
                break;
        }
    }

    private void check(HttpServletRequest request, HttpServletResponse response) {
        
    }

    /**
     * 分析Request获得resource
     * @param request
     * @return
     */
    private String getOperation(HttpServletRequest request) {
        String uri = request.getRequestURI();
        //最后一个/
        int index = uri.lastIndexOf("/");
        String operation = uri.substring(index);
        return operation;
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        doGet(req, resp);
    }

}

3.4.3 解析请求参数

由于我们提交的是Json字符串请求参数,要获得字符流获得Json字符串,然后解析

引入jackson依赖,用来做Json序列化和反序列化

<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-databind</artifactId>
    <version>2.13.3</version>
</dependency>

定义Json解析的方法如下

/**
 * 解析Json字符串为Map
 */
private Map parseJson(HttpServletRequest request) throws IOException {
    // 首先获得Json字符串
    String jsonStr = request.getReader().readLine();
    ObjectMapper objectMapper = new ObjectMapper();
    return objectMapper.readValue(jsonStr,Map.class);
}

那么就可以在doGet(/doPost)分发的check方法中,获得map,然后获得其中的userAccount

private void check(HttpServletRequest request, HttpServletResponse response) throws IOException {
    Map parameterMap = parseJson(request);
    String userAccount = (String) parameterMap.get("userAccount");
}

3.4.5 用户名长度

判断用户名长度是否小于6位,如果小于6位,则响应对应的Json字符串

我们可以定义一下响应的Json数据对应的Vo类

@Data
public class BaseRespVo<T> {
    T data;
    String errmsg;
    int errno;
    public static BaseRespVo ok(){
        BaseRespVo<Object> respVo = new BaseRespVo<>();
        respVo.setErrmsg("成功");
        respVo.setErrno(0);
        return respVo;
    }
    
    public static <T> BaseRespVo<T> ok(T data){
        BaseRespVo<T> respVo = new BaseRespVo<>();
        respVo.setData(data);
        respVo.setErrmsg("成功");
        respVo.setErrno(0);
        return respVo;
    }
    public static BaseRespVo accountExist() {
        BaseRespVo vo = new BaseRespVo();
        vo.setErrno(200);
        vo.setErrmsg("用户存在");
        return vo;
    }
    
    public static BaseRespVo fail(){
        BaseRespVo<Object> respVo = new BaseRespVo<>();
        respVo.setErrmsg("失败");
        respVo.setErrno(500);
        return respVo;
    }
    
    public static BaseRespVo fail(String msg){
        BaseRespVo<Object> respVo = new BaseRespVo<>();
        respVo.setErrmsg(msg);
        respVo.setErrno(500);
        return respVo;
    }
    public static BaseRespVo fail(String msg,int number){
        BaseRespVo<Object> respVo = new BaseRespVo<>();
        respVo.setErrmsg(msg);
        respVo.setErrno(number);
        return respVo;
    }
}

那么我们根据字符串长度判断之后,进行Json的响应

private void check(HttpServletRequest request, HttpServletResponse response) throws IOException {
    ObjectMapper objectMapper = new ObjectMapper();
    Map parameterMap = parseJson(request);
    String userAccount = (String) parameterMap.get("userAccount");
    if (userAccount == null || userAccount.length() < 6) {
        BaseRespVo vo = BaseRespVo.fail("字符串长度至少6位", 400);
        String respJsonStr = objectMapper.writeValueAsString(vo);
        response.setContentType("application/json;charset=utf-8");
        response.getWriter().println(respJsonStr);
        return;
    }
}

在这里插入图片描述

3.4.6 查询信息

继续查询根据user_t中的account列的值,查询信息,如果信息不为空,则说明存在该账户;

查询,则需要整合MyBatis,提供工具类,可以获得会话或Mapper

public class MyBatisUtil {
    private static SqlSessionFactory sqlSessionFactory;
    static {
        try {
            sqlSessionFactory = new SqlSessionFactoryBuilder().build(Resources.getResourceAsStream("mybatis.xml"));
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public static SqlSession getSqlSession() {
        return sqlSessionFactory.openSession();
    }
    public static SqlSession getSqlSession(boolean autoCommit) {
        return sqlSessionFactory.openSession(autoCommit);
    }

    public static <T> T getMapper(Class<T> clazz) {
        T mapper = getSqlSession(true).getMapper(clazz);
        return mapper;
    }
}

在Mapper接口和映射文件中分别定义查询方法和sql语句

public interface UserMapper {
    UserPo selectByAccount(@Param("account") String account);
}
<mapper namespace="com.cskaoyan.mapper.UserMapper">
    <select id="selectByAccount" resultType="com.cskaoyan.model.UserPo">
        select id, account, password
        from user_t
        where account = #{account}
    </select>
</mapper>

那么在check方法中做查询如下

private void check(HttpServletRequest request, HttpServletResponse response) throws IOException {
    ObjectMapper objectMapper = new ObjectMapper();
    Map parameterMap = parseJson(request);
    String userAccount = (String) parameterMap.get("userAccount");
    if (userAccount == null || userAccount.length() < 6) {
        BaseRespVo vo = BaseRespVo.fail("字符串长度至少6位", 400);
        String respJsonStr = objectMapper.writeValueAsString(vo);
        response.setContentType("application/json;charset=utf-8");
        response.getWriter().println(respJsonStr);
        return;
    }

    UserMapper userMapper = MyBatisUtil.getMapper(UserMapper.class);
    UserPo userPo = userMapper.selectByAccount(userAccount);
    BaseRespVo vo;
    if (userPo == null) {
        vo = BaseRespVo.fail("用户不存在", 502);
    }else {
        vo = BaseRespVo.accountExist();
    }
    String respJsonStr = objectMapper.writeValueAsString(vo);
    response.getWriter().println(respJsonStr);
}

3.4.7 小结

通过这个案例我们已经完整的完成业务了,但是大家可以看到,除了UserMapper之外,其他的所有的代码都是写在UserServlet中的check方法里的,也就是所有的内容都耦合在check方法里。

M:这里的Model指使用UserMapper完成对应的查询(或增删改)

V:这里的View主要是响应Json数据

C:这里的Controller主要是check方法中,组织Model的获取和View的响应

我们开发应用程序不符合“高内聚,低耦合”的特点

为了让程序开发人员分工更明确,更专注于应用系统核心业务逻辑的分析、设计和开发,提高了开发效率,增加项目的可维护性,我们提出了三层架构

4 三层架构

三层架构这里和MVC并不是冲突的概念,而是在MVC的基础上进一步解耦,之前在Controller控制层直接调用了Model,为了后续业务上的解耦,在中间增加增加了一层业务逻辑层,在业务逻辑层中处理大部分业务

在这里插入图片描述

4.1 介绍

三层架构:表示层、业务逻辑层、数据访问层

  • 避免了表示层直接访问数据访问层,表示层只和业务逻辑层有联系,提高了数据安全性
  • 如果切换B/S、C/S架构,直接替换表示层即可,比如替换Servlet
  • 项目结构更清楚,分工明确,增加可维护性

实际在开发过程中的体现,就是控制层(Servlet)中直接调用Service,在Service中调用Dao

4.2 案例修改

那我们把前面的案例修改为三层架构

4.2.1 表示层

@WebServlet("/auth/account/*")
public class AuthServlet extends HttpServlet {
    
    private UserService userService = new UserServiceImpl();

    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        //首先获得请求URL中的*的部分,也就是resource(operation)
        String operation = getOperation(request);
        switch (operation) {
            case "/check":
                check(request, response);
                break;
        }

    }

    /**
     * 解析Json字符串为Map
     */
    private Map parseJson(HttpServletRequest request) throws IOException {
        // 首先获得Json字符串
        String jsonStr = request.getReader().readLine();
        ObjectMapper objectMapper = new ObjectMapper();
        return objectMapper.readValue(jsonStr,Map.class);
    }

    private void check(HttpServletRequest request, HttpServletResponse response) throws IOException {
        ObjectMapper objectMapper = new ObjectMapper();
        Map parameterMap = parseJson(request);
        String userAccount = (String) parameterMap.get("userAccount");
        
        BaseRespVo vo = userService.check(userAccount);
        
        String respJsonStr = objectMapper.writeValueAsString(vo);
        response.setContentType("application/json;charset=utf-8");
        response.getWriter().println(respJsonStr);
    }

    /**
     * 分析Request获得resource
     * @param request
     * @return
     */
    private String getOperation(HttpServletRequest request) {
        String uri = request.getRequestURI();
        //最后一个/
        int index = uri.lastIndexOf("/");
        String operation = uri.substring(index);
        return operation;
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        doGet(req, resp);
    }

}

在表示层中,引入Service层的实例(第4行)

在check方法中,可以获得参数

然后使用Service层的方法获得实例

然后在后面将其转换为 视图对象的Json字符串,响应给客户端

4.2.2 业务逻辑层

public class UserServiceImpl implements UserService {
    @Override
    public BaseRespVo check(String userAccount) {
        BaseRespVo vo;
        if (userAccount == null || userAccount.length() < 6) {
            vo = BaseRespVo.fail("字符串长度至少6位", 400);
            return vo;
        }

        UserMapper userMapper = MyBatisUtil.getMapper(UserMapper.class);
        UserPo userPo = userMapper.selectByAccount(userAccount);
        if (userPo == null) {
            vo = BaseRespVo.fail("用户不存在", 502);
        }else {
            vo = BaseRespVo.accountExist();
        }

        return vo;
    }
}

业务逻辑层和数据访问层交互

4.2.3 数据访问层

和MVC讲的没有变化

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

基于SpringBoot的宠物寄领养网站管理系统

作者&#xff1a;计算机学姐 开发技术&#xff1a;SpringBoot、SSM、Vue、MySQL、JSP、ElementUI等&#xff0c;“文末源码”。 专栏推荐&#xff1a;前后端分离项目源码、SpringBoot项目源码、SSM项目源码 系统展示 【前后端分离】基于JavaSpringBootVueMySQL的宠物寄领养网站…

北斗卫星系统信号介绍

覆盖范围亚太区域全球范围 卫星数量35颗区域服务卫星30颗全球服务卫星 信号频段B1I, B2IB1C, B2a, B3, 兼容GPS/Galileo 定位精度区域内10米全球2.5~5米&#xff0c;中国内更高 新增功能区域短报文通信全球短报文通信、星基增强、精密定位 抗干扰能力相对有限更强 互操作…

无人机 PX4 飞控 | 如何检测状态估计EKF性能

无人机 PX4 飞控 | 如何检测状态估计EKF性能 前言检查EKF性能缺少pyulog问题解决脚本崩溃没有输出文件生成对应文件 结语 前言 ECL &#xff08;Estimation and Control Library&#xff0c;估计和控制库&#xff09;&#xff0c;其中的状态估计使用扩展卡尔曼滤波算法&#x…

图像检测【YOLOv5】——深度学习

Anaconda的安装配置&#xff1a;&#xff08;Anaconda是一个开源的Python发行版本&#xff0c;包括Conda、Python以及很多安装好的工具包&#xff0c;比如&#xff1a;numpy&#xff0c;pandas等&#xff0c;其中conda是一个开源包和环境管理器&#xff0c;可以用于在同一个电脑…

计算机网络基本概述

欢迎大家订阅【计算机网络】学习专栏&#xff0c;开启你的计算机网络学习之旅&#xff01; 文章目录 前言一、网络的基本概念二、集线器、交换机和路由器三、互连网与互联网四、网络的类型五、互连网的组成1. 边缘部分2. 核心部分 六、网络协议 前言 计算机网络是现代信息社会…

安装node 报错需要:glibc >= 2.28

--> 解决依赖关系完成 错误&#xff1a;软件包&#xff1a;2:nodejs-18.20.4-1nodesource.x86_64 (nodesource-nodejs) 需要&#xff1a;libm.so.6(GLIBC_2.27)(64bit) 错误&#xff1a;软件包&#xff1a;2:nodejs-18.20.4-1nodesource.x86_64 (nodesource-nodej…

【数据结构篇】~排序(1)之插入排序

排序~插入排序 前言插入排序1.直接插入排序&#xff08;时间复杂度&#xff1a;O(N^2)&#xff09;1.思想2.代码 2.希尔排序(时间复杂度&#xff1a;O(N∙))1.思路简易证明希尔排序的复杂度 2.代码 前言 四大排序&#xff0c;今天解决插入排序 堆排序和冒泡排序已经写过了&am…

C++笔记---继承(上)

1. 继承的简单介绍 1.1 继承的概念 继承(inheritance)机制是面向对象程序设计使代码可以复用的最重要的手段&#xff0c;它允许我们在保持原有类特性的基础上进行扩展&#xff0c;增加方法(成员函数)和属性(成员变量)&#xff0c;这样产生新的类&#xff0c;称派生类。 继承呈…

如何利用 Smarter Balanced 塑造教育领域的 AI 治理

目录 定义挑战 以人为本的设计引领 融入多样性 探索以学生为中心的价值观 探索效果的层次和不同的影响 部位于加利福尼亚州的Smarter Balanced Assessment Consortium 是一个由会员主导的公共组织&#xff0c;为 K-12 和高等教育领域的教育工作者提供评估系统。该组织成立…

09_Tensorflow2图像处理大赏:让你的图片笑出AI感,惊艳朋友圈!

1. 图像处理案例 1.1 逆时针旋转90度 import tensorflow as tf import matplotlib.pyplot as plt import matplotlib.cm as cm import numpy import osdef show_pic(pic,name,cmapNone):显示图像plt.imshow(pic,cmapcmap) plt.axis(off) # 打开坐标轴为 on # 设置图像标题…

C语言数据类型、变量及数据类型的长度、取值范围

文章目录 一、数据类型介绍1.字符型2.整型3.浮点型4.布尔类型 二、变量1.变量的创建2.变量的分类 三、数据类型的长度(字节)1.sizeof 操作符2.各种数据类型的长度3.sizeof中表达式不计算 四、各种类型的取值范围1.signed和unsigned2.数据类型的取值范围 五、整型提升练习1练习2…

【Obsidian】当笔记接入AI,Copilot插件推荐

当笔记接入AI&#xff0c;Copilot插件推荐 自己的知识库笔记如果增加AI功能会怎样&#xff1f;AI的回答完全基于你自己的知识库余料&#xff0c;是不是很有趣。在插件库中有Copilot插件这款插件&#xff0c;可以实现这个梦想。 一、什么是Copilot&#xff1f; 我们知道githu…

el-input-number设置了min值,希望默认值展示为空

data() {return {editForm: {num: undefined, //input}} } <el-input-number v-model.trim"editForm.num" controls-position"right" :min"1" placeholder"请输入" clearable /> 展示效果如下:

C++中的左值(Lvalue)和右值(Rvalue)详解

C中的左值&#xff08;Lvalue&#xff09;和右值&#xff08;Rvalue&#xff09;详解 在C中&#xff0c;左值&#xff08;Lvalue&#xff09;和右值&#xff08;Rvalue&#xff09;的概念是理解表达式和变量的重要基础。为了提高C的性能和灵活性&#xff0c;C11引入了一些新的…

F1C100S/F1C200S的资料来源说明

文章目录 常用板子开源创客荔枝派榴莲派 我想说是的官网啥资料都没有。但是它的资料又很多&#xff0c;从淘宝或者其他地方能都搜到很多。 http://wiki.lcmaker.com/index.php?titleLC-PI-200S https://github.com/peng-zhihui/Planck-Pi?tabreadme-ov-file#head4 http://do…

时序必读论文04|Non-stationary Transformers:序列平稳性优化【NeurIPS 2022】

我们在先前的一篇文章中已经总结了直接把Transformer应用到时间序列数据存在的问题&#xff0c;其中序列平稳化是transformer也是其他很多模型都未解决好的一个不足。实际上&#xff0c;序列平稳和非平稳是矛盾的存在&#xff0c;这篇文章探索了&#xff1a;原始数据-->平稳…

python基础知识(四)--if语句,for\while循环

目录 if语句 if-else语句 if...elif...else...语句的语法 if嵌套语句语法 while循环 死循环 for循环 例题&#xff1a; 1.请使用*代替&#xff0c;并输出一个正方形的显示效果。 2.逢7必过的游戏 3.九九乘法表 4.案例: 逢七必过游戏 [0, 999] 碰到特殊天气, 150 就…

停车位检测-停车场车位识别

YOLO Parking Spot 概述 停车场获取的图像训练了四个YOLO模型来检测车辆。目标是收集信息&#xff0c;并可能开发一种停车解决方案以改善交通流量并优化空间利用率。通过识别汽车&#xff0c;我们生成了一份报告&#xff0c;其中包含图像细节&#xff0c;如可用停车位的数量、…

Ai+若依(智能售货机运营管理系统---帝可得)--工单管理【08篇---0005:工单管理】

工单管理 需求说明 工单是一种专业名词,是指用于记录、处理、跟踪一项工作的完成情况。 管理人员登录后台系统选择创建工单,在工单类型里选择合适的工单类型,在设备编号里输入正确的设备编号。 工作人员在运营管理App可以看到分配给自己的工单,根据实际情况选择接收工单并…

复现PointNext代码

一、首先第一步&#xff0c;我们就需要下载代码&#xff1a;guochengqian/PointNeXt: [NeurIPS22] PointNeXt: Revisiting PointNet with Improved Training and Scaling Strategies (github.com) 二、然后下载好了之后&#xff0c;然后最关键的一点&#xff0c;这个点进去也要…