Java Web基础教程
Java Web基础教程
1. Servlet基础
1.1 什么是Servlet
Servlet是JavaEE中的标准组件之一,专门用于处理客户端的HTTP请求。并且它必须依赖于Servlet容器(Tomcat就是一个标准的Servlet容器)才能运行。因为Servlet实例的创建和销毁都是由容器负责管理的,我们不能自行通过new关键去创建和使用Servlet。
1.2 编写一个简单的Servlet
-
在任意地方创建一个myweb文件夹,这个文件夹相当于一个web项目根目录
-
在根目录下创建WEB-INF子目录
-
在WEB-INF目录下创建src和classes子目录
-
在src目录下编写一个类,继承HttpServlet这个父类
public class HelloServlet extends HttpServlet {
}
- 重写父类的service方法,这个就是专门处理客户端请求的方法,web容器会为这个方法传入两个参数HttpServletRequest和HttpServletResponse,并且这个方法还需要抛出ServletException和IOException给容器捕获
public class HelloServlet extends HttpServlet {public void service(HttpServletRequest request, HttpServletResponse response)throws ServletException, IOException{//设置响应类型及编码response.setContentType("text/html;charset=utf-8");//获取字符输出流输出html信息response.getWriter().println("<h1>Hello Servlet</h1>")}
}
- 编译Servlet,需要依赖servlet-api.jar文件,将它添加到classpath中
javac -cp d:\servlet-api.jar; HelloServlet.java
-
将编译后的HelloServlet.class文件剪切到classes目录中
-
在WEB-INF目录下创建并编辑web.xml文件,为servlet配置请求映射
<?xml version="1.0" encoding="utf-8"?>
<!-- 配置根节点 -->
<web-app><!-- 配置servlet类 --><servlet><!-- 指定servlet的别名 --><servlet-name>hello</servlet-name><!-- 指定Servlet的完整类名--><servlet-class>HelloServlet</servlet-class></servlet><!-- 配置请求映射--><servlet-mapping><!-- 这里的servlet-name和上面的servlet-name要一一对应 --><servlet-name>hello</servlet-name><!-- 配置请求映射的url,必须以“/”开头--><url-pattern>/hello</url-pattern></servlet-mapping>
</web-app>
-
将myweb项目拷贝到tomcat的webapps目录中,并执行bin目录下的startup.bat启动容器
-
打开浏览器,在地址栏输入http://localhost:8080/myweb/hello访问Servlet
1.3 Servlet的请求处理流程
浏览器发起http的请求,这个请求首先会被servlet容器(Tomcat)截获,然后容器会根据web.xml文件中配置servlet的来找到相应的这个别名,然后再根据这个别名找到具体Servlet的类,然后容器会创建这个Servlet类的实例并调用service方法来处理这个请求。通常Servlet是以单实例多线程的方式处理客户端请求。
1.4 Servlet的生命周期
所谓的生命周期,就是从Servlet的创建一直到它销毁的整个过程。并且它的这个生命周期都是由Servlet容器(如:Tomcat)负责管理和维护的。
1.4.1 Servlet对象创建的过程
当第一次请求某个Servlet的时候,容器会先查找之前有没有创建过这个Servlet的实例,如果没有则创建一个并缓存起来。后续相同的请求都由这个缓存的对象来处理。(注意:这里说的是第一次请求时创建。另外一种情况则是在容器启动的时候就创建Servlet的实例,在web.xml中为Servlet指定配置,这个配置的值是一个整型,哪一个Servlet配置的数值越小,创建的优先级别越高)
1.4.2 生命周期方法
方法名 | 描述 |
---|---|
init | 在Servlet对象创建之后立即执行的初始化方法,且只执行一次 |
service | 核心的请求处理方法,这个方法可以执行多次 |
destroy | 容器准备销毁Servlet实例之前执行这个方法,也是执行一次(容器重启或关闭) |
1.5 HTTP报文组成
1.5.1 请求报文
请求行:请求报文的第一行就是请求行。包括请求方法、请求URL地址、HTTP协议版本。
请求头:请求行之后的信息就是请求头,它是以“名称:内容”的格式体现。主要包括服务器主机地址及端口、连接状态、系统信息、编码、语言等等。
请求体:请求头结束之后会有一个空行,空行之后就是请求体的内容。通常使用POST提交的数据信息会存放在请求体当中,然后传递给服务器。
1.5.2 响应报文
状态行:主要包括HTTP协议、响应状态码(例如:200表示OK,成功响应)。
响应头:主要包括服务器信息、响应的类型及编码、内容的长度、响应的时间等。
响应体:服务端可以将信息数据携带到响应体中,带回客户端。
1.6 HTTP请求方法
在HTTP/1.1协议中,请求方法主要包括8个,下面列举常用的请求方法进行说明。
方法 | 说明 |
---|---|
GET | 向服务器请求指定的资源,并返回响应主体。一般来说GET方法应该只用于数据的读取(类似于查询) |
POST | 向指定的服务器提交数据(例如:表单数据的提交、文件上传等),并且提交的数据会放入请求体中(类似于新增) |
PUT | 向服务器提交数据,但是和POST有所区别。如果服务器不存在此资源的时候,则执行新增,如果存在则执行修改(类似于修改) |
DELETE | 根据uri的标识删除服务器上的某个资源(类似于删除) |
其他 | … |
备注:GET与POST区别:
-
GET主要用于获取数据,POST用于提交数据。
-
GET请求所带的参数是放在请求行的url地址后面,而POST这是放在请求体中。
-
通常浏览器会对GET请求的url长度有所限制,而POST提交的数据在请求体中,可以提交更多的内容。
-
浏览器会对GET请求进行缓存。
1.7 Servlet的请求处理方法
方法 | 说明 |
---|---|
service | 可以处理任何的请求类型 |
doGet | 处理对应的GET请求 |
doPost | 处理对应的POST请求 |
doPut | 处理对应的PUT请求 |
doDelete | 处理对应的DELETE请求 |
其他 | … |
说明:通过HttpServlet的源代码得知,默认的所有请求都会先经过service方法,然后service方法根据请求的方法类型判断来决定交给doGet或者是doPost方法来处理请求。如果子类重写了父类的service方法同时还重写了其他的doXxx的方法,那么只有service方法会处理请求,其他方法将失效。
1.8 Request与Response对象
当web容器调用某个Servlet的Service方法时,会创建一个HttpServletRequest和HttpServletRespinse对象作为参数传入到这个方法中,那么我们可以通过HttpServletRequest来获取相关的请求内容等,而响应客户端可以利用HttpServletResponse对象来完成。
1.8.1 HttpServletRequest常用API
方法 | 说明 |
---|---|
getParameter(String name) | 获取请求参数,根据请求参数的name指定 |
getParameterValues(Spring name) | 获取相同name的请求参数,返回的是字符串数组 |
getParameterNames() | 获取所有的请求参数名称 |
getParameterMap() | 获取所有请求参数,包括参数名称和值 |
getMethod() | 获取客户端的请求方法 |
getHeader(String name) | 根据请求头的名称获取响应的信息 |
getRemoteAddr() | 获取远程客户端的IP地址 |
getServletPath() | 获取Servlet的请求地址,也就是url-pattern |
getRequestURL() | 获取请求完整的URL地址 |
getRealPath(String path) 废弃 | 获取项目的绝对路径。(这个方法在request对象中已废弃,建议通过ServletContext对象获取) |
其他 | … |
1.8.2 HttpServletResponse常用API
方法 | 说明 |
---|---|
setContentType(String str) | 设置响应内容的类型及编码 |
getWriter() | 获取响应字符输出流 |
getOutputStream() | 获取字节输出流 |
setHeader(String name, String value) | 设置响应头信息,如果存在响应头信息,则执行更新 |
addHeader(String name, String value) | 设置响应头,不管存不存在都会新加入一个 |
setStatus(int code) | 设置响应状态码 |
其他 | … |
1.8.3 常见的响应状态码
状态码 | 说明 |
---|---|
200 | 请求已成功 |
302 | 重定向 |
401 | 请求被禁止,未授权 |
404 | 请求的资源不存在 |
405 | 请求行的方法不被支持 |
500 | 服务器内部错误 |
其他 | … |
1.9 Servlet之间的通信
1.9.1 转发
所谓转发,就是在多个Servlet之间共享请求和响应对象,所有参与转发过程的Servlet都可以共享同一个请求对象的信息,在Servlet的API中,转发的操作是由HttpServletRequest对象完成的。
转发的特点:
- URL地址栏不会发生改变
- 转发的过程是在服务端自动完成
示例代码:
public class ServletA extends HttpServlet{public void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException{//获取页面提交的参数String name = request.getParameter("userName"); //转发由HttpServletRequest完成//第一步先获取一个请求转发器RequestDispatcher//获取请求转发器的同时要告诉转发器转发到哪里,转发给谁//如果要转发给ServletB,那么就是对应ServletB的url-patternRequestDispatcher rd = request.getRequestDispatcher("servletB");//调用转发器的forward方法执行转发,同时将request和response对象一并转发ServletBrd.forward(request, response);}
}
public class ServletB extends HttpServlet{public void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {//这里可以获得同一个请求的参数String name = request.getParameter("userName");System.out.println("ServletB获取请求参数:"+name);}
}
请求作用域:
每一个请求对象都有一个独立的空间,这个空间我们称之为请求作用域(RequestScope)。可以为当前这个请求携带额外的一些数据信息,这些信息同样可以在多个Servlet之间进行共享。
示例代码:
public class ServletA extends HttpServlet {public void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException{//在ServletA中为request对象设置请求作用域request.setAttribute("age", 35);}
}
public class ServletB extends HttpServlet{public void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException{//在ServletB中获取统一个请求对象作用域的值Integer age = (Integer)request.getAttribute("age");}
}
1.9.2 重定向
重定向的机制和转发不同,首先重定向是通过HttpServletResponse对象来完成。一次重定向的过程中会有两次请求和两次响应,服务器在接收第一次请求后会先做一次302的响应(302表示重定向状态码),告诉客户端浏览器必须发起一个新的请求地址,服务端再次接收这个请求处理,最后再次响应客户端。由于会产生不同的请求对象,因此并不同共享同一个请求中的参数。
重定向的特点:
- URL地址栏会发生改变
- 重定向的操作是在客户端浏览器完成的
示例代码:
方式一:
public class ServletC extends HttpServlet {@Overrideprotected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {//执行重定向//方式一:设置302响应状态码,并在响应头中添加location属性指定重定向的地址response.setStatus(302);response.addHeader("location", "http://localhost:8080/ch06/servletD");}
}
方式二:
public class ServletC extends HttpServlet {@Overrideprotected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {//执行重定向//方式二:使用response的sendRedirect方法response.sendRedirect("servletD");}
}
1.10 会话跟踪
由于HTTP协议是无状态的,服务端并不会记录每一个客户端的状态,因此,如果想要实现服务器能记录客户端的状态的话,那么就需要会话跟踪技术。
1.10.1 cookie
cookie是客户端浏览器内部的一个文本文件,用于记录服务器发送过来的一些文本信息。可以在Servlet中创建Cookie对象,在响应时将cookie写回浏览器。在接下来的每次请求中,客户端会都把这个cookie信息带回给服务器,服务器就可以获取cookie的信息,达到会话跟踪的目的。由于cookie是保存在客户端浏览器,也就是会话信息将由客户端维护。
设置cookie:
public class SetCookieServlet extends HttpServlet{protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {//创建一个Cookie的实例Cookie cookie1 = new Cookie("userId","10001");Cookie cookie2 = new Cookie("age","21");//将cookie对象设置到响应对象中response.addCookie(cookie1);response.addCookie(cookie2);}
}
获取cookie:
public class GetCookieServlet extends HttpServlet{@Overrideprotected void service(HttpServletRequest request, HttpServletResponse repsonse) throws ServletException, IOException {//cookie是通过request对象来得到的//从请求中可以获取多个cookie对象Cookie[] cookies = request.getCookies();for (Cookie cookie : cookies) {//判断cookie,只获取name为userId的cookie对象if("userId".equals(cookie.getName())) {System.out.println(cookie.getValue());}}}
}
cookie保存中文:
在保存cookie的时候如果需要保存中文,那么中文信息需要经过编码后才可以写入cookie
示例代码:
编码使用URLEncoder
String str = URLEncoder.encode("张三", "utf-8");
Cookie cookie = new Cookie("userName", str);
解码使用URLDecoder
String str = URLDecoder.decode(cookie.getValue(), "utf-8");
System.out.println(str);
cookie的生命周期:
默认cookie只会保存在浏览器进程的内存中,并不会写入cookie文件,如果关闭了浏览器,那么浏览器的进程也就消失了,那么对应的内存就会释放空间,因此cookie也就销毁。如果想将cookie写入文件,那么就必须设置cookie的生命时长,一旦设置了生命时长,那么就表示这个cookie会在文件中保留多长的时间,到了这个时间之后,浏览器就会自动销毁这个cookie。
设置cookie存活时间:
Cookie cookie = new Cookie("userId","10001");
//设置为0表示立即删除cookie
cookie.setMaxAge(0);
//设置为正数表示cookie在cookie文件的存活时间,单位:秒
cookie.setMaxAge(60*60);
//设置为-1表示cookie只保留在浏览器器的进程中,关闭浏览器之后会销毁cookie
cookie.setMaxAge(-1);
1.10.2 Session
Session是基于服务端来保存用户的信息,这个是和cookie的最大区别。不同的客户端在请求服务器的时候,服务器会为每一个客户端创建一个Session对象并保存在服务器端,Session对象是每个客户端所独有的,相互之间不能访问。服务器为了区别不同的Session属于哪一个客户端,因此Session对象也有一个唯一标识,叫做SessionID。需要注意的是这个SessionID是以cookie的机制保存在客户端浏览器。每次请求的时候,浏览器都会把这个SessionID带回服务端,服务端根据这个SessionID就可以找到对应的Session对象。注意:session的创建是在第一次调用request.getSession()方法时创建的,因为这个方法会判断之前有没有创建过Session,如果有则直接使用,没有则创建一个,不会出现同一个客户端同时创建多个Session的情况。
示例代码:
public class SessionServlet extends HttpServlet{@Overrideprotected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {//HttpSession对象是在第一次调用request的getSession()方法时才会创建//注意:getSession()的方法会先判断之前是否为客户端创建了session实例,//如果创建了,则使用之前创建好的Session对象,没有则创建一个新的SessionHttpSession session = request.getSession();//创建HttpSession的同时,会创建一个唯一的标识SessionID//这个sessionId会保存在浏览器的cookie中,每次请求会带回这个id找到相应的session对象String sessionId = session.getId();System.out.println(sessionId);}
}
session的生命周期:
SessionId是保存在浏览器的cookie中,但是不会写入cookie文件,这也就表示当关闭浏览器之后,SessionId就会销毁。SesisonId销毁以后,服务端的Session就没有任何作用了。但是,服务器并不会立刻销毁这个Session对象,至于什么时候销毁是由服务器自己决定的。除非我们手动调用了session.invalidate() 方法,服务器就会立即销毁这个session实例。
例如:
HttpSession session = request.getSession();
//立即销毁session
session.invalidate();
Session默认也有存活时间,服务器在创建Session的时候为Session设置默认的存活时间为30分钟,如果在30分钟之内,客户端没有发起任何请求到服务器,那么服务器就会销毁这个Session对象。我们也可以设置Session的存活时间。可以为当前的Session设置,也可以为全局(服务器端的所有Session)的Session设置。
设置当前Session的存活时间:
HttpSession session = request.getSession();
//设置当前Session的存活时间,单位:秒
session.setMaxInactiveInterval(3600);
设置全局的Session的存活时间:
在web.xml中进行设置
<!-- 设置全局Session的存活时间,单位:分钟 -->
<session-config><session-timeout>60</session-timeout>
</session-config>
Session的作用域:
当我们需要将一些数据信息存入Session的时候,就需要操作Session作用域(SessionScope),它和请求作用域类似,也有相应的setAttribute和getAttribute方法,只不过Session作用域的范围要比请求作用域更宽。请求作用域在一次请求响应之后就会消失(因为相应之后请求对象就会销毁)。而Sesison对象只要浏览器不关闭或者Session对象未超时,那么Session对象会一直驻留在服务器端,因此不管重新请求多少次还是转发和重定向,都可以从Session中获取之前保存的数据信息。
示例代码:
User user = new User();
user.setUid("1001");
user.setUserName("wangl");
HttpSession session = request.getSession();
//将数据保存在会话作用域中
session.setAttribute("user", user);
从会话作用域取值
HttpSession session = request.getSession();
//取值
User user = (Object)session.getAttribute("user");
URL重写:
浏览器是可以禁用cookie的,一旦禁用了cookie,那么SessionId将无法写入cookie的缓存中,这样就导致无法实现会话跟踪了,因此解决办法就是使用URL重写。URL重写的目的就是把SessionId在放在请求URL地址的后面提交回服务器(类似GET请求后面带上参数,而这个参数就是一个SessionId),服务器会解析这个URL的地址并得到SessionId。
示例代码:
//重写请求的URL地址,这个地址后面会自动带上SessionId
String url = response.encodeRedirectURL("/getUser");
//重定向URL
response.sendRedirect(url);
浏览器地址演示
http://localhost:8080/ch07/getUser;jsessionid=6F1BA8C92D7E5D7CC479ED8DD30D3ED0
注意:;号后面跟着就是SessionId
1.11 ServletContext
web容器在启动时会为每一个web应用创建唯一的上下文对象,这个对象就是ServletContext,这个上下文对象可以理解为是当前项目的一个共享内存空间,为项目中的所有Servlet提供一个共享的区域。
常用API:
方法 | 说明 |
---|---|
getContextPath() | 获取项目的相对路径 |
getRealPath(String path) | 获取项目的绝对路径 |
getInitParameter(String name) | 获取上下文的初始化参数(web.xml中配置的) |
setAttribute(String name, String value) | 将数据放入上下文作用域 |
getAttribute(String name) | 从上下文作用域中去获取数据 |
上下文作用域:
上下文作用域是为当前项目所有Servlet提供的一个共享内存区域,可以将需要的数据信息保存在作用域中。这个作用域的的范围是最大的,只要容器没有停止,它就会一直存在。
三种作用域比较:
结合前面所学的作用域共有三个,分别是请求作用域、会话作用域、上下文作用域。
范围从小到大来划分:
请求作用域 < 会话作用域 < 上下文作用域
1.12 过滤器
过滤器可以在请求到达servlet之前和servlet响应客户端之前进行拦截,相当于一个拦截器。主要用于进行一些请求和响应的预处理操作。常用的场景包括用户的认证授权、统一请求字符编码等等。
1.12.1 编写过滤器
要实现一个过滤器,必须实现一个FIlter接口,只有实现了这个接口的类才称之为过滤器。
示例代码:
public class DemoFilter implements Filter {...
}
web.xml配置过滤器:
<filter><filter-name>demoFilter</filter-name><filter-class>edu.nf.ch09.filter.DemoFilter</filter-class><!-- 初始化参数 --><init-param><param-name>param</param-name><param-value>hello</param-value></init-param></filter><filter-mapping><filter-name>demoFilter</filter-name><!-- 什么请求可以经过此过滤器,/*表示所有请求 --><url-pattern>/*</url-pattern></filter-mapping>
1.12.2 过滤器的生命周期
与Servlet类似,Filter同样也是容器负责创建和销毁,与Servlet的区别在于,容器会在启动的时候最先创建所有的过滤器,并执行init方法进行初始化。
生命周期方法:
方法 | 说明 |
---|---|
init | 初始化方法,容器启动时执行一次 |
doFilter | 请求过滤方法,决定请求是否放行 |
destroy | 容器销毁过滤器之前执行的方法 |
代码实例:
public class DemoFilter implements Filter{@Overridepublic void destroy() {System.out.println("准备销毁DemoFilter");}@Overridepublic void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)throws IOException, ServletException {//FilterChain表示一个过滤链对象,因为过滤器可能会存在多个//同时这个对象将决定是否放行当前请求,//放行的话则请求会继续到达下一个过滤器或者servlet中System.out.println("请求经过DemoFileer..放行");chain.doFilter(request, response);System.out.println("响应前经过DemoFilter...");}@Overridepublic void init(FilterConfig config) throws ServletException {String name = config.getInitParameter("param");System.out.println("初始化DemoFilter,获取初始化参数:"+name);}
}
1.12.3 过滤链
在一个web项目中可能存在多个过滤器,当有多个过滤器存在的时候就会形成一个过滤链。请求会按照过滤器链的顺序一直传递下去,最终到达某个Servlet来处理请求。(注意:过滤链的顺序是按照web.xml中的先后配置顺序决定的)
配置示例:
<!-- 按先后顺序配置 -->
<!-- 配置第一个过滤器 -->
<filter><filter-name>firstFilter</filter-name><filter-class>edu.nf.ch09.filter.FirstFilter</filter-class>
</filter>
<filter-mapping><filter-name>firstFilter</filter-name><url-pattern>/*</url-pattern>
</filter-mapping>
<!-- 配置第二个过滤器-->
<filter><filter-name>secondFilter</filter-name><filter-class>edu.nf.ch09.filter.SecondFilter</filter-class>
</filter>
<filter-mapping><filter-name>secondFilter</filter-name><url-pattern>/*</url-pattern>
</filter-mapping>
1.13 监听器
监听器用于监听对象的上的事件发生,在Servlet中监听器主要监听请求对象、会话对象、上下文对象以及监听这些对象的作用域操作。JavaEE为我们提供了一系列的监听器接口,开发时按需实现相应的接口即可。
1.13.1 监听作用域对象的创建与销毁
监听器 | 说明 |
---|---|
ServletRequestListener | 监听请求对象的创建和销毁 |
HttpSesisonListener | 监听会话对象的创建和销毁 |
ServletContextListener | 监听Servlet上下文对象的创建和销毁 |
代码示例:
1.请求对象监听器
public class DemoRequestListener implements ServletRequestListener{/*** 当请求对象销毁后容器执行此方法* 销毁方法中同样也有一个ServletRequestEvent事件对象*/@Overridepublic void requestDestroyed(ServletRequestEvent event) {//通过这个事件对象就可以获取当前的请求对象HttpServletRequest request = (HttpServletRequest)event.getServletRequest();System.out.println("销毁请求对象..."+request);}/*** 当请求对象创建之后容器调用此方法* ServletRequestEvent这个参数就是一个事件对象*/@Overridepublic void requestInitialized(ServletRequestEvent event) {//通过这个事件对象就可以获取当前的请求对象HttpServletRequest request = (HttpServletRequest)event.getServletRequest();System.out.println("初始化了请求对象..."+request);}}
web.xml配置
<!-- 配置监听器 --><listener><!-- 指定监听器的完整类名 --><listener-class>edu.nf.ch10.listener.DemoRequestListener</listener-class></listener>
2.会话对象监听器
public class DemoSessionListener implements HttpSessionListener{/*** 监听HttpSession对象的创建* HttpSessionEvent参数是一个事件对象* 通过它可以获得当前的HttpSession*/@Overridepublic void sessionCreated(HttpSessionEvent event) {HttpSession session = event.getSession();System.out.println("创建了Session对象"+session);}/*** 监听HttpSession对象的销毁*/@Overridepublic void sessionDestroyed(HttpSessionEvent event) {HttpSession session = event.getSession();System.out.println("销毁了Session对象"+session);}
}
注意:当第一次调用了request.getSesison()方法创建Session时,监听器才会起作用。
web.xml配置
<listener><!-- 指定监听器的完整类名 --><listener-class>edu.nf.ch10.listener.DemoSessionListener</listener-class></listener>
3.Servlet上下文监听器
public class DemoContextListener implements ServletContextListener{/*** 监听ServletContext的销毁*/@Overridepublic void contextDestroyed(ServletContextEvent event) {//通过事件对象获取ServletContextServletContext sc = event.getServletContext();System.out.println("销毁了ServletContext对象..."+sc);}/*** 监听SerlvetContext的创建*/@Overridepublic void contextInitialized(ServletContextEvent event) {//通过事件对象获取ServletContextServletContext sc = event.getServletContext();System.out.println("创建了ServletContext对象..."+sc);}
}
web.xml
<listener><!-- 指定监听器的完整类名 --><listener-class>edu.nf.ch10.listener.DemoContextListener</listener-class></listener>
1.13.2 监听作用域的操作
监听器 | 说明 |
---|---|
ServletRequestAttributeListener | 监听请求作用域的操作 |
HttpSessionAttributeListener | 监听会话作用域的操作 |
ServletContextAttributeListener | 监听Servlet上下文作用域的操作 |
示例代码:
这里以HttpSessionAttributeListener说明,其他作用于监听器用法相似。
public class DemoSessionAttributeListener implements HttpSessionAttributeListener{/*** 当有数据添加到会话作用域时,执行此方法*/@Overridepublic void attributeAdded(HttpSessionBindingEvent event) {//获取存入作用域的键和值System.out.println("存入会话作用域..."+event.getName() + " : " + event.getValue());}/*** 当从会话作用域移除数据时,执行此方法*/@Overridepublic void attributeRemoved(HttpSessionBindingEvent event) {System.out.println("移除会话作用域..."+event.getName() + " : " + event.getValue());}/*** 当替换了会话作用域中的某个数据时,执行此方法*/@Overridepublic void attributeReplaced(HttpSessionBindingEvent event) {HttpSession session = event.getSession();//从session中获取的是替换之后的值System.out.println("替换的值: "+session.getAttribute("userName").toString());//注意:这里event.getValue()获取到的是替换之前的值System.out.println("替换会话作用域..."+event.getName() + " : " + event.getValue());}
}
web.xml
<listener><!-- 指定监听器的完整类名 --><listener-class>edu.nf.ch10.listener.DemoSessionAttributeListener</listener-class></listener>
1.14 注解配置
Servlet3.0开始提供了一系列的注解来配置Servlet、Fiilter、Listener等等。这种方式可以极大的简化在开发中大量的xml的配置。从这个版本开始,web.xml可以不再需要,使用相关的注解同样可以完成相应的配置。
注解 | 说明 |
---|---|
@WebServlet | 这个注解标识在类上,用于配置Servlet。例如:@WebServlet(name=“hello”, urlPatterns=“/hello”) 也可简化配置@WebServlet(“/hello”)。initParams属性设置Servlet的初始化参数。loadOnStartup设置容器启动时初始化Servlet。 |
@MultipartConfig | 这个注解标识在类上,标识启用当前Servlet的文件上传功能。location属性设置文件上传的路径。maxFileSize属性限制单个文件上传的大小,默认为-1,表示没有限制。maxRequestSize属性限制一次请求上传总文件的大小,默认为-1,表示没有限制,fileSizeThreshold属性表示当前数据量大于该值时,内容将被写入文件。 |
@WebFilter | 这个注解标识在类上,用于配置Filter。例如:@WebFilter(filterName=“encode”,urlPatterns=“/*”) 也可简化配置@WebFilter(“/*”),initParams属性设置Servlet的初始化参数。 |
@WebListener | 这个注解标识在类上,用于配置各种监听器 |
示例:
Servlet配置示例:
@WebServlet(urlPatterns = "/hello",initParams = {@WebInitParam(name="name", value="aaa")},loadOnStartup = 0)
@MultipartConfig(maxFileSize = 5242880, maxRequestSize = 104857600)
public class TestServlet extends HttpServlet {...
}
文件上传配置示例:
@WebServlet(urlPatterns = "/hello")
@MultipartConfig(maxFileSize = 5242880, maxRequestSize = 104857600)
public class TestServlet extends HttpServlet {...
}
Filter配置示例:
@WebFilter(urlPatterns = "/*", initParams = {@WebInitParam(name="name", value="aaa")})
public class TestFilter implements Filter {...
}
Listener配置示例:
@WebListener
public class ApplicationListener implements ServletContextListener {...
}
1.15 动态注册
从Servlet3.0开始,支持ServletContext来动态注册相关的Servlet组件。例如可以在自定义的ServletContextListener中,通过在contextInitialized方法中获取ServletContext对象来注册Servlet、Filter、Listener。
动态注册Servlet
public class TestServlet extends HttpServlet { ...
}
@WebListener
public class ApplicationListener implements ServletContextListener {@Overridepublic void contextInitialized(ServletContextEvent sce) {//获取ServletContextServletContext sc = sce.getServletContext();//动态注册Servlet,返回动态注册器实例ServletRegistration.Dynamic registration = sc.addServlet("testServlet", TestServlet.class);//映射请求urlregistration.addMapping(new String[]{"/test"});//初始化参数registration.setInitParameter("name", "aaa");//启用文件上传registration.setMultipartConfig(new MultipartConfigElement(sc.getRealPath("/uplaod"), 5242880, 104857600, 104857600));}}
动态注册Filter
public class TestFilter implements Filter {...
}
@WebListener
public class ApplicationListener implements ServletContextListener {@Overridepublic void contextInitialized(ServletContextEvent sce) {ServletContext sc = sce.getServletContext();//动态注册FilterFilterRegistration.Dynamic registration = sc.addFilter("testFilter", TestFilter.class);//设置初始化参数registration.setInitParameter("name", "aaa");//请求映射配置//参数一:设置过滤器拦截的类型FORWARD,INCLUDE,REQUEST,ERROR,ASYNC//EnumSet是Set接口的一个特别实现,可用于对枚举类型进行特定的分组//参数二:设置该过滤器是否放在当前web应用中已经存在的过滤器之后,true表示之后,false表示之前//参数三:设置请求拦截的urlregistration.addMappingForUrlPatterns(EnumSet.of(DispatcherType.REQUEST), true, new String[]{"/*"});}
}
动态注册Listener
public class UserSessionListener implements HttpSessionListener {...
}
@WebListener
public class ApplicationListener implements ServletContextListener {@Overridepublic void contextInitialized(ServletContextEvent sce) {ServletContext sc = sce.getServletContext();//动态注册Listenersc.addListener(UserSessionListener.class);}
}
1.16 文件上传
从Servlet3.0开始提供了文件上传的功能,操作起来更加的简单和方便。要想使用这个功能,首先必须在web.xml或者使用注解开启Servlet的上传功能,否则无效。
xml配置:
<servlet><servlet-name>upload</servlet-name><servlet-class>edu.nf.ch04.servlet.UploadServlet</servlet-class><!-- 开启上传功能 --><multipart-config/>
</servlet>
<servlet-mapping><servlet-name>upload</servlet-name><url-pattern>/upload</url-pattern>
</servlet-mapping>
注解配置:
@WebServlet("/login")
@MultipartConfig //开启上传功能
public class LoginServlet extends HttpServlet {...
}
参数说明:
参数 | 说明 |
---|---|
location | 指定文件上传的目录 |
maxFileSize | 限制单个文件上传的大小 |
maxRequestSize | 限制一次请求上传总文件的大小 |
fileSizeThreshold | 设置缓存的大小,当达到缓存大小的时候,会将内存的数据写入location指定的目录中(也就是写入磁盘) |
文件上传的核心接口是Part,通过HttpServletRequest对象可获得该接口的实现类对象。
//获取单个上传文件的Part
Part part = request.getPart("name");
//获取多个上传文件的Part
Collection<Part> cool = request.getParts();
常用API:
方法 | 说明 |
---|---|
getContentType() | 获取上传的文件类型 |
getSize() | 获取上传文件的大小 |
getSubmittedFileName() | 获取上文件的文件名 |
write() | 将文件上传(写入)到指定位置 |
当客户端使用form表单来上传文件时,必须将表单的enctype属性设置为"multipart/form-data"。
<form method="post" action="upload" enctype="multipart/form-data"><!-- input使用file类型 --><input type="file" name="file"/>
</form>
案例:
@WebServlet("/upload")
@MultipartConfig
public class UploadServlet extends HttpServlet {@Overrideprotected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {//指定上传的路径String uploadPath = "/Users/wangl/uploads";//创建文件夹,如果不存在的情况下File dir = new File(uploadPath);if(!dir.exists()){dir.mkdir();}//上传单个文件,参数对用input的name属性的值//Part part = req.getPart("file");//uploadPath = uploadPath + "/" + part.getSubmittedFileName();//执行上传//part.write(uploadPath);//获取文件的类型//System.out.println(part.getContentType());//获取文件的大小//System.out.println(part.getSize());//获取上传的文件名//System.out.println(part.getSubmittedFileName());//上传多个文件Collection<Part> parts = req.getParts();for (Part p : parts) {p.write(uploadPath + "/" + p.getSubmittedFileName());}}
}
upload.html
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>Title</title>
</head>
<body>
<h2>文件上传</h2><form method="post" action="upload" enctype="multipart/form-data">Username:<input type="text" name="userName"/><br/>File1:<input type="file" name="file"/><br/>File2:<input type="file" name="file"/><br/><input type="submit" value="submit"/></form>
</body>
</html>
2. JSP基础
2.1 简介
JSP全名为Java Server Pages,中文名叫java服务器页面,是一种动态页面技术,而HTML是属于静态页面。JSP可以在HTML中嵌入Java脚本代码,因为JSP本质上还是一个Servlet,因此JSP也必须依赖于web容器才能运行。JSP的出现并不是为了取代Servlet,而是简化了Servlet的工作,将Servlet中繁琐的视图呈现代码脱离出来,交给JSP来完成,让Servlet专注于请求的处理,所以在开发中通常将JSP和Servlet结合一起使用。
2.2 JSP引擎
JSP引擎主要负责将JSP文件转义成一个Servlet的java源文件,然后再通过javac将这个源文件编译成class字节码文件并装载到JVM中执行。JSP引擎的核心类是JspServlet,位于Jasper.jar文件中,并且在Tomcat的web.xml中也默认就配置好了这个类(JspServlet就是一个Servlet)。因此,凡是以“.jsp”结尾的请求都会先经过JspServlet,那么这个引擎就可以开始工作。通常引擎转义和编译后的文件放在容器的work工作目录中。
注意:如果第一次访问JSP文件的时候,由于work目录中并不存在源文件和字节码文件,JSP引擎就必须完成这两个工作,因此,有可能在第一次访问JSP时会比较缓慢。当字节码编译出来加载后,第二次访问时速度就会很快了。
2.3 JSP三大元素
2.3.1 指令元素
语法:<%@ %>
指令 | 说明 |
---|---|
page指令 | 用于设置JSP页面的相关信息以及编码 |
include指令 | 用于静态包含其他的JSP页面代码,所谓静态包含,就是在编译期,将另外的JSP页面的代码合并到当前的JSP中,最终只会产生一个Java源文件 |
taglib指令 | 这个指令用于引入标签库 |
2.3.2 动作元素
语法:jsp:xxx
动作 | 说明 |
---|---|
include | 动态包含其他JSP页面的内容,所谓的动态包含是指在编译期将不同的JSP文件转义成不同的Java源文件,然后在运行时才将目标内容包含到当前的JSP页面中 |
forward | 相当于Servlet中的转发,转发到其他的JSP页面或者Servlet |
param | 用于传递参数,通常结合其他的动作一起使用,例如转发时需要提交一些而外的参数 |
useBean | 用于在JSP页面中使用JavaBean对象,通常结合setProperty和getProperty来使用,完成bean对象的赋值和取值操作 |
2.3.3 脚本元素
脚本元素主要就是在JSP中嵌入Java脚本代码,包括声明、表达式、Java脚本
声明语法:<%! %>
表达式::<%= %>
Java脚本: <% %>
示例:
<%@ page language="java" contentType="text/html; charset=UTF-8"pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body><%-- 声明变量和方法,这里声明的变量a是实例变量 --%><%! int a = 10;public void say(){System.out.println("hello");}%><%-- 表达式,注意:表达式后面是不允许有;号结束的 --%>3 + 1 = <%=3+1%><br/><%-- Java脚本,脚本代码最终会生成在servlet中的service方法中作为代码片段 --%><table border="1"><tr><th>Name</th><th>Age</th></tr><% for(int i=0;i<5;i++){%><tr><td>user<%=i%></td><td><%=10+i%></td></tr><%}%></table>
</body>
</html>
2.4 JSP内置对象
内置对象,是在容器运行时将创建好的9个对象内嵌在JSP中,在JSP里可以直接拿来使用的对象。
对象 | 说明 |
---|---|
out | 字符流输出对象 |
config | 等同于Servlet中的ServletConfig |
page | 表示当前JSP页面(等同于Servlet中的this) |
request | 等同于Servlet中的HttpServletRequest |
response | 等同于Servlet中的HttpServletResponse |
session | 等同于Servlet中的HttpSession |
application | 等同于Servlet中的ServletContext |
pageContext | 表示当前JSP页面的上下文对象 |
exception | 表示当前JSP中的异常对象 |
示例代码:
<%@ page language="java" contentType="text/html; charset=UTF-8"pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body><%-- 常用内置对象 --%><%//使用request,API使用同HttpServletRequest一样request.getParameter("userName");request.setAttribute("user", "user1");request.getAttribute("user");//使用session,API使用等同于HttpSessionsession.setAttribute("user", "user2");session.getAttribute("user");session.getId();//使用response//response.sendRedirect("demo.jsp");//out对象,等同于字符输出流对象,在JSP页面输出相关内容out.println("hello world");//这个是输出在控制中System.out.println("hello");//使用application,等同于ServletContextapplication.setAttribute("userName", "user3");//pageContext使用//从pageContext中获取相关的其他对象HttpServletRequest req = (HttpServletRequest)pageContext.getRequest();HttpServletResponse res = (HttpServletResponse)pageContext.getResponse();HttpSession ses = pageContext.getSession();ServletConfig conf = pageContext.getServletConfig();ServletContext sc = pageContext.getServletContext();//也可以通过pageContext来统一设置不同的作用域//第三个参数表示要放入到哪个作用域,是一个int类型的参数//1代表page作用域(当前页面有效)//2代表请求作用域//3代表会话作用域//4代表上下文作用域pageContext.setAttribute("userName", "zhangsan", 2);//也可以指定中哪个作用域取出相应的值String name = (String)pageContext.getAttribute("userName", 2);out.println(name);%>
</body>
</html>
2.5 EL表达式
EL(Expression Language),全称叫做表达式语言,是JSP2.0推出的一种技术。主要简化了在JSP中使用Java脚本表达式。EL表达式的特点在于使用简单,支持四则运算、逻辑运算等,并且还可以对Servlet API中的对象进行数据访问。EL的语法: ${expression}
算数与逻辑运算:
示例 | 结果 |
---|---|
${1+1} | 2 |
${2*2} | 4 |
${1==1} | true |
${2>5} | false |
${3>=2} | true |
… | … |
数据访问:
1.使用“.”来访问
示例 | 说明 |
---|---|
${param.参数名} | 获取请求参数的值,相当于使用request.getParameter()方法 |
${requestScope.xxx} | 从请求作用域中访问数据 |
${sessionScope.xxx} | 从会话作用域中访问数据 |
${applicationScope.xxx} | 从上下文作用域中访问数据 |
${xxx} | 不指定作用域范围时,默认按照作用域范围从小到大的顺序自动查找 |
其他 | … |
2.使用"[]"来访问
示例 | 说明 |
---|---|
${requestScope[“userName”]} | 从请求作用域中取值 |
${sessionScope[“userName”]} | 从会话作用域取值 |
其他… | 同上 |
注意:通常使用"[]"来访问数据的时候,主要是访问一些特殊的名称,例如:request.setAttribute(“user.userName”)
这种方式如果使用${requestScope.user.userName}
是无效的,应改为${requestScope["user.userName"]}
来访问。
2.6 JSTL
JSTL是JSP中的标准标签库,主要用于取代JSP中大量的Java脚本代码,让页面看起来更趋向于HTML。使用也很简单,通常结合EL表达式一起使用。
核心标签库(core) | 说明 |
---|---|
<c:out> | 输出标签 |
<c:set> | 声明某个变量并存入指定的作用域 |
<c:redirect> | 重定向标签 |
<c:if> | 条件判断 |
<c:forEach> | 循环标签 |
… | … |
备注:其他标签库请参阅相关官方文档
相关文章:
Java Web基础教程
Java Web基础教程 1. Servlet基础 1.1 什么是Servlet Servlet是JavaEE中的标准组件之一,专门用于处理客户端的HTTP请求。并且它必须依赖于Servlet容器(Tomcat就是一个标准的Servlet容器)才能运行。因为Servlet实例的创建和销毁都是由容器负…...

BUUCTF john-in-the-middle 1
BUUCTF:https://buuoj.cn/challenges 题目描述: 注意:得到的 flag 请包上 flag{} 提交 密文: 下载附件,解压得到john-in-the-middle.pcap文件。 解题思路: 1、双击文件,打开wireshark。 看到很多http流…...

HashMap的死循环及数据覆盖问题
目录 一,HashMap 线程不安全的原因 二,HashMap 死循环问题 死循环发生的条件 死循环的具体过程 死循环执行步骤1 死循环执行步骤2 死循环执行步骤3 三,HashMap 数据覆盖问题 数据覆盖执行流程1 数据覆盖执行流程2 数据覆盖执行流…...

数据库数据恢复—MongoDB数据库文件拷贝出现错误的数据恢复案例
MongoDB数据库数据恢复环境: 一台Windows Server操作系统的虚拟机,虚拟机上部署有MongoDB数据库。 MongoDB数据库故障&检测: 在未关闭MongoDB服务的情况下,工作人员将MongoDB数据库文件拷贝到其他分区,然后将原数…...
2023年11月个人工作生活总结
本文为 2023 年 11 月工作生活总结。 研发编码 GIS 模仿了一些有名的地图服务商的网站,将离线地图页面做成全屏,对于大屏幕更加好友。再美化一下全区的边界和区内地域的边界。不过主要工作量还是绘制路线,而绘线作为内部工作,还…...

Spark-06:Spark 共享变量
目录 1.广播变量(broadcast variables) 2.累加器(accumulators) 在分布式计算中,当在集群的多个节点上并行运行函数时,默认情况下,每个任务都会获得函数中使用到的变量的一个副本。如果变量很…...

Spring整合web环境
目录 Javaweb三大组件及环境特点 Spring整合web环境的思路及实现 Spring的web开发组件spring-web MVC框架思想及其设计思路 Javaweb三大组件及环境特点 Spring整合web环境的思路及实现 package com.xfy.listener;import com.xfy.config.SpringConfig; import org.springfra…...

分享从零开始学习网络设备配置--任务4.3 使用动态路由RIPng实现网络连通
任务描述 某公司使用IPv6技术搭建企业网络,由于静态路由需要管理员手工配置,在网络拓扑发生变化时,也不会自动生成新的路由,因此采用IPv6动态路由协议RIPng实现网络连通,实现任意两个节点之间的通信,并降低…...

vue2.0+elementui集成file-loader之后图标失效问题
背景 跑vue2elementUI项目时,由于前端这边需要在本地存放xlsx模板文件,供用户下载模板文件,所以需要在webpack构建的时候增加file-loader进行解析xlsx文件打包。 vue版本2.x element-ui 版本 2.13.x 注意 npm i -D file-loader版本号给vue项…...
C# 文件帮助类(FileHelper)
引言 在研究程序反射的时候我们往往需要获取当前运行程序所引用的dll文件,按照传统的方式我们可以维护一个这样的列表,但是这样维护成本实在是太高,而且不利于团队合作开发,在高版本的.net 4.6.2之后官方出了专门的dll帮我们做这个事情Microsoft.Extensions.DependencyMod…...

WordPress 外链跳转插件
WordPress 外链跳转插件是本站开发的一款WordPress插件,能对文中外链添加一层过滤,有效防止追踪,以及提醒用户。 类似于知乎、CSDN打开其他链接的提示。 后台可以设置白名单 学习资料源代码:百度网盘 密码:123...
算法的10大排序
10大排序算法--python 一颗星--选择排序一颗星--冒泡排序一颗星--插入排序两颗星--归并排序(递归-难)三颗星--桶排序三颗星--计数排序四颗星--基数排序四颗星--快速排序,寻找标志位(递归-难)四颗星--又是比较难的希尔排…...
“十道机器学习问题,帮助你了解基础知识和常见算法“
目录 简介: 1. 什么是机器学习?它与传统编程有什么不同之处?2. 请解释监督学习和无监督学习的区别。3. 什么是过拟合和欠拟合?如何解决这些问题?4. 请解释交叉验证在机器学习中的作用。5. 什么是特征选择?为…...
部署WAF安全应用防火墙(openresty部署)
使用NGINX+Openresty实现WAF功能 一、了解WAF 1.1 什么是WAF Web应用防护系统(也称:网站应用级入侵防御系统 。英文:Web Application Firewall,简称: WAF)。利用国际上公认的一种说法:Web应用 防火墙 是通过执行一系列针对HTTP/HTTPS的 安全策略 来专门为Web应用提供保…...

yml转properties工具
目前搜索到的大部分代码都存在以下问题: 复杂结构解析丢失解析后顺序错乱 所以自己写了一个,经过不充分测试,基本满足使用。可以直接在线使用 在线地址 除了yml和properties互转之外,还可以生成代码、sql转json等,可…...

zerotier 搭建 moon中转服务器 及 自建planet
搭建moon 服务器 环境准备 # 安装依赖 yum install wget gcc gcc-c git -y yum install json-devel -y# 下载及安装 curl -s https://install.zerotier.com/ | sudo bash节点ID 配置 配置moon.json文件 cd /var/lib/zerotier-one/# 导出依赖 zerotier-idtool initmoon ide…...

深入了解Rabbit加密技术:原理、实现与应用
一、引言 在信息时代,数据安全愈发受到重视,加密技术作为保障信息安全的核心手段,得到了广泛的研究与应用。Rabbit加密技术作为一种新型加密方法,具有较高的安全性和便捷性。本文将对Rabbit加密技术进行深入探讨,分析…...

Linux常用命令——mv命令
文章目录 1. 简介2. 命令格式3. 主要参数4. 常见用法及示例4.1 移动文件4.2 重命名文件4.3 交互式移动文件4.4 强制移动文件4.5 移动多个文件4.6 使用通配符移动文件 5. 注意事项6. 结论 1. 简介 mv 命令在Linux系统中用于移动文件或目录,同时也可以用于重命名文件…...

Panalog 日志审计系统 前台RCE漏洞复现
0x01 产品简介 Panalog是一款日志审计系统,方便用户统一集中监控、管理在网的海量设备。 0x02 漏洞概述 Panalog日志审计系统 sy_query.php接口处存在远程命令执行漏洞,攻击者可执行任意命令,接管服务器权限。 0x03 复现环境 FOFA…...
Android设置文字颜色渐变
项目中用到了很多文字颜色渐变的设计,因此做一下记录。 核心代码如下: /*** 统一文字渐变色设置* param colors 渐变色字符串数组* param positions 渐变色位置数组,可为空* param start 渐变起始点,可为空* param end 渐变结束…...

基于uniapp+WebSocket实现聊天对话、消息监听、消息推送、聊天室等功能,多端兼容
基于 UniApp + WebSocket实现多端兼容的实时通讯系统,涵盖WebSocket连接建立、消息收发机制、多端兼容性配置、消息实时监听等功能,适配微信小程序、H5、Android、iOS等终端 目录 技术选型分析WebSocket协议优势UniApp跨平台特性WebSocket 基础实现连接管理消息收发连接…...
1688商品列表API与其他数据源的对接思路
将1688商品列表API与其他数据源对接时,需结合业务场景设计数据流转链路,重点关注数据格式兼容性、接口调用频率控制及数据一致性维护。以下是具体对接思路及关键技术点: 一、核心对接场景与目标 商品数据同步 场景:将1688商品信息…...

PL0语法,分析器实现!
简介 PL/0 是一种简单的编程语言,通常用于教学编译原理。它的语法结构清晰,功能包括常量定义、变量声明、过程(子程序)定义以及基本的控制结构(如条件语句和循环语句)。 PL/0 语法规范 PL/0 是一种教学用的小型编程语言,由 Niklaus Wirth 设计,用于展示编译原理的核…...
鸿蒙DevEco Studio HarmonyOS 5跑酷小游戏实现指南
1. 项目概述 本跑酷小游戏基于鸿蒙HarmonyOS 5开发,使用DevEco Studio作为开发工具,采用Java语言实现,包含角色控制、障碍物生成和分数计算系统。 2. 项目结构 /src/main/java/com/example/runner/├── MainAbilitySlice.java // 主界…...
Go 语言并发编程基础:无缓冲与有缓冲通道
在上一章节中,我们了解了 Channel 的基本用法。本章将重点分析 Go 中通道的两种类型 —— 无缓冲通道与有缓冲通道,它们在并发编程中各具特点和应用场景。 一、通道的基本分类 类型定义形式特点无缓冲通道make(chan T)发送和接收都必须准备好࿰…...
JS手写代码篇----使用Promise封装AJAX请求
15、使用Promise封装AJAX请求 promise就有reject和resolve了,就不必写成功和失败的回调函数了 const BASEURL ./手写ajax/test.jsonfunction promiseAjax() {return new Promise((resolve, reject) > {const xhr new XMLHttpRequest();xhr.open("get&quo…...
libfmt: 现代C++的格式化工具库介绍与酷炫功能
libfmt: 现代C的格式化工具库介绍与酷炫功能 libfmt 是一个开源的C格式化库,提供了高效、安全的文本格式化功能,是C20中引入的std::format的基础实现。它比传统的printf和iostream更安全、更灵活、性能更好。 基本介绍 主要特点 类型安全:…...

MySQL体系架构解析(三):MySQL目录与启动配置全解析
MySQL中的目录和文件 bin目录 在 MySQL 的安装目录下有一个特别重要的 bin 目录,这个目录下存放着许多可执行文件。与其他系统的可执行文件类似,这些可执行文件都是与服务器和客户端程序相关的。 启动MySQL服务器程序 在 UNIX 系统中,用…...

Qwen系列之Qwen3解读:最强开源模型的细节拆解
文章目录 1.1分钟快览2.模型架构2.1.Dense模型2.2.MoE模型 3.预训练阶段3.1.数据3.2.训练3.3.评估 4.后训练阶段S1: 长链思维冷启动S2: 推理强化学习S3: 思考模式融合S4: 通用强化学习 5.全家桶中的小模型训练评估评估数据集评估细节评估效果弱智评估和民间Arena 分析展望 如果…...

Qt的学习(二)
1. 创建Hello Word 两种方式,实现helloworld: 1.通过图形化的方式,在界面上创建出一个控件,显示helloworld 2.通过纯代码的方式,通过编写代码,在界面上创建控件, 显示hello world; …...