课时1 过滤器的入门

JavaWeb三大组件
1、都需要在web.xml中进行配置
Servlet
Filter
Listener

2、过滤器
会在一组资源(jsp, servlet, css, html等等)的前面执行
可以让请求得到目标资源,也可以不让请求达到
过滤器有拦截请求的能力

3、编写过滤器
(1)实现Filter接口
(2)在web.xml中进行配置
(3)Filter是单例的

4、配置web.xml

<web-app><filter><filter-name>FilerName</filter-name><filter-class>FilerClass</filter-class></filter><filter-mapping><filter-name>FilerName</filter-name><url-pattern>/*</url-pattern></filter-mapping>
</web-app>

继承示例

package com.pengshiyu.filtrer;import javax.servlet.*;
import java.io.IOException;public class Afilter implements Filter {/*** 创建之后马上执行,用来做初始化*/@Overridepublic void init(FilterConfig filterConfig) throws ServletException {}/*** 每次过滤都会执行*/@Overridepublic void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {System.out.println("进入过滤器");// 调用后序方法filterChain.doFilter(servletRequest, servletResponse);System.out.println("离开过滤器");}/*** 销毁之前的调用,用来释放资源*/@Overridepublic void destroy() {}
}

FilterConfig -> 与ServletConfig相似
获取初始化参数 getInitParameter()
获取过滤器名称 getFilterName()
获取application getServletContext()

FilterChain
放行,执行后序方法 doFilter()

课时2 多个过滤器的执行顺序

执行下一个过滤器或目标资源
FilterChain.doFilter()

Afilter进入过滤器
Bfilter进入过滤器
getAge
Bfilter离开过滤器
Afilter离开过滤器

课时3 四种拦截方式

请求 REQUEST 默认
转发 FORWARD
包含 INCLUDE
错误 ERROR

<filter-mapping><filter-name>FilerName</filter-name><url-pattern>/*</url-pattern><dispatcher>REQUEST</dispatcher>
</filter-mapping>

页面出错

<error-page><error-code>500</error-code><location>500.html</location>
</error-page>

课时4 使用filter-mapping控制多个Filter的执行顺序

filter-mapping的配置顺序决定过滤器执行顺序

课时5 Filter的应用场景、Filter的目标资源、小结

预处理:执行目标资源之前做预处理工作,例如设置编码
拦截:通过条件判断是否放行,例如用户登录校验
回程拦截:目标资源执行之后,做一些后序的特殊处理工作,例如目标资源输出的数据进行处理

直接指定servlet-name

<filter-mapping><filter-name>FilerName</filter-name><servlet-name>ServletName</servlet-name>
</filter-mapping>

小结
Filter3个方法
FilterChain类
4中拦截方式

课时6 案例1:分IP统计访问次数

数据结构:

ip count
192.168.0.1 32
192.168.0.2 22

统计工作在所有资源之前都执行,使用Filter
这个过滤器只做统计,不做拦截
数据Map<String, Integer>
Map保存到ServletContext中
从request中获取客户端ip

使用监听器创建 map
AListener.java

package com.pengshiyu.listener;import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
import java.util.LinkedHashMap;
import java.util.Map;public class AListener implements ServletContextListener {// 服务器启动时创建mappublic void contextInitialized(ServletContextEvent sce) {Map<String, Integer> map = new LinkedHashMap<String, Integer>();sce.getServletContext().setAttribute("map", map);}public void contextDestroyed(ServletContextEvent sce) {}
}

使用过滤器统计数据
AFilter.java

package com.pengshiyu.filter;import javax.servlet.*;
import java.io.IOException;
import java.util.Map;public class AFilter implements Filter {private FilterConfig config;/*** 创建之后马上执行,用来做初始化*/@Overridepublic void init(FilterConfig filterConfig) throws ServletException {this.config = filterConfig;}/*** 每次过滤都会执行*/@Overridepublic void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain) throws IOException, ServletException {ServletContext app = this.config.getServletContext();Map<String, Integer> map = (Map<String, Integer>)app.getAttribute("map");String ip  =  request.getRemoteAddr();System.out.println("ip: " + ip);if(map.containsKey(ip)){Integer count = map.get(ip);map.put(ip, count+1);} else{map.put(ip, 1);}// 放行filterChain.doFilter(request, response);}/*** 销毁之前的调用,用来释放资源*/@Overridepublic void destroy() {}
}

显示数据
BServlet.java

package com.pengshiyu.servlet;import javax.servlet.ServletContext;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Map;public class BServlet extends HttpServlet {@Overrideprotected void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException {ServletContext app = getServletContext();Map<String, Integer> map = (Map<String, Integer>) app.getAttribute("map");response.setContentType("text/html; charset=UTF-8");response.getWriter().println(map.toString());}
}

配置监听器和过滤器生效
web.xml

<?xml version="1.0" encoding="utf-8"?><web-app><servlet><servlet-name>BServlet</servlet-name><servlet-class>com.pengshiyu.servlet.BServlet</servlet-class></servlet><servlet-mapping><servlet-name>BServlet</servlet-name><url-pattern>/b</url-pattern></servlet-mapping><filter><filter-name>AFilter</filter-name><filter-class>com.pengshiyu.filter.AFilter</filter-class></filter><filter-mapping><filter-name>AFilter</filter-name><url-pattern>/*</url-pattern></filter-mapping><listener><listener-class>com.pengshiyu.listener.AListener</listener-class></listener>
</web-app>

课时7 案例2:粗粒度权限管理

基于角色的权限控制RBAC
tb_user
tb_role
tb_userrole
tb_menu
tb_rolemenu

web.xml

<?xml version="1.0" encoding="utf-8"?><web-app><servlet><servlet-name>AServlet</servlet-name><servlet-class>com.pengshiyu.servlet.AServlet</servlet-class></servlet><servlet-mapping><servlet-name>AServlet</servlet-name><url-pattern>/hello</url-pattern></servlet-mapping><filter><filter-name>AFilter</filter-name><filter-class>com.pengshiyu.filter.AFilter</filter-class></filter><filter-mapping>
<!--        不能将过滤器设置在login.html上,不然没法登录了--><filter-name>AFilter</filter-name><url-pattern>/hello.html</url-pattern></filter-mapping></web-app>

AServlet.java

package com.pengshiyu.servlet;import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;public class AServlet extends HttpServlet {@Overrideprotected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {String username = request.getParameter("username");System.out.println("post: " + username);// 设置sessionrequest.getSession().setAttribute("username", username);// 跳转页面request.getRequestDispatcher("hello.html").forward(request, response);}
}

过滤器进行简单的权限校验
AFilter.java

package com.pengshiyu.filter;import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;public class AFilter implements Filter {private FilterConfig config;@Overridepublic void init(FilterConfig filterConfig) throws ServletException {this.config = filterConfig;}@Overridepublic void doFilter(ServletRequest req, ServletResponse response, FilterChain filterChain) throws IOException, ServletException {HttpServletRequest request = (HttpServletRequest)req;String username = (String) request.getSession().getAttribute("username");System.out.println("filter: " + username);if(username != null){// 放行filterChain.doFilter(request, response);} else{// 跳转到登录页request.getRequestDispatcher("login.html").forward(request, response);}}@Overridepublic void destroy() {}
}

课时8 案例3:全站编码问题

// post编码
request.setCharacterEncoding("utf-8");// get编码
String username = request.getParameter("username");
username = new String(username.getBytes(StandardCharsets.ISO_8859_1), StandardCharsets.UTF_8);// 响应编码
response.setContentType("text/html; charset=UTF-8");

HttpServletRequest装饰类
EncodingRequest.java

package com.pengshiyu.filter;import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import java.nio.charset.StandardCharsets;// 装饰器
public class EncodingRequest extends HttpServletRequestWrapper {public EncodingRequest(HttpServletRequest request) {super(request);}@Overridepublic String getParameter(String name) {// 处理编码问题String value = super.getParameter(name);value = new String(value.getBytes(StandardCharsets.ISO_8859_1), StandardCharsets.UTF_8);return value;}
}

过滤器AFilter.java

package com.pengshiyu.filter;import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;public class AFilter implements Filter {@Overridepublic void init(FilterConfig filterConfig) throws ServletException {}@Overridepublic void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain) throws IOException, ServletException {HttpServletRequest httpServletRequest = (HttpServletRequest) request;String method = httpServletRequest.getMethod();// 设置响应编码response.setContentType("text/html; charset=UTF-8");if ("GET".equals(method)) {// 放行EncodingRequest encodingRequest = new EncodingRequest(httpServletRequest);filterChain.doFilter(encodingRequest, response);} else if ("POST".equals(method)) {request.setCharacterEncoding("utf-8");filterChain.doFilter(request, response);}}@Overridepublic void destroy() {}
}

响应处理AServlet.java

package com.pengshiyu.servlet;import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;public class AServlet extends HttpServlet {@Overrideprotected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {System.out.println(request.getParameter("name"));response.getWriter().print("你好");}
}

web.xml

<?xml version="1.0" encoding="utf-8"?><web-app><!-- 注册 Servlet,帮助web服务器反射该类 --><servlet><servlet-name>AServlet</servlet-name><servlet-class>com.pengshiyu.servlet.AServlet</servlet-class></servlet><!-- 映射 Servlet 资源,用url-pattern元素标示 URL --><servlet-mapping><servlet-name>AServlet</servlet-name><url-pattern>/hello</url-pattern></servlet-mapping><filter><filter-name>AFilter</filter-name><filter-class>com.pengshiyu.filter.AFilter</filter-class></filter><filter-mapping>
<!--        不能将过滤器设置在login.html上,不然没法登录了--><filter-name>AFilter</filter-name><url-pattern>/*</url-pattern></filter-mapping>
</web-app>

课时9 案例4:页面静态化之准备工作(图书管理小项目)

功能:
查询所有
按分类查看BookServletfindAll()       查询全部findByCategory() 按分类查询BookService: 省略BookDao:List<Book> findAll()List<Book> findByCategory()Book:bidbnamepricecategory

静态化:
第一次访问从数据库取数据,保存到html中
第二次之后访问就直接从html中读取,不再从数据库中取数据

数据准备:

create table tb_book(bid int primary key auto_increment,bname varchar(50),price decimal(10, 2),category int
);insert into tb_book(bname, price, category) values("Java", 12, 1);
insert into tb_book(bname, price, category) values("Python", 12, 1);
insert into tb_book(bname, price, category) values("JavaScript", 12, 1);
insert into tb_book(bname, price, category) values("Go", 12, 1);insert into tb_book(bname, price, category) values("三国演义", 12, 2);
insert into tb_book(bname, price, category) values("西游记", 12, 2);
insert into tb_book(bname, price, category) values("水浒传", 12, 2);
insert into tb_book(bname, price, category) values("红楼梦", 12, 2);

创建对应的Book类

package com.pengshiyu.bean;public class Book {private int bid;private String bname;private double price;private int category;public Book() {}public int getBid() {return bid;}public void setBid(int bid) {this.bid = bid;}public String getBname() {return bname;}public void setBname(String bname) {this.bname = bname;}public double getPrice() {return price;}public void setPrice(double price) {this.price = price;}public int getCategory() {return category;}public void setCategory(int category) {this.category = category;}@Overridepublic String toString() {return "Book{" +"bid=" + bid +", bname='" + bname + '\'' +", price=" + price +", category=" + category +'}';}
}

BookDao.java

package com.pengshiyu.dao;import com.pengshiyu.bean.Book;
import org.apache.commons.dbutils.QueryRunner;
import org.apache.commons.dbutils.handlers.BeanListHandler;
import util.TxQueryRunner;import java.sql.SQLException;
import java.util.List;public class BookDao {private QueryRunner qr = new TxQueryRunner();public  List<Book> findAll() {String sql = "select * from tb_book";try {List<Book> list = qr.query(sql, new BeanListHandler<Book>(Book.class));System.out.println(list);return list;} catch (SQLException e) {throw new RuntimeException(e);}}public  List<Book> findByCategory(int category) {String sql = "select * from tb_book where category = ?";try {return qr.query(sql, new BeanListHandler<Book>(Book.class), category);} catch (SQLException e) {throw new RuntimeException(e);}}
}

BookServlet

package com.pengshiyu.servlet;import com.pengshiyu.dao.BookDao;import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;public class BookServlet extends BaseServlet {private BookDao bookDao = new BookDao();public void findAll(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException {request.setAttribute("bookList", bookDao.findAll());request.getRequestDispatcher("book.jsp").forward(request, response);}public void findByCategory(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {int category = Integer.parseInt(request.getParameter("category"));request.setAttribute("bookList", bookDao.findByCategory(category));request.getRequestDispatcher("book.jsp").forward(request, response);}
}

用到的工具类 TxQueryRunner.java

package util;import java.sql.Connection;
import java.sql.SQLException;
import org.apache.commons.dbutils.QueryRunner;
import org.apache.commons.dbutils.ResultSetHandler;public class TxQueryRunner extends QueryRunner {@Overridepublic int[] batch(String sql, Object[][] params) throws SQLException {Connection con = JdbcUtil.getConnection();int[] result = super.batch(con, sql, params);JdbcUtil.releaseConnection(con);return result;}@Overridepublic <T> T query(String sql, ResultSetHandler<T> rsh, Object... params)throws SQLException {Connection con = JdbcUtil.getConnection();T result = super.query(con, sql, rsh, params);JdbcUtil.releaseConnection(con);return result;}@Overridepublic <T> T query(String sql, ResultSetHandler<T> rsh) throws SQLException {Connection con = JdbcUtil.getConnection();T result = super.query(con, sql, rsh);JdbcUtil.releaseConnection(con);return result;}@Overridepublic int update(String sql) throws SQLException {Connection con = JdbcUtil.getConnection();int result = super.update(con, sql);JdbcUtil.releaseConnection(con);return result;}@Overridepublic int update(String sql, Object param) throws SQLException {Connection con = JdbcUtil.getConnection();int result = super.update(con, sql, param);JdbcUtil.releaseConnection(con);return result;}@Overridepublic int update(String sql, Object... params) throws SQLException {Connection con = JdbcUtil.getConnection();int result = super.update(con, sql, params);JdbcUtil.releaseConnection(con);return result;}
}

JdbcUtil.java

package util;import com.mchange.v2.c3p0.ComboPooledDataSource;import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.SQLException;public class JdbcUtil {// 需要配置c3p0-config.xmlprivate static ComboPooledDataSource dataSource = new ComboPooledDataSource();// 返回连接对象public static Connection getConnection() {try {return dataSource.getConnection();} catch (SQLException e) {throw new RuntimeException(e);}}// 返回连接池对象public static DataSource getDataSource() {return dataSource;}// 释放连接public static void releaseConnection(Connection connection) {try {connection.close();} catch (SQLException e) {throw new RuntimeException(e);}}}

book.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%><h2>图书列表</h2>
分类:
<a href="book?method=findAll">全部</a>
<a href="book?method=findByCategory&category=1">第一类</a>
<a href="book?method=findByCategory&category=2">第二类</a><table border="1"><tr><th>ID</th><th>书名</th><th>价格</th><th>分类</th></tr><c:forEach items="${bookList}" var="book"><tr><td>${book.bid}</td><td>${book.bname}</td><td>${book.price}</td><td>${book.category}</td></tr></c:forEach>
</table>

配置文件

pom.xml

<dependency><groupId>jstl</groupId><artifactId>jstl</artifactId><version>1.2</version>
</dependency>
<dependency><groupId>taglibs</groupId><artifactId>standard</artifactId><version>1.1.2</version>
</dependency>

web.xml

<servlet-mapping><servlet-name>BookServlet</servlet-name><url-pattern>/book</url-pattern>
</servlet-mapping><servlet><servlet-name>BookServlet</servlet-name><servlet-class>com.pengshiyu.servlet.BookServlet</servlet-class>
</servlet>

c3p0-config.xml

<?xml version="1.0" encoding="utf-8"?>
<c3p0-config><!-- 这是默认配置信息 --><default-config><!-- 连接四大参数配置 --><property name="driverClass">com.mysql.cj.jdbc.Driver</property><property name="jdbcUrl">jdbc:mysql://localhost:3306/data</property><property name="user">root</property><property name="password">123456</property><!-- 池参数配置 --><property name="acquireIncrement">2</property><property name="initialPoolSize">2</property><property name="minPoolSize">2</property><property name="maxPoolSize">10</property></default-config>
</c3p0-config>

访问路径
http://localhost:8080/demo/book?method=findAll
http://localhost:8080/demo/book?method=findByCategory&category=1

课时10 案例4:页面静态化之如果文件存在直接重定向到html

使用一个过滤器,把servlet请求的资源输出保存到html中
第二次访问资源的时候,如果已存在就直接重定向到html文件

课时11 案例5:页面静态之生成html页面

CacheFilter.java

package com.pengshiyu.filter;import com.pengshiyu.response.StaticResponse;import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.IOException;public class CacheFilter implements Filter {private FilterConfig config;private final String cacheFileName = "cache";private String cacheFilePath = null;@Overridepublic void init(FilterConfig filterConfig) throws ServletException {this.config = filterConfig;this.cacheFilePath = this.config.getServletContext().getRealPath(this.cacheFileName);File file = new File(this.cacheFilePath);if(file.exists()){file.mkdir();}}/*** 访问路径* http://localhost:8080/demo/book?method=findByCategory&category=4*/@Overridepublic void doFilter(ServletRequest servletRequest, ServletResponse servletResponse,FilterChain filterChain) throws IOException, ServletException {HttpServletRequest request = (HttpServletRequest)servletRequest;HttpServletResponse response = (HttpServletResponse)servletResponse;String category = request.getParameter("category");String filepath = this.cacheFilePath + "/" + category + ".html";File file = new File(filepath);// 如果页面不存在就缓存页面if(!file.exists()){StaticResponse staticResponse = new StaticResponse(response, filepath);filterChain.doFilter(request, staticResponse);}System.out.println("文件存在了");request.getRequestDispatcher(this.cacheFileName + "/" + category + ".html").forward(request, response);}@Overridepublic void destroy() {}
}

StaticResponse.java

package com.pengshiyu.response;import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpServletResponseWrapper;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.PrintWriter;public class StaticResponse extends HttpServletResponseWrapper {private PrintWriter pw;public StaticResponse(HttpServletResponse response, String filename) throws FileNotFoundException {super(response);this.pw = new PrintWriter(filename);}@Overridepublic PrintWriter getWriter() throws IOException {// 掉包输出流return this.pw;}
}

Java学习路线-49:Servlet过滤器Filter相关推荐

  1. [转]从入门到精通,Java学习路线导航

    引言 最近也有很多人来向我"请教",他们大都是一些刚入门的新手,还不了解这个行业,也不知道从何学起,开始的时候非常迷茫,实在是每天回复很多人也很麻烦,所以在这里统一作个回复吧. J ...

  2. 【最新版】Java学习路线(含B站口碑推荐视频链接)

    文章目录 关于如何自学 一.计算机网络 二.数据结构与算法 三.操作系统 四.计算机组成原理 五.编译原理 六.设计模式 七.MySQL 八.实操工具 九.JAVA并发与JVM 十.Redis 十一. ...

  3. 这可能是最全面的Java学习路线了

    大家好,我是大彬~ 我本科学的不是计算机,大四开始自学Java,并且拿到了几个互联网中大厂的offer.在学习Java这方面还是比较有经验的,下面我来分享下我整理的Java自学路线. 在这里也提醒学弟 ...

  4. 从入门到精通,Java学习路线导航

    引言 最近也有很多人来向我"请教",他们大都是一些刚入门的新手,还不了解这个行业,也不知道从何学起,开始的时候非常迷茫,实在是每天回复很多人也很麻烦,所以在这里统一作个回复吧. J ...

  5. 从入门到精通,Java学习路线导航(附学习资源)

    引言 最近也有很多人来向我"请教",他们大都是一些刚入门的新手,还不了解这个行业,也不知道从何学起,开始的时候非常迷茫,实在是每天回复很多人也很麻烦,所以在这里统一作个回复吧. J ...

  6. 从入门到精通,Java学习路线导航(附学习资料+持续更新)

    引言 最近也有很多人来向我"请教",他们大都是一些刚入门的新手,还不了解这个行业,也不知道从何学起,开始的时候非常迷茫,实在是每天回复很多人也很麻烦,所以在这里统一作个回复吧. J ...

  7. Java学习路线详解

    如今互联网行业发展火爆,各大企业对于[Java开发请添加链接描述](http://www.suzxms.com.cn/java/)人才的需求也在不断增加,很多刚毕业的同学找不到合适的工作,想学习一门赖 ...

  8. B站有哪些值得Java初学者看的视频,Java学习路线

    我的读者中有很大一部分学生读者,以前也分享过一些Java学习路线,但是我收到的反馈并不好,因为学习路线包含的内容太多了,对于初学者来说难度太大,时间也不够用,根本学不完.今天我将结合B站优秀视频整理一 ...

  9. 十年架构师留下最完整的Java学习路线

    原文地址:https://club.dingding.xin/topic/5081/十年架构师留下最完整的java学习路线?code=cb850abbce523c7f886f583d6dc00f5e ...

最新文章

  1. 发布一个验证码生成组件
  2. 全国默哀 网站首页都要变成灰色的简单解决办法
  3. 解决Eclipse代码分析插件SonarLint在Console输出乱码问题
  4. 年轻工程师如何锻炼成高手的
  5. ubuntu终端彻底删除软件
  6. 东北大学c语言及程序设计,东大20秋学期《C语言及程序设计》在线平时作业1参考...
  7. Impala之DDL、DML
  8. 单webview上拉刷新下拉加载
  9. 修改服务器控件的ID和Name
  10. 计算机丢失disrupt,disrupt造句
  11. android 获取高德地图坐标,获取地址描述数据-获取地图数据-开发指南-Android 地图SDK | 高德地图API...
  12. 编程珠玑第一章-位图压缩存储
  13. 【Kay】Java多线程
  14. ASP微信头像保存到服务器,asp微信小程序获取用户头像和微信名-asp写的服务端...
  15. 局部替换算法最小生成树
  16. 思科无线POC测试要包含哪些测试项
  17. RPG Maker mv框架代码解析之Sprite_Button
  18. Cocos Creator 超简洁代码实现有限状态机 FSM,打造丝滑角色动作
  19. 计算机开机怎么设置网络连接,电脑怎么设置开机自动连接宽带
  20. 整理下使用yum localinstall做离线安装的点

热门文章

  1. docker commit 和docker build (实战使用以及区别)
  2. AutoMod(项目技术线回忆)
  3. 5.2.3 IP数据报(一)IP数据报的格式
  4. oracle 数据比对工具
  5. 《微信小程序》 数据访问实例详解
  6. chromebook刷机_如何将iPhone与Windows PC或Chromebook集成
  7. 哈希表(模拟散列表 字符串哈希)
  8. 爱了爱了!美到窒息的超美3D作品
  9. iOS 通过scheme进行应用间跳转
  10. 更新Photoshop 2023后两个首选项设置