zoukankan      html  css  js  c++  java
  • javaWEB

    javaWEB

    2020/9/3 17:21:44


    服务器搭建

    tomcat独立环境 安装 
    
    	前置条件: 
    		1.	环境变量中, 配置JAVA_HOME变量, 变量值为: JDK安装的路径, 切记不包含in
    		2.	关闭防火墙, 卸载杀毒软件 卸载**安全管家 , 卸载 *60安全卫士 
    
    	步骤:
    		1.	下载tomcat  (官网- http://tomcat.apache.org/ )(下载tomcat85)
    		2.	解压到任意英文目录下 . 
    		3.	使用管理员身份, 进入dos命令行
    		4.	在dos命令行中, 进入tomcat解压目录/bin
    		5.	输入安装指令并回车
    			service install
    		6.	观察安装成功的提示信息:
    			the service 'tomcat8' has been installed;
    
    运行tomcat , 通过网址访问
    
    	前置条件:
    		tomcat默认端口号为 8080 , 将其修改为 8090
    		1.	找到tomcatconfserver.xml
    		2.	在service.xml 大概70行的位置, 将8080更改为8090
    
    	1.	打开tomcatin	omcat8w.exe
    	2.	点击start启动服务器
    	3.	在浏览器中, 通过ip:8090 来访问网站
    

    卸载tomcat:

    方式1.	通过tomcat自身service命令 完成卸载
    		1.	管理员身份, dos命令行进入tomcatin
    		2.	执行卸载指令: service remove
    
    方式2.	通过windows指令, 删除服务
    		1.	管理员身份, dos命令行输入: sc delete 服务名
    

    HTTP协议

    超文本传输协议 , 是一个应用层的网络传输协议 . 属于TCP/IP协议集中的一个子协议.
    
    特点:
    	1.	简单 , 快速
    	2.	无连接协议 ,  每次连接服务器只处理一次客户端的请求, 处理完毕, 立即断开.
    	3.	无状态协议 ,  处理请求时 , 以及给客户端回复时. 没有记忆能力 .
    	4.	支持多种不同的数据提交方式, GET/POST等等
    	5.	数据传输灵活, 支持任意数据类型.
    

    HTTP协议的组成部分

    由两部分组成 
    	1.	请求
    		请求由四部分组成:
    			1.	请求头	 request header
    					请求的头部信息 , 由一个个的键值对组成, 这些键值对描述的是客户端的信息.
    			2.	请求体
    					GET请求不存在请求体 ,将请求的参数存储在网址中
    					POST请求请求体的作用:	用于存储客户端请求的数据
    			3.	请求空行
    					请求头部与请求体之间的一行空白行	
    			4.	请求行
    					由一个个的键值对组成, 这些键值对描述的是 请求的方式 ,访问的网址, 以及http协议的版本.
    
    
    	2.	响应
    		响应由三部分组成:
    			1.	响应头
    					响应头部的信息, 由一个个的键值对组成,  这些键值对描述的是服务器的信息.
    			2.	响应体
    					一个单独的数据报, 是服务器给客户端回复的主要内容.
    			3.	响应行
    					由一个个的键值对组成, 这些键值对描述的是 : 响应的状态码, 以及响应成功与失败的提示
    
    
    常见的响应状态码:
    
    	200	:	成功
    	404	:	资源不存在
    	500	:	服务器内部错误, (当程序出现异常时)
    

    动态网页技术

    网页会根据数据的不同, 展示不同的效果.
    

    HttpServlet类

    简介:
    	是JavaWeb体系中 三大组件之一.
    	本质上, 就是一个运行在tomcat中的java类, 
    	作用是:可以用来处理客户端的请求, 以及对客户端进行响应.
    
    实现步骤:
    	1.	编写一个类 , 继承自HttpServlet类
    	2.	重写父类的service(HttpServletRequest request,HttpServletResponse response)
    	3.	在service方法中, 准备响应体.
    
    
    将HttpServlet 映射到网址上 *****
    
    	web3.0版本之前:
    		步骤:
    			1.	通过eclipse的javaEE 工具, 创建web.xml  (在3.0之前的项目创建时, web.xml默认是存在的)
    			2.	向web.xml的根节点, 加入子节点:
    					将Java类配置到tomcat中.
    					<servlet>
    						<servlet-name>给servlet起别名</servlet-name>
    						<servlet-class>servlet的包名.类名</servlet-class>
    					</servlet>
    					给tomcat中的某个类, 添加网址 
    					<servlet-mapping>
    						<serlvet-name>要添加网址的别名</servlet-name>
    						<url-pattern>/映射网址</url-pattern>
    					</servlet-mapping>
    
    			注意:
    				如果映射网址为:  /demo1
    				则浏览器访问时:	http://ip地址:端口号/项目名称/demo1
    

    案例:
    Java:
    	public class Servlet1 extends HttpServlet{
    		@Override
    		protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    			//1.	设置响应内容的编码格式, 以及内容类型
    			response.setContentType("text/html;charset=utf-8");
    			//2.	通过响应对象, 得到用于准备响应体的打印流
    			PrintWriter out = response.getWriter();
    			//3.	打印内容
    			out.println("<h1>Hello JavaWeb</h1>");
    			
    		}
    	}
    
    web.xml
    	<servlet>
    		<servlet-name>haha</servlet-name>
    		<servlet-class>cn.xdl.demo1.Servlet1</servlet-class>
    	</servlet>
    	<!-- 给tomcat中的某个类, 添加网址 -->
    	<servlet-mapping>
    		<servlet-name>haha</servlet-name>
    		<url-pattern>/s1</url-pattern>
    	</servlet-mapping>
    

    web3.0版本 和 3.0版本之后:
    
    通过注解来完成映射网址的配置 : 
    
    	
    案例:
    
    	@WebServlet("/s2")
    	public class Servlet2 extends HttpServlet {
    	
    		/**
    		 * service方法, 表示服务方法
    		 * 当用户每次请求对应的网址时 , 此方法自动执行.
    		 * 方法中的参数: 
    		 * 		参数1.	HttpServletRequest  http协议中请求部分由tomcat进行了封装, 封装为了此对象, 对象中包含的是请求的相关信息.
    		 * 		参数2.	HttpServletResponse 
    		 * 				http协议存在响应部分, 而响应的内容, 是由我们的代码所生成的 , 
    		 * 				而HttpServletResponse , 它就是tomcat为了方便我们进行响应, 将响应的操作 ,封装为了这个对象.
    		 */
    		@Override
    		protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    			//1.	设置内容的类型 以及 编码
    			response.setContentType("text/html;charset=utf-8");
    			//2.	通过响应对象 得到向响应体输出的流
    			PrintWriter out = response.getWriter();
    			//3.	通过打印流, 向响应体中打印内容
    			out
    			.append("<html>")
    			.append("<head><title>Servlet准备的网页内容</title></head>")
    			.append("<body>")
    			.append("<h1>示例标题</h1>");
    			
    			for (int i = 0; i < 100; i++) {
    				out.append("<div>嘿嘿嘿"+i+"</div>");
    			}
    			out
    			.append("</body>")
    			.append("</html>")
    			;
    			//4.	当此方法执行完毕后, 则tomcat认为 响应体准备完毕, 会将内容响应给客户端.
    		}
    

    servlet的生命周期

    指的是 Servlet从创建到消亡的周期
    
    Servlet的创建时机:
    	默认情况下, 当用户第一次访问Servlet的映射地址时, Servlet的对象被创建。
    Serlvet的销毁时机:
    	当tomcat关闭, 或 应用被卸载时, Servlet被销毁 
    	(当tomcat关闭后 或 应用被卸载后, 用户无法再访问Servlet , 我们Servlet也就不需要在内存中存在了, 所以此时被销毁)
    
    
    tocmat为了方便我们 进行资源的缓存 ,
    为了让我们能在Servlet创建时, 初始化一些重复利用的资源, 
    为了让我们能在Servlet销毁时, 释放这些重复利用的资源, 
    提供了生命周期对应的方法 : 
    
    	1.	init	方法:	当Servlet被创建时, 此方法执行,用于告知程序员, 第一次访问产生了. 我们可以在此方法中初始化后续重复利用的资源.
    	2.	service	方法:	服务方法, 当用户每一次访问时, 都会执行, 用于处理用户的请求, 以及对用户进行响应.
    	3.	destroy	方法:	当servlet即将被销毁时 , tomcat会调用此方法,  告知程序员. 我们可以在此方法中释放初始化的那些重复利用的资源.
    

    调整Servlet对象的创建时机 (可以理解为: 懒汉变饿汉)

    使用web.xml的配置方式: 
    	在servlet节点中, 加入子节点: load-on-startup 来调整启动时机
    	
    	格式:
    
    		<serlvet>
    			<servlet-name></..
    			<servlet-class>></...
    			<load-on-startup>整型数字</load-on-startup>
    		</servlet>
    
    	load-on-startup: 
    		取值为数字 , 默认值为-1:
    			当值为负数时:			第一次请求时加载.
    			当值≥0时 , 含义是: 服务器启动时 , 就加载servlet. 
    								多个servlet之间值越小 越早加载. 值相同按照web.xml中自上而下的配置顺序加载 
    

    ServletConfig

    是Servlet的配置对象, 每一个Servlet都拥有一个配置对象.
    我们在web.xml中, 进行servlet的配置时 , 可以向Servlet中添加初始化的参数
    这些参数, 会被存储到一个ServletConfig 对象中.
    

     web.xml中配置的格式:
    	<servlet>
    		...
    		...
    		<!-- servlet节点中, 可以编写n个init-param节点, 每一个init-param节点都表示一个键值对. -->
    		<init-param>
    			<param-name>键</param-name>
    			<param-value>值</param-value>
    		</init-param>
    	</servlet>
    

    从servlet中得到SerlvetConfig对象的格式:
    	在Servlet类中, 可以通过两种方式, 来获取配置对象 , 这两种方式在使用中, 是互斥的.
    	
    	方式1.	
    		生命周期init方法中, 存储ServletConfig 参数. 在方法中使用参数即可.
    
    	方式2.
    		在Servlet的任意代码位置, 通过getServletConfig()方法得到对象
    

    从servletConfig对象中, 根据键得到值的格式:
    
    	String value = config对象.getInitParameter(String name);
    

    GET请求 与 POST请求的区别

    GET请求:
    	-	请求的参数 以多个键值对的形式存储在网址中, 在网址的?后 , 键与值键值使用等号连接, 多个键值对之间&分割
    	-	只能阐述字符串类型的参数
    	-	网址的最大长度为4kb , 通常支持的文字数量是: 最大2048 
    	-	数据传输的时 不安全
    
    POST请求:
    	-	请求的参数 以多个键值对的形式存储在单独的数据包中 , 这个数据包叫做请求体.
    	-	请求体中可以包含任意类型的数据, 例如: 图片 . 音频 等..
    	-	数据大小, 理论上是无上限的.
    	-	因为请求体是单独的数据包, 所以较GET请求而言 安全;
    

    什么样的请求 是 GET

    以我们目前掌握的技术来说, 只有表单提交时method=POST , 请求方式是POST.  其他方式都是GET:
    
    	-	浏览器输入网址 , 回车
    	-	点击超链接访问
    	-	表单提交时, method=GET
    	-	通过js:  wondow.location对象, 进行替换与跳转 
    	-	ajax的get请求
    

    什么样的请求 是 POST

    -	表单提交时method=POST 
    -	ajax的post请求 
    

    如何接受请求的参数:

    1.	根据一个name , 接收单个参数
    		String value = request.getParameter(String name);
    
    2.	根据一个name , 接收一组参数
    		String[] values = request.getParameterValues(String name);
    
    上述的两个方法在获取数据时,  name不存在, 则获取的结果为null
    

    请求乱码的问题解决:

    解决乱码问题的两种方式:
    
    	方式1. 
    		适用于tomcat8版本之前的GET请求乱码解决, 以及所有版本的POST乱码解决:
    
    		解决乱码的原理: 
    				将文字乱码的流程, 倒序执行一遍,得到正确的文字.
    				倒序:	
    					1.	将乱码的文字,按照ISO-8859-1转换为字节数组
    					2.	将字节数组按照UTF-8的编码转换为文字
    
    		格式:
    			//1.	将乱码的文字,按照ISO-8859-1转换为字节数组
    			byte[] bytes = 乱码文字.getBytes("ISO-8859-1");
    			//2.	将字节数组按照UTF-8的编码转换为文字
    			String text = new String(bytes,"UTF-8");
    

    方式2.
    	适用于tomcat所有版本的post乱码的解决.
    
    	解决乱码的原理:
    		在使用请求体之前, 将请求体的默认编码更改为utf-8.
    
    	格式:
    		//设置请求体的编码,  一定要写在获取参数之前.
    		request.setCharacterEncoding("UTF-8");
    

    Servlet线程安全问题

    Servlet的service方法,  在用户每次请求时调用.
    service方法的调用比较特殊:
    	service方法的执行, 每一次都是在新的线程中.
    
    因为service方法,  执行在新线程中,  有可能同时存在多个线程.  多线程操作时, 有可能发生线程安全问题.1
    
    
    1.	静态的同步方法的 同步锁  --> 类.class 这个对象
    
    2.	非静态的同步方法的 同步锁 --> this对象
    
    3.	同步代码块的 同步锁 --> 程序员使用时 提供.
    
    
    案例:
    
    	@WebServlet("/s1.do")
    	public class Servlet1 extends HttpServlet {
    		//余票
    		private int count = 10;
    		protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    			synchronized(this) {
    				if(count>0) {
    					//有票
    					System.out.println("有票, 正在出票");
    					try {
    						Thread.sleep(1000);
    					} catch (InterruptedException e) {
    						// TODO Auto-generated catch block
    						e.printStackTrace();
    					}
    					count--;
    					System.out.println("出票完成, 余票:"+count);
    				}else {
    					System.out.println("很遗憾 ,没有余票了");
    				}
    			}
    			response.getWriter().append("ok");
    		}
    	}
    

    请求的转发

    概念:	将一个web组件 为处理完毕的请求, 通过tomcat转交给另一个web组件处理.
    
    步骤:
    		1.	获取请求转发器
    				RequestDispatcher rd = request.getRequestDispatcher("转发的地址");
    
    		2.	通过转发器 发起转发
    				rd.forward(请求对象 , 响应对象);
    
    简写步骤:
    		 request.getRequestDispatcher("转发的地址").forward(请求对象 , 响应对象);
    
    原理(tomcat内部执行流程):
    		1.	当浏览器访问服务器中的tomcat时.
    		2.	tomcat将协议中的请求进行封装, 封装为HttpServletRequest对象 , 将响应封装到HttpServletResponse对象中
    		3.	找到对应映射地址的Servlet , (第一次寻找会创建对象) , 调用对象的service方法, 传入请求与响应对象.
    		4.	在serlvet中, 我们可以控制将请求转发到另一个地址 , 
    		5.	tomcat接收到请求转发指令后, 将原请求对象 和 新的响应对象传给转发的新地址.
    		6.	此时旧的响应对象就失效了, 无法在进行响应了. 由新的地址的响应对象 负责给用户进行响应.
    		7.	新地址准备完毕响应体, 发送给浏览器
    特点:
    	1.	转发的过程中, 只发生了一次请求, 多个Servlet之间共享一份请求信息.
    	2.	转发无法跨域实现 ( 无法跨网站进行转发, 例如: 京东无法转发给淘宝.)
    	3.	无论转发过程中, 触发了多少个web组件 , 对于浏览器而言, 它能感知到的 只是发起了一次请求, 接收到了一次响应.
    	4.	转发过程中, 浏览器地址不会发生改变
    	5.	相较于重定向而言, 效率高.
    

    请求的重定向

    概念:	在进行响应时, 告知浏览器新的请求地址, 浏览器接到后,自动发起新的请求找指定的地址.
    
    步骤:
    		response.sendRedirect("重定向的新地址");
    
    原理(执行流程):
    		1.	当浏览器请求某个Servlet时.  
    		2.	Servlet对浏览器响应一个302的状态码 ,  以及一个 键为location的值, 这个值表示新的地址;
    		3.	当浏览器接收到302的状态码以后, 会自动寻找lcoation的值, 并网页自动跳转到location的值表示地址上 !
    特点:
    	1.	重定向会产生新的请求 和 新的响应.
    	2.	使用重定向,  可以跨域实现 (可以跨网站操作 ,例如: 京东可以将请求重定向到淘宝)
    	3.	浏览器地址会发生改变 , 显示的是重定向的地址;
    	4.	相较于请求转发而言, 效率较低.
    

    注意:

    1.	在用户的一次访问过程中, 我们可以进行无限次的转发/重定向 ! 但是记住一点: 在多次转发/重定向的操作中, 一定要有出口 !
    2.	不只是可以将请求转发/重定向到servlet上, 任何的web资源都可以接受转发 和 重定向. 
    3.	当我们的代码进行了转发/重定向时 , 相当于将响应的操作交给了其他资源. 那么在进行转发和重定向的代码后面, 就不要再编写响应的操作了. 因为无效. 
    

    HttpServletRequest 类的常用操作

    1.	获取客户端的ip地址:
    		String ip = request.getRemoteAddr();
    
    2.	获取客户端访问的地址:
    		String url = reuqest.getRequestURI();
    
    3.	获取tomcat运行的ip地址
    		String ip = request.getServerName();
    
    4.	获取tomcat运行的端口号
    		String port = request.getServerPort();
    
    5.	获取请求方式
    		String method = request.getMethod();	
    
    6.	获取get请求的?后的参数列表
    		String params = request.getQueryString();	
    

    HttpServletResponse 类的常用操作

    1.	设置响应的内容类型(网页) 以及 编码格式(UTF-8)
    		response.setContentType("text/html;charset=utf-8");
    
    2.	设置响应的内容编码格式 (常用于响应JSON数据)
    		response.setCharacterEncoding("UTF-8"); 
    
    3.	响应错误码给客户端
    		response.sendError(int status,String msg);
    

    ServletContext 对象 (Servlet上下文)

    每一个Servlet都是一个独立的网页地址 , 它们之间无法进行通信以及交流 ,
    例如:	我们想统计网站的访问次数 , 每一个servlet只能统计自己被访问的次数, 无法汇总.
    
    Servlet上下文对象, 就是用于Servlet之间信息共享的, 是多个servlet之间通信的桥梁 
    Servelt上下文对象, 在项目的运行过程中, 只有一个.每一个Servlet获取的上下文对象, 都是同一份.
    Servlet上下文对象, 就像一个Map集合, 可以存储n个键值对信息!
    
    如何得到项目的Servlet上下文对象
    格式:
    	ServletContext context = getServletContext();
    
    上下文对象常用方法 熟悉
    1.	存储数据
    		context.setAttribute(String name,Object value);
    2.	获取数据
    		Object value = context.getAttribute(String name);
    3.	删除数据
    		context.removeAttribute(String name);
    
    4.	获取项目运行时的文件夹 绝对路径.
    		String path = context.getRealPath("/");
    

    会话跟踪技术 (状态管理)

    Http协议是无状态的 , 没有记忆能力.
    在浏览器与服务器交互时, 因为无状态的特性, 会导致无法连贯的交互.
    
    HTTP协议实现状态管理, 有两种方式:
    
    	1.	Cookie技术		:	将交互时产生的状态 存储在客户端中.
    	2.	Session技术		:	将交互时产生的状态 存储在服务器中.
    

    Cookie技术

    技术步骤, 以及原理:
    
    	1.	当服务器向客户端响应时,  可以向响应头中加入Cookie , 每一个Cookie表示一个键值对
    	2.	当浏览器接收到响应头中的Cookie后, 会将其存储在本地的一个文本文件中, 
    	3.	当浏览器再次访问相同的服务器时,  会去文本文件中寻找这个服务器之前存储的Cookie 
    	4.	将寻找到的Cookie 携带到请求头中, 发送给服务器.
    
    
    如何创建一个Cookie 
    	Cookie 在Java程序中的体现 是一个表示键值对的 Java类. 类名为Cookie
    
    	格式:
    		Cookie  cookie  = new Cookie(String name,String value);
    
    如何将创建的Cookie 添加到响应的头部 
    
    	通过响应对象, 将Cookie 添加到响应的头部
    
    	格式:
    		response.addCookie(Cookie cookie);
    
    	一次响应, 可以添加0-n个Cookie  , 
    	浏览器接收后, 会存储到文本文件中 , 如果相同域的相同路径 存储相同键的Cookie , 会导致旧值被覆盖.
    
    
    
    如何从请求头部 得到 我们之前存储的多个Cookie 
    
    	可以从请求对象中, 得到之前存储的Cookie信息, 得到的是一个Cookie数组 , 如果从未存储过Cookie ,则得到的数据是null
    	
    	格式:
    		Cookie[] cookies = request.getCookies();
    
    如何调整Cookie的存活时长 
    
    	cookie.setMaxAge(int 秒);
    
    	传入的值:
    		-	负数		:	默认值为-1, 负数表示浏览会话结束时 删除. (指的是浏览器关闭)
    		-	正数		:	存活的秒数
    		-	0		:	存活的秒数, 通常用户覆盖一个Cookie ,来完成删除操作.
    
    
    Cookie存储时的路径问题 
    
    	因为Cookie发送时, 需要匹配域 和路径 . 
    	我们在编写项目时, 经常因为路径不同, 导致cookie无法读取. 
    
    	Java为我们提供了设置Cookie路径的方法, 我们可以在任意的servlet中, 将Cookie路径设置为一致, 来存储和读取.
    
    	格式:
    		在cookie添加到响应头部之前 设置:
    		cookie.setPath("/");
    
    
    Cookie技术的优缺点: 
    
    	缺点:
    		1.	Cookie存储的数据类型有限制, 只能存储字符串 		( 早期无法存储中文 )
    		2.	数据存储的大小有限制, 不能超过4kb (4096个字节)	
    		3.	数据存储在用户的计算机的 文本文件中 , 不安全, 有可能被恶意程序读取.
    		4.	受限于用户的浏览器设置,  当浏览器禁用Cookie时, cookie就无法使用了.
    		
    	优点:
    		数据存储在客户端中, 分散了服务器的压力. 
    

    Session技术

    技术步骤, 以及原理
    
    	1.	浏览器访问服务器时,  服务器可以主动创建Session对象 , 一个session表示一个键值对的容器 ,类似Map集合
    	2.	每一个Session创建时, 会产生一个id , 这个id会存储到一个Cookie中, 并发送给浏览器
    	3.	等浏览器下一次访问时, 会携带Cookie, cookie中包含session的id , 
    	4.	我们就可以根据得到的sessionid , 从服务器中寻找到属于这个浏览器的session对象.
    

    如何获取session对象

    格式1. ****
    		调用请求的获取session的方法 (无参) , 方法的内部调用了一参方法, 传入了true
    		HttpSession session = request.getSession();
    
    格式2.
    		调用请求的获取session的方法 (一参)
    		HttpSession session =  request.getSession(boolean isNew);
    
    		参数的含义:
    			true:	根据浏览器发来的sessionid 寻找session对象并返回, 如果不存在 ,则创建新的并返回
    			false:	根据浏览器发来的sessionid 寻找session对象并返回, 如果不存在 ,则返回null
    

    session的常用方法

    1.	存储数据
    		session.setAttribute(String name,Object value);
    2.	取出数据
    		Object value = session.getAttribute(String name);
    3.	删除数据
    		session.removeAttribute(String name);
    4.	销毁这个Session
    		session.invalidate();
    

    session的存活时长

    session的默认存活时长为30分钟, 
    当用户的上一次访问 距离现在已经超过30分钟时,  session会自动销毁.
    
    设置session的存储时长:
    
    	方式1.	修改单个session的时长:
    				session.setMaxInactiveInterval(int 秒);
    
    	方式2.	修改tomcat下, 所有session的默认时长
    				独立环境:	找到conf/web.xml文件
    				开发环境:	找到servers/web.xml
    
    				修改其中的session-config节点
    				
    				案例:
    					<session-config>
    						<session-timeout>数值分钟</session-timeout>
    					</session-config>
    

    session的优缺点

    优点:
    		1.	数据存储在服务器中, 安全
    		2.	session中可以存储任意类型数据
    		3.	存储的数据大小, 理论上是无限制的.
    缺点:
    		数据存储在服务器中, 大量的用户存储session时, 会对服务器造成极大的压力, 极易导致服务器资源耗尽.
    

    Cookie技术 和 Session技术 不是互斥的.

    Cookie和session 我们是结合使用的.
    
    	对于安全无要求的字符串数据, 存储在cookie中
    	对于安全敏感的数据, 存储在session中
    	对于安全敏感 , 且较大数据, 存储在数据库中...
    

    JSP简介

    Java Server Pages Java的动态网页技术
    

    JSP引擎

    引擎原理:
    
    	JSP引擎用于将JSP文件, 转换为Servlet
    
    原理步骤:
    	1.	在服务器启动时, JSP引擎读取JSP文件
    	2.	将文件转换为Servlet , 并给Servlet添加映射地址为 原JSP 文件名称.
    	3.	当用户访问  xxxx.jsp时, 请求的不是jsp文件, 而是JSP引擎根据文件转换的Servlet
    

    JSP语法结构 *

    JSP文件保存在 .jsp文件中 .  保存的路径:  webContent目录下.
    
    JSP语法存在三大语法结构:
    
    	1.	HTML代码
    	2.	Java代码
    	3.	JSP特有的一些语法结构.
    
    
    Java代码声明区 
    
    	指的是 Java类的成员位置 , 在JSP文件中的Java代码声明区中编写的Java代码, 会原封不动的生成到Servlet的成员位置!
    
    
    	语法格式:
    		<%!
    			编写声明区的Java代码
    		%>
    
    Java代码执行区 
    
    	指的是Servlet的service方法. 用于每次请求都会执行
    
    	语法格式:
    		<%
    			编写Java逻辑代码,  生成到service方法中
    		%>
    
    输出表达式 
    
    	用于快速的将Java代码中的变量, 输出到网页中
    
    	语法格式:
    		<%=变量名 %>
    
    	上述语法格式中的值, 其实是被生成到了out的print方法中,
    
    	例如:			<%=count  %>
    	生成的Servlet:	out.print(count);
    

    JSP中的注释:

    因为JSP包含了三种语法结构.
    
    所以三种结构的注释, 都可以使用.
    
    HTML注释:
    	语法:	<!-- 注释 -->
    	注意:	在JSP中, 只能编写在HTML代码的位置 ,只能用于注释HTML部分. 
    			编写的HTML注释会被JSP引擎认为是HTML代码 , 转换为:	out.write("<!-- 注释 -->");
    	
    Java注释:
    	语法:	//单行  /*多行*/ /**文档*/
    	注意:	在JSP中, 只能编写在Java代码的位置, 只能用于注释Java部分.
    			编写Java注释会被JSP引擎认为是Java代码, 原封不动的转换到Servlet中
    
    JSP注释:
    	语法:	<%-- JSP的注释 --%>
    	注意:	指的是JSP文件的注释, 被JSP注释的内容, 在JSP引擎转换时期被忽略.
    

    JSP三大指令

    指令的格式:
    	<%@ 指令名称 属性名1=值 属性名2=值 ... 属性名n=值 %>
    

    page指令

    完整格式:
    	<%@ page
    		language="java"
    		contentType="text/html;charset=utf-8"
    		pageEncoding="UTF-8"
    		extends="继承的类"
    		buffer="数字|none" -- 是否允许缓存, 以及允许缓存的大小. 默认缓存, 大小为8kb
    		autoFlush="true|false" -- 缓冲器是否自动清除, 默认为true
    		session="true|false" -- 是否在service方法中,  提前创建好session , 默认true
    		isThreadSafe="true|false" -- service方法是否是线程安全的. 默认false
    		errorPage="网址" -- 当JSP中的代码出异常时,  页面自动跳转到网址 ,通常用语提示BUG.  **
    		isErrorPage="true|false" -- 用于处理错误的页面 , 通常isErrorPage=true的页面, 会被其他页面通过errorPage引入.  **
    								 --	为true时, 会在service中提前准备好, 异常对象 exception
    		import="导包列表" -- 属性值是导包的内容,  多个包之间使用逗号隔开 ** 
    	%>
    

    指定项目全局错误码的 处理页面 *

    步骤:
    	1.	先打开项目的 web.xml文件
    	2.	在根节点中, 加入子节点:
    			<error-page>
    				<error-code>错误码</error-code>
    				<location>处理的页面地址</location>
    			</error-page>
    
    	注意:	error-page可以编写多个
    
    	例如:
    			<error-page>
    				<error-code>404</error-code>
    				<location>/error.jsp</location>
    			</error-page>
    			<error-page>
    				<error-code>500</error-code>
    				<location>/error.jsp</location>
    			</error-page>
    

    include指令

    用于将一个JSP 或 HTML文件, 引入到另一个JSP文件中
    
    格式:	<%@ include file="地址" %>
    

    include动作

    用于将一个JSP或HTML文件 引入到另一个JSP文件中.
    
    格式:	<jsp:include page="地址" flush="true" />
    

    include 指令与动作的区别

    include指令:	在JSP的转换时期 , 将引入的JSP文件嵌入到了include指令的位置 (合并为了一个文件), 然后转换为Servlet ! 最终生成的是一个.java文件
    include动作:	在JSP程序的转换时期, 引入的JSP文件会独立转换为Servlet , 到访问时, 在将响应体合并, (将被引入文件的响应体 , 动态加载在文件中) !
    

    JSP内置对象(隐含对象)

    内置对象指的是:	JSP引擎在转换时期 , 在service方法的前面, 帮我们提前准备好的一些对象. 我们在<%%>代码块中, 可以直接使用它们.
    
    作用:
    	这些对象包含了我们进行动态网页开发 常用的一些对象. 提供了大量的便于我们开发的功能, 可以简化我们的开发过程.
    

    九大内置对象

    1. * 
    	对象名	:	request
    	类型		:	HttpServletRequest
    	作用		:	请求对象, 包含了请求相关的信息 !
    
    2. *
    	对象名	:	response
    	类型		:	HttpServletResponse
    	作用		:	响应对象, 包含了一些用于响应的功能
    
    3. *
    	对象名	:	pageContext
    	类型		:	PageContext
    	作用		:	页面的上下文 ,  用于获取其它8大内置对象. 
    
    4. *
    	对象名	:	session
    	类型		:	HttpSession
    	作用		:	会话对象 , 用于会话跟踪 与 状态管理
    
    5. *
    	对象名	:	application
    	类型		:	ServletContext
    	作用		:	Servlet的上下文 , 一个应用程序启动中, 只会存在一个Servlet上下文, 用于多个Servlet之间的数据共享与通信.
    
    6. 
    	对象名	:	out
    	类型		:	JSPWriter
    	作用		:	打印流, 用于向响应体中输出数据.
    
    7.
    	对象名	:	config
    	类型		:	ServletConfig
    	作用		:	Servlet的配置对象, 用于初始化一些键值对信息
    
    8.
    	对象名	:	page
    	类型		:	Object , 但是在赋值时, 使用的是this . 所以本质上是当前JSP转换的Servlet类的类型.
    	作用		:	指当前页面自身 .
    
    9.
    	对象名	:	exception
    	类型		:	Throwable
    	作用		:	当页面的page指令: isErrorPage="true"时, 才会存在的一个对象.
    				当别的页面发生异常后, 跳转到此页面时 , exception是发生的异常对象.
    

    JSP的四大域对象

    九大内置对象中, 包含四个较为特殊的对象. 这四个对象我们称其为:		域对象! 
    域对象存在一个特点: 
    	都包含存储数据 / 删除数据 / 获取数据的方法 , 所谓的域, 指的是存储的数据使用的作用域.
    	
    	存储数据:
    		setAttribute(String key,Object value);
    	获取数据:
    		Object value = getAttribute(String key);
    	删除数据:
    		removeAttribute(String key);
    
    四大域对象:
    	-	pageContext (一个JSP页面的域对象) 
    			页面的上下文 , 存储在pageContext中的数据 , 域是最小的, 只有当前请求的当前页面才可以使用. 页面响应完毕对象就被垃圾回收了. 
    	-	request (一次请求域对象)
    			请求对象 , 存储在request对象中的数据, 只有当前请求 , 才可以使用 ! 一次请求可能发生多次转发 ,所以有可能跨越多个Servlet/JSP页面.
    	-	session (一次会话域对象)
    			会话对象, 存储在session对象中的数据, 在当前会话中可以使用. 一次会话可能包含多次请求.
    	-	application (一次服务域对象)
    			Servlet上下文对象, 存储在application中的数据, 在服务器关闭之前都可以使用.  
    			应用的一次启动, 服务器中只会存在一个application对象, 一次服务器启动中可能包含多次会话.
    

    EL表达式

    作用, 用于快速的从域对象中 取出数据, 并将结果输出到网页中 .
    
    格式:
    	${ 表达式 }
    

    EL表达式的使用

    1.	用于运算
    
    	${1+2+3+4+5}	输出的结果是:	15
    
    2.	用于取出域对象中的数据  
    
    	访问存储的数据格式: ***
    		${存储的key}
    
    	访问存储的对象的属性:
    		格式1.	${存储的key.属性名} ***
    		格式2.	${存储的key["属性名"]}
    		格式3.	
    			动态取值:	
    				指的是按照域对象中存储的一个属性名, 以及对象名, 从对象中取出属性值
    			
    				${存储的对象key[属性名的key]}
    
    3.	用于取出域对象中的集合/数组中的对象属性	
    		格式1.	${存储的key[下标].属性名}
    		格式2.	${存储的key[下标]["属性名"]}
    		格式3.	
    			动态取值:	
    				指的是按照域对象中存储的一个属性名, 以及对象名, 从对象中取出属性值
    			
    				${存储的对象key[下标][属性名的key]}
    

    EL表达式, 取出数据的流程

    寻找的顺序:	从域范围小的 到 域范围大的	
    
    步骤:
    
    	1.	先从pageContext中, 寻找数据是否存在.
    	2.	如果pageContext中不存在数据, 则去request中寻找数据是否存在.
    	3.	如果request中不存在数据, 则去session中寻找数据是否存在.
    	4.	如果session中不存在数据, 则去application中寻找数据是否存在
    	5.	如果application中不存在数据, 则向网页输出 :  ""
    
    在上述的步骤中, 一旦寻找到数据, 则流程结束, 将寻找到的数据输出到网页中.
    

    taglib 指令 熟悉

    用于在JSP中, 引入标签库.
    
    语法格式:
    	<%@ taglib prefix="短名称" uri="地址"%>
    
    
    属性:
    	prefix	:	是引入的标签库的名称, 用于区分引入的多个标签库 , 
    				在使用标签库中的标签时 , 需要在前面加入库名:
    
    	uri		:	引入标签库的操作 与 网页中引入其他静态资源不一样. 
    				引入静态资源(图片,音乐等等) , 使用相对路径.
    				每一个标签库都拥有uri属性, 引入标签库, 不需要路径, 只需要匹配uri就可以
    

    JSTL标签库的使用

    JSTL 是一套JSP的标准标签库
    
    IF标签
    格式
    	<短名称:if test=""></短名称:if>
    	test值:	boolean值 , 可以是具体的boolean值, 也可以是el表达式运算的结果
    
    	如果test值为true , 则内容显示
    	如果test值为false , 则内容不显示 (内容不会通过流, 输出到响应体中)
    
    案例:
    		<heihei:if test="${ flag }">
    			从前有一座山 , 山上有座尼姑庵 , 庵里有个老尼姑 和 一个小姐姐 . 
    		</heihei:if>
    

    choose + when + otherwise

    类似Java中的:	switch + case + default
    
    作用:
    	用于多分支, 显示.
    
    案例:
    	<%
    		pageContext.setAttribute("flag",6);
    	%>
    	<heihei:choose>
    		<heihei:when test="${ flag==1 }">孔子东游 , 见两小儿辩日</heihei:when>
    		<heihei:when test="${ flag==2 }">千呼万唤始出来</heihei:when>
    		<heihei:when test="${ flag==3 }">白日依山尽</heihei:when>
    		<heihei:when test="${ flag==4 }">日照香炉生紫烟</heihei:when>
    		<heihei:when test="${ flag==5 }">万里江陵十日还</heihei:when>
    		<heihei:otherwise>
    			停车坐爱枫林晚
    		</heihei:otherwise>
    	</heihei:choose>
    
    
    forEach 标签
    
    	用于遍历集合或数组元素
    
    	格式:
    		<标签库名称:forEach items="" var="">
    		
    		</标签库名称:forEach>
    
    	属性:
    		items	:	使用el表达式, 从域对象中取出的要遍历的数组或集合对象.
    		var		:	在进行遍历时, 数组或集合中的每一个元素被取出后, 会单独存储在pageContext中, var的值就是存储时的键
    
    	案例:
    		
    		<%
    			ArrayList<String> data = new ArrayList<>();
    			data.add("孔子东游 , 见两小儿辩日");
    			data.add("千呼万唤始出来");
    			data.add("白日依山尽");
    			data.add("日照香炉生紫烟");
    			data.add("万里江陵十日还");
    			data.add("停车坐爱枫林晚");
    			data.add("老骥伏枥志在千里");
    			pageContext.setAttribute("data", data);
    		%>
    		<heihei:forEach items="${data }" var="str">
    			<h1>${ str }</h1>
    		</heihei:forEach>
    

    自定义标签库

    步骤:
    	1.	编写一个类, 继承SimpleTagSupport 类
    	2.	重写类的doTag方法 ,
    	3.	在doTag方法中, 通过getJspContext() 方法 得到JSP的上下文.
    	4.	通过上下文对象, 可以得到其他8大内置对象 , 通常我们是getOut()得到输出流, 向网页输出内容.
    
    	5.	编写tld文件, 描述标签库和标签.
    
    
    案例:
    	Class:
    		public class MyTag extends SimpleTagSupport {
    
    		private static ArrayList<String> data = new ArrayList<>();
    		static {
    			data.add("理想是人生的太阳。 —— 德莱赛");
    			data.add("真实是人生的命脉,是一切价值的根基。 —— 德莱塞");
    			data.add("真的猛士,敢于直面惨淡的人生,敢于正视淋漓的鲜血。 —— 鲁迅");
    			data.add("人生须自重。 —— 黄宗羲");
    			data.add("在人生的道路上,当你的希望一个个落空的时候,你也要坚定,要沉着。 —— 朗费罗");
    			data.add("真理惟一可靠的标准就是永远自相符合。 —— 欧文");
    			data.add("竹子用了4年扎根,第五年一出头就被做成竹笋炒肉。");
    			data.add("公主病的成因没别的,不是丑就是穷。“那有钱又漂亮脾气却不好的呢?”那本来就是公主,不叫病。");
    			data.add("所谓的女汉子,只不过是因为长得丑而已,但凡有些爷们气质的漂亮姑娘,都被称为女王大人。");
    			data.add("腾不出时间来睡觉的人,迟早会腾出时间来生病;腾不出时间来恋爱的人,迟早会腾出时间来相亲。");
    			data.add("快乐分享错了人就成了显摆,难过分享错了人就成了矫情。");
    			data.add("请记住,你并不是一无所有,你还有病!哈哈");
    			data.add("鱼和熊掌不可兼得,但单身和穷可以!");
    			data.add("用钱当然买不到快乐,但只要你有钱,别人会想办法让你快乐。");
    			data.add("你才二十多岁,没有遇到对的人很正常,以后你就知道,大概遇不到了!");
    			data.add("这年头,当个宅男宅女,也得先买房子!");
    			data.add("能背后给你一刀的,往往是你信任的!");
    			data.add("失败并不可怕,可怕的是你还相信这句话。");
    			data.add("比一个人吃火锅更寂寞可怜的是,一个人没有钱吃火锅。");
    			data.add("你只知道人家化妆比你好看,却不知道,她们卸了妆,不仅比你好看,皮肤还吹弹可破。");
    			data.add("从前车马很慢,书信很远,一生只够爱一个人,但是能纳很多妾。");
    			data.add("岁月告诉我,除了快递,我啥也等不到!");
    			data.add("假如今天的你被生活辜负了,别伤心,因为明天生活还会继续辜负你!");
    		}
    		
    		@Override
    		public void doTag() throws JspException, IOException {
    			JspContext context = getJspContext();
    			JspWriter out = context.getOut();
    			String text = getText();
    			out.print(text);
    			
    		}
    		public String getText() {
    			Random r = new Random();
    			int i = r.nextInt(data.size());
    			return data.get(i);
    		}
    	}
    
    
    
    
    	TLD文件:
    
    	<?xml version="1.0" encoding="UTF-8" ?>
    	<taglib xmlns="http://java.sun.com/xml/ns/j2ee"
    	    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    	    xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-jsptaglibrary_2_0.xsd"
    	    version="2.0">
    	  	<!-- 注释 ,描述  -->
    	  	<description>这是兄弟连Java30期自定义的一个标签库</description>
    	  	<!-- 名称 -->
    	  	<display-name>XDL</display-name>
    	  	<!-- 版本 -->
    	  	<tlib-version>5.21</tlib-version>
    	  	<!-- 短名称 -->
    	  	<short-name>x</short-name>
    	  	<!-- 用于匹配的uri -->
    	  	<uri>http://hahaha.heiheihei.com</uri>
    	  	<!-- 在标签库中 一个tag节点 , 表示库中的一个自定义标签 -->
    		<tag>
    			<!-- 标签的描述 ,在使用时, 会提示 -->
    			<description>这个标签会向网页中输出一条鸡汤句子</description>
    			<!-- 标签的名称 -->
    			<name>djt</name>
    			<!-- 标签的类的全名 -->
    			<tag-class>cn.xdl.tag.MyTag</tag-class>
    			<body-content>empty</body-content>
    		</tag>
    	</taglib>
    

    web三大组件

    1.	Servlet
    2.	Filter	过滤器
    3.	Listener 监听器
    

    Filter 过滤器

    所谓的过滤. 指的是过滤请求.
    有时我们进行后段项目开发时, 有些请求 ,需要特定的条件才能操作 我们可以通过Filter , 来过滤不满足的用户操作.
    例如:	
    	用于的个人中心, 应该是登录	后才可以查看的.
    	当用户请求个人中心页面时, 我们就可以编写过滤器, 将未登录的所有用户过滤掉, 并重定向至登录页面.
    
    采用了面向切面编程思想(AOP) .
    

    使用步骤:

    	1.	编写一个类 , 实现Filter接口
    	2.	通过web.xml 或 注解的方式, 配置过滤器的过滤地址. 
    
    案例:
    
    	public class ParamFilter implements Filter {
    		/**
    		 * 当发生匹配过滤地址的请求时, doFilter执行
    		 * 过滤器执行在web的所有资源之前 , 默认此方法是拦截请求的, 
    		 * 如果需要放行 , 需编写: chain.doFilter(request,response);
    		 */
    		@Override
    		public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
    				throws IOException, ServletException {
    			
    			HttpServletRequest req = (HttpServletRequest) request;
    			HttpServletResponse resp = (HttpServletResponse) response;
    			if(req.getParameter("a")==null) {
    				resp.setContentType("text/html;charset=utf-8");
    				//拦截
    				resp.getWriter().append("<h1>很遗憾, 参数错误, 无法访问</h1>");
    			}else {
    				//放行
    				chain.doFilter(request, response);
    			}
    		}
    	}
    
    
    web.xml
    
      <filter>
      	<filter-name>pf</filter-name>
      	<filter-class>cn.xdl.demo1.ParamFilter</filter-class>
      </filter>
      <filter-mapping>
      	<filter-name>pf</filter-name>
      	<url-pattern>/home.jsp</url-pattern>
      </filter-mapping>
    
    过滤器链 
    
    	当多个过滤器, 过滤地址重复时 , 就形成了过滤器链 . 用户的请求, 需要过滤器链中的所有过滤器放行, 才可以正常访问.
    
    	过滤器链执行的顺序: 
    		web.xml中的顺序: 按照web.xml中配置的先后顺序, 来执行. 
    
    		注解配置的顺序:	 按照类名的自然排序, 顺序执行. 
    
    		web.xml中配置的过滤器, 一定执行在注解配置过滤器的前面.
    

    Listener

    监听器 , 监听是服务器的一些事件 . 
    
    两类事件:
    	1.	服务器中组件的生命周期相关事件. 
    	2.	域对象中数据的变化事件 . 
    
    
    ServletContextListener 
    
    	用于监听Servlet上下文的创建与销毁.  
    
    	案例:
    		@WebListener
    		public class MyServletContextListener implements ServletContextListener {
    		
    			/**
    			 * 当上下文对象 即将销毁时, 方法执行
    			 * 上下文对象销毁的时机, 是 服务器关闭或项目被卸载. 所以我们常在这里进行全局资源释放的操作. 
    		     */
    		    public void contextDestroyed(ServletContextEvent arg0)  { 
    		    	System.out.println("项目关闭了");
    		    }
    		
    			/**
    			 * 当上下文对象 被创建初始化时  , 这个方法执行
    			 * 因为上下文对象创建的时机是 服务器中项目启动时  , 所以我们常在这里进行全局资源的初始化操作.
    		     */
    		    public void contextInitialized(ServletContextEvent arg0)  { 
    		    	System.out.println("项目启动了");
    		    }
    		}
    
    
    ServletContextAttributeListener 
    
    	用于监听ServletContext中属性的变化 
    
    	案例:
    		@WebListener
    		public class MyServletContextAttributeListener implements ServletContextAttributeListener {
    		
    			/**
    			 * 当向上下文中 添加属性时, 代码执行
    		     */
    		    public void attributeAdded(ServletContextAttributeEvent e)  { 
    		    	//从事件对象中, 得到存储的键和值
    		    	String key = e.getName();
    		    	Object value = e.getValue();
    		    	System.out.println("监听到ServletContext中数据的增加:"+key+" --> "+value);
    		    }
    		
    			/**
    			 * 当从上下文中 移除属性时, 代码执行
    		     */
    		    public void attributeRemoved(ServletContextAttributeEvent e)  { 
    		    	//从事件对象中, 得到被移除的键和值
    		    	String key = e.getName();
    		    	Object value = e.getValue();
    		    	System.out.println("监听到ServletContext中数据的移除:"+key+" --> "+value);
    		    }
    		
    			/**
    			 * 当操作上下文对象 存储数据 ,发生替换时, 代码执行.
    		     */
    		    public void attributeReplaced(ServletContextAttributeEvent e)  { 
    		    	//从事件对象中, 得到被替换掉的旧的键和值
    		    	String key = e.getName();
    		    	Object oldValue = e.getValue();
    		    	System.out.println("监听到ServletContext中数据的替换, 旧数据:"+key+" --> "+oldValue);
    		    	//从事件对象中, 得到上下文对象
    		    	Object newValue = e.getServletContext().getAttribute(key);
    		    	System.out.println("监听到ServletContext中数据的替换, 新数据:"+key+" --> "+newValue);
    		    }
    			
    		}
    
    
    HttpSessionListener 
    
    	用于监听session的创建与销毁. 
    
    	@WebListener
    	public class MySessionListener implements HttpSessionListener {
    		
    		private static int count = 0;
    		private static Random r = new Random();
    		ArrayList<Integer> nums = new ArrayList<>();
    	    public void sessionCreated(HttpSessionEvent arg0)  { 
    	    	//30-60的随机数字
    	    	int num = r.nextInt(31)+30;
    	    	count+=num;
    	    	nums.add(num);
    	    }
    	
    	    public void sessionDestroyed(HttpSessionEvent arg0)  { 
    	    	int num = nums.remove(nums.size()-1);
    	    	count-=num;
    	    }
    	    
    	    public static int getCount() {
    	    	return count;
    	    }
    		
    	}
    

    HttpSessionAttributeListener

    用于监听session中的数据的增加 ,删除, 修改.
    
    	public class LoginServlet2 extends HttpServlet {
    
    		/**
    		 * @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse response)
    		 */
    		protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    			String uname = request.getParameter("uname");
    			String upass = request.getParameter("upass");
    			
    			request.getSession().setAttribute("username", request.getRemoteAddr()+":"+uname);
    			response.sendRedirect("home2.jsp");
    		}
    	
    		/**
    		 * @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse response)
    		 */
    		protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    			// TODO Auto-generated method stub
    			doGet(request, response);
    		}
    	
    	}
  • 相关阅读:
    MySQL之ERROR 1558 (HY000): Column count of mysql.user is wrong.解决方案
    手动发布本地jar包到Nexus私服
    Git的常用命令
    手游录屏直播技术详解 | 直播 SDK 性能优化实践
    【容器云】十分钟快速构建 Influxdb+cadvisor+grafana 监控
    【容器云】传统金融企业的 Docker 实践
    直播推流端弱网优化策略 | 直播 SDK 性能优化实践
    云存储之覆盖上传——七牛云
    「视频直播技术详解」系列之七:直播云 SDK 性能测试模型
    「视频直播技术详解」系列之六:现代播放器原理
  • 原文地址:https://www.cnblogs.com/lzy1212/p/13608925.html
Copyright © 2011-2022 走看看