JavaEE第一阶段

目录

学习要求:

学习过程

提问:

jsp是啥?

Servlet是啥?

filter是啥?

Ajax是啥?

Git是啥?

正式学习:

IDEA常用快捷键

Eclipse和Idea选中多行代码,同时右称,左移。

自动补全返回值,自动补全变量名称和属性名称

IDEA中自动生成get/set的方式

实现接口方法快捷键

处理异常

项目之间跳转

多光标同时编辑

反向撤销

一键格式化代碼:

JSP学习

脚本

JSP的内置对象

指令

MVC:开发模式

jsp演变历史

M:Model,模型。JavaBean

V:View,视图。JSP

C:Controller,控制器。Servlet

优点

缺点

EL表达式

概念:Expression Language 表达式语言

语法:${ 表达式 }

使用

JSTL

概念:JSP标准标签库

使用步骤:

常用的JSTL标签

三层架构:软件设计架构

Servlet:server applet

概念:运行再服务器端口的小程序

执行原理:

Servlet中的生命周期方法:

Servlet3.0

Filter:过滤器

概念:

步骤:

JDBC

步骤:

详解各个对象:

数据库连接池

概念:其实就是一个容器(集合),存放数据库链接的容器。

实现:

Spring JDBC

步骤:

AJAX:

概念:异步的JavaScript 和 XML

实现方式:JQuery

项目制作

个人博客需求分析

抽离对象

ER图

建表

写后端操作


学习要求:

第一阶段 jsp+servlet+JDBC

1、学习内容:jsp基本语法(两种注解、3个脚本元素、3个指令元素、8个动作指令),jsp九大内置对象、servlet、filter、JDBC(MySQL增删查改、索引、事务), Ajax+json,Git使用

2、时间计划:14(天)

3、实战例子:博客或管理系统

学习过程

提问:

  • jsp是啥?

JSP全称Java Server Pages,是一种动态网页开发技术。它使用JSP标签在HTML网页中插入Java代码。标签通常以<%开头以%>结束。

JSP是一种Java servlet,主要用于实现Java web应用程序的用户界面部分。网页开发者们通过结合HTML代码、XHTML代码、XML元素以及嵌入JSP操作和命令来编写JSP。

JSP通过网页表单获取用户输入数据、访问数据库及其他数据源,然后动态地创建网页。

JSP标签有多种功能,比如访问数据库、记录用户选择信息、访问JavaBeans组件等,还可以在不同的网页中传递控制信息和共享信息。

参考:https://www.runoob.com/jsp/jsp-intro.html

  • Servlet是啥?

Java Servlet 是运行在 Web 服务器或应用服务器上的程序,它是作为来自 Web 浏览器或其他 HTTP 客户端的请求和 HTTP 服务器上的数据库或应用程序之间的中间层。

使用 Servlet,您可以收集来自网页表单的用户输入,呈现来自数据库或者其他源的记录,还可以动态创建网页。

Java Servlet 通常情况下与使用 CGI(Common Gateway Interface,公共网关接口)实现的程序可以达到异曲同工的效果。但是相比于 CGI,Servlet 有以下几点优势:

  • 性能明显更好。

  • Servlet 在 Web 服务器的地址空间内执行。这样它就没有必要再创建一个单独的进程来处理每个客户端请求。

  • Servlet 是独立于平台的,因为它们是用 Java 编写的。

  • 服务器上的 Java 安全管理器执行了一系列限制,以保护服务器计算机上的资源。因此,Servlet 是可信的。

  • Java 类库的全部功能对 Servlet 来说都是可用的。它可以通过 sockets 和 RMI 机制与 applets、数据库或其他软件进行交互。

参考:https://www.runoob.com/servlet/servlet-intro.html

  • filter是啥?

Filter也称之为过滤器,它是Servlet技术中最激动人心的技术之一,WEB开发人员通过Filter技术,对web服务器管理的所有web资源:例如Jsp,

Servlet, 静态图片文件或静态html文件等进行拦截,从而实现一些特殊的功能。例如实现URL级别的权限访问控制、过滤敏感词汇、压缩响应信息等

一些高级功能。

参考:https://blog.csdn.net/reggergdsg/article/details/52821502

  • Ajax是啥?

  • AJAX 是一种在无需重新加载整个网页的情况下,能够更新部分网页的技术。

参考:https://www.w3school.com.cn/ajax/ajax_intro.asp

  • Git是啥?

Git是目前世界上最先进的分布式版本控制系统(没有之一)。

参考:https://www.liaoxuefeng.com/wiki/896043488029600/896067008724000

正式学习:

IDEA常用快捷键

Eclipse和Idea选中多行代码,同时右称,左移。

首先选中同时代码,按tab键,右移,如果要左移,按shift+tab键

自动补全返回值,自动补全变量名称和属性名称

ctrl+alt+v

IDEA中自动生成get/set的方式

alt+insert

实现接口方法快捷键

 Ctrl-I 

处理异常

alt+回车

项目之间跳转

ctrl+alt+}/{

多光标同时编辑

1. 按下滚轮上下拖动鼠标即可,

2. Shift + ctrl + alt + 鼠标左键   

反向撤销

ctrl+shift+z

一键格式化代碼:

 Ctrl+Alt+L

查看接口方法的实现方法:

 Ctrl+Alt+鼠标左键

  • JSP学习

    • 脚本

      • <% 代码 %>——定义的java代码,在service方法中,方法中可以定义的东西,脚本中都可以定义,如变量、数组、容器等等。

      • <%!代码 %>——定义的java代码,在sjp转换后的java类的成员位置(方法之外)

      • <%= 代码 %>——“=”相当于输出,会将其中的东西输出到页面上。输出语句可以写生么,他就可以写什么。

  • JSP的内置对象

    • 在jsp页面中不需要获取和创建,可以直接使用的对象。

    • 九大内置对象

变量名

真是类型

作用

pageContext

PageContext

当前页面共享数据,还可以获取其他八个内置对象

request

HttpServletRequest

一次请求访问多个资源(转发)

sesion

HttpSession

一次会话的多个请求间

application

ServletContext

所用用户间共享数据

response

HttoServletResponse

响应对象

page

Object

当前页面(Servlet)的对象 this

out

JspWritr

输出对象,将数据输出到页面

config

ServletConfig

Servlet的配置对象

exception

Throwable

异常对象

  • request

  • response

  • out——字符输出流对象,可以将数据输出到页面上,和response.getWriter()类似

    • 两者的区别:response.getWriter()会在out.write()之前输出。

  • 指令

    • 作用:用于配置JSP页面,导入资源文件

    • 格式

      • <%@ 指令名称 属性名1=属性值1 属性名2=属性值2 %>

      • 分类:

        • page :配置JSP页

          • contenType:等同于response.setContenType()

            • 设置相应体的mime类型以及字符集

            • 设置当前JSP页面的编码(只用IDE才能生效)

          • import:导包

          • errorPage:当前页面发生异常后,指定跳转的页面

          • isErrorPage:表示当前页面是否时错误页面

            • true:可以使用内置对象exception

            • false:不可以使用内置对象exception

  • include :页面包含的。导入页面的资源文件

    • <%@include file="xxx.jsp"%>

  • taglib :导入资源

    • <%@taglib prefix="前缀" uri="" %>

  • MVC:开发模式

    • jsp演变历史

      • 早期只有servlet,只能使用response输出标签数据,非常麻烦

      • 后再有了jsp,简化了Servlet的开发,但是,如果过渡使用jsp,在jsp文件里边写大量的java和html和script代码,而且相互嵌套,,,会造成,非常难于阅读,维护,很不利于写作开发

      • 再后来,java的web开发,借鉴mvc的开发模式,使得程序设计更加合理。

  • MVC:

  • M:Model,模型。JavaBean

    • 完成具体的业务操作,如:查询数据库,封装对象

  • V:View,视图。JSP

    • 展示数据

  • C:Controller,控制器。Servlet

    • 获取用户的输入

    • 调用模型

    • 将数据交给视图进行展示

  • 优点

    • 耦合性低,方便分工开发,方便维护

    • 重用性高

  • 缺点

    • 项目架构复杂,对开发人员要求高

  • EL表达式

    • 概念:Expression Language 表达式语言

    • 作用:替换和简化jsp页面中的java代码的编写

    • 语法:${ 表达式 }

    • 注意:jsp默认支持ek表达式。如果要忽略el表达式

      • 设置jsp的page指令:isELIgnored="true" 忽略当前jsp也i按的所有el表的是

      • \${表达式} :忽略当前的el表达式

    • 使用

      • 运算

        • 支持的运算符

          • 算数运算符:+ - /(也可以用div表除法)%(mod)

          • 比较运算符:> < >= <= !=

          • 逻辑运算符: &&(ang) ||(or) !(not)

          • 空运算符:empty

            • 功能:

              • ${empty list}——判断字符串、集合、数组对象是否为null或者长度是否为0

              • ${not empty list}——判断字符串、集合、数组对象是否不为null或者长度是否为>0

      • 获取值

        • el表达式只能从域对象中获取值

        • 语法:

          • ${域名称.键名}:从指定域中获取指定的值

            • 域名称:

              • pageScope --> pageContext

              • requestScope --> request

              • sessionScope --> session

              • applocationScope --> application(ServletContext)

            • 举例:在request域中存储了 name=张三

            • 获取:${requesTcope.name}

          • ${键名}:依次从最小的域总查找是否有该键对应的值,知道找到为为止

            • ${name}

          • 获取对象、List、Map集合的值

            • 对象:${域名称.键名.属性名}

              • 本质上会去调用对象的get方法

            • List集合:${域名称.键名[索引]}

            • 对象:

              • ${域名称.键名.key名称}

              • ${域名称.键名["key名称"]}

      • 隐式对象

        • el表达式中有11个隐式对象

        • pageContext:

          • 获取jsp其他8个内置对象

            • ${pageContext.request.contextPate}:动态获取虚拟目录

  • JSTL

    • 概念:JSP标准标签库

      • 由Apache组织提供的开源免费的jsp标签

    • 作用:用于简化和替换jsp页面给上的java代码

    • 使用步骤:

      • 导入jstl相关jar包

      • 引入标签库:taglib指令:<%@ taglib %>

      • 使用标签

    • 常用的JSTL标签

      • if :相当与if

        • 属性:

          • test 必须属性,接受Boolean表达式

            • 如果表达式为true,则显示if标签体德荣,反之不显示

            • 一般情况下test属性值会结合二el表达式一起使用

        • 注意:

          • c:if标签没有else情况,想要else,则可以再定义一个c:if标签

      • choose :相当于switch

        • 使用choose标签声明 相对与switch声明

        • 使用when标签做判断 相对与case

        • 使用otherwise标签做其他情况的声明 相当于default

      • foreach :相当于for

  • 三层架构:软件设计架构

    • 界面层:用户看见的界面。用户可以通过界面上的组件和服务进行交互

    • 业务逻辑层:处理业务逻辑

    • 数据访问层:操作数据存储文件

  • Servlet:server applet

    • 概念:运行再服务器端口的小程序

      • Servlet就是一个接口,定义了Java类被浏览器访问到的规则。

      • 将来自己定义一个类,实现Servlet接口,复写方法

    • 快速人们:

      • 创建JavaE项目

      • 定义一个类,实现Servlet接口

        • implements Servlet

      • 配置Servlet

        • 在xml中配置

<servlet>
	<servlet-name>demo1</servlet-name>
	<servlet-class>cn.itcast.web.servlet.ServletDemo1</servlet-class>
</servlet>
<servlet-mapping>
	<servet-name>demo1</servet-name>
	<url-pattern>/demo1</url-pattern>
</servlet-mapping>
  1. 执行原理:

    1. 当服务器接收到客户端浏览器请求后,会解析请求URL路径,获取访问的Servle的资源路径
    2. 查找web.xml文件,是否有对应的<url-pattern>标签体内容
    3. 如果有,则在找到对应的<sevlet-class>全类名
    4. tomcat会将字节码文件加载进内存,并且创建其对象
    5. 调用方法
  2. Servlet中的生命周期方法:

    1. 被创建:执行init方法,只执行一次
      1. Servlet什么时候被创建?
        1. 默认情况下,第一次访问时,Servlet被创建
        2. 可以配置执行Servlet的创建时机:在<servlet>标签下配置
          1. 第一次访问时:<load-on-startup>的值为负值
          2. 第一次访问时:<load-on-startup>的值为非负值
      2. Servlet的init方法,只执行一次,说明一个Servlet在内存中只存在一个对象,Servlet时单例的
        1. 多个用户同时访问时,可能存在线程安全问题
        2. 解决:尽量不要在Sevlet中定义成员变量。即使定义了成员变量,也不要修改值。
    2. 提供服务:执行service方法,执行多次
      1. 每次访问Servlet时,Servlet方法都会被调用一次
    3. 被销毁:执行destroy方法,只执行一次
      1. Servlet被销毁时执行。服务器关闭时,Servlet被销毁
      2. 只有服务器正常关闭,才会执行destoy方法
      3. destroy方法在Sevlet被销毁之前执行,一般用于释放资源

Servlet3.0

  1. 好处:支持注解配置,可以不需要web.xml了
  2. 步骤:
    1. 创建JavaEE项目,选择Servlet3.0以上的版本,可以不创建web.xml
    2. 定义一个类,实现Servlet接口
    3. 复写方法
    4. 在类上使用@WevServlet知识,进行配置:@WebServlet("资源路径")

Filter:过滤器

  1. 概念:

    1. web中的过滤器:当访问服务器资源时,过滤器可以将请求拦击下来,完成一些特殊的功能
    2. 过滤器的作用:
      1. 一般用于完成通用的操作。如:登录验证、同一编码处理、敏感字符过滤。。。
  2. 快速入门:
    1. 步骤:

      1. 定义一个类,实现接口Filter
      2. 复写方法
      3. 配置拦截路径
        1. web.xml
        2. 注解
    2. 过滤器细节:
      1. web.xml配置
      2. 过滤器执行流程
        1. 执行过滤器
        2. 执行放行后的资源
        3. 执行过滤器放行代码下变的代码
      3. 过滤器声明周期方法
        1. init:在服务器启动后,会创建Filter对象,然后调用init方法。只执行一次。用于加载资源
        2. doFilter:没一次请求被拦截时,都会执行
        3. destroy:在服务器关闭后,Filter对象被销毁。如果服务器是正常关闭,则会执行sestroy方法。只执行一次。用于释放资源。
      4. 过滤器配置详解
        1. 拦截路径配置:
          具体资源路: /index.jsp 只有访问index.jsp时,过滤器才会执行
          拦截目录: /user/* 访问/user下的所有资源时,过滤器会执行
          后缀名拦截: *.jsp 访问所有后缀名为jsp时,过滤器会会执行
          拦截所有资源: /* 访问所有资源时,过滤器会执行
        2. 拦截方式配置:资源被访问的方式
          1. 注释配置:设置dispatcherTypes属性
             

            REQUEST 默认值。浏览器直接请求资源
            FORWARD 转发访问资源
            INCLUDE 包含访问资源
            ERROR 错误调跳转资源
            ASYNC 异步访问资源
          2. web.xml配置

            1. 设置<dispatcher></dispatcher>标签即可

      5. 过滤器链(配置多个过滤器)
        1. 执行顺序:如果有两个过滤器:过滤器1、过滤器2
          1. 过滤器1
          2. 过滤器2
          3. 资源文件
          4. 过滤器2
          5. 过滤器1
        2. 过滤器先后顺序问题
          1. 注解配置:按照类名的字符串比较规则,值小的先执行
            1. 如:AFilter和 BFilter,AFilter就先执行了
          2. web.xml配置:<filter-mapping>谁定义在上边,谁先执行

JDBC

  1. 概念,Java数据库链接,Java语言操作数据库
    1. JDBC本质:是官方{sun公司)定义的一套操作所有关系型数据库的规则,即接口。各个数据库厂商去实现这套接口,提供数据库驱动jar包。我们可以使用这套接口(JDBC)编程,真正执行代码是jar包中的实现类。
  2. 快速入门:
    1. 步骤:

      1. 导入驱动jar包
        1. 复制驱动jar包到libs目录下
        2. 右击libs目录,选择Add As Library
      2. 注册驱动
      3. 获取数据库链接对象Connection
      4. 定义sql
      5. 获取执行sql语句的对象Statement
      6. 执行sql,接受返回结果
      7. 处理结果
      8. 释放资源
  3. 详解各个对象:

    1. DriverManager:驱动管理对象
      1. 功能:
        1. 注册驱动:告诉程序该使用哪个数据库驱动jar:Class,forName("com.mysql.jdbc.Driver");
        2. 获取数据库链接
          1. 方法: static Connection getConnection(String url, String user,Sting pawword)
          2. 参数:
            1. url:指定链接 的路径
              1. jdbc:mysql://ip地址(域名):端口号/数据库名称
              2. 例子:jdbc:mysql://localhost:3306/db3
              3. 细节:如果链接的市本级mysql夫妻,并且mysql夫妻默认端口是3306,则可以简写为:jdbc:mysql:///数据库名称
            2. user:用户名
            3. password:密码
      2. Connection:数据库链接对象
        1. 功能:
          1. 执行sql的对象
            1. Statement createStatement()
            2. PrearedStatement prepareStatement(String sql)
          2. 管理事务:
            1. 开启事务:setAutoCommit(boolean autoCommit):调用该方法设置参数为false,即开启事务
              1. 再执行sql之前开启事务
            2. 提交事务:commit()
              1. 当所有sql都执行完,提交事务
            3. 回滚事务:rollback()
              1. 在catch中回滚事务
      3. Statement:执行sql的对象
        1. 执行sql
          1. boolean excute(String sql):可以执行任意的sql语句
          2. int executeUpdat(String sql):执行DML(insert、update、delete)DDL(create、alter、drop)
            1. 返回值:影响的行数,可以通过这个影响的行数判断DML语句是否执行成功,返回值>0则执行成功,反之失败。
          3. ResultSet executeQuery(String sql):执行DQL(select)语句 
      4. ResultSet:结果集对象,封装查询结
        1. next():游标向下移动一行,并且判断当前航是否是最后一行末尾(是否有数据),如果是,则返回false,如果不是则返回true  
        2. getXxx(参数):获取数据
          1. Xxx:代表数据类型   如:  int getInt() ,   Dtring getString()
          2. 参数:
            1. int:代表列的编号  ,从1开始    如:getString(1)
            2. String:代表列的名称   如:  getDouble("balance")
        3. 注意:
          1. 使用步骤:
            1. 游标向下移动一行
            2. 判断是否有数据
            3. 获取数据
      5. PreparedStatement:执行sql的对象
        1. SQL注入问题:在拼接sql时,有一些sql的特殊关键字参与字符串的拼接。会造成安全问题
          1. 哦用户名随便输,输入密码: a' or 'a' = 'a
          2. sql:select *from user whre username = 'asdfasdf' and password = 'a' or 'a' = 'a'
        2. 解决sql注入问题:使用PrepardStatement对象来解决
        3. 预编译的SQL:参数使用?作为占位符
        4. 步骤:
          1. 导入驱动jar包
          2. 注册驱动
          3. 获取数据库链接对象Connection
          4. 定义sql——注意:sq的参数使用?作为占位符。
          5. 获取执行sql语句的对象 PreparedStatement Connection.prepareStatement(String sql)
          6. 给?复制
            1. 方法:setXxx(参数1, 参数2)
              1. 参数1:?的位置编号,从1 开始
              2. 参数2:?的值
          7. 执行sql,接受返回结果,不需要传递sql语句
          8. 处理结果
          9. 释放资源
        5. 注意:后期都会使用PerparedStatement来完成增删改查的所用操作
          1. 可以防止SQL注入
          2. 效率更高

数据库连接池

  1. 概念:其实就是一个容器(集合),存放数据库链接的容器。

    1. 当系统初始化后,容器被创建,容器中回升请一些连接对象,当用户来访问数据库时,从容器中获取链接对象,用户访问完之后,会将对象还给容器。
  2. 好处:节约资源、用户访问高效
  3. 实现:

    1. 标准接口DataSource    javax.sql包下的
      1. 方法:
        1. 获取链接:getConnection()
        2. 归还链接:Connection.close()。如果辽宁姐对象Connection是从连接池中获取的,那么调用Connection.close()方法,则不会再关闭链接了。而是归还链接。
      2. 一般偶们不去实现它,有数据库厂商实现
        1. C3P0:数据库连接池实现技术
          1. 步骤:
            1. 导入jar包:c3p0-0.9.5.2.jar、mchange-commons-java-02.12.jar、数据库启动jar包
            2. 定义配置文件
              1. 名称:c3p0.properties 或者 c3p0-config.xml
              2. 路径:直接将摁键放在src目录下即可。
            3. 创建核心对象   数据库连接池对象 CombopooledDataSource
            4. 获取链接:getConnection
        2. Druid:数据库连接池实现技术,由阿里巴巴提供的
          1. 步骤
            1. 导入jar包   druid-1.0.9.jar
            2. 定义配置文件:
              1. 是properties形式的
              2. 可以叫任意名称,可以放在任意目录下
            3. 加载配置文件。pro[erties
            4. 获取数据库连接池对象:通过工程类来获取   DruidDataSourceFactory
            5. 获取链接:getConnection

Spring JDBC

  1. Spring 框架对JDBC的简单封装。i共了一个JDBCTemplate对象简化JDC的开发
  2. 步骤:

    1. 导入jar包
    2. 创建JdbcTemplate对象。依赖于数据源DataSource
      1. JdbcTemple template = new JdbcTemple(ds);
    3. 调用JdbcTemplate的方法来进行CRUD的操作
      1. update():执行DML语句。增删改语句
        1. 插入、更改、删除:update(sql ,占位符的值 );
      2. queryForMap():查询结果封装为Mpa集合,列名为key,值为value
        1. 只能查询一条记录,
      3. queryForList():查询结果封装为List集合
        1. 查询多条记录,将每一条记录封装为Map集合,然后把所有Map集合存到List集合中
      4. query():查询结果封装为JavaBean对象的集合
        1. query的参数:RowMapper
          1. 一般我们使用BoanPropertyRowMapper 实现类。可以完成数据到JavaBean的自动封装
          2. new BoanPropertyRowMapper<类型>(类型.class)
      5. queryForObject():查询结果封装为对象
        1. //查询单个结果:参数解释——第一个为sql语句第二个为条件数组、第三个为希望返回的类
          User user = jdbcTemplate.queryForObject(sql, new Object[]{"root"}, new BeanPropertyRowMapper<User>(User.class));

AJAX:

  1. 概念:异步的JavaScript 和 XML

    1. 异步和同步:客户端和服务器端相互通信的基础上
      1. 客户端必须等待服务器的响应。在等待期间客户端不能进行其他操作。
      2. 客户端不必须等待服务器的响应。在等待期间客户端能进行其他操作。
      3. Ajax 是一种在无需重新加载整个网页的情况下,能够更新部分网页的技术。

        通过在后台与服务器进行少量数据交换,Ajax 可以使网页实现异步更新。这意味着可以在不重新加载整个网页的情况下,对网页的某部分进行更新。

        传统的网页(不使用 Ajax)如果需要更新内容,必须重载整个网页页面。
        提升用户体验

    2. 实现方式:JQuery

      1. &.ajsx()

        1. 语法:&.ajax{{键值对}};

        2. 使用&ajax()发送异步请求

          1. &ajax({
            url:"路径",
            type:"请求方式"——包括POST、GTT
            date:{"uname":"jack","age":23},
            success:function (date) {
            alert("响应后的回调函数");
            },
            error:function(){
            alert("出错执行的回调函数")
            },
            dataType:"text"——设置接受到的响应数据的格式
            });

      2. &get():发送get请求

        1. 语法:&get(url,[data],pcallback],[type])

        2. 参数:

          1. url:请求路径

          2. data:请求参数

          3. callback:回调函数

          4. type:响应结果类型

      3. &post():发送post请求

        1. 语法:&post(url,[data],pcallback],[type])

        2. 参数:

          1. url:请求路径

          2. data:请求参数

          3. callback:回调函数

          4. type:响应结果类型

项目制作

  1. 个人博客需求分析

    1. 个人信息展示,要类似这个:
      1. 也就是说只有一个用户,且可以更改自己的信息。信息包括:
        1. 账号
        2. 密码
        3. 昵称
        4. 个人资料或者个性签名
        5. 头像路径?
    2. 文章内容导航(分类、标签)类似:
    3. 文章类似:
      1. 所以文章字段应该包括
        1. id
        2. title
        3. 标签
        4. 内容
        5. 发布时间
        6. 更新时间
        7. 浏览次数
        8. 被点赞次数
        9. 评论数
    4. 这样的话,就有评论的信息了
      1. 评论时间
      2. 品评论内容
      3. 评论对应的文章

抽离对象

  1. 综上分析,总共由1个强实体,2个弱实体

ER图

建表

  1. user
  2. article
  3. comment

写后端操作

发布了33 篇原创文章 · 获赞 22 · 访问量 6791

猜你喜欢

转载自blog.csdn.net/qq_42909053/article/details/99677934