Servlet

​ HTTP消息由客户端到服务器的请求和服务器到客户端的响应组成。请求消息和响应消息都是由开始行(对于请求消息…

Http协议

​ HTTP消息由客户端到服务器的请求和服务器到客户端的响应组成。请求消息和响应消息都是由开始行(对于请求消息,开始行就是请求行,对于响应消息,开始行就是状态行),消息报头(可选),空行(只有CRLF的行),消息正文(可选)组成。 ​ 每一个报头域都是由名字+”:“+空格+值组成,消息报头域的名字是大小写无关的。

请求头

请求报头充许客户端向服务器端传递请求的附加信息以及客户端自身的信息。·

  • Referer:该请求头指明请求从哪里来。

​ 如果是地址栏中输入地址访问的都没有该请求头地址栏输入地址,通过请求可以看到,此时多了一个 Referer的请求头,并且后面的值该请求从哪里发出。比如:百度竞价,只能从百度来的才有效果,否则不算;通常用来做统计工作、防盗链

响应头

​ 响应报头允许服务器传递不能放在状态行中的附加响应信息,以及关于服务器的信息和对Request-URI所标 识的资源进行下一步访问的信息。

  • Location:Location响应报头域用于重定向接受者到一个新的位置 Location响应报头域,常用在更换域名的时候。

    response.sendRedirect("http://www.baidu.com")
  • Refresh:自动跳转(单位是秒),可以在页面通过meta标签实现,也可在后台实现

    <meta http-equiv="refresh" content="3;url=http://www.baidu.com">

Tomcat服务器

什么是Tomcat

​ Tomcat是一个符合JaVaEE WEB标准的最小的WEB容器,所有的JSP程序一定要有WEB容器的支持才能运 行,而直在给定的WEB容器里面都会支持事务处理操作。 ​ Tomcat是由Apache提供的(www.apache.org)提供的可以用安装版和解压版,安装版可以在服务中出现一个Tomcat的服务,免安装没有,开发中使用免安装版。Tomcat简单的说就是一个运行Java的网络服务器底层是Socket的一个程序,它也是 JSP 和Servlet的一个容器。Tomcat是Apache软件基金会(Apache Software Foundation)的Jakarta项目中的一个核心项目,由Apache、Sun和其他一些公司及个人共同开发而成。 ​ 由于有了Sun的参与和支持,最新的Servlet和iSP规范总是能在Tomcat中得到体现。因为Tomcat技术先 进、性能稳定,而且免费,因而深受ava爱好者的喜爱并得到了部分软件开发商的认可,成为自前比较流行的 Web应用服务器

​ Tomcat服务器是一个免费的开放源代码的Web应用服务器,属于轻量级应用服务器,在中小型系统和并发访问用户不是很多的场合下被普遍使用,是开发和调试JSP程序的首选。对于一个初学者来说,可以这样认为当在一台机器上配置好Apache服务器,可利用它响应HTML(标准通用标记语言下的一个应用)页面的访问请求。实际上Tomcat部分是Apache服务器的扩展,但它是独立运行的,所以当你运行tomcat时,它实际上作为一个与Apache独立的进程单独运行的。 ​ 当配置正确时,Apache为HTML页面服务,而Tomcat实际上是在运行JSP页面和Servlet。另外,Tomcat 和IIS等Web服务器一样,具有处理HTML页面的功能,另外它还是一个Servlet和JSP容器,独立的Servlet 容器是Tomcat的默认模式。不过,Tomcat处理静态HTML的能力不如Apache服务器。目前Tomcat最新版本为9.0。

JSP生成源码

Servlet的实现

​ Servlet是Server与Applet的缩宿写,是服务端小程序的意思。使用Java语言编写的服务器端程序,可以像生 成动态的WEB页,Servlet主要运行在服务器端,并由服务器调用执行,是一种按照Servlet标准来开发的类是SUN公司提供的一门用于开发动态Web资源的技术。(言外之意:要实现web开发,需要实现Servlet标准) ​ Servlet本质上也是Java类,但要遵循Servlet规范进行编写,没有mainO方法,它的创建、使用、销毁都由 Servlet容器进行管理(如Tomcat)。(言外之意:写自己的类,不用写main方法,别人自动调用) ​ Servlet是和HTTP协议是紧密联系的,其可以处理HTTP协议相关的所有内容。这也是 Servlet应用广泛的原因之一。 ​ 提供了Servlet功能的服务器,叫做Servlet容器,其常见容器有很多,如Tomcat,Jetty,WebLogicServer WebSphere,JBoss 等等。

实现 Servlet规范

​ 实现Servlet规范,即继承HttpServlet类,并到如响应的包,该类中已经完成了通信的规则,我们只需要进行业务的实现即可。

package com.xxxx.servlet;
importjavax.servlet.http.Httpservlet;
public class servletol extends HttpServlet {

}

重写service方法

​ 满足Servlet规范只是让我们的类能够满足接收请求的要求,接收到请求后需要对请求进行分析,以及进行业务逻辑处理,计算出结果,则需要添加代码,在规范中有一个叫做service的方法,专门用来做请求处理的操作,业务代码则可以写在该方法中。

java
package com.xxxx.servlet;
import javax.servlet.ServletException; import javax.servlet.http.Httpservlet;
import javax.servlet.http.HttpServletRequest; importjavax.servlet.http.HttpservletResponse; import java.io.IoException;
public class Servlet0l extends Httpservlet {
	@override
	protected void service(HttpServletRequest req, HttpservletResponse resp) throws ServletException, IoException {
		System.out.println("Hello Servlet!"); 
		resp.getwriter().write("Hello World");
	}
}

设置注解

​ 在完成好了一切代码的编写后,还需要向服务器说明,特定请求对应特定资源。 ​ 开发servlet项目,使用@WebServlet将一个继承于javax.servlet.http.HttpServlet的类定义为Servlet组件。在Servlet3.0中,可以使用@WebServlet注解将一个继承于javax.servlet.http.HttpServlet的类标注为可以处理用户请求的Servlet。

​ 这个注解的value是一个String[]因此可以输入一个数组

@WebServlet(value={"/s1","/s2"})
//也可以使用
@WebServlet(urlPatterns={"/s1","/s2"})
两者只能使用一种

其他实现方式

除了继承Httpservlet,也可以继承GenericServlet类或者实现Servlet接口来创建servlet实例

public class Test extends GenericServlet {

    @Override
    public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
        
    }
}

实现Servleti接口需要重写方法

public class Test implements Servlet {

    @Override
    public void init(ServletConfig servletConfig) throws ServletException {
        
    }

    @Override
    public ServletConfig getServletConfig() {
        return null;
    }

    @Override
    public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {

    }

    @Override
    public String getServletInfo() {
        return "";
    }

    @Override
    public void destroy() {

    }
}

注意

在IDEA专业版中配置项目的工件是IDEA对项目创建的而不是Maven打包的,整个过程也和Maven没有任何关系。

使用流程是

  1. 创建展开的工件

    或者归档的

  2. 在编辑配置部署这里添加工件

Servlet的生命周期

Servlet没有main()方法,不能独立运行,它的运行完全由Servlet引擎来控制和调度。所谓生命周期,指的 是servlet容器何时创建servlet实例、何时调用其方法进行请求的处理、何时并销毁其实例的整个过程

  • 实例和初始化时机 当请求到达容器时,容器查找该servlet对象是否存在,如果不存在,则会创建实例并进行初始化
  • 就绪/调用/服务阶段 有请求到达容器,容器调用servlet对象的service()方法,处理请求的方法在整个生命周期中可以被多次调用 HttpServlet的service()方法,会依据请求方式来调用doGet()或者doPost(O)方法。但是,这两个do方法默认情况下,会抛出异常,需要子类去override。
  • 销毁时机 当容器关闭时(应用程序停正时),会将程序中的Servlet实例进行销毁 上述的生命周期可以通过Servlet中的生命周期方法来观察。在Servlet中有三个生命周期方法,不由用户手动调用,而是在特定的时机有容器自动调用,观察这三个生命周期方法即可观察到Servlet的生命周期。

init方法,在Servlet实例创建之后执行(证明该Servlet有实例创建了)

public void init(ServletConfig servletConfig) throws ServletException {

    }

service方法,每次有请求到达某个Servlet方法时执行,用来处理请求(证明该Servlet进行服务了)

public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {

    }

destroy方法,Servlet实例销毁时执行(证明该Servlet的实例被销毁了)

public void destroy() {
    }

HttpServletRequest

​ HttpServletRequest对象:主要作用是用来接收客户端发送过来的请求信息,例如:请求的参数,发送的头 信息等都属于客户端发来的信息,service()方法中形参接收的是HttpServletRequest接口的实例化对象,表示该对象主要应用在TTP协议上,该对象是由Tomcat封装好传递过来。

​ HttpServletRequest是ServletReguest的子接口,ServletRequest只有一个子接口,就是 HttpServletRequest。既然只有一个子接口为什么不将两个接口合并为一个?

​ 从长远上讲:现在主要用的协议是HTTP协议,但以后可能出现更多新的协议。若以后想要支持这种新协议,只需要直接继承ServletRequest接口就行了。

​ 在HttpServletReguest接口中,定义的方法很多,但都是围绕接收客户端参数的。但是怎么拿到该对象呢 不需要,直接在Service方法中由容器传入过来,而我们需要做的就是取出对象中的数据,进行分析、处理。

接受请求

常用方法
常用方法描述
getRequestURL()获取客户端发出请求时的完整URL
getRequestURI()获取请求行中的资源名称部分(项目名称开始)
getQueryString()获取请求行中的参数部分
getMethod()获取客户端请求方式
getProtocol()获取HTTP版本号
getContextPath()获取 webapp 名字

获取请求的参数

获取指定名称的参数值,返回字符串(重点!!!)

String uname = request.getParameter( "uname") 
String upwd=request.getParameter("upwd");

请求乱码问题

​ 由于现在的request属于接收客户端的参数,所以必然有其默认的语言编码,主要是由于在解析过程中默认 使用的编码方式为ISO-8859-1(此编码不支持中文),所以解析时一定会出现乱码。要想解决这种乱码问题,需要设置request中的编码方式,告诉服务器以何种方式来解析数据。或者在接收到乱码数据以后,再通过相应的编码格式还原。

方式一

request.setcharacterEncoding("uTF-8");

这种方式只针对POST有效(必须在接收所有的数据之前设定)

方式二:

newstring(request.getParameter(name).getBytes("Iso-8859-1"),"uTF-8");

借助了String对象的方法,该种方式对任何请求有效,是通用的。

注意

  • Tomcat8以上

    • GET方式请求是不会出现乱码的。

    • POST方式请求会出现乱码的。

      • 解决

        request.setCharacterEncoding("UTF-8");
  • Tomcat8以前

    • GET方式请求会出现乱码的。

      • 解决

        newstring(request.getParameter(name).getBytes("Iso-8859-1"),"uTF-8");
    • POST方式请求会出现乱码的。

      • 解决

        request.setCharacterEncoding("UTF-8");

请求转发

​ 请求转发,是一种服务器的行为,当客户端请求到达后,服务器进行转发,此时会将请求对象进行保存,地 址栏中的URL地址不会改变,得到响应后,服务器端再将响应发送给客户端,从始至终只有一个请求发出实现方式如下,达到多个资源协同响应的效果。

request.getRequestDispatcher(url).forward(request,response);

request作用域

​ 通过该对象可以在一个请求中传递数据,作用范围:在一次请求中有效,即服务器跳转有效。

//设置域对象内容
request.setAttribute(string name,stringvalue); 
//获取域对象内容
request.getAttribute(string name);
//删除域对象内容
request.removeAttribute(stringname);

​ request域对象中的数据在一次请求中有效,则经过请求转发,request域中的数据依然存在,则在请求转发 的过程中可以通过request来传输/共享数据。

HttpServletResponse

​ Web服务器收到客户端的http请求,会针对每一次请求,分别创建一个用于代表请求的request对象和代表响应的response对象。 ​ request和response对象代表请求和响应:获取客户端数据,需要通过request对象;向客户端输出数据需要通过response对象。 ​ HttpServletResponse的主要功能用于服务器对客户端的请求进行响应,将Web服务器处理后的结果返回给客户端。service()方法中形参接收的是HttpServletResponse接口的实例化对象,这个对象中封装了向客户端发 送数据、发送响应头,发送响应状态码的方法。

响应数据

接收到客户端请求后,可以通过HttpServletResponse对象直接进行响应,响应时需要获取输出流有两种形式: getWriter()获取字符流(只能响应回字符 getoutputstream()获取字节流(能响应一切数据) 响应回的数据到客户端被浏览器解析注意:两者不能同时使用

注意

两者不能同时使用。

响应乱码问题

​ 在响应中,如果我们响应的内容中含有中文,则有可能出现乱码。这是因为服务器响应的数据也会经过网络 传输,服务器端有一种编码方式,在客户端也存在一种编码方式,当两端使用的编码方式不同时则出现乱码。 getWriter()的字符乱码 ​ 对于getWriter()获取到的字符流,响应中文必定出乱码,由于服务器端在进行编码时默认会使用ISO-8859-1 格式的编码,该编码方式并不支持中文。 ​ 要解决该种乱码只能在服务器端告知服务器使用一种能够支持中文的编码格式,比如我们通常用的”UTF-8”

response.setcharacterEncodingc"uTF-8");

此时还只完成了一半的工作,要保证数据正确显示,还需要指定客卢端的解码方式

response.setHeader("content-type","text/html;charset=uTF-8")

两端指定编码后,乱码就解决了。一句话:保证发送端和接收端的编码一致

//设置服务端的编码
response.setcharacterEncoding("uTF-8");
//设置客户端的响应类型及编码
response.setHeader("content-type","text/html;charset=UTF-8"); 
//得到字符输出流
Printwriter writer = response.getwriterO; 
writer.write("<h2>你好</h2>")
以上两端编码的指定也可以使用一句替代,同时指定服务器和客户端

以上两端编码的指定也可以使用一包替代,同时指定服务器和客户端

response.setcontentType("text/html;charset=uTF-8") 

getoutputstream()字节乱码 对于getOutputStream()方式获取到的字节流,响应中文时,由于本身就是传输的字节,所以此时可能出现乱码,也可能正确显示。当服务器端给药字节恰好和客户端使用的编码方式一致时则文本正确显示,否则出现乱码。无论如何我们都应该准确掌握服务器和客户端使用的是那种编码格式,以确保数据正确显示。 指定客户端和服务器使用的编码方式一致。

response.setHeader("content-type","text/html;charset=UTF-8");
设置客户端的编码及响应类型
Servletoutputstreamout-response.getoutputstreamO;
response.setHeader("content-type","text/html;charset=UTF-8") 
out.write("<h2>你好</h2>".getBytes("uTF-8"));

重定向

​ 重定向是一种服务器指导,客户端的行为。客户端发出第一个请求,被服务器接收处理后,服务器会进行响 应,在响应的同时,服务器会给客户端一个新的地址(下次请求的地址response.sendRedirect(url);),当客户端接收到响应后,会立刻、马上、自动根据服务器给的新地址发起第二个请求,服务器接收请求并作出响应,重定向完成。 ​ 从描述中可以看出重定尚当中有两个请求存在,并且属于客户端行为

//重定向跳转到index.jsp
response.sendRedirect("index.jsp");
通过观察浏览器我们发现第一次请求获得的响应码为302,并直含有一个location头信息。并直地址栏最终

看到的地址是和第一次请求地址不同的,地址栏已经发生了变化

请求转发与重定向的区别

请求转发和重定向比较:

两者都可进行跳转,根据实际需求选取即可,

​ Cookie是浏览器提供的一种技术,通过服务器的程序能将一些只须保存在客户端,或者在客户端进行处理的 数据,放在本地的计算机上,不需要通过网络传输,因而提高网页处理的效率,并且能够减少服务器的负载,但是 由于cookie是服务器端保存在客户端的信息,所以其安全性也是很差的。例如常见的记住密码则可以通过 Cookie来实现。 ​ 有一个专门操作Cookie的类javax.servlet.http.cookie。随着服务器端的响应发送给客户端,保存在浏览器。当下次再访问服务器时把cookie再带回服务器。 ​ cookie的格式:键值对用”=链接,多个键值对间通过”;“隔开。

Cookie的创建的发送

​ 通过newCookie("key""value");来创建一个Cookie对象,要想将Cookie随响应发送到客户端,需要先添加 到response对象中,response.addcookie(cookie)此时该cookie对象则随着响应发送至了客户端。在浏览器上可以看见。

//创建cookie对象
cookie cookie=new cookiec"uname","zhangsan");//发送cookie对象
response.addcookie(cookie);

Cookie的获取

​ 在服务器端只提供了一个getCookies()的方法用来获取客户端回传的所有cookie组成的一个数组,如果需要获取单个cookie则需要通过遍历,getName()获取Cookie的名称,getValue()获取Cookie的值。

//获取cookie数组
Cookie[] cookies=request.getcookiesO;
//判断数组是否为空
if(cookies!=nu11 && cookies.length>0){
	//遍历cookie数组
	for(cookie cookie:cookies){
		System.out.println(cookie.getName()) 
		System.out.printin(cookie.getvalue());
	}
}

cookie设置到期时间

​ 除了cookie的名称和内容外,我们还需要关心一个信息,到期时间,到期时间用来指定该cookie何时失 效。默认为当前浏览器关闭即失效。我们可以手动设定cookie的有效时间(通过到期时间计算),通过 setMaxAge(inttime);方法设定cookie的最大有效时间,以秒为单位 到期时间的取值

  • 负整数 若为负数,表示不存储该cookie。 cookie的maxAge属性的默认值就是-1,表示只在浏览器内存中存活,一旦关闭浏览器窗口,那么cookie就 会消失。
  • 正整数 若大于0的整数,表示存储的秒数。 表示cookie对象可存活指定的秒数。当生命大于o时,浏览器会把cookie保存到硬盘上,就算关闭浏览器,就算重启客户端电脑,cookie也会存活相应的时间。
  • 零 若为o,表示删除该cookie。 cookie生命等于o是一个特殊的值,它表示cookie被作废!也就是说,如果原来浏览器已经保存了这个 Cookie,那么可以通过Cookie的setMaxAge(o)来删除这个Cookie。无论是在浏览器内存中,还是在客户端硬盘上都会删除这个Cookie。

设置cookie对象指定时间后失效

Cookie的注意点

  1. Cookie保存在当前浏览器中

在一般的站点中常常有记住用户名这样一个操作,该操作只是将信息保存在本机上,换电脑以后这些信息就无效了。而且cookie还不能跨浏览器。

  1. Cookie存中文问题 Cookie中不能出现中文,如果有中文则通过URLEncoder.encode()来进行编码,获取时通过 URLDecoder.decode()来进行解码。
string name="姓名"; 
string value="张三";
//通过uRLEncoder.encode()来进行编码
name=URLEncoder.encode(name); 
value = URLEncoder.encode(value)
//创建cookie对象
Cookie cookie = new cookie(name,value);
//发送cookie对象
response.addcookie(cookie);
//获取时通过uRLDecoder.decodeO来进行解码
URLDecoder.decode(cookie.getName()); 
URLDecoder.decode(cookie.qetValueO)
  1. 同名Cookie问题 如果服务器端发送重复的cookie那么会覆盖原有的Cookie。
  2. 浏览器存放cookie的数量 不同的浏览器对Cookie也有限定,Cookie的存储有是上限的。Cookie是存储在客户端(浏览器)的,而且一股是由服务器端创建和设定。后期结合Session来实现回适跟踪。

Cookie的路径

​ Cookie的setPath设置cookie的路径,这个路径直接决定服务器的请求是否会从浏览器中加载某些cookie

**情景一:**当前服务器下任何项目的任意资源都可获取Cookie对象

/*当前项目路径为:s01*/
Cookiecookie=newCookie("xxx","xxx");
//设置路径为"/",表示在当前服务器下任何项目都可访问到cookie对象 
cookie.setPathc"/");
response.addcookie(cookie);

**情景二:**当前项目下的资源可获取Cookie对象(默认不设置Cookie的path)

/*当前项目路径为:s01*/
Cookie cookie =new cookie("xxx","xxx");
//设置路径为"/so1",表示在当前项目下任何项目都可访问到cookie对象
cookie.setPath("/so1");//默认情况,可不设置path的值 
response.addcookie(cookie);

**情景三:**指定项目下的资源可获取Cookie对象

/*当前项目路径为:s01*/
Cookiecookie=newCookie("xxx","xxx");
//设置路径为"/s02",表示在s02项目下才可访问到cookie对象
cookie.setPath("/so2");//只能在so2项目下获取cookie,就算cookie是so1产生的,so1也不能获取它
response.addcookie(cookie):

情景四:指定目录下的资源可获取Cookie对象

/*当前项目路径为:so1*/
Cookiecookie=newcookie("xxx""xxx")
//设置路径为"/so1/cook",表示在so2/cook目录下才可访问到cookie对象
cookie.setPath("/so1/cook"); 
response.addcookie(cookie);

​ 如果我们设置path,如果当前访问的路径包含了cookie的路径(当前访问路径在cookie路径基础上要比 cookie的范围小)cookie就会加载到request对象之中。 ​ cookie的路径指的是可以访问该cookie的顶层目录,该路径的子路径也可以访问该cookie。

备注

​ 总结:当访问的路径包含了cookie的路径时,则该请求将带上该cookie;如果访问路径不包含cookie路径则该请求不会携带该cookie。

HttpSession

HttpSession对象是javax.servlet.http.HttpSession的实例,该接口并不像HttpServletRequest或 HttpServletResponse还存在一个父接口,该接口只是一个纯粹的接口。这因为session本身就属于HTTP协议的范畴。 ​ 对于服务器而言,每一个连接到它的客户端都是一个session,servlet容器使用此接口创建HTTP客户端和 HTTP服务器之间的会话。会话将保留指定的时间段,跨多个连接或来自用户的页面请求。一个会话通常对应于一个用户,该用户可能多次访问一个站点。可以通过此接口查看和操作有关某个会话的信息,比如会话标识符、创建时间和最后一次访问时间。在整个session中,最重要的就是属性的操作 ​ session无论客户端还是服务器端都可以感知到,若重新打开一个新的浏览器,则无法取得之前设置的 session,因为每一个session只保存在当前的浏览器当中,并在相关的页面取得。 ​

​ Session的作用就是为了标识一次会话,或者说确认一个用户:并且在一次会话(一个用户的多次请求)期间共享数据。我们可以通过request.getSessiono)方法,来获取当前会话的session对象。

//如果session对象存在,则获取;如果session对象不存在,则创建
Httpsession session=request.getsession();

标识符JSESSIONID

​ session既然是为了标识一次会话,那么此次会话就应该有一个唯一的标志,这个标志就是sessionld。 ​ 每当一次请求到达服务器,如果并开启了会话(访问了session),服务器第一步会查看是否从客户端回传一人名为JSESSIONID的cookie,如果没有则认为这是一次新的会话,会创建一个新的session对象,并用唯一的 sessionld为此次会话做一个标志。如果有sSIONID这个cookie回传,服务器则会根据JSESSIONID这个值去查看是否含有id为sEsSION值的session对象,如果没有则认为是一个新的会话,重新创建一个新的session对象并标志此次会话;如果找到了相应的session对象,则认为是之前标志过的一次会话,返回该session对象,数据达到共享。 ​ 这里提到一个叫做ISESSIONID的cookie,这是一个比较特殊的cookie,当用户请求服务器时,如果访问了 session,则服务器会创建一个名为ISEssIONID,值为获取到的session(无论是获取到的还是新创建的)的 sessionld的cookie对象,并添加到response对象中,响应给客户端,有效时间为关闭浏览器 ​ 所以Session的底层依赖cookie来实现

session域对象

​ Session用来表示一次会话,在一次会话中数据是可以共享的,这时session作为域对象存在,可以通过 setAttribute(namevalue)方法向域对象中添加数据,通过getAttribute(name)从域对象中获取数据,通过 removeAttribute(name)从域对象中移除数据,

//获取session对象
Httpsessionsession=request.getsessionO
//设置session域对象
session.setAttribute("uname","admin")
//获取指定名称的session域对象
String uname=(string) request.getAttribute("uname");
//移除指定名称的session域对象
session.removeAttribute("uname");

​ 数据存储在session域对象中,当session对象不存在了,或者是两个不同的session对象时,数据也就不 能共享了。这就不得不谈到session的生命周期

session对象的销毁

默认时间到期

​ 当客户端第一次请求servlet并且操作session时,session对象生成,Tomcat中session默认的存活时间 为3omin,即你不操作界面的时间,一旦有操作,session会重新计时, ​ 那么session的默认时间可以改么?答案是肯定的。 ​ 可以在Tomcat中的conf目录下的web.xml文件中进行修改。

<!--session默认的最大不活动时间。单位:分钟。 
<session-config>
	<session-timeout>30</session-timeout>
</session-config>
自己设定到期时间

​ 当然除了以上的修改方式外,我们也可以在程序中自已设定session的生命周期,通过 session.setMaxlnactivelnterval(int)来设定session的最大不活动时间,单位为秒。

//获取session对象
Httpsession session=request.getsession();
//设置session的最大不活动时间
session.setMaxInactiveInterval(i5)://15秒

​ 当然我们也可以通过getMaxlnactivelntervalo方法来查看当前Session对象的最大不活动时间。

//获取session的最大不活动时间
int time = session.getMaxInactiveInterval(); 
立刻失效

​ 或者我们也可以通过session.invalidateo方法让session立刻失效

//销毁session对象
session.invalidate(); 
关闭浏览器

​ 从前面的JESSlON可知道,session的底层依赖cookie实现,并且该cookie的有效时间为关闭浏览器,从 而session在浏览器关闭时也相当于失效了(因为没有JSESSION再与之对应)。

关闭服务器

​ 当关闭服务器时,session销毁。

​ Session失效则意味着此次会话结束,数据共享结束。

ServletContext

​ 每一个web应用都有且仅有一个ServletContext对象,又称Application对象,从名称中可知,该对象是与应用程序相关的。在WEB容器启动的时候,会为每一个WEB应用程序创建一个对应的ServletContext对象。 ​ 该对象有两大作用,第一、作为域对象用来共享数据,此时数据在整个应用程序中共享;第二、该对象中保存了当前应用程序相关信息。例如可以通过getServerlnfo()方法获取当前服务器信息,getRealPath(String path)获取资源的真实路径挛

ServletContext对象的获取

​ 获取ServletContext对象的途径有很多。比如

1.通过request对象获取

Servletcontext servletcontext=request.getServletcontext();

2.通过session对象获取

Servletcontext servletcontext=request.getsession().getservletcontext();

3.通过servletConfig对象获取,在Servlet标准中提供了ServletConfig方法

Servletconfig servletconfig=getservletconfig();
Seryletcontext seryletcontext=seryletconfig.getseryletcontext()

4.直接获取Servlet类中提提供了直接获取ServletContext对象的方法

ServletcontextservletContext4=getServletcontext();

ServletContext域对象

​ ServletContext也可当做域对象来使用,通过向ServletContext中存取数据,可以使得整个应用程序共享某些数据。当然不建议存放过多数据,因为ServletContext中的数据一旦存储进去没有手动移除将会一直保存。

//获取seryletcontext对象
Servletcontextservletcontext=request.getservletcontext()
//设置域对象
servletcontext.setAttribute("name","zhangsan");
//获取域对象
string name=(string)servletcontext.getAttribute("name")
//移除域对象
servletcontext.removeAttribute("name");

Servlet的三大域对象

1.request域对象 在一次请求中有效。请求转发有效,重定向失效。

2.session域对象 在一次会话中有效。请求转发和重定向都有效,session销毁后失效

3.servletContext域对象 在整个应用程序中有效。服务器关闭后失效。

文件的上传和下载

​ 在上网的时候我们常常遇到文件上传的情况,例如上传头像、上传资料等;当然除了上传,遇见下载的情况 也很多,接下来看看我们servlet中怎么实现文件的上传和下载。

文件上传

​ 文件上传涉及到前台页面的编写和后台服务器端代码的编写,前台发送文件,后台接收并保存文件,这才是 一个完整的文件上传

前台页面

​ 在做文件上传的时候,会有一个上传文件的界面,首先我们需要一个表单,并且表单的请求方式为POST:其 次我们的form表单的enctype必须设为”multipart/form-data”,即enctype=“multipart/form-data”,意思是设置表单的类型为文件上传表单。默认情况下这个表单类型是”application/x-www-form-urlencoded”,不能用于文件上传。只有使用了multipart/form-data才能完整地传递文件数据。


文件上传表单
I
1.表单提交类型method="post"
2.表单类型enctype="multipart/form-data' 
3.表单元素类型文件域设置name属性值

<form method="post" action="uploadservlet" enctype="multipart/form-data">
姓名:<inputtype="text" name="uname”><br> 
文件:<inputtype="file" name="myfile"><br>
	<button type="submit">提交</button> 
</form>
后台实现

​ 使用注解MultipartConfig将一个Servlet标识为支持文件上传。Servletmultipart/form-data的 POST请求封装成Part,通过Part对上传的文件进行操作。

@webservlet("/uploadservlet")
@Multipartconfig//如果是文件上传表单,一定要加这个注解工 
public class Uploadservlet extends HttpServlet {
	@override
	protected void service(HttpServletRequest request,HttpServletResponse response)
throwsServletException,IoException{
        //设置请求的编码格式
        request.setcharacterEncoding("uTF-8")
        //获取普通表单项(文本框)
        stringunamerequest.getParameter("uname");//"uname"代表的是文本框的name属性值
        //通过getPart(name)方法获取Part对象(name代表的是页面中file文件域的name属性值)
        Part part=request.getPart("myfile")
        //通过Part对象,获取上传的文件名
        stringfileName=part.getsubmittedFileNameO;
        //获取上传文件需要存放的路径(得到项目存放的真实路径)
        stringrealpath=reguest.getservletcContextO.getRealpath("/"):
        //将文件上传到指定位置
        part.write(realpath + fileName);
    }
}

文件下载

​ 文件下载,即将服务器上的资源下载(拷贝)到本地,我们可以通过两种方式下载。第一种是通过超链接本 身的特性来下载;第二种是通过代码下载。

超链接下载

​ 当我们在HTML或JSP页面中使用a标签时,原意是希望能够进行跳转,但当超链接遇到浏览器不识别的资源时会自动下载;当遇见浏览器能够直接显示的资源,浏览器就会默认显示出来,比如txt、png、jpg等。当然我们 也可以通过download属性规定浏览器进行下载。但有些浏览器并不支持。

默认下载

<!--当超链接遇到浏览器不识别的资源时,会自动下载--> 
<a href="test.zip">超链接下载</a>

指定download属性下载

<!--当超链接遇到浏览器识别的资源时,默认不会下载。通过down1oad属性可进行下载--> <ahref="test.txt"download>超链接下载</a>

​ download属性可以不写任何信息,会自动使用默认文件名。如果设置了download属性的值,则使用设置的 值做为文件名。当用户打开浏览器点击链接的时候就会直接下载文件

后台实现下载

实现步骤

  1. 需要通过response.setContentType方法设置Content-type头字段的值,为浏览器无法使用某种方式或激活某个程序来处理的MiME类型,例如”application/octet-stream”或”application/x-msdownload”等。

  2. 需要通过response.setHeader方法设置Content-Disposition头的值为”attachment;filename=文件名

  3. 读取下载文件,调用response.getoutputstream方法向客户端写含附件内容。

public class Downloadservlet extends HttpServlet{
	protected void service(HttpservletRequest request,HttpServletResponse response)
throwsServletException,IoException{
	//设置请求的编码
    request.setcharacterEncoding("uTIg")
    //获取文件下载路径
    string path=getservletcontextO.getRealPath("/"):
    //获取要下载的文件名
    String name=request.getParameter("fileName");
    //通过路径得到file对象
    Filem file=newFile(path+name):
    //判断fi1e对象是否存在,且是否是一个标准文件 
    if(file.existsO&file.isFile()){
    	//设置响应类型(浏览器无法使用某种方式或激活某个程序来处理的类型)
        response.setcontentType("application/x-msdownload");
        //设置头信息
        response.setHeader("content-Disposition","attachment;filename="+name);
        //得到输入流
        Inputstreamis=newFileInputstream(file);
        //得到输出流
        Servlet outputstream os=response.getoutputstream();
        //定义byte数组
        byte[] car=newbyte[10o24];
        //定义长度 
        int 1en =O;
        //循环输出
        while((len=is.read(car))!=-1){
        os.write(car,o,len);
    }
    //关闭流释放资源
	os.close(); 
	is.close(); 
	}else{
		System.out.print1n("文件不存在,下载失败!");
	}
}
}

Resource文件夹标记作用

用IDEA 编写Java过程中需要访问数据文件,为了统一存放数据文件,所以就把数据文件统一放在了resources文件中。

存放数据文件

此时应该注意代码文件和数据文件存放路径的对应关系

具体路径:

36

最终编译效果:

重要

这样位于同一级目录的类可以直接访问到资源

Java命令行新见解

使用javac编译单个文件直接在文件目录即可

E:\CodeProgram\JAVA_Project\WEB\src\main\java\fun\maluyao> javac Test.java

java命令运行带有包的.class文件需要在根目录运行,并且带有类的完整路径

PS E:\CodeProgram\JAVA_Project\WEB\target\classes> java fun.maluyao.Test
输出

通过创建文件得到结论,文件根目录是在执行目录,即工作目录

下面是例子:

在命令行执行:

E:\CodeProgram\JAVA_Project\WEB\target\classes> java fun.maluyao.Test

在IDEA执行结果

要获取类同级资源,使用下面代码:

java
package fun.maluyao;

import java.io.*;
import java.net.URL;

public class Test {
    public static void main(String[] args) throws IOException {
        URL resource = Test.class.getResource("1.png");
        System.out.println(resource);
    }
}

输出结果:

file:/E:/CodeProgram/JAVA_Project/WEB/target/classes/fun/maluyao/1.png

转为File

File file = new File(resource.getPath());

Servlet配置中servlet-mapping的配置问题

12.1关于URL路径 web容器接收到客户端请求后,首选要确定使用容器内的那个web应用来处理该请求,比如http://host:port/xxx/user/add.jsp?id=1,其中/xxx/user/add.jsp为请求URL,/xxx为context path,用来匹配使用那个web应用处理该请求,如果各个web应用都无法匹配该路径,则使用根应用(ROOT)来处理,tomcat中的根应用可以直接放到目录下的webapps目录下的ROOT文件夹内,或者在<Host>元素中配置,主要正确的配置方式是path=""空字符串。

<Context path="" docBase="F:/Workspaces/MyEclipse2014/test/WebRoot" reloadable="false" crossContext="false"/>

确定使用哪个web应用处理请求后,根据具体web应用的配置(主要是Servlet配置的<url-pattern>),确定使用哪一个Servlet来处理请求,在请求URL中去掉context path和请求参数后,按照顺序,根据如下规则进行匹配,使用第一个匹配成功的规则进行处理。

1.完整路径匹配;

2.根据路径前缀,进行最长匹配,使用’/‘作为路径分隔符;

3.使用请求路径中的后缀进行匹配,比如’.jsp’;

4.如果以上都没有匹配到合适的Servlet,将使用默认(default)Servlet来处理请求;

注意:路径匹配过程中,区分大小写;

12.2如何配置Servlet中的<url-pattern> 使用‘/’开头,使用‘/’结尾,表示使用路径匹配,比如/foo/bar/

使用’*.xxx’表示使用后缀匹配;

只使用‘/*’,表示匹配所有的请求;

只使用’/‘,表示是一个默认的Servlet;

除此之外,其他的字符都是准确匹配;

配对例子

spring MVCDispatcherServlet的配置

<!-- 配置spring mvc -->
<servlet>
    <servlet-name>springMvc</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <init-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>classpath:spring-mvc.xml</param-value>
    </init-param>
    <load-on-startup>1</load-on-startup>
</servlet>

映射方式,采用如下两种方式:

<!-- 配置spring mvc mapping -->
<servlet-mapping>
    <servlet-name>springMvc</servlet-name>
    <url-pattern>/</url-pattern>
</servlet-mapping>

或者采用后缀映射

<!-- 配置spring mvc mapping -->
<servlet-mapping>
    <servlet-name>springMvc</servlet-name>
    <url-pattern>*.do</url-pattern>
</servlet-mapping>

【注意】一定不能写为<url-pattern>/*</url-pattern>,这样的话,所有的请求将全部由Spring的DispatcherServlet来处理,显然是不合适的,jsp文件还是应该由Tomcat配置的JSP Servlet来处理。

Tomcat,在conf/web.xml中配置了一个default Servlet,一个jsp Servlet

<servlet>
	<servlet-name>default</servlet-name>
	<servlet-class>org.apache.catalina.servlets.DefaultServlet</servlet-class>
	<init-param>
		<param-name>debug</param-name>
		<param-value>0</param-value>
	</init-param>
	<init-param>
		<param-name>listings</param-name>
		<param-value>false</param-value>
	</init-param>
	<load-on-startup>1</load-on-startup>
</servlet>
<servlet>
	<servlet-name>jsp</servlet-name>
	<servlet-class>org.apache.jasper.servlet.JspServlet</servlet-class>
	<init-param>
		<param-name>fork</param-name>
		<param-value>false</param-value>
	</init-param>
	<init-param>
		<param-name>xpoweredBy</param-name>
		<param-value>false</param-value>
	</init-param>
	<load-on-startup>3</load-on-startup>
</servlet>
 
<!-- The mapping for the default servlet -->
<servlet-mapping>
	<servlet-name>default</servlet-name>
	<url-pattern>/</url-pattern>
</servlet-mapping>
 
<!-- The mappings for the JSP servlet -->
<servlet-mapping>
	<servlet-name>jsp</servlet-name>
	<url-pattern>*.jsp</url-pattern>
	<url-pattern>*.jspx</url-pattern>
</servlet-mapping>

使用Spring的DispatcherServlet作为默认Servlet后,如果静态资源还需要由Tomcat配置的default Servlet来配置,可以参考Spring中静态资源的配置方式,个人比较倾向于使用如下配置,这样对静态资源的请求就不需要DispatcherServlet来处理了。

<!-- 使用Tomcat默认Servlet处理静态资源,该配置仅适用Tomcat容器 -->
<servlet-mapping>
	<servlet-name>default</servlet-name>
	<url-pattern>*.css</url-pattern>
	<url-pattern>*.jpg</url-pattern>
	<url-pattern>*.gif</url-pattern>
	<url-pattern>*.js</url-pattern>
</servlet-mapping>0

遇到的版本问题

Tomcat运行时出现Unable to process Jar entry [module-info.class] from Jar

解决办法一 找到Tomcat下的apache-maven-3.6.1\repository\org\apache\logging\log4j\log4j-api\2.10.0路径打开,可以找到log4j-2.10.0.jar 用压缩方式打开不是解压删除module-info.class即可 解决办法二 原因:Tomcat版本底,Spring boot 2.2.1对应的Tomcat版本应该是:tomcat 8.5.16以上将Tomcat版本更新至8.6以上(亲测可用)附下载地址

Maven项目下:@WebServlet注解失效的解决方法

一般有两种原因:

1.web.xml版本太低不支持注解

2.servlet.jar版本太低,里面的接口失效(命名空间从 javax 变成了 jakarta)

(大部分都是这个原因,tomcat 10 以前版本为javax , tomcat 10 为jakarta)

解决方法:

1.修改web.xml的version至少3.0以上 2.检查web.xml中

metadata-complete="false"

metadata-complete="true"表示仅支持配置映射

metadata-complete="false"表示支持配置映射和注解映射

3.如果发现以上修改均无用,但是配置web.xml文件的映射路径后可以打开路径,然后报错提示找不到类,则是第二种原因

在pom.xml文件的<dependencies>标签里换用

<dependency>
    <groupId>jakarta.servlet</groupId>
    <artifactId>jakarta.servlet-api</artifactId>
    <version>5.0.0</version>
    <scope>provided</scope>
</dependency>

并修改servlet类即可

tomcat10以上
<dependency>
  <groupId>jakarta.servlet</groupId>
  <artifactId>jakarta.servlet-api</artifactId>
  <version>6.0.0</version>
</dependency>
tomcat10以下
<dependency>
  <groupId>javax.servlet</groupId>
  <artifactId>javax.servlet-api</artifactId>
  <version>4.0.1</version>
</dependency>

IDEA打WAR包解决办法Cannot access defaults field of Properties

原因分析:

出现这个问题是由于 JDK 版本和打 war 包的插件版本不匹配,由于更改 JDK 版本会影响项目,所以我们需要修改插件的版本,可以在 pom 文件中指定版本的 maven-war-plugin

解决方案: 解决办法就是在对应模块的 .pom 文件中加入如下

插件代码:

<build>
    <plugins>
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-war-plugin</artifactId>
            <version>3.2.2</version>
        </plugin>
    </plugins>
</build>

插入之后更新一下 pom 文件,即可成功打包,这里我的JDK版本是17,所以需要这个版本的插件,如果是其他版本,可以去官网查询一下。

只是在maven的生命周期里会报错,配置运行不会报错的

使用Maven打包项目的时候,出现错误:webxml attribute is required (or pre-existing WEB-INF/web.xml if executing in update)

原因分析 web项目下缺少WEB-INF/web.xml

当是在servlet 3.0之后,对于web.xml文件本身是可选的

解决方案 第一种、在pom.xml文件中定义一个参数配置

  <properties>
        <failOnMissingWebXml>false</failOnMissingWebXml>
    </properties>

第二种,配置类似下面的配置:

即在插件中配置好web资源目录和web.xml文件位置:

下面是一个参考:

<plugin>
	<groupId>org.apache.maven.plugins</groupId>
	<artifactId>maven-war-plugin</artifactId>
	<version>3.3.2</version>
	<configuration>
		<warName>war打包之后的名称</warName>
		<warSourceDirectory>web资源目录</warSourceDirectory>
		<webXml>web.xml路径</webXml>
	</configuration>
</plugin>

简单解决方案:

创造下图一样的目录就不用着急扫瞄的问题了,这是标准的web目录(注意webapps位置)

写SpringMVC时遇到 org.springframework.web.servlet.DispatcherServlet报错和404路径错误

首先得知道Tomcat 10版本和历史版本不同之处 : Oracle将Java EE(Java SE还自己保留)交给开源组织,Eclipse基金会接手。但Oracle不允许开源组织使用Java名号,所以Jakarta EE名称于2018.02.26应运而生,所以Tomcat 10版本那些原本javax.*包都改名为jakarta.*;这样一改我们一旦写Servlet的时候还是导入javax.*包就会报错,得全部改成jakarta.*;

*方式1. (推荐方案,版本一改直接解决)*

想要继续使用javax.*;更老一点的Tomcat 版本例如Tomcat 9 及以下

pom.xml里面的servlet依赖和jsp依赖不用动还是j前缀还是 *javax-*

方式2. (不推荐,Tomcat10和jsp,servlet各种版本对应问题可能还会导致404错误)

要是坚持使用Tomcat 10那pom.xml里的Servlet和jsp依赖就得改成 jakarta前缀

实际解决:

注意

导入springMVC框架之后还要导入servlet规范api

<dependency>
	<groupId>org.springframework</groupId>
	<artifactId>spring-webmvc</artifactId>
	<version>6.1.3</version>
</dependency>
<dependency>
	<groupId>jakarta.servlet</groupId>
	<artifactId>jakarta.servlet-api</artifactId>
	<version>6.0.0</version>
</dependency>

严重SpringMVC找不到资源或者找不到controller

我在web.xml中配置了

<welcome-file-list>
        <welcome-file>index.html</welcome-file>
    </welcome-file-list>

这会导致spring mvc加载异常

而且请务必把springmvc的DispatcherServlet配置到根目录,否则会导致项目无效!

<servlet>
	<servlet-name>SpringServlet</servlet-name>
	<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
	<init-param>
		<param-name>contextConfigLocation</param-name>
		<param-value>classpath:spring-mvc.xml</param-value>
	</init-param>
	<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>SpringServlet</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>

java.lang.IllegalArgumentException: Name for argument of type [java.lang.String] not specified, and parameter name information not available via reflection. Ensure that the compiler uses the ‘-parameters’ flag.

正如错误信息所说,这个异常信息表明,Spring MVC在尝试处理请求时遇到了一个问题,具体来说,是在解析Controller方法参数时出现了问题。异常指出,对于类型为java.lang.String的参数,没有指定名称,并且通过反射无法获取参数名称信息。这个问题的根本原因通常是因为编译器没有使用-parameters标志,导致参数名称信息没有保留在编译后的字节码中。

确保编译器使用了-parameters标志

  • 当你使用Java 8或更高版本的编译器编译代码时,确保编译器使用了-parameters标志。这个标志允许编译器在编译后的字节码中保留方法参数的名称信息,这样反射时就能获取到这些信息。

  • 对于Maven项目,在pom.xml中添加以下配置:

    <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-compiler-plugin</artifactId>
        <version>你的maven-compiler-plugin版本</version>
        <configuration>
            <compilerArgs>
                <arg>-parameters</arg>
            </compilerArgs>
        </configuration>
    </plugin>
  1. 检查Controller方法

    • 确保你的Controller方法参数名称与请求中的参数名称匹配。如果你使用的是@RequestParam注解,确保注解中指定了参数名称,或者方法参数名称与请求中的参数名称相同。

    例如:

    @GetMapping("/example")
    public ResponseEntity<String> example(@RequestParam String param) {
        // 使用param参数
        return ResponseEntity.ok("Param value: " + param);
    }

    在这个例子中,param是方法参数的名称,如果请求中没有指定参数名称,那么它应该与这个名称匹配。

idea项目使用tomcat运行乱码问题(全部解决,亲测有效)

-Dfile.encoding=UTF-8

在tomcat Server中设置 VM options , 值为-Dfile.encoding=UTF-8

Tomcat 10 在 idea 中无法使用 pageContext.setAttribut();

由于 Tomcat 10 使用的 servlet-api 和 jsp-api 为 jakarta 的依赖。

这样在使用 pageContext.setAttribute(); 或者 ${pageContext.request.contextPath} 都不出现提示和报错。

<dependency>
    <groupId>jakarta.servlet</groupId>
    <artifactId>jakarta.servlet-api</artifactId>
    <version>5.0.0</version>
</dependency>

<dependency>
    <groupId>jakarta.servlet.jsp</groupId>
    <artifactId>jakarta.servlet.jsp-api</artifactId>
    <version>3.0.0</version>
</dependency>

重要

实际使用的时候,上面的依赖就可以了,下面的是给老版本的

这时候再添加以下版本的 javax 的依赖即可。

<dependency>
    <groupId>javax.servlet</groupId>
    <artifactId>servlet-api</artifactId>
    <version>2.5</version>
</dependency>

<dependency>
    <groupId>javax.servlet.jsp</groupId>
    <artifactId>jsp-api</artifactId>
    <version>2.1</version>
</dependency>

添加最新的 javax 版本还是会报错的。

(报错)

<dependency>
    <groupId>javax.servlet</groupId>
    <artifactId>javax.servlet-api</artifactId>
    <version>4.0.1</version>
</dependency>

<dependency>
    <groupId>javax.servlet.jsp</groupId>
    <artifactId>javax.servlet.jsp-api</artifactId>
    <version>2.3.3</version>
</dependency>

原文链接:https://blog.csdn.net/MZa1213123/article/details/123008399

使用 JSTL

如果您已经使用 Tomcat 10.1.x(第二个 Jakartified 版本,使用 jakarta.* 包而不是 javax.* 包,但第一个版本使用更新的 jakarta.tags.* 命名空间 URN 而不是 http:

<dependency>
    <groupId>jakarta.servlet.jsp.jstl</groupId>
    <artifactId>jakarta.servlet.jsp.jstl-api</artifactId>
    <version>3.0.0</version>
</dependency>
<dependency>
    <groupId>org.glassfish.web</groupId>
    <artifactId>jakarta.servlet.jsp.jstl</artifactId>
    <version>3.0.1</version>
</dependency>
As said, the namespace URIs have been changed to become URNs instead of URLs. JSTL core is since JSTL version 3.0 available via an easier to remember namespace URI in URN format:
如前所述,名称空间 URI 已更改为 URN,而不是 URL。 JSTL 核心自 JSTL 版本 3.0 起可通过更容易记住的 URN 格式的名称空间 URI 获得:

<%@ taglib prefix="c" uri="jakarta.tags.core" %>
<%@ taglib prefix="c" uri="jakarta.tags.core" %>

tomcat10以下版本,导入依赖

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

JavaWeb的学习中,学到了JSTL,在网上找了几个相应的包后,一直在报错java.lang.NoClassDefFoundError: javax/servlet/jsp/tagext/TagLibraryValidator,

经过多番努力后才发现是因为我用的Tomcat10.0,用的jakarta.*软件包而不是javax.*软件包,故类似下图的jstl包是用不了的,下面的包是javax,用Tomcat10.0服务器运行的话,就会显示找不到包

下载地址如下:

jakarta.servlet.jsp.jstl-2.0.0.jar

jakarta.servlet.jsp.jstl-api-2.0.0.jar

或者用maven依赖:

<dependency>
    <groupId>org.glassfish.web</groupId>
    <artifactId>jakarta.servlet.jsp.jstl</artifactId>
    <version>2.0.0</version>
</dependency>

具体细节可看:

下面是一套web项目配置:(Tomcat 10.0.5,Idea 2021.1,Mavenpom.xml 这样写有效:)

<dependency>
    <groupId>org.glassfish.web</groupId>
    <artifactId>jakarta.servlet.jsp.jstl</artifactId>
    <version>2.0.0</version>
</dependency>

<dependency>
    <groupId>org.apache.taglibs</groupId>
    <artifactId>taglibs-standard-spec</artifactId>
    <version>1.2.5</version>
</dependency>

// 如果不够,再补充下面这个依赖
<dependency>
    <groupId>org.apache.taglibs</groupId>
    <artifactId>taglibs-standard-impl</artifactId>
    <version>1.2.5</version>
</dependency>
依赖包层级关系:

这套 web 方案包,已含 servlet-api , jspel, 在 “项目结构 - 库” , 可查看包依赖使用情况:未使用的,或冲突的,都可移除,简化管理。

jakarta.servlet.jsp.jstl 依赖项提供了 JavaServer Pages Standard Tag Library (JSTL) 的实现。使用 JSTL,您可以在 JSP 页面中轻松地执行常见的任务,例如循环、条件判断、格式化文本等。通过引入 jakarta.servlet.jsp.jstl 依赖项,您可以在应用程序中使用 JSTL 标签库,从而简化 JSP 页面的开发和维护工作。这有助于将业务逻辑与呈现代码分离,使代码更易于维护和理解。

<dependency>
    <groupId>jakarta.servlet.jsp.jstl</groupId>
    <artifactId>jakarta.servlet.jsp.jstl-api</artifactId>
    <version>3.0.0</version>
</dependency>
<dependency>
	<groupId>org.glassfish.web</groupId>
	<artifactId>jakarta.servlet.jsp.jstl</artifactId>
	<version>3.0.1</version>
</dependency>

下面是另外一种:

<dependency>
  <groupId>org.apache.taglibs</groupId>
  <artifactId>taglibs-standard-spec</artifactId>
  <version>1.2.5</version>
</dependency>

<dependency>
  <groupId>org.apache.taglibs</groupId>
  <artifactId>taglibs-standard-impl</artifactId>
  <version>1.2.5</version>
</dependency>

这两个依赖项是 Apache Taglibs 项目的一部分,用于提供 JSTL 标签库的实现。

  • taglibs-standard-spec 是 JSTL 标签库的规范依赖项。它包含了 JSTL 标签库的接口和规范定义,以及其他必要的类和资源文件。这个依赖项适用于任何符合 JSTL 规范的版本。

  • taglibs-standard-impl 是 JSTL 标签库的实现依赖项。它包含了实际执行 JSTL 标签的类和相关实现代码。这个依赖项的版本应与您使用的 JSTL 版本相匹配。

通常情况下,您需要同时引入这两个依赖项,并确保它们具有相同的版本号,以确保 JSTL 标签库能够正常工作。在您的 Maven 项目中使用这两个依赖项时,请根据您所需的 JSTL 版本选择适当的版本号,并确保它们与其他依赖项和应用服务器的兼容性良好。

备注

jakarta.servlet.jsp.jstl-api 是 Jakarta EE 规范中 JSTL 的 API 接口的依赖项。它包含了编写和运行 JSTL 页面所需的接口和类。这个依赖项提供了 JSTL 标签库的核心功能,但不包含具体的标签实现。

taglibs-standard-spec 相比,jakarta.servlet.jsp.jstl-api 更加轻量级,只提供了 JSTL 的接口和规范定义,而不包含具体的标签实现。它适用于任何符合 JSTL 规范的版本。

在使用 JSTL 时,通常需要同时引入 jakarta.servlet.jsp.jstl-api 和 JSTL 实现(例如 taglibs-standard-impl)。jakarta.servlet.jsp.jstl-api 提供了对 JSTL 标签库的API访问,而实际的实现依赖项提供了 JSTL 标签库的具体实现。

因此,jakarta.servlet.jsp.jstl-api 用于引入 JSTL API 接口,而 taglibs-standard-spectaglibs-standard-impl 用于引入 JSTL 标签库的规范和实现。

请你详细说明下面两种引入JSTL方式的区别: 方式一:

<dependency>
    <groupId>jakarta.servlet.jsp.jstl</groupId>
    <artifactId>jakarta.servlet.jsp.jstl-api</artifactId>
    <version>3.0.0</version>
</dependency>
<dependency>
	<groupId>org.glassfish.web</groupId>
	<artifactId>jakarta.servlet.jsp.jstl</artifactId>
	<version>3.0.1</version>
</dependency>

方式二:

<dependency>
  <groupId>org.apache.taglibs</groupId>
  <artifactId>taglibs-standard-spec</artifactId>
  <version>1.2.5</version>
</dependency>

<dependency>
  <groupId>org.apache.taglibs</groupId>
  <artifactId>taglibs-standard-impl</artifactId>
  <version>1.2.5</version>
</dependency>

方式一使用的是 Jakarta EE 标准的 JSTL API 和实现。

  • jakarta.servlet.jsp.jstl-api 是 Jakarta EE 规范中 JSTL 的 API 接口的依赖项。它包含了编写和运行 JSTL 页面所需的接口和类。
  • jakarta.servlet.jsp.jstl 是 JSTL 的实现依赖项。它提供了 JSTL 的具体实现,包括标签库的功能和实现代码。

这种方式使用了 Jakarta EE 在 JSTL 上进行了更新和迁移的新版本。

方式二使用的是 Apache Taglibs 提供的 JSTL 实现。

  • taglibs-standard-spec 是 JSTL 标签库的规范依赖项。它包含了 JSTL 标签库的接口和规范定义,以及其他必要的类和资源文件。
  • taglibs-standard-impl 是 JSTL 标签库的实现依赖项。它包含了实际执行 JSTL 标签的类和相关实现代码。

这种方式使用的是 Apache Taglibs 项目所提供的 JSTL 实现。

这两种引入 JSTL 的方式基本上都能达到相同的目的,即在应用程序中使用 JSTL 标签库来简化 JSP 页面的开发。不同之处在于它们使用了不同的库和依赖项,因此在使用时需要根据您的项目和环境的具体情况选择适合的方式。注意确保所选的版本与您的应用程序服务器兼容,并与其他依赖项的版本匹配。

根据您提供的依赖项信息,可以推断出各自使用的 JSTL 版本如下:

方式一:

  • jakarta.servlet.jsp.jstl-api 的版本为 3.0.0。
  • jakarta.servlet.jsp.jstl 的版本为 3.0.1。

方式二:

  • taglibs-standard-spec 的版本为 1.2.5。
  • taglibs-standard-impl 的版本为 1.2.5。

需要注意的是,这些版本号仅适用于您提供的依赖项。实际上,JSTL 的版本可能会有更多选择,并且取决于您的应用程序服务器和其他依赖项的要求,您可能需要使用特定的 JSTL 版本。在选择版本时,请确保与您的环境和其他组件兼容,并参考 JSTL 文档或相关文档以了解每个版本的功能和差异。

  • org.apache.taglibs 是 Apache Taglibs 项目的组织名称。Apache Taglibs 是一个开源项目,旨在提供一套用于开发和使用标签库的工具和实现。该项目提供了多个标签库的实现,包括 JSTL (JavaServer Pages Standard Tag Library)、EL (Expression Language) 等。它提供了一种简化 JSP 页面开发的途径,并且可以通过 Maven 等构建工具进行引入和管理。

  • javax.servlet 是 Jakarta EE(以前称为 Java EE)规范中的一个关键包。它定义了 Java Servlet API,这是开发基于 Java 的 Web 应用程序的标准接口。Servlet API 提供了处理 HTTP 请求和响应的功能,允许开发人员编写 servlet 对象来处理来自客户端的请求并生成相应的响应。javax.servlet 包中包含了 servlet 相关的类和接口。

  • org.glassfish.web 是 GlassFish 项目的一部分,GlassFish 是一个开源的 Java EE 应用服务器。org.glassfish.web 包提供了一些与 Web 开发相关的实用工具和类,例如支持 JSP 和 Servlet 的容器实现、Web 相关的类和接口等。它提供了对 Jakarta EE 规范的实现,并支持开发和部署 Java Web 应用程序。

这三个历史上都与 Java Web 开发密切相关。Apache Taglibs 提供了标签库的实现,javax.servlet 定义了 Servlet API,而 org.glassfish.web 则提供了 GlassFish 服务器相关的功能和实现。它们共同为 Java Web 开发提供了重要的工具和规范。

In your specific case,

org.apache.jasper.JasperException: The absolute uri: http://java.sun.com/jstl/core cannot be resolved in either web.xml or the jar files deployed with this application

that URI is for JSTL 1.0, but you’re actually using JSTL 1.2 which uses URIs with an additional /jsp path. This URI change is because JSTL, who invented EL expressions, was since version 1.1 integrated as part of JSP 2.0 (released way back in 2001!) in order to share/reuse the EL logic in plain JSP too. See also Difference between JSP EL, JSF EL and Unified EL.

So, fix the taglib URI accordingly based on JSTL 1.2 documentation:

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

Further you need to make absolutely sure that you do not throw multiple different versioned JSTL JAR files together into the runtime classpath or versions which don’t match the expected version by the target runtime (the server) or merely the API without impl. Otherwise you risk seeing other errors like below:

org.apache.jasper.JasperException: Can not find the tag library descriptor for “http://java.sun.com/jsp/jstl/core

org.apache.jasper.JasperException: Unable to find taglib [c] for URI: [jakarta.tags.core]

java.lang.NoClassDefFoundError: javax/servlet/jsp/tagext/TagLibraryValidator

java.lang.NoClassDefFoundError: jakarta/servlet/jsp/tagext/TagLibraryValidator

java.lang.ClassCastException: class org.apache.taglibs.standard.tlv.JstlCoreTLV cannot be cast to class jakarta.servlet.jsp.tagext.TagLibraryValidator

This is a pretty common mistake among Tomcat users. The problem with Tomcat is that it does not offer JSTL out the box and thus you have to manually install it. This is not necessary in normal Jakarta EE servers. See also What exactly is Java EE? and How to properly configure Jakarta EE libraries in Maven pom.xml for Tomcat?

In your specific case, your pom.xml basically tells you that you have jstl-1.2.jar and standard-1.1.2.jar together. This is wrong. You’re basically mixing JSTL 1.2 API+impl from Oracle (GlassFish) with JSTL 1.1 impl from Apache. You should remove duplicate JSTL implementations and stick to only one JSTL implementation and the API version must match the impl version and the API version must be supported by the target runtime.

Installing JSTL on Tomcat 10.1.x

In case you’re already on Tomcat 10.1.x (the second Jakartified version, with jakarta.* package instead of javax.* package, but the first version with the updated jakarta.tags.* namespace URNs instead of http://java.sun.com/jsp/jstl/* namespace URLs), use JSTL 3.0 via these dependency using the default Maven scope of compile (because Tomcat doesn’t provide it out the box!):

<dependency>
    <groupId>jakarta.servlet.jsp.jstl</groupId>
    <artifactId>jakarta.servlet.jsp.jstl-api</artifactId>
    <version>3.0.0</version>
</dependency>
<dependency>
    <groupId>org.glassfish.web</groupId>
    <artifactId>jakarta.servlet.jsp.jstl</artifactId>
    <version>3.0.1</version>
</dependency>

Note that the API dependency is since this version not transitively included via the impl dependency, so you do need to explicitly declare it.

Non-Maven users can achieve the same by dropping the following two physical files in /WEB-INF/lib folder of the web application project (do absolutely not drop standard*.jar or any loose .tld files in there! remove them if necessary).

As said, the namespace URIs have been changed to become URNs instead of URLs. JSTL core is since JSTL version 3.0 available via an easier to remember namespace URI in URN format:

<%@ taglib prefix="c" uri="jakarta.tags.core" %>

See also JSTL 3.0 documentation.

In case you actually wanted to use the JSTL impl of Apache instead of EE4J then you need to know that they don’t have a 3.0 let alone 2.0. The currently latest released version of Apache’s JSTL impl is 1.2.3 and it is not anymore actively maintained since Feb 2015. The JSTL impl from EE4J (formerly Oracle / Sun) is currently (Jul 2023) your only choice.

Installing JSTL on Tomcat 10.0.x

In case you’re on Tomcat 10.0.x (the first Jakartified version, with jakarta.* package instead of javax.* package), use JSTL 2.0 via this sole dependency using the default Maven scope of compile (because Tomcat doesn’t provide it out the box!):

<dependency>
    <groupId>org.glassfish.web</groupId>
    <artifactId>jakarta.servlet.jsp.jstl</artifactId>
    <version>2.0.0</version>
</dependency>

Note that the API dependency is already transitively included via this impl dependency, so you do not need to explicitly declare it.

Non-Maven users can achieve the same by dropping the following two physical files in /WEB-INF/lib folder of the web application project (do absolutely not drop standard*.jar or any loose .tld files in there! remove them if necessary).

Installing JSTL on Tomcat 9-

In case you’re not on Tomcat 10 yet, but still on Tomcat 9 or older, use JSTL 1.2 via this sole dependency (this is compatible with Tomcat 9 / 8 / 7 / 6 / 5 but not older) using the default Maven scope of compile (because Tomcat doesn’t provide it out the box!):

<dependency>
    <groupId>org.glassfish.web</groupId>
    <artifactId>jakarta.servlet.jsp.jstl</artifactId>
    <version>1.2.6</version>
</dependency>

Note that the API dependency is already transitively included via this impl dependency, so you do not need to explicitly declare it.

Non-Maven users can achieve the same by dropping the following two physical files in /WEB-INF/lib folder of the web application project (do absolutely not drop standard*.jar or any loose .tld files in there! remove them if necessary).

Installing JSTL on normal JEE server

In case you’re actually using a normal Jakarta EE server such as WildFly, Payara, TomEE, GlassFish, WebSphere, OpenLiberty, WebLogic, etc instead of a barebones servletcontainer such as Tomcat, Jetty, Undertow, etc, then you do not need to explicitly install JSTL at all. Normal Jakarta EE servers already provide JSTL out the box. In other words, you don’t need to add JSTL to pom.xml nor to drop any JAR/TLD files in webapp. Solely the provided scoped Jakarta EE coordinate is sufficient:

<dependency>
    <groupId>jakarta.platform</groupId>
    <artifactId>jakarta.jakartaee-api</artifactId>
    <version><!-- 10.0.0, 9.1.0, 9.0.0, 8.0.0, etc depending on your server --></version>
    <scope>provided</scope>
</dependency>

Make sure web.xml version is right

Further you should also make sure that your web.xml is declared conform at least Servlet 2.4 and thus not as Servlet 2.3 or older. Otherwise EL expressions inside JSTL tags would in turn fail to work. Pick the highest version matching your target container and make sure that you don’t have a <!DOCTYPE> anywhere in your web.xml as that would otherwise still trigger Servlet 2.3 modus. Here’s a Servlet 6.0 (Tomcat 10.1.x) compatible example:

<?xml version="1.0" encoding="UTF-8"?>
<web-app 
    xmlns="https://jakarta.ee/xml/ns/jakartaee"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="https://jakarta.ee/xml/ns/jakartaee https://jakarta.ee/xml/ns/jakartaee/web-app_6_0.xsd"
    version="6.0">

    <!-- Config here. -->

</web-app>

And here’s a Servlet 5.0 (Tomcat 10.0.x) compatible example:

<?xml version="1.0" encoding="UTF-8"?>
<web-app 
    xmlns="https://jakarta.ee/xml/ns/jakartaee"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="https://jakarta.ee/xml/ns/jakartaee https://jakarta.ee/xml/ns/jakartaee/web-app_5_0.xsd"
    version="5.0">

    <!-- Config here. -->

</web-app>

And here’s a Servlet 4.0 (Tomcat 9) compatible example:

<?xml version="1.0" encoding="UTF-8"?>
<web-app
    xmlns="http://xmlns.jcp.org/xml/ns/javaee"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
    version="4.0">

    <!-- Config here. -->

</web-app>

See also:

评论

评论加载中……