那些年关于JavaWeb的点点滴滴,你想看的这里全都有噢~


使用PC浏览,体验更加!!!!

一、Servlet

  • 概念:servlet是一个在Web服务器中运行的小型Java程序。主要功能在于交互
    式地浏览和修改数据,⽣成动态Web内容。

    A servlet is a small Java program that runs within a Web server

    • Servlet就是一个接口,定义了Java类被浏览器访问到(tomcat识别)的规则。
    • 未来我们可以自定义一个类,实现Servlet接口,复写方法。
  • 快速入门

    1. 创建JavaEE项目

    2. 定义一个类,实现Servlet接口
      public class ServletDemo1 implements Servlet

    3. 实现接口中的抽象方法

    4. 配置Servlet
      在web.xml中配置:

           <!--配置Servlet -->
           <servlet>
               <servlet-name>demo1</servlet-name>
               <!--全类名-->
               <servlet-class>cn.itcast.web.servlet.ServletDemo1</servlet-class>
           </servlet>
       
           <servlet-mapping>
               <servlet-name>demo1</servlet-name>
               <!--资源路径-->
               <url-pattern>/demo1</url-pattern>
           </servlet-mapping>
      
  • 执行原理:

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

    1. 当客户端⾸次发送第⼀次请求后,由容器(web服务器(tomcat))去解析请求。
    2.  根据请求找到对应的servlet,判断该类的对象是否存在。
    3. 不存在则创建servlet实例,调取init()⽅法 进⾏初始化操作。
    4. 初始化完成后调取service()⽅法,由service()判断客户端的请求⽅式。
    5. 如果是get,则执⾏doGet()。
    6. 如果是post则执⾏doPost()。
    7. 处理⽅法完成后,作出相应结果给客户端.单次请求处理完毕。
    8. 当服务器关闭时调取destroy()⽅法进⾏销毁
    

    对于同一个Servlet,当⽤户发送第2~n次请求时,不再执⾏init(),⽽直接执⾏service()⽅法,调取doGet()/doPost()⽅法。

在这里插入图片描述

  • Servlet中的生命周期方法
    1. 被创建:执行init()方法,只执行一次
    	Servlet什么时候被创建?
    		 默认情况下,第一次被访问时,Servlet被创建。
    		 通过web.xml文件,可以配置Servlet的创建时机。
    			 在<servlet>标签下配置
    				1. 第一次被访问时,创建
                		<load-on-startup>的值为负数。
    	            2. 在服务器启动时,创建
    	                <load-on-startup>的值为0或正整数,
    	                正数情况下,数值越⼩,加载该Servlet的优先级越⾼。
    	    Servlet的init()方法,只执行一次,说明一个Servlet在内存中只存在一个对象,Servlet是单例的
    		多个用户同时访问时,可能存在线程安全问题。
    		解决:尽量不要在Servlet中定义成员变量。即使定义了成员变量,也不要对修改值
    
    2. 提供服务:执行service()方法,执行多次
    	 每次访问Servlet时,service()方法都会被调用一次。
    	
    3. 被销毁:执行destroy()方法,只执行一次
    	 Servlet被销毁时执行。服务器关闭时,Servlet被销毁
    	只有服务器正常关闭时,才会执行destroy()方法。
    	 destroy()方法在Servlet被销毁之前执行,一般用于释放资源
    	
    *  Servlet3.0:
    		好处:
    			 支持注解配置。可以不需要web.xml了。
    	
    		步骤:
    			1. 创建JavaEE项目,选择Servlet的版本3.0以上,可以不创建web.xml
    			2. 定义一个类,实现Servlet接口
    			3. 复写方法
    			4. 在类上使用@WebServlet注解,进行配置
    				* @WebServlet("资源路径")
    				示例:
    				@WebServlet(urlPatterns = {"/test"},
    					        initParams ={
    					            @WebInitParam(name = "code",value = "utf-8")         
    					        },loadOnStartup = 1)
    			    public class TestServlet extends HttpServlet {}
    

关于@WebServlet的注解类如下:

	@Target({ElementType.TYPE})
	@Retention(RetentionPolicy.RUNTIME)
	@Documented
	public @interface WebServlet {
	    String name() default "";//相当于<Servlet-name>
	
	    String[] value() default {};//代表urlPatterns()属性配置
	
	    String[] urlPatterns() default {};//相当于<url-pattern>
	
	    int loadOnStartup() default -1;//相当于<load-on-startup>
	
	    WebInitParam[] initParams() default {};
	
	    boolean asyncSupported() default false;
	
	    String smallIcon() default "";
	
	    String largeIcon() default "";
	
	    String description() default "";
	
	    String displayName() default "";
	}
  • Servlet体系结构
    	Servlet -- 接口
    		|
    	GenericServlet -- 抽象类
    		|
    	HttpServlet  -- 抽象类
    
    	* GenericServlet:将Servlet接口中其他的方法做了默认空实现,只将service()方法作为抽象
    		* 将来定义Servlet类时,可以继承GenericServlet,实现service()方法即可
    
    	* HttpServlet:对service()方法做了详细的实现,不再需要我们写代码判断用户的请求方式,对http协议的一种封装,简化操作
    		1. 定义类继承HttpServlet
    		2. 复写doGet/doPost方法
    
  • Servlet相关配置
    1. urlpartten:Servlet访问路径(资源路径)
    		1. 一个Servlet可以定义多个访问路径 : @WebServlet({"/d4","/dd4","/ddd4"})
    		2. 路径定义规则:
    			1. /xxx:路径匹配
    			2. /xxx/xxx:多层路径,目录结构
    			3. *.do:扩展名匹配
    			ps: /user/.do、/.do、test*.do都是非法的,启动时候会报错
    

二、HTTP

1. HTTP协议简介

  • 概念:超⽂本传输协议(英⽂:HyperText Transfer Protocol,缩写:HTTP)是⼀种⽤于分布式、协作式和超媒体信息系统的应⽤层协议。HTTP是万维⽹的数据通信的基础。定义了客户端和服务器端通信时,交互报文的格式

  • 特点:
    1. 基于TCP/IP的高级协议
    2. 默认端口号:80
    3. 基于请求/响应模型的:一次请求对应一次响应
    4. 在HTTP/1.0中默认使⽤短连接。也就是说,客户端和服务器每进⾏⼀次HTTP操作,就建⽴⼀次连接,任务结束就中断连接。
    5. 无状态协议:HTTP协议自身不对请求和响应之间的通信状态进行保存。每次请求之间相互独立,不能交互数据。
    6. HTTP/1.1起,默认使⽤⻓连接,⽤以保持连接特性。使⽤⻓连接的HTTP协议,会在响应头加⼊这⾏
    代码:

    Connection:keep-alive

    在使⽤⻓连接的情况下,当⼀个⽹⻚打开完成后,客户端和服务器之间⽤于传输HTTP数据的TCP连接不会关闭,客户端再次访问这个服务器时,会继续使⽤这⼀条已经建⽴的连接。Keep-Alive不会永久保持连接,它有⼀个保持时间,可以在不同的服务器软件(如Apache)中设定这个时间。实现⻓连接需要客户端和服务端都⽀持⻓连接。

  • 请求消息(报文)格式: 客户端发送给服务器端的数据

    1. 请求行
    	请求方式                请求url                                       请求协议/版本
    	GET                http://localhost:8080/thumbupTest/index.jsp	        HTTP/1.1
    
    	 请求方式:
    		 HTTP协议有7中请求方式,常用的有2种
    			 GET:
    				1. 请求参数在请求行中,在url后。
    				2. 请求的url长度有限制的
    				3. 不太安全
    			 POST:
    				1. 请求参数在请求体中
    				2. 请求的url长度没有限制的
    				3. 相对安全
    2. 请求头:客户端浏览器告诉服务器一些信息
    	请求头名称: 请求头值
    	常见的请求头:
    		1. User-Agent:浏览器告诉服务器,我访问你使用的浏览器版本信息
    			* 可以在服务器端获取该头的信息,解决浏览器的兼容性问题
    
    		2. Referer:http://localhost/login.html
    			告诉服务器,我(当前请求)从哪里来?
    				作用:
    					1. 防盗链:
    					   通过条件判断指定从某条URL跳转过来到当前页面才正常显示
    					2. 统计工作:
    					   可以统计从某个URL跳转到当前页面的用户数
    3. 请求空行
    	空行,就是用于分割POST请求的请求头,和请求体的。
    4. 请求体(正文):
    	 封装POST请求消息的请求参数的
    	 格式: username=zhangsan	
    
    * 字符串格式(请求头):
    	POST /login.html	HTTP/1.1
    	Host: localhost
    	User-Agent: Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:60.0) Gecko/20100101 Firefox/60.0
    	Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
    	Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2
    	Accept-Encoding: gzip, deflate
    	Referer: http://localhost/login.html
    	Connection: keep-alive
    	Upgrade-Insecure-Requests: 1		
    
  • 响应消息(报文)格式: 服务器端发送给客户端的数据

    数据格式:
    1. 响应行
    	1. 组成:协议/版本     响应状态码      状态码描述
    		示例: HTTP/1.1      200             OK
    	2. 响应状态码:服务器告诉客户端浏览器本次请求和响应的一个状态。
    		1. 状态码都是3位数字 
    		2. 分类:
    			1. 1xx:服务器接收客户端消息,但没有接受完成,等待一段时间后,发送1xx多状态码
    			2. 2xx:成功。代表:200
    			3. 3xx:重定向。代表:302(重定向),304(访问缓存)
    			4. 4xx:客户端错误。
    				* 代表:
    					* 404(请求路径没有对应的资源) 
    					* 405:请求方式没有对应的doXxx方法
    			5. 5xx:服务器端错误。代表:500(服务器内部出现异常)
    2. 响应头:
    	1. 格式:头名称: 值
    	2. 常见的响应头:
    		1. Content-Type:服务器告诉客户端本次响应体数据格式以及编码格式
    		2. Content-disposition:服务器告诉客户端以什么格式打开响应体数据
    			* 值:
    				* in-line:默认值,在当前页面内打开
    				* attachment;filename=xxx:以附件形式打开响应体。文件下载需设置此响应头
    3. 响应空行
    4. 响应体:传输的数据
    5. 
    * 响应字符串格式
    		HTTP/1.1 200 OK
    		Content-Type: text/html;charset=UTF-8
    		Content-Length: 101
    		Date: Wed, 06 Jun 2018 07:08:42 GMT
    
    		<html>
    		  <head>
    		    <title>$Title$</title>
    		  </head>
    		  <body>
    		  hello , response
    		  </body>
    		</html>
    
    
  • 常见状态码

    状态码状态码描述含义
    100Continue只有⼀部分请求被服务器接收,但只要没被服务器拒绝,客户端就会延续这个请求
    101Switching Protocols服务器交换机协议
    200OK请求被确认
    201Created请求时完整的,新的资源被创建
    202Accepted请求被接受,但未处理完
    300Multiple Choices⼀个超链接表,⽤户可以选择⼀个超链接并访问,最⼤⽀持5个超链接
    301Moved Permanently被请求的⻚⾯已经移动到了新的URL下
    302Found被请求的⻚⾯暂时性地移动到了新的URL下
    303See Other被请求的⻚⾯可以在⼀个不同的URL下找到
    400Bad Request服务器⽆法识别请求
    403Forbidden禁⽌访问所请求的⻚⾯
    404Not Found服务器⽆法找到所请求的⻚⾯
    405Method Not Allowed请求中所指定的⽅法不被允许
    500Internal Server Error请求不完整,服务器遇⻅了出乎意料的状况
    501Not Implemented请求不完整,服务器不提供所需要的功能
    502Bad Gateway请求不完整,服务器从上游服务器接受了⼀个⽆效的响应
    503Service Unavailable请求不完整,服务器暂时重启或关闭
    504Gateway Timeout⽹关超时
    505HTTP Version Not Supported服务器不⽀持所指定的HTTP版本

2.HTTPS

HTTPS (全称:Hyper Text Transfer Protocol over SecureSocket Layer),
是以安全为目标的 HTTP 通道,在HTTP的基础上通过传输加密和身份认证保证了传输过程的安全性 。
HTTPS 在HTTP 的基础下加入SSL,HTTPS 的安全基础是SSL,因此加密的详细内容就需要SSL。 
HTTPS 存在不同于 HTTP 的默认端口及一个加密/身份验证层(在 HTTP与 TCP 之间)。
这个系统提供了身份验证与加密通讯方法。
它被广泛用于万维网上安全敏感的通讯,例如交易支付等方面。

三、Request

  • Request对象和Response对象的原理

    1.客户端发送请求后,Tomcat服务器会根据请求URL中的资源路径(Servlet访问路径),创建对应的Servlet对象。
    2. Tomcat服务器,会创建request和response对象,并将请求消息(请求报文)数据封装到request对象中。
    3. Tomcat将request和response两个对象传递给service()方法,并且调用service()方法。
    4. 后续,程序员可以通过request对象获取请求消息数据,通过response对象设置响应消息(响应报文)数据。
    5. 服务器给浏览器作出响应之前会从response对象中获取程序员设置的响应消息数据。

  • Request对象继承体系结构

    ServletRequest		--	接口
    	|	继承
    HttpServletRequest	-- 接口
    	|	实现
    org.apache.catalina.connector.RequestFacade 类(tomcat)
    
    public class RequestFacade implements HttpServletRequest 
    
  • Request功能

    1. 获取请求消息(报文)数据
    	1. 获取请求行数据
    		* GET        http://localhost:8080/day14/demo1?name=zhangsan           HTTP/1.1
    		* 方法:
    			1. 获取请求方式 :GET
    				* String getMethod()  
    			2. (*)获取虚拟目录:/day14
    				* String getContextPath()
    			3. 获取Servlet路径: /demo1
    				* String getServletPath()
    			4. 获取get方式请求参数:name=zhangsan
    				* String getQueryString()
    			5. (*)获取请求URI:/day14/demo1
    				* String getRequestURI():		/day14/demo1
    				* StringBuffer getRequestURL()  :http://localhost/day14/demo1
    
    				* URL:统一资源定位符 : http://localhost/day14/demo1	    例如:中华人民共和国
    				* URI:统一资源标识符 : /day14/demo1					例如:共和国
    			
    			6. 获取协议及版本:HTTP/1.1
    				* String getProtocol()
    
    			7. 获取客户机的IP地址:
    				* String getRemoteAddr()  // 0:0:0:0:0:0:0:1
    			
    	2. 获取请求头数据
    		* 方法:
    			* (*)String getHeader(String name):通过请求头的名称获取请求头的值
    			* Enumeration<String> getHeaderNames():获取所有的请求头名称
    		
    	3. 获取请求体(正文)数据:
    		* 请求体:只有POST请求方式,才有请求体,在请求体(正文)中封装了POST请求的请求参数
    		* 步骤:
    			1. 获取流对象
    				*  BufferedReader getReader():获取字符输入流,只能操作字符数据
    				*  ServletInputStream getInputStream():获取字节输入流,可以操作所有类型数据
    
    			2. 再从流对象中拿数据
    2. 其他功能:
    	1. 获取请求参数通用方式:不论get还是post请求方式都可以使用下列方法来获取请求参数
    		1. String getParameter(String name):根据参数名称获取参数值    username=zs&password=123
    		2. String[] getParameterValues(String name):根据参数名称获取参数值的数组  hobby=xx&hobby=game
    		3. Enumeration<String> getParameterNames():获取所有请求的参数名称
    		4. Map<String,String[]> getParameterMap():获取所有参数的map集合
    
    	* 中文乱码问题:
    		* get方式:tomcat 8 已经将get方式乱码问题解决了
    		* Tomcat 8以前:
    			⽅式1: 
    				//针对于get提交时中⽂乱码
    				String s=new String(请求参数.getBytes("ISO-8859-1"),"UTF-8");
    				示例: 
    				String s=new String(request.getParameter("key").getBytes("ISO-8859-1"),"GBK");
    				
    			⽅式2:修改tomcat中配置⽂件:
    				//使⽤于get提交在Tomcat⽬录结构\conf\server.xml中设置字符集  URLEncoding
    				<Connector port="8080" protocol="HTTP/1.1"  
    					connectionTimeout="20000" 
    					 redirectPort="8443" URIEncoding="UTF-8" />
    
    		* post方式:会乱码
    			* 解决:在获取参数前,设置request的编码request.setCharacterEncoding("utf-8");
    	2. 请求转发:一种在服务器内部的资源跳转方式
    		1. 步骤:
    			1. 通过request对象获取请求转发器对象:RequestDispatcher getRequestDispatcher(String path)
    			2. 使用RequestDispatcher对象来进行转发:forward(ServletRequest request, ServletResponse response) 
    
    		2. 特点:
    			1. 浏览器地址栏路径不发生变化
    			2. 只能转发到当前服务器内部资源中。
    			3. 转发是一次请求,可以使用request对象来共享数据
    	
    	3. 共享数据:
    	* 域对象:一个有作用范围的对象,可以在范围内共享数据
    	* request域:代表一次请求的范围,一般用于请求转发的多个资源中共享数据
    	* 方法:
    		1. void setAttribute(String name,Object obj):存储数据
    		2. Object getAttitude(String name):通过键获取值
    		3. void removeAttribute(String name):通过键移除键值对
    
    	4. 获取ServletContext:
    		* ServletContext getServletContext()
    
  • GET和POST的区别

    1、GET请求:请求的数据会附加在URL之后,以?分割URL和传输数据,多个参数⽤&连接。
    	URL的编码格式采⽤的是ASCII编码,而不是uniclde,
    	即是说所有的非ASCII字符都要编码之后再传输。
    	POST请求:请求的数据放在请求消息(报文)的请求体(正文)中。
    	因此,GET请求的数据会暴露在地址栏中,⽽POST请求则不会。
    2、传输数据的大小
    	在HTTP规范中,没有对URL的⻓度和传输的数据⼤⼩进⾏限制。
    	但是在实际开发过程中,对于GET,特定的浏览器和服务器对URL的⻓度有限制。
    	因此,在使⽤GET请求时,传输数据会受到URL⻓度的限制。
    	对于POST,由于不是URL传值,理论上是不会受限制的,
    	但是实际上各个服务器会规定对POST提交数据⼤⼩进⾏限制,
    	Apache、IIS都有各⾃的配置。	
    3、安全性
    	POST的安全性比GET的相对较⾼。
    

四、Response

  • 功能: 设置响应消息

    1. 设置响应行
    		1. 格式:HTTP/1.1 200 ok
    		2. 设置状态码:setStatus(int sc) 
    2. 设置响应头:setHeader(String name, String value) 
    	添加响应头:void addHeader(String name, String value)     //两者作用一致
    3. 设置响应体:
    	* 使用步骤:
    		1. 获取输出流
    			* 字符输出流:PrintWriter getWriter()
    
    			* 字节输出流:ServletOutputStream getOutputStream()
    
    		2. 使用输出流,将数据输出到客户端浏览器
    
  • 案例

    1. 完成重定向
    	* 重定向:资源跳转的方式
    	* 代码实现:
    		//1. 设置状态码为302
            response.setStatus(302);
            //2.设置响应头location
            response.setHeader("location","/day15/responseDemo2");
            //简单的重定向方法
            response.sendRedirect("/day15/responseDemo2");
            
    * forward 和  redirect 区别
    	* 重定向的特点:redirect
    		1. 地址栏发生变化
    		2. 重定向可以访问其他站点(服务器)的资源
    		3. 重定向是两次请求。不能使用request对象来共享数据
    	* 转发的特点:forward
    		1. 转发地址栏路径不变
    		2. 转发只能访问当前服务器下的资源
    		3. 转发是一次请求,可以使用request对象来共享数据
    
    2. 服务器输出字符数据到浏览器
    		* 步骤:
    			1. 获取字符输出流
    			2. 输出数据
    
    		* 注意:
    			* 乱码问题:
    				1. PrintWriter pw = response.getWriter();
    					获取的流的默认编码是ISO-8859-1
    				2. 设置该流的默认编码     
    				  response.setHeader("content-type","text/htm;charset=utf-8")
    				 告诉浏览器响应体所使用的编码
    
    				//简单的形式,设置编码,是在获取流之前设置
        			response.setContentType("text/html;charset=utf-8");
        			
    	3. 服务器输出字节数据到浏览器
    		* 步骤:
    			1. 获取字节输出流  ServletOutputStream getOutputStream()
    			2. 输出数据
    
  • 路径写法

    1. 路径分类
    	1. 相对路径:通过相对路径不可以确定唯一资源
    		* 如:./index.html    或者 servletDemo1
    		* 不以/开头、以.开头路径
    
    		* 规则:找到当前资源和目标资源之间的相对位置关系
    			* ./:当前目录
    			* ../:后退一级目录
    	2. 绝对路径:通过绝对路径可以确定唯一资源
    		* 如:http://localhost/day15/responseDemo2		/day15/responseDemo2
    		* 以/开头的路径
    
    		* 规则:判断定义的路径是给谁用的?判断请求将来从哪儿发出
    			* 给客户端浏览器使用:需要加虚拟目录(项目的访问路径)
    				* 建议虚拟目录动态获取:request.getContextPath()
    				*如 <a> , <form> 标签重定向到某个页面
    			* 给服务器使用:不需要加虚拟目录,使用相对路径即可
    				* 转发路径
    

五、ServletContext

  1. 概念:ServletContext是javax.servlet包内定义的接口,Web容器会为每个Web程序构造一个实现该接口的对象实例,代表整个web应用,Servlet可以和web容器(服务器)进行交互
  2. 获取:
    1. 通过request对象获取
      request.getServletContext();
    2. 通过HttpServlet获取
      this.getServletContext();
  3. 功能:
    1. 获取MIME类型:
      • MIME类型:在互联网通信过程中定义的一种文件数据类型

        • 格式: 大类型/小类型 text/html image/jpeg
      • 获取:String getMimeType(String file)

    2. 域对象:共享数据
      1. void setAttribute(String name,Object value)

      2. Object getAttribute(String name)

      3. void removeAttribute(String name)

        // ServletContext对象范围:所有用户所有请求的数据

    3. 获取文件的真实(服务器)路径
      1. 方法:String getRealPath(String path)
        String b = context.getRealPath("/b.txt");//web目录下资源访问
        System.out.println(b);

        String c = context.getRealPath("/WEB-INF/c.txt");//WEB-INF目录下的资源访问
        System.out.println(c );

        String a = context.getRealPath("/WEB-INF/classes/a.txt");//src目录下的资源访问
        System.out.println(a);

六、ServletConfig(了解)

  • 概述

    ServletConfig是用来获得Servlet相关的配置的对象

  • 获取

    通过当前Servlet实例来获取
    ServletConfig config = this.getServletConfig();

  • 功能
    1. 获取ServletContext对象
      * ServletContext getServletContext();
    2. 获取当前Servlet的初始化参数
      * String getInitParameter(String name)
    3. 获取当前Servlet的所有初始化参数的名称
      * Enumeration getInitParameterNames()
    4. 获取当前Servlet实例的名称
      * String getServletName()
  • Servlet初始化参数
  1. 针对某个Servlet的初始化参数

    实现⽅式:
    (1) web.xml中先定义初始化参数
    	<servlet>
    		 <servlet-name></servlet-name>
    	 	<servlet-class></servlet-class>
    	 	<init-param>
    	 	<param-name>encoding</param-name>
    	 	<param-value>utf-8</param-value>
    	 	</init-param>
    	</servlet>
    (2) 注解方式实现 initParams
    	@WebServlet(urlPatterns = {"/test"},
    			        initParams ={
    			            @WebInitParam(name = "code",value = "utf-8")         
    			        })
    	 public class TestServlet extends HttpServlet {}
    
    *获取:
    String encode = this.getServletConfig().getInitParameter("encoding");
    

    2.对于当前web程序中所有的Servlet都有效的初始化参数

    (1)定义
    	<context-param>
    			<param-name>forAll</param-name>
    			<param-value>utf-8</param-value>
        </context-param>
    (2)获取
    this.getServletConfig().getServletContext().getInitParameter("forAll");
    

七、Cookie和Session

1.会话技术

  1. 会话:会话跟踪是Web程序中常⽤的技术,⽤来跟踪⽤户的整个会话。保持对⽤户会话期间的数据管理。常⽤的会话跟踪技术是Cookie与Session。一次会话中包含多次请求和响应。

    一次会话:浏览器第一次给服务器资源发送请求,会话建立,直到有一方断开为止

  2. 功能:在一次会话的范围内的多次请求间,共享数据
  3. 方式:
    1. 客户端会话技术:Cookie
    2. 服务器端会话技术:Session

2.Cookie

  • 概念
    Cookie是客户端(⼀般指浏览器)请求服务器后,服务器发给客户端的⼀个辨认标识,保存在客户端,当客户端再次向服务器发送请求时,会携带着这个辨认标识,服务器就可以通过这个标识来识别客户端的身份或状态等。

  • Cookie的设置和获取

    • 使用步骤:
      1. 创建Cookie对象,绑定数据
      * new Cookie(String name, String value)
      2. 发送Cookie对象
      * response.addCookie(Cookie cookie)
      3. 获取Cookie,拿到数据
      * Cookie[] request.getCookies()
      *
      * 示例:
      * Cookie[] cs = request.getCookies();
      * // 通过遍历获取各个cookie的值
      for (Cookie c : cs) {
      String name = c.getName(); //获取cookie的名称
      String value = c.getValue();//获取cookie的值
      }
  • 实现原理

  • 基于响应头set-cookie和请求头cookie实现。
  1. 客户端请求服务器中发送cookie的Servlet,服务器设置set-cookie: msg = hello头发送到客户端浏览器。
  2. 浏览器将msg = hello保存到本地cookie中,后续的请求都会通过请求头Cookie携带此客户端的cookie数据,服务器可以获取cookie进行相应的操作

在这里插入图片描述

  • 细节
  1. 一次可不可以发送多个cookie?
    * 可以
    * 可以创建多个Cookie对象,使用response调用多次addCookie方法发送cookie即可。
  2. cookie在浏览器中保存多长时间?
    1. 默认情况下,当浏览器关闭后,Cookie数据被销毁
    2. 持久化存储:
    * setMaxAge(int seconds)
    (1)正数:将Cookie数据写到硬盘的文件中。持久化存储。并指定cookie存活时间,时间到后,cookie文件自动失效
    (2)负数:默认值
    (3)零:删除cookie信息
  3. cookie能不能存中文?
    * 在tomcat 8 之前 cookie中不能直接存储中文数据。
    * 需要将中文数据转码—一般采用URL编码(%E3)
    * 在tomcat 8 之后,cookie支持中文数据。特殊字符还是不支持,建议使用URL编码存储,URL解码解析
  4. cookie共享问题?
    1. 假设在一个tomcat服务器中,部署了多个web项目,那么在这些web项目中cookie能不能共享?
    * 默认情况下cookie不能共享。
    * setPath(String path):设置cookie的获取范围。默认情况下,被设置为当前的虚拟目录
    * 如果要共享,则可以将path设置为"/"
    ~
    2. 不同的tomcat服务器间cookie共享问题?
    ​ * setDomain(String path):如果设置一级域名相同,那么多个服务器之间cookie可以共享
    ​ * setDomain(".baidu.com"),那么tieba.baidu.com和news.baidu.com中cookie可以共享。
  5. Cookie的特点和作用
    * 特点
    1. cookie存储数据在客户端浏览器
    2. 浏览器对于单个cookie 的大小有限制(4kb) 以及 对同一个域名下的总cookie数量也有限制,不同浏览器对于cookie的数量限制不同。
    * 作用:
    1. cookie一般用于存出少量的不太敏感的数据
    2. 在不登录的情况下,完成服务器对客户端的身份识别

3.Session

  1. 概念:服务器端会话技术,在一次会话的多次请求间共享数据,将数据保存在服务器端的对象中。HttpSession

  2. 快速入门:

    1. 获取HttpSession对象:
      HttpSession session = request.getSession();
    2. 使用HttpSession对象:
      Object getAttribute(String name)
      void setAttribute(String name, Object value)
      void removeAttribute(String name)
  3. 原理

    • Session的实现是依赖于Cookie的。
    • 第一次获取Session,没有Cookie,服务器会在内存中创建一个新的Session对象,假设其对应的Id属性值为 ID = 742938a4289。
    • 服务器会自动设置响应头set-cookie:JSESSIONID= 742938a4289响应消息给浏览器。
    • 浏览器解析set-cookie响应头,将JSESSIONID=742938a4289存入Cookie请求头。
    • 后续服务器再次创建Session对象时会根据请求头Cookie中的JSESSIONID先在内存中寻找对应的session实例,然后返回其引用。
  4. 细节:
    ​ 1. 当客户端关闭后,服务器不关闭,两次获取session是否为同一个?
    ​ * 默认情况下。不是。
    ​ * 如果需要相同,则可以创建Cookie,键为JSESSIONID,设置最大存活时间,让 cookie持久化保存。
    ​ Cookie c = new Cookie(“JSESSIONID”,session.getId());
    ​ c.setMaxAge(60*60); //单位:秒
    ​ response.addCookie(c );

     2. 客户端不关闭,服务器关闭后,两次获取的session是同一个吗?
      * 不是同一个,但是要确保数据不丢失。tomcat自动完成以下工作
          * session的钝化:
              * 在服务器正常关闭之前,将session对象序列化到硬盘上
          * session的活化:
              * 在服务器启动后,将session文件转化为内存中的session对象即可。
    
  5. session什么时候被销毁?
    1. 本地服务器正常关闭后不会被销毁,idea上配置的tomcat服务器关闭后会被销毁
    2. session对象调用invalidate() 。
    3. session默认失效时间 30分钟
    * Session的超时时间为maxInactiveInterval属性,可以通过对应的 getMaxInactiveInterval()获取,通过 setMaxInactiveInterval(longinterval)修改
    * 还可以在config目录下的web.xml中选择性配置修改

     	<session-config>
             <session-timeout>30</session-timeout>
         </session-config>
    

    4.浏览器中含有JSESSIONID的cookie被销毁时,服务器会重新创建一个新的session对象,原有的session对象会在失效时间过后被销毁。

  6. session的特点

    1. session用于存储一次会话的多次请求的数据,存在服务器端。
    2. 为了获得更⾼的存取速度,服务器⼀般把Session放在内存⾥。
    3. session可以存储任意类型,任意大小的数据

    什么时候创建Session?
    1.Session在⽤户第⼀次访问page指令中的session属性值不为false的JSP页面时被创建。
    2.若servelt是第浏览器客户端访问的第一个WEB应用的资源,则只有调用了request.getSession()或request.getSession(true)才会创建session对象。

    其中request.getSession(boolean),
    boolean为false时,若没有和当前JSP页面关联的session对象则返回null,若有,则返回true。
    Boolean为true时,一定返回一个session对象,若没有和当前JSP页面关联的session对象,则服务器创建一个新的session对象,若有,直接返回。
    request.getSession()等同于request.getSession(true)。

  • session与Cookie的区别:
    1. session存储数据在服务器端,Cookie在客户端。
    2. session没有数据大小限制,Cookie有,一般为4KB。
    3. session数据安全,Cookie相对于不安全。
    4. Session是由应⽤服务器维持的⼀个服务器端的存储空间,⽤户在连接服务器时,会由服务器⽣成⼀个唯⼀的SessionID,⽤该SessionID 为标识符来存取服务器端的Session存储空间。⽽SessionID这⼀数据则是保存到客户端,⽤Cookie保存的,⽤户提交⻚⾯时,会将这⼀SessionID提交到服务器端,来存取Session数据。这⼀过程,是不⽤开发⼈员⼲预的。所以⼀旦客户端禁⽤Cookie,那么Session也会失效。

八、JSP

1. 概念:
* Java Server Pages: java服务器端页面
* 可以理解为:一个特殊的页面,其中既可以指定定义html标签,又可以定义java代码
* 用于简化书写!!!

2. 原理
* JSP本质上就是一个Servlet,当浏览器访问http://localhost:8080/day9_1/index.jsp。服务器发现后缀为.jsp,它会根据路径找到index.jsp⽂件,会将index.jsp翻译成index_jsp.java⽂件,对这个java⽂件进⾏编译,产⽣⼀个index_jsp.class⽂件,将class⽂件加载运⾏。将JSP翻译成java⽂件,它是将JSP中的所有的HTML代码通过流进⾏输出,也就是说最终翻译成class,被虚拟机加载,它本质是servlet,它就会往回响应,响应回去就是把JSP中的HTML代码以流的⽅式写回浏览器。所以在JSP中展示出了HTML代码。
在这里插入图片描述
3. JSP指令

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

    • 格式:
      <%@ 指令名称 属性名1=属性值1 属性名2=属性值2 … %>
  • 分类:
    1. page :

    • contentType: 配置JSP页面的 contentType属性:等同于response.setContentType()
    1. 设置响应体的mime类型以及字符集
    2. 设置当前jsp页面的编码(只能是高级的IDE才能生效,如果使用低级工具,则需要设置pageEncoding属性设置当前页面的字符集)
    • import:导包
    • errorPage:当前页面发生异常后,会自动跳转到指定的错误页面
    • isErrorPage:标识当前也是是否是错误页面。
      • true:是,可以使用内置对象exception <% String message = exception.getMessage(); %>
      • false:否。默认值。不可以使用内置对象exception

page 指令相关属性:
在这里插入图片描述

  1. include : JSP可以通过include指令来包含其他⽂件。被包含的⽂件可以是JSP⽂件、HTML⽂件或⽂本⽂件。包含的⽂件就好像是该JSP⽂件的⼀部分,会被同时编译执⾏。
    <%@ include file=“⽂件相对 url 地址” %>

  2. taglib : 导入资源(引入jsp标签库)

    • <%@ taglib prefix=“c” uri=“http://java.sun.com/jsp/jstl/core” %>
      • prefix:前缀,自定义的
  3. 注释:

	1. html注释:
		<!-- -->:只能注释html代码片段
	2. jsp注释:推荐使用
		<%-- --%>:可以注释所有

4. JSP的脚本: JSP定义Java代码的方式
1. <% 代码 %>:定义的java代码,在service方法中。service方法中可以定义什么,该脚本中就可以定义什么。
2. <%! 代码 %>:定义的java代码,在jsp转换后的java类的成员位置。
3. <%= 代码 %>:定义的java代码,会输出到页面上。输出语句中可以定义什么,该脚本中就可以定义什么。

5. JSP的内置对象:
* 在jsp页面中不需要获取和创建,可以直接使用的对象
* jsp一共有9个内置对象。

变量名真实类型作用
pageContextPageContext当前页面共享数据,还可以获取其他八个内置对象
requestHttpServletRequest一次请求访问的多个资源(转发)间共享数据。
sessionHttpSession一次会话的多个请求间共享数据
applicationServletContext所有用户间共享数据
responseHttpServletResponseresponse 代表的是对客户端的响应,主要是将JSP容器处理过的对象传回到客户端。只在JSP⻚⾯内有效。
pageObject当前页面(Servlet)的对象,类似于this
outJspWriter输出对象,数据输出到页面上
configServletConfig主要作⽤是取得服务器的配置信息。通过 pageConext对象的 getServletConfig() ⽅法可以获取⼀个config对象。当⼀个Servlet 初始化时,容器把某些信息通过config对象传递给这个Servlet。 开发者可以在web.xml ⽂件中为应⽤程序环境中的Servlet程序和JSP⻚⾯提供初始化参数。
exceptionThrowableexception 对象的作⽤是显示异常信息,只有在包含 isErrorPage=“true” 的⻚⾯中才可以被使⽤,通常用于打印错误信息输出到日志文件,exception.getMessage()
  • out:字符输出流对象。可以将数据输出到页面上。和response.getWriter()类似
    * response.getWriter()和out.write()的区别:
    * 在tomcat服务器真正给客户端做出响应之前,会先找response缓冲区数据,再找out缓冲区数据。
    * response.getWriter()数据输出永远在out.write()之前。

九、 EL &JSTL

1. EL表达式

  1. 概念:Expression Language 表达式语言
  2. 作用:替换和简化jsp页面中java代码的编写
  3. 语法:${表达式}
  4. 注意:
    • jsp默认支持el表达式的。如果要忽略el表达式
      1. 设置jsp中page指令中:isELIgnored=“true” 忽略当前jsp页面中所有的el表达式
      2. ${表达式} :忽略当前这个el表达式
  5. 使用:
    1. 运算:
      • 运算符:
        1. 算数运算符: + - * /(div) %(mod)
          ${30 + 40}
          ${20 div 5}
        2. 比较运算符: > < >= <= == !=
        3. 逻辑运算符: &&(and) ||(or) !(not)
        4. 空运算符: empty
          • 功能:用于判断字符串、集合、数组对象是否为null或者长度是否为0
          • ${empty list}:判断字符串、集合、数组对象是否为null或者长度为0
          • ${not empty str}:表示判断字符串、集合、数组对象是否不为null 并且 长度>0
    2. 获取值
      1. el表达式只能从域对象中获取值
      2. 语法:
        1. ${域名称.键名}:从指定域中获取指定键的值

          • 域名称:
            1. pageScope --> pageContext
            2. requestScope --> request
            3. sessionScope --> session
            4. applicationScope --> application(ServletContext)
          • 举例:在request域中存储了name=张三
          • 获取:${requestScope.name}
        2. ${键名}:表示依次从最小的域中查找是否有该键对应的值,直到找到为止。

        3. 获取对象、List集合、Map集合的值

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

            • 本质上会去调用对象的getter方法
          2. List集合:${域名称.键名[索引]}

            • 索引越界会返回空字符
          3. Map集合:

            • ${域名称.键名.key名称}
            • ${域名称.键名[“key名称”]}
    3. 隐式对象:
      * el表达式中有11个隐式对象
      * pageScope
      * requestScope
      * sessionScope
      * applicationScope
      * pageContext:
      * 获取jsp其他八个内置对象
      * ${pageContext.request.contextPath}:动态获取虚拟目录

2. JSTL

(1) 什么是JSTL
JSP标准标签库(JSTL)是⼀个JSP标签集合,它封装了JSP应⽤的通⽤核⼼功能。
JSTL⽀持通⽤的、结构化的任务,⽐如迭代,条件判断,XML⽂档操作,国际化标签,SQL标签。 除了这些,它还提供了⼀个框架来使⽤集成JSTL的⾃定义标签。
根据JSTL标签所提供的功能,可以将其分为5个类别。核⼼标签 格式化标签 sql标签 xml标签 jstl函数(后⾯详细解释)
(2) JSTL的作⽤和语法格式
作⽤:用于简化和替换jsp页面上的java代码
语法格式:

  1. 下载 jakarta-taglibs-standard-1.1.2.zip 包并解压,将 jakarta-taglibs-standard-1.1.2/lib/ 下的两
    个 jar ⽂件:standard.jar 和 jstl.jar ⽂件拷⻉到 /WEB-INF/lib/ 下。

  2. 在JSP⻚⾯中引⼊<%@ taglib prefix=”⻚⾯使⽤的名称” uri=”功能范围的路径”%>

    在这里插入图片描述

  3. 常用的JSTL标签

    • 核⼼标签
      核⼼标签是最常⽤的 JSTL标签。引⽤核⼼标签库的语法如下:
      <%@ taglib prefix=“c” uri=“http://java.sun.com/jsp/jstl/core” %>
      1. if:相当于java代码的if语句

        1. 属性:
          • test 必须属性,接受boolean表达式
            • 如果表达式为true,则显示if标签体内容,如果为false,则不显示标签体内容
            • 一般情况下,test属性值会结合el表达式一起使用
        2. 注意:
          • c:if标签没有else情况,想要else情况,则可以再定义一个c:if标签
      2. choose:相当于java代码的switch语句

        1. 使用choose标签声明 相当于switch声明
        2. 使用when标签做判断 相当于case
        3. 使用otherwise标签做其他情况的声明 相当于default
      3. foreach:相当于java代码的for语句
        在这里插入图片描述

代码案例:

<%@ page import="java.util.List" %>
<%@ page import="java.util.ArrayList" %>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>

<html>
<head>
    <title>if标签</title>
</head>
<body>

    <%--

    c:if标签
        1. 属性:
            * test 必须属性,接受boolean表达式
                * 如果表达式为true,则显示if标签体内容,如果为false,则不显示标签体内容
                * 一般情况下,test属性值会结合el表达式一起使用

        2. 注意:c:if标签没有else情况,想要else情况,则可以在定义一个c:if标签


    --%>

    <c:if test="true">
        <h1>我是真...</h1>
    </c:if>
    <br>

    <%
        //判断request域中的一个list集合是否为空,如果不为null则显示遍历集合

        List list = new ArrayList();
        list.add("aaaa");
        request.setAttribute("list",list);

        request.setAttribute("number",4);

    %>

    <c:if test="${not empty list}">
        遍历集合...

    </c:if>
    <br>

    <c:if test="${number % 2 != 0}">

            ${number}为奇数

    </c:if>

    <c:if test="${number % 2 == 0}">

        ${number}为偶数

    </c:if>

</body>
</html>

在这里插入图片描述
代码案例:

<%@ page contentType="text/html;charset=UTF-8" language="java" %>

<%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>

<html>
<head>
    <title>choose标签</title>
</head>
<body>

    <%--
        完成数字编号对应星期几案例
            1.域中存储一数字
            2.使用choose标签取出数字         相当于switch声明
            3.使用when标签做数字判断         相当于case
            4.otherwise标签做其他情况的声明  相当于default
    --%>

    <%
        request.setAttribute("number",51);
    %>

    <c:choose>
        <c:when test="${number == 1}">星期一</c:when>
        <c:when test="${number == 2}">星期二</c:when>
        <c:when test="${number == 3}">星期三</c:when>
        <c:when test="${number == 4}">星期四</c:when>
        <c:when test="${number == 5}">星期五</c:when>
        <c:when test="${number == 6}">星期六</c:when>
        <c:when test="${number == 7}">星期天</c:when>

        <c:otherwise>数字输入有误</c:otherwise>
    </c:choose>


</body>
</html>

在这里插入图片描述
代码案例:

<%@ page import="java.util.ArrayList" %>
<%@ page import="java.util.List" %>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>

<%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>

<html>
<head>
    <title>foreach标签</title>
</head>
<body>

<%--

    foreach:相当于java代码的for语句
        1. 完成重复的操作
            for(int i = 0; i < 10; i ++){

            }
            * 属性:
                begin:开始值
                end:结束值
                var:临时变量
                step:步长
                varStatus:循环状态对象
                    index:容器中元素的索引,从0开始
                    count:循环次数,从1开始
        2. 遍历容器
            List<User> list;
            for(User user : list){

            }

            * 属性:
                items:容器对象
                var:容器中元素的临时变量
                varStatus:循环状态对象
                    index:容器中元素的索引,从0开始
                    count:循环次数,从1开始


--%>

<c:forEach begin="1" end="10" var="i" step="2" varStatus="s">
    ${i} <h3>${s.index}<h3> <h4> ${s.count} </h4><br>

</c:forEach>

    <hr>


    <%
        List list = new ArrayList();
        list.add("aaa");
        list.add("bbb");
        list.add("ccc");

        request.setAttribute("list",list);


    %>

    <c:forEach items="${list}" var="str" varStatus="s">

            ${s.index} ${s.count} ${str}<br>

    </c:forEach>


</body>
</html>
  1. 格式化标签
    1、fmt:formatDate 作⽤:将⽇期类型格式化为指定模式的字符串
    属性
    value:将要被格式化的数据
    pattern:格式化的模式,与SimpleDateFormat的参数设置⼀样
    var:格式化后的字符串所要存放的变量,若不指定var,则会将格式化的结果直接显示在⻚⾯
    scope:变量存放的域属性空间,默认page
    type:其取值为date、time、both,表示给出的value是⽇期、时间、还是两者都包含,默认是date

代码案例:


<%@ page import="java.util.Date" %>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>

<%@taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %>
<html>
<head>
    <title>Title</title>
</head>
<body>
   
   <%
     pageContext.setAttribute("mytime",new Date());
   %>

  date=<fmt:formatDate value="${mytime}" pattern="yyyy-MM-dd"></fmt:formatDate>
</body>
</html>

十、Filter

  1. 概念:

    • 生活中的过滤器:净水器,空气净化器,土匪、
    • web中的过滤器:当访问服务器的资源时,过滤器可以将请求拦截下来,完成一些特殊的功能。
    • 过滤器的作用:
      • 一般用于完成通用的操作。如:登录验证、统一编码处理、敏感字符过滤…
  2. 快速入门:

    1. 步骤:
      1. 定义一个类,实现接口Filter
      2. 复写方法
      3. 配置拦截路径
        1. web.xml
        2. 注解
    2. 代码:
    	@WebFilter("/*")//访问所有资源之前,都会执行该过滤器
    	public class FilterDemo1 implements Filter {
    	    @Override
    	    public void init(FilterConfig filterConfig) throws ServletException {	
    	    }		
    	    @Override
    	    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
    	        System.out.println("filterDemo1被执行了....");
    	        //放行
    	        filterChain.doFilter(servletRequest,servletResponse);
    	
    	    }
    	
    	    @Override
    	    public void destroy() {
    	
    	    }
    	}
    
  3. 过滤器细节:

    1. web.xml配置

      demo1
      cn.itcast.web.filter.FilterDemo1


      demo1

      /*

    2. 过滤器执行流程

      1. 执行过滤器
      2. 执行放行后的资源
      3. 回来执行过滤器放行代码下边的代码
    3. 过滤器生命周期方法

      1. init:在服务器启动后,会创建Filter对象,然后调用init方法。只执行一次。用于加载资源
      2. doFilter:每一次请求被拦截资源时,会执行。执行多次
      3. destroy:在服务器关闭后,Filter对象被销毁。如果服务器是正常关闭,则会执行destroy方法。只执行一次。用于释放资源
    4. 过滤器配置详解

      • 拦截路径配置:
        1. 具体资源路径: /index.jsp 只有访问index.jsp资源时,过滤器才会被执行
        2. 拦截目录: /user/* 访问/user下的所有资源时,过滤器都会被执行
        3. 后缀名拦截: *.jsp 访问所有后缀名为jsp资源时,过滤器都会被执行
        4. 拦截所有资源:/* 访问所有资源时,过滤器都会被执行
      • 拦截方式配置:资源被访问的方式
        • 注解配置:
          • 设置dispatcherTypes属性
            1. REQUEST:默认值。浏览器直接请求资源
            2. FORWARD:转发访问资源
            3. INCLUDE:包含访问资源
            4. ERROR:错误跳转资源
            5. ASYNC:异步访问资源

        //浏览器直接请求index.jsp资源时,该过滤器会被执行
        //@WebFilter(value="/index.jsp",dispatcherTypes = DispatcherType.REQUEST)
        //只有转发访问index.jsp时,该过滤器才会被执行
        //@WebFilter(value="/index.jsp",dispatcherTypes = DispatcherType.FORWARD)
        //浏览器直接请求index.jsp或者转发访问index.jsp。该过滤器才会被执行
        //@WebFilter(value="/*",dispatcherTypes ={ DispatcherType.FORWARD,DispatcherType.REQUEST})

        1. web.xml配置
          设置<dispatcher></dispatcher>标签即可
          如: 
            <filter-mapping>
                  <filter-name>demo1</filter-name>
                  <url-pattern>/*</url-pattern>
                  <dispatcher>REQUEST</dispatcher>
            </filter-mapping>
          
    5. 过滤器链(配置多个过滤器)

      • 执行顺序:如果有两个过滤器:过滤器1和过滤器2

        1. 过滤器1
        2. 过滤器2
        3. 资源执行
        4. 过滤器2
        5. 过滤器1
      • 过滤器先后顺序问题:

        1. 注解配置:按照类名的字符串比较规则比较,值小的先执行
          • 如: AFilter 和 BFilter,AFilter就先执行了。
        2. web.xml配置:
          <filter-mapping>谁定义在上边,谁先执行
          
  • 使⽤场景
    1.如何防⽌⽤户未登录就执⾏后续操作
    String name=(String)session.getAttribute(“key”);
    if(name==null){
    //跳转到登录⻚⾯
    }
    2.设置编码⽅式–统⼀设置编码
    3.加密解密(密码的加密和解密)
    4.⾮法⽂字筛选
    5.下载资源的限制
    过滤器的特点:在servlet之前和之后都会被执⾏

十一、Listener

1.什么是监听器
监听器就是监听某个域对象的的状态变化的组件
监听器的相关概念:
事件源:被监听的对象(三个域对象 request、session、servletContext)
监听器:监听事件源对象事件源对象的状态的变化都会触发监听器
注册监听器:将监听器与事件源、事件进⾏绑定。事件源上发生某个事件后,执行监听器代码
响应⾏为:监听器监听到事件源的状态变化时所涉及的功能代码(程序员编写代码)

  • ServletContextListener:监听ServletContext对象的创建和销毁
    • 方法:
      • void contextDestroyed(ServletContextEvent sce) :ServletContext对象被销毁之前会调用该方法
      • void contextInitialized(ServletContextEvent sce) :ServletContext对象创建后会调用该方法
    • 步骤:
      1. 定义一个类,实现ServletContextListener接口
      2. 复写方法
      3. 配置
        1. web.xml
        		<listener>
        				<!-- 监听器所在的路径 -->
        			 <listener-class>cn.itcast.web.listener.ContextLoaderListener</listener-class>
        		</listener>
        
        	
        2. 注解:
        	* @WebListener
        

2.监听器分类
在这里插入图片描述
3.监听三⼤域对象的创建与销毁的监听器

  • ServletContextListener
    监听ServletContext域的创建与销毁的监听器
    Servlet域的⽣命周期
    何时创建:服务器启动创建
    何时销毁:服务器关闭销毁
    ServletContextListener监听器的主要作⽤
    初始化的⼯作:初始化对象、初始化数据(加载数据库驱动、连接池的初始化)
    加载⼀些初始化的配置⽂件(spring的配置⽂件)
    任务调度(定时器—Timer/TimerTask)
  • HttpSessionListener
    监听Httpsession域的创建和销毁的监听器
    HttpSession对象的⽣命周期
    何时创建:第⼀次调⽤request.getSession时创建
    何时销毁:服务器关闭销毁、session过期(默认30分钟,修改默认的30分钟是在
    Tomcat的web.xml,修改当前项⽬的过期时间是在⾃⼰项⽬的web.xml中)、⼿动销毁
    HttpSessionListener监听器的主要作⽤:
    由于每次访问⽹站都会默认创建session对象(jsp⻚⾯中page指令中的session属性默认为
    true,即被访问时创建session),可以⽤于计数⽹站访问过的⼈
  • ServletRequestListener
    监听ServletRequest域创建与销毁的监听器
    ServletRequest的⽣命周期
    创建:每⼀次请求都会创建request
    销毁:请求结束
    ⽤法同上,⽤处不是很⼤,此处省略。

十二、 MVC和三层架构

1.MVC设计模式

  1. jsp演变历史
    1. 早期只有servlet,只能使用response输出标签数据,非常麻烦
    2. 后来又jsp,简化了Servlet的开发,如果过度使用jsp,在jsp中即写大量的java代码,有写html表,造成难于维护,难于分工协作
    3. 再后来,java的web开发,借鉴mvc开发模式,使得程序的设计更加合理性

在这里插入图片描述

  1. MVC:
    1. M:Model,模型。JavaBean
      • 完成具体的业务操作,如:查询数据库,封装对象
    2. V:View,视图。JSP
      • 展示数据
    3. C:Controller,控制器。Servlet
      • 获取用户的输入
      • 调用模型
      • 将数据交给视图进行展示
  • 优缺点:

      1. 优点:
      	1. 耦合性低,方便维护,可以利于分工协作
      	2. 重用性高
    
      2. 缺点:
      	1. 使得项目架构变得复杂,对开发人员要求高
    

JavaBeans :是Java中⼀种特殊的类(换⾔之:JavaBean就是⼀个Java类).
⼀个Java类 ,满⾜以下要求,则可称为⼀个JavaBean
a. public修饰的类,提供public ⽆参构造⽅法
b. 所有属性 都是private
C. 提供getter和setter⽅法
从使⽤层⾯来看,JavaBean分为2⼤类:
a. 封装业务逻辑的JavaBean(eg:LoginDao.java 封装了登录逻辑)
b. 封装数据的JavaBean(实体类:eg:Student.java Vadio.java 。往往对应于数据库中的⼀张
表,即数据库中有个Student表,项⽬中就有个Student.java类)通常:表名=类名,列名=属性名
JavaBean是⼀个可以重复使⽤的组件,通过编写⼀个组件来实现某种通⽤功能,“⼀次编写、任何地⽅执⾏、任何地⽅重⽤”。

2.三层架构
三层架构 通常意义上的三层架构就是将整个业务应⽤划分为:表现层(UI)、业务逻辑层(BLL)、数据访问层(DAL)。区分层次的⽬的即为了“⾼内聚,低耦合”的思想。
1、表现层(UI):通俗讲就是展现给⽤户的界⾯,即⽤户在使⽤⼀个系统的时候他的所⻅所得。
jsp/html
2、业务逻辑层(BLL):针对具体问题的操作,也可以说是对数据层的操作,对数据业务逻辑处理。
servlet,service
3、数据访问层(DAL):该层所做事务直接操作数据库,针对数据的增添、删除、修改、更新、查找
等。dao
表现层实现的代表作品是Struts,springmvc框架,
业务层实现的代表作品是Spring,
持久层实现的代表作品是Hibernate,mybatis。
层就相当于⼀个⿊盒⼦,我们不⽤知道它内部怎么实现,只需要知道如何去调⽤它就⾏了。每层只与上下相邻的两层打交道。当⼀层内部由于技术变迁发⽣变化时,只要接⼝不变,其他层不⽤做任何改变。分层之后灵活性提⾼,也便于团队分⼯开发。

3.三层架构和MVC的区别与联系
在这里插入图片描述

MVC可以是三层中的⼀个表现层框架,属于表现层。三层和mvc可以共存。
三层是基于业务逻辑来分的,⽽MVC是基于⻚⾯来分的。
MVC主要⽤于表现层,3层主要⽤于体系架构,3层⼀般是表现层、中间层、数据层,其中表现层⼜可以分成M、V、C,(Model View Controller)模型-视图-控制器
MVC是表现模式(Presentation Pattern)
三层架构是典型的架构模式(Architecture Pattern)
三层架构的分层模式是典型的上下关系,上层依赖于下层。但MVC作为表现模式是不存在上下关系的,⽽是相互协作关系。即使将MVC当作架构模式,也不是分层模式。MVC和三层架构基本没有可⽐性,是应⽤于不同领域的技术。

十三、Ajax

1. 概念: ASynchronous JavaScript And XML 异步的JavaScript 和 XML
Ajax 是一种在无需重新加载整个网页的情况下,能够更新部分网页的技术。
通过在后台与服务器进行少量数据交换,Ajax 可以使网页实现异步更新。这意味着可以在不重新加载整个网页的情况下,对网页的某部分进行更新。
传统的网页(不使用 Ajax)如果需要更新内容,必须重载整个网页页面。
功能:提升用户的体验

异步和同步:客户端和服务器端相互通信的基础上
* 同步: 客户端必须等待服务器端的响应。在等待的期间客户端不能做其他操作。
* 异步:客户端不需要等待服务器端的响应。在服务器处理请求的过程中,客户端可以进行其他的操作。
* 浏览器的普通交互⽅式(同步)
在这里插入图片描述

2.工作原理:
1.客户端浏览器发送JS请求Ajax引擎。
2.Ajax将JS请求转换成HTTP请求。
3.服务器对接收到的数据进行处理。
4.服务器返回XML、JSON或文本文档类型的数据给Ajax引擎。
5.AJax引擎接收服务器返回的数据进行渲染。
在这里插入图片描述

3. 实现方式:
(1)原生的JS实现方式(了解)

AJAX 的核⼼是 XMLHttpRequest 对象。
不同的浏览器创建 XMLHttpRequest 对象的⽅法是有差异的。
IE 6及以下浏览器使⽤ ActiveXObject,⽽其他的浏览器使⽤名为 XMLHttpRequest 的 JavaScript 内建对象

		//1.创建核心对象
         var xmlhttp;
         if (window.XMLHttpRequest)
         {// code for IE7+, Firefox, Chrome, Opera, Safari
             xmlhttp=new XMLHttpRequest();
         }
         else
         {// code for IE6, IE5
             xmlhttp=new ActiveXObject("Microsoft.XMLHTTP");
         }

         //2. 建立连接
         /*
             参数:
                 1. 请求方式:GET、POST
                     * get方式,请求参数在URL后边拼接。send方法为空参
                     * post方式,请求参数在send方法中定义
                 2. 请求的URL:
                 3. 同步或异步请求:true(异步)或 false(同步)

          */
         xmlhttp.open("GET","ajaxServlet?username=tom",true);

         //3.发送请求
         xmlhttp.send();

         //4.接受并处理来自服务器的响应结果
         //获取方式 :xmlhttp.responseText
         //什么时候获取?当服务器响应成功后再获取

         //当xmlhttp对象的就绪状态改变时,触发事件onreadystatechange。
         xmlhttp.onreadystatechange=function()
         {
             //判断readyState就绪状态是否为4,判断status响应状态码是否为200
             if (xmlhttp.readyState==4 && xmlhttp.status==200)
             {
                //获取服务器的响应结果
                 var responseText = xmlhttp.responseText;
                 alert(responseText);
             }
     	 }   
  • XMLHttpRequest常⽤属性
    • onreadystatechange 属性
      onreadystatechange 属性存有处理服务器响应的函数。
    xmlHttp.onreadystatechange = function() { }
    
    • readyState 属性
      readyState 属性存有服务器响应的状态信息。每当 readyState 改变时,onreadystatechange 函数就会
      被执⾏。
      readyState 属性可能的值:
      在这里插入图片描述
    • responseText 属性
      可以通过 responseText 属性来取回由服务器返回的数据。
      在这里插入图片描述

AJAX状态码说明
1xx:请求收到,继续处理
2xx:操作成功收到,分析、接受
3xx:完成此请求必须进⼀步处理
4xx:请求包含⼀个错误语法或不能完成
5xx:服务器执⾏⼀个 完全有效请求 失败
再具体就如下:
100——客户必须继续发出请求
101——客户要求服务器根据请求转换HTTP协议版本
200——交易成功
201——提示知道新⽂件的URL
202——接受和处理、但处理未完成
203——返回信息不确定或不完整
204——请求收到,但返回信息为空
205——服务器完成了请求,⽤户代理必须复位当前已经浏览过的⽂件
206——服务器已经完成了部分⽤户的GET请求
300——请求的资源可在多处得到
301——删除请求数据
302——在其他地址发现了请求数据
303——建议客户访问其他URL或访问⽅式
304——客户端已经执⾏了GET,但⽂件未变化
305——请求的资源必须从服务器指定的地址得到
306——前⼀版本HTTP中使⽤的代码,现⾏版本中不再使⽤
307——申明请求的资源临时性删除
400——错误请求,如语法错误
401——请求授权失败
402——保留有效ChargeTo头响应
403——请求不允许
404——没有发现⽂件、查询或URl
405——⽤户在Request-Line字段定义的⽅法不允许
406——根据⽤户发送的Accept拖,请求资源不可访问
407——类似401,⽤户必须⾸先在代理服务器上得到授权
408——客户端没有在⽤户指定的饿时间内完成请求
409——对当前资源状态,请求不能完成
410——服务器上不再有此资源且⽆进⼀步的参考地址
411——服务器拒绝⽤户定义的Content-Length属性请求
412——⼀个或多个请求头字段在当前请求中错误
413——请求的资源⼤于服务器允许的⼤⼩
414——请求的资源URL⻓于服务器允许的⻓度
415——请求资源不⽀持请求项⽬格式
416——请求中包含Range请求头字段,在当前请求资源范围内没有range指示值,请求也不包含IfRange请求头字段
417——服务器不满⾜请求Expect头字段指定的期望值,如果是代理服务器,可能是下⼀级服务器不能
满⾜请求
500——服务器产⽣内部错误
501——服务器不⽀持请求的函数
502——服务器暂时不可⽤,有时是为了防⽌发⽣系统过载
503——服务器过载或暂停维修
504——关⼝过载,服务器使⽤另⼀个关⼝或服务来响应⽤户,等待时间设定值较⻓
505——服务器不⽀持或拒绝⽀请求头中指定的HTTP版本

(2).JQeury实现方式

		1. $.ajax()
			* 语法:$.ajax({键值对});
			 //使用$.ajax()发送异步请求
	            $.ajax({
	                url:"ajaxServlet1111" , // 请求路径
	                type:"POST" , //请求方式
	                //data: "username=jack&age=23",//请求参数
	                data:{"username":"jack","age":23},
	                success:function (data) {
	                    alert(data);
	                },//响应成功后的回调函数
	                error:function () {
	                    alert("出错啦...")
	                },//表示如果请求响应出现错误,会执行的回调函数
	
	                dataType:"text"//设置接受到的响应数据的格式(预期服务器返回的数据类型)
	            });
		2. $.get():发送get请求
			* 语法:$.get(url, [data], [callback], [type])
				* 参数:
					* url:请求路径
					* data:请求参数
					* callback:回调函数
					* type:响应结果的类型(预期服务器返回的数据类型)

		3. $.post():发送post请求
			* 语法:$.post(url, [data], [callback], [type])
				* 参数:
					* url:请求路径
					* data:请求参数
					* callback:回调函数
					* type:响应结果的类型

十四、JSON

1. 概念: JavaScript Object Notation (JavaScript对象表示法)
JSON (JavaScript Object Notation) 是⼀种轻量级的数据交换格式。 易于⼈阅读和编写。同时也易于机器解析和⽣成。 它基于JavaScript Programming Language, Standard ECMA-262 3rd Edition -December 1999的⼀个⼦集。 JSON采⽤完全独⽴于语⾔的⽂本格式,但是也使⽤了类似于C语⾔家族的习惯(包括C, C++, C#, Java, JavaScript, Perl, Python等)。 这些特性使JSON成为理想的数据交换语⾔。

* json现在多用于存储和交换文本信息的语法
* 进行数据的传输
* JSON 比 XML 更小、更快,更易解析。

1.jackson

  1. 语法:

    1. 基本规则
      • 数据在名称/值对中:json数据是由键值对构成的
        • 键用引号(单双都行)引起来,也可以不使用引号
        • 值得取值类型:
          1. 数字(整数或浮点数)
          2. 字符串(在双引号中)
          3. 逻辑值(true 或 false)
          4. 数组(在方括号中) {“persons”:[{},{}]}
          5. 对象(在花括号中) {“address”:{“province”:“陕西”…}}
          6. null
      • 数据由逗号分隔:多个键值对由逗号分隔
      • 花括号保存对象:使用{}定义json 格式
      • 方括号保存数组:[]
    2. 获取数据:
      1. json对象.键名

      2. json对象[“键名”]

      3. 数组对象[索引]

      4. 遍历
        //1.定义基本格式

         var person = {"name": "张三", age: 23, 'gender': true};
         var ps = [{"name": "张三", "age": 23, "gender": true},
             {"name": "李四", "age": 24, "gender": true},
             {"name": "王五", "age": 25, "gender": false}];
              //获取person对象中所有的键和值
         //for in 循环
        /* for(var key in person){
             //这样的方式获取不行。因为相当于  person."name"
             //alert(key + ":" + person.key);
             alert(key+":"+person[key]);
         }*/
        
        //获取ps中的所有值
         for (var i = 0; i < ps.length; i++) {
             var p = ps[i];
             for(var key in p){
                 alert(key+":"+p[key]);
             }
         }
        
  2. JSON数据和Java对象的相互转换

    • JSON解析器:
      • 常见的解析器:Jsonlib,Gson,fastjson,jackson
    1. JSON转为Java对象(jackson)
      1. 导入jackson的相关jar包
        在这里插入图片描述

      2. 创建Jackson核心对象 ObjectMapper

      3. 调用ObjectMapper的相关方法进行转换

        1. readValue(json字符串数据,Class)
    2. Java对象转换JSON
      1. 使用步骤:
        1. 导入jackson的相关jar包
        2. 创建Jackson核心对象 ObjectMapper
        3. 调用ObjectMapper的相关方法进行转换
          1. 转换方法:

            • writeValue(参数1,obj):
              参数1:
              File:将obj对象转换为JSON字符串,并保存到指定的文件中
              Writer:将obj对象转换为JSON字符串,并将json数据填充到字符输出流中
              OutputStream:将obj对象转换为JSON字符串,并将json数据填充到字节输出流中
            • writeValueAsString(obj):将对象转为json字符串
          2. 注解:

            1. @JsonIgnore:被此注解标注的属性将不再被转为JSON格式
            2. @JsonFormat:属性值的格式化
              • @JsonFormat(pattern = “yyyy-MM-dd”) 如日期对象格式化,pattern用法与SimpleDateFormat类似
          3. 复杂java对象转换

            1. List:数组 存储的为对象则为对象数组 [{“key”:“value”},{“key”:“value”},{“key”:“value”}]
            2. Map:与对象格式一致

案例:
Person类:

public class Person {

    private String name;
    private int age ;
    private String gender;

    //@JsonIgnore // 忽略该属性
    @JsonFormat(pattern = "yyyy-MM-dd")
    private Date birthday;

	public Person(String name, int age, String gender) {
	        this.name = name;
	        this.age = age;
	        this.gender = gender;
    }
    
    public Date getBirthday() {
        return birthday;
    }

    public void setBirthday(Date birthday) {
        this.birthday = birthday;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public String getGender() {
        return gender;
    }

    public void setGender(String gender) {
        this.gender = gender;
    }

    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", gender='" + gender + '\'' +
                '}';
    }
}

测试类

public class JacksonTest {


    //Java对象转为JSON字符串
    @Test
    public void test1() throws Exception {
        //1.创建Person对象
        Person p  = new Person();
        p.setName("张三");
        p.setAge(23);
        p.setGender("男");

        //2.创建Jackson的核心对象  ObjectMapper
        ObjectMapper mapper = new ObjectMapper();
        //3.转换
        /*

            转换方法:
                writeValue(参数1,obj):
                    参数1:
                        File:将obj对象转换为JSON字符串,并保存到指定的文件中
                        Writer:将obj对象转换为JSON字符串,并将json数据填充到字符输出流中
                        OutputStream:将obj对象转换为JSON字符串,并将json数据填充到字节输出流中
                writeValueAsString(obj):将对象转为json字符串

         */
        String json = mapper.writeValueAsString(p);
        //{"name":"张三","age":23,"gender":"男"}
        //System.out.println(json);//{"name":"张三","age":23,"gender":"男"}



        //writeValue,将数据写到d://a.txt文件中
        //mapper.writeValue(new File("d://a.txt"),p);

        //writeValue.将数据关联到Writer中
        mapper.writeValue(new FileWriter("d://b.txt"),p);
    }


    @Test
    public void test2() throws Exception {
        //1.创建Person对象
        Person p = new Person();
        p.setName("张三");
        p.setAge(23);
        p.setGender("男");
        p.setBirthday(new Date());



        //2.转换  Object -> JSON
        ObjectMapper mapper = new ObjectMapper();
        String json = mapper.writeValueAsString(p);

        System.out.println(json);//{"name":"张三","age":23,"gender":"男","birthday":1530958029263}
                                //{"name":"张三","age":23,"gender":"男","birthday":"2018-07-07"}
    }


	// List集合 - > JSON    Set集合类似
    @Test
    public void test3() throws Exception {
        //1.创建Person对象
        Person p = new Person();
        p.setName("张三");
        p.setAge(23);
        p.setGender("男");
        p.setBirthday(new Date());

        Person p1 = new Person();
        p1.setName("张三");
        p1.setAge(23);
        p1.setGender("男");
        p1.setBirthday(new Date());

        Person p2 = new Person();
        p2.setName("张三");
        p2.setAge(23);
        p2.setGender("男");
        p2.setBirthday(new Date());


        //创建List集合
        List<Person> ps = new ArrayList<Person>();
        ps.add(p);
        ps.add(p1);
        ps.add(p2);


        //2.转换
        ObjectMapper mapper = new ObjectMapper();
        //   List -> JSON
        String json = mapper.writeValueAsString(ps);
        // [{},{},{}]
        //[{"name":"张三","age":23,"gender":"男","birthday":"2018-07-07"},{"name":"张三","age":23,"gender":"男","birthday":"2018-07-07"},{"name":"张三","age":23,"gender":"男","birthday":"2018-07-07"}]
        System.out.println(json);
        
        //  JSON -> List
        List<Person> list = mapper.readValue(json, new TypeReference<List<Person>>() {
        });  // class java.util.ArrayList

        for (Person pp : list) {
            System.out.println(pp);
        }
    }

	//Map集合 -> JSON
    @Test
    public void test4() throws Exception {
        //1.创建map对象
        Map<String,Object> map = new HashMap<String,Object>();
        map.put("name","张三");
        map.put("age",23);
        map.put("gender","男");


        //2.转换 map -> JSON
        ObjectMapper mapper = new ObjectMapper();
        String json = mapper.writeValueAsString(map);
        //{"name":"张三","age":23,"gender":"男"}
        System.out.println(json);//{"gender":"男","name":"张三","age":23}

		 //  JSON -> Map
		 //方式一: //class   java.util.LinkedHashMap
        Map<String,Object> hm = mapper.readValue(json, Map.class);
        
        //方式二: //class    java.util.LinkedHashMap
        //Map<String, Object> lhm= mapper.readValue(json, new TypeReference<Map<String, Object>>() {
        }); 

        Set<String> set = hm.keySet();
        for (String str : set) {
            System.out.println(str + ":" + hm.get(str));
        }
    }

    //演示 JSON字符串转为Java对象
    @Test
    public void test5() throws Exception {
       //1.初始化JSON字符串
        String json = "{\"gender\":\"男\",\"name\":\"张三\",\"age\":23}";

        //2.创建ObjectMapper对象
        ObjectMapper mapper = new ObjectMapper();
        //3.转换为Java对象 Person对象
        Person person = mapper.readValue(json, Person.class);
        System.out.println(person);
    }

	/**
	     * 将数组转换成json字符串
	     * [{"name":"张三","age":23,"gender":"男"},{"name":"李四","age":24,"gender":"女"},{"name":"王五","age":25,"gender":"男"}]
	     * @throws Exception
	 */
    @Test
    public void test6() throws Exception {
        Person p1 = new Person("张三", 23, "男");
        Person p2 = new Person("李四", 24, "女");
        Person p3 = new Person("王五", 25, "男");
        Person[] persons = {p1, p2, p3};
        ObjectMapper mapper = new ObjectMapper();
        // Array -> JSON
        String json= mapper.writeValueAsString(persons);
        System.out.println(json);
		// JSON ->Array
		Person[] persons = mapper.readValue(json, Person[].class); // [Lcn.itcast.domain.Person;@43195e57
        String content = "";
        for (Person person : persons) {
            content+=person + "  ";
        }
        System.out.println(content);
    }

}

2.Jsonlib

  1. JSON转为Java对象
    1. 导入Jsonlib的相关jar包
    在这里插入图片描述
    java对象和json之间的转换
    《1》单个对象或map集合
    java->json:

    Users user2=new Users();
    user2.setUsername(“李四”);
    user2.setPassword(“abc”);
    user2.setAge(20);
    JSONObject obj=JSONObject.fromObject(user);//obj就是json格式的

    json->java:

    String str="{‘username’:‘李四’,‘password’:‘admin’,‘age’:19}";
    JSONObject json=JSONObject.fromObject(str);
    Users user=(Users)JSONObject.toBean(json,Users.class);

《2》对象集合和json的转换
java集合->json数组:

List list=new ArrayList();
list.add(“dd”);
list.add(“aa”);
JSONArray obj=JSONArray.fromObject(list);//set也是这么转

json数组->java集合:
⽅式1:
String str2="[{‘age’:20,‘password’:‘abc’,‘username’:‘李四’},
{‘age’:10,‘password’:‘adb’,‘username’:‘张三’}]";
JSONArray json2=JSONArray.fromObject(str2);
Object[] obj=(Object[])JSONArray.toArray(json2,Users.class);
⽅式2:
String str3="[{‘age’:20,‘password’:‘abc’,‘username’:‘李四’},
{‘age’:10,‘password’:‘adb’,‘username’:‘展示⼲’}]";
JSONArray json3=JSONArray.fromObject(str3);
//默认转换成ArrayList
List list=(List) JSONArray.toCollection(json3,Users.class);

感谢您能阅读至此!!respect!!!!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值