文章目录
Servlet简介:
server applet的缩写。
- 概念:运行在服务器端的小程序
- Servlet就是一个接口,定义了Java类被浏览器访问到(tomcat识别)的规则。
- 将来我们自定义一个类,实现Servlet接口,复写方法。
简言之,Servlet是一个类,服务器认识这个类,我们只要继承他后传给服务器,服务器就可以调用我们写的代码。
所以此时我们新建一个JavaEE项目,然后创建类public class ServletDemo1 implements Servlet
,实现接口中的service等抽象方法。
Servlet标准的java类,必须由程序员开发、修改,美工人员难以参数Servlet页面的开发。这一系列的问题,都阻碍了Servlet作为表现层的使用。自MVC规范出现后,Servlet的责任开始明确下来,仅仅作为控制器使用,不再需要生成页面标签,也不再作为视图层角色使用。
Servlet执行原理:
-
当服务器接受到客户端浏览器的请求后,会解析请求URL路径,获取访问的Servlet的资源路径
-
在 webapps 目录下找到对应的项目文件夹,然后查找WEB-INF/web.xml文件,找是否有每个servlet对应的
<url-pattern>
标签体内容。<servlet-mapping> <servlet-name>dispatcher</servlet-name> <url-pattern>/rest/*</url-pattern> </servlet-mapping>
-
如果有,则在找到对应的
<servlet-class>全类名</servlet-class>
<servlet> <servlet-name>dispatcher</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>
-
tomcat会将WEB-INF/classes目录下字节码文件加载进内存(java文件先编译为class文件),并且创建其对象
-
调用其方法
service()
进一步解释怎么找到servlet的:
这个问题的关键是Tomcat写了一个抽象类,tomcat(作为主函数)主动new出了一个servlet对象,然后调用对应的init和service方法等。这是通过继承与多态实现的。所以我们只需要继承对应的Servlet类,然后实现service方法即可。而我们通常继承的不是Servlet类,而是它的子类httpserlet等,因为这个子类帮我们实现了一些函数,减少了我们的工作。
比如我们访问的是项目project1,我们在
web.xml
文件中写了如下内容,代表我们访问/demo1
时,通过对应的servlet-name
就会找到对应的servlet-class
全类名,从而通过反射创建servlet对象。tips:在IDEA中创建servlet类的时候,系统会自动帮我们把内容写到web.xml中。
web.xml示例:
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" id="WebApp_ID" version="3.0">
<!--配置servlet的名字-->
<servlet>
<!--指定Servlet的名字标识,相当于指定@WebServlet的name属性-->
<servlet-name>demo1</servlet-name>
<!--指定Servlet的实现全类名-->
<servlet-class>全类名cn.itcast.web.servlet.ServletDemo1</servlet-class>
</servlet>
<servlet-mapping>
<!--指定Servlet的名字-->
<servlet-name>demo1</servlet-name>
<!--指定Servlet映射的URL地址,相当于指定@WebServlet的urlPatterns属性。虚拟地址帮我们包装了全类名,不要忘了加/-->
<url-pattern>/虚拟地址/demo1</url-pattern>
</servlet-mapping>
<!--可以配置第二个servlet,格式与上面一致。每组servlet对应一组servlet-mapping-->
<servlet>
<servlet-name>第二个servlet</servlet-name>
<servlet-class>第二个全类名</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>第二个servlet</servlet-name>
<url-pattern>/虚拟地址2</url-pattern>
</servlet-mapping>
</web-app>
虚拟地址处可以填/*、 /后缀
注解方式的写法:
@WebServlet(name="firstServlet",urlPatterns={"/firstServlet"})
Servlet程序案例
如果不想在xml中写上面servlet,可以直接在eclipse中,new servlet,Name为类名,Next–Servlet Name和Servlet Mapping URL即我们刚才在xml里填的,创建后IDEA就自动帮我们添加进xml了。
package com.bjsxt.servlet;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class ServletLife extends HttpServlet {
//初始化方法,在servlet第一次加载内容的时候被调用
@Override
public void init() throws ServletException {
System.out.println("servlet初始化完成");
}
//service方法,真正处理请求的方法
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.getWriter().write("servlet life");
System.out.println("servlet life");
}
@Override
public void destroy() {
System.out.println("我被销毁了...");
}
}
Servlet的体系结构
Servlet -- 接口
|
GenericServlet -- 子类 抽象类 //将Servlet接口中其他的方法做了默认空实现,只将service()方法作为抽象 //将来定义Servlet类时,可以继承GenericServlet,实现service()方法即可
|
HttpServlet -- 子类 抽象类(真正开发中用这个)//对http协议的一种封装,简化操作//1. 定义类继承HttpServlet//2. 复写doGet/doPost方法
访问servlet方法
服务器地址:端口号/虚拟项目名 /servlet的别名
JSP/Servlet的生命周期
每个Servlet的运行都遵循如下周期。
- 1、 服务器创建Servlet实例。(可以配置执行Servlet的创建时机。)
- 2 、(Web容器调用Servlet的init方法,对Servlet进行初始化。)每个servlet仅初始化一次。(说明一个Servlet在内存中只存在一个对象,Servlet是单例的)
- 3、 Servlet初始化后,将一直存在于容器中,用于响应客户端请求。如果客户端发送GET请求,容器调用Servlet的doGet方法处理并响应请求;如果客户端发送POST请求,容器调用Servlet的doPost方法处理并响应请求。或者统一使用Service()方法处理来响应用户请求。
- 4、 Web容器决定销毁Servlet时,先调用Servlet的destory方法,通常在正常关闭Web应用之时销毁Servlet,一般用于释放资源。
Servlet的创建时机2种
其中第2项,Servlet创建的时机可以分为两种:启动tomcat时或第一次访问Servlet时。
在<servlet>
标签下配置
- 第一次被访问时创建(默认)。此时
<load-on-startup>
的值为负数 - 在Web应用启动时创建。
<load-on-startup>
的值为0或正整数(也可以在注解的loadOnStartup属性指定),整数值越小,Servlet就越优先实例化。这种Servlet通常用于某些后台服务的Servlet,或者拦截很多请求的Servlet;这种Servlet通常作为应用的基础Servlet使用,提供重要的后台服务。
同时,执行init方法时,多个用户同时访问时,可能存在线程安全问题。
解决:尽量不要在Servlet中定义成员变量。即使定义了成员变量,也不要对修改值
@WebServlet(loadOnStartup=1)//使用注解方式配置自动启动servlet
public class TestServlet extends HttpServlet{}
<!--配置servlet的名字-->
<servlet>
<!--指定Servlet的名字,相当于指定@WebServlet的name属性-->
<servlet-name>demo1</servlet-name>
<!--指定Servlet的实现类-->
<servlet-class>cn.itcast.web.servlet.ServletDemo1</servlet-class>
<load-on-start/>1<load-on-start/>
</servlet>
<servlet-mapping>
<!--指定Servlet的名字-->
<servlet-name>demo1</servlet-name>
<!--指定Servlet映射的URL地址,相当于指定@WebServlet的urlPatterns属性-->
<url-pattern>/demo1</url-pattern>
</servlet-mapping>
Servlet与内存的关系
服务器中可以存着多个servlet,任一servlet被调用就被加载进服务器内存,且只要不关服务器tomcat,该servlet就会一直在内存中。所以如果我们删除了对应的.classes文件,不重启tomcat的话,该页面还是能访问的;重启后,因为没有了该classes文件,所以就不能访问对应的网页了(所以tomcat经常需要重启)。servlet对应的类中有init()方法和serve()方法,对应的网页第一次被调用时执行init方法,然后执行serve()方法。只要tomcat不重启,再次调用该网页不会再执行init(),只会执行service()。
JSP与Servlet的区别:
- Servlet中没有内置对象,原来JSP中的内置对象都必须由程序显示创建
- 对于静态的HTML标签,Servlet都必须使用页面输出流逐行输出
PrintStream out = new PrintStream(response.getOutputStream);
out.println("<html>");
注解配置Servlet
文章目录
//com.bjsxt.servlet.MyServlet文件
package com.bjsxt.servlet;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class MyServlet extends HttpServlet{
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp)throws ServletException, IOException {
resp.getWriter().write("this is my first servlet.");
System.out.println("this is my first servlet.");
}
}
WEB-INF下找到web.xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://java.sun.com/xml/ns/javaee"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" id="WebApp_ID"
version="2.5">
<!--配置Servlet -->
<!--配置servlet类路径 -->
<servlet>
<servlet-name>my</servlet-name>
<servlet-class>com.bjsxt.servlet.MyServlet</servlet-class>
</servlet>
<!--配置访问方式 -->
<servlet-mapping>
<servlet-name>my</servlet-name>
<url-pattern>/my</url-pattern>
</servlet-mapping>
</web-app>
web.xml的根元素是<web-app>
。在Servlet3.0规范中,该元素新增了如下属性。
- metadata-complete:true代表该web应该不会加载Annotation配置的web组件(如servlet,filter,listener等)
web.xml文件中配置首页使用welcome-file-list元素。该元素能包含多个welcome-file子元素,其中每个welcome子元素配置一个首页。优先级不同,第一个没有才访问第二个。只会访问一个
<welcome-file-list>
<welcome-file>index.html</welcome-file>
<welcome-file>index.htm</welcome-file>
<welcome-file>index.jsp</welcome-file>
</welcome-file-list>
注解@WebServlet代替配置web.xml
步骤:
1. 创建JavaEE项目,选择Servlet的版本3.0以上,可以不创建web.xml
2. 定义一个类,实现Servlet接口
3. 复写方法
4. 在类上使用@WebServlet注解,进行配置
WebServlet源码:
@WebServlet(name="",urlPatterns={"/资源路径"}) //urlPatterns与value等价
@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 "";
}
属性名 | 类型 | 描述 |
---|---|---|
name | String | 指定Servlet 的 name 属性,等价于 <servlet-name> 。如果没有显式指定,则该 Servlet 的取值即为类的全限定名。 |
value | String[] | 该属性等价于 urlPatterns 属性。两个属性不能同时使用。可使用正则表达式 |
urlPatterns | String[] | 指定一组 Servlet 的 URL 匹配模式。等价于<url-pattern> 标签。 |
loadOnStartup | int | 指定 Servlet 的加载顺序,等价于 <load-on-startup> 标签。 |
initParams | WebInitParam[] | 指定一组 Servlet 初始化参数,等价于<init-param> 标签。 |
asyncSupported | boolean | 声明 Servlet 是否支持异步操作模式,等价于<async-supported> 标签。 |
description | String | 该 Servlet 的描述信息,等价于 <description> 标签。 |
displayName | String | 该 Servlet 的显示名,通常配合工具使用,等价于 <display-name> 标签。 |
如果打算用注解来配置Servlet,有两点需要指出:
- 不要在web.xml文件的根元素<web-app…>中指定metadata-complete=“true”
- 不要在web.xml文件中配置该Servlet
初始化参数initParams
为Servlet配置参数有两种方式:
- 通过注解@WebServlet的initParams属性来指定。有name和value两个属性
- 通过在web.xml文件的
<servlet>
属性中添加<init-param>
子元素来指定。有para-name,para-value两个属性
获取配置参数:
java.lang.String getInitParameter(java.lang.String name);//用于获取初始化参数
package lee;
import java.sql.*;
import javax.servlet.http.*;
import javax.servlet.*;
import javax.servlet.annotation.*;
import java.io.*;
@WebServlet(name="testServlet"
, urlPatterns={"/testServlet"}
, initParams={
@WebInitParam(name="driver", value="com.mysql.jdbc.Driver"),
@WebInitParam(name="url", value="jdbc:mysql://localhost:3306/javaee"),
@WebInitParam(name="user", value="root"),
@WebInitParam(name="pass", value="32147")})
public class TestServlet extends HttpServlet{
//重写init方法,
public void init(ServletConfig config)
throws ServletException{
//重写该方法,应该首先调用父类的init方法
super.init(config);
}
//响应客户端请求的方法
public void service(HttpServletRequest request,
HttpServletResponse response)
throws ServletException,java.io.IOException{
try{
//获取ServletConfig对象
ServletConfig config = getServletConfig();
//通过ServletConfig对象获取配置参数:dirver
String driver = config.getInitParameter("driver");
//通过ServletConfig对象获取配置参数:url
String url = config.getInitParameter("url");
//通过ServletConfig对象获取配置参数:user
String user = config.getInitParameter("user");
//通过ServletConfig对象获取配置参数:pass
String pass = config.getInitParameter("pass");
//注册驱动
Class.forName(driver);
//获取数据库驱动
Connection conn = DriverManager.getConnection(url,user,pass);
//创建Statement对象
Statement stmt = conn.createStatement();
//执行查询,获取ResuletSet对象
ResultSet rs = stmt.executeQuery("select * from news_inf");
response.setContentType("text/html;charSet=gbk");
//获取页面输出流
PrintStream out = new PrintStream(response.getOutputStream());
//输出HTML标签
out.println("<html>");
out.println("<head>");
out.println("<title>访问Servlet初始化参数测试</title>");
out.println("</head>");
out.println("<body>");
out.println("<table bgcolor=\"#9999dd\" border=\"1\"" +
"width=\"480\">");
//遍历结果集
while(rs.next()){
//输出结果集内容
out.println("<tr>");
out.println("<td>" + rs.getString(1) + "</td>");
out.println("<td>" + rs.getString(2) + "</td>");
out.println("</tr>");
}
out.println("</table>");
out.println("</body>");
out.println("</html>");
}
catch (Exception e){
e.printStackTrace();
}
}
}
上下文
四大域对象:PageContext、ServletRequest、HttpSession、ServletContext
1、上下文context的概念
我们在说到Servlet的继承关系时,提到自定义Servlet实际上间接实现了Servlet和ServletConfig两个接口,其中ServletConfig接口中定义了一个方法叫getServletContext,用以获取Servlet运行的上下文环境对象。
这里就要先说明一下,如何理解所谓的 “上下文”。
每个Web项目,运行时部署在Web应用服务器(如Tomcat、Jetty、WebLogic etc.)下,我们称之为一个应用(Application)。我们知道一个Web应用里可以有多个Servlet,而这里的Servlet上下文就可以理解为这些Servlet的运行环境。一个项目对应一个ServletContext
抽象地说,是Web服务器中已知路径的根,是一个域,一个环境范围:
如果现在有一份数据需要传给所有的Servlet使用,那么我们就可以使用ServletContext对象了。就像某只鸭子(Servlet)肚子里的鱼(数据)是没办法共享给另一只鸭子的,可是放在湖里的鱼,至少每个鸭子都可以去咀一口,这里的湖就是ServletContext了。
2、ServletContext对象的获取
其实从上面我们对ServletContext的描述,也不难推断出它的创建时机:每当一个Web应用被加载,那么它的上下文环境就被封装为一个ServletContext对象创建出来了。
如何得到ServletContext对象?
实际上,ServletContext对象被放到了每一个Servlet中的ServletConfig对象里,还记得继承关系中GenericServlet的init方法吗:
public void init(ServletConfig config) throws ServletException {
this.config = config;
this.init();
}
初始化时,就把装有ServletContext的ServletConfig,在init方法中赋值给了属性config。而GenericServlet有一个getServletContext方法,它先调用自身的getServletConfig,再调用ServletConfig对象的getServletContext的方法:
public ServletContext getServletContext() {
return getServletConfig().getServletContext();
}
也就是说,我们自定义的Servlet由于继承关系,可以直接调用getServletContext方法,获取上下文对象:
ServletContext servletContext = this.getServletContext();//getServletContext()
3、ServletContext的方法
Servlet API 中定义了ServletContext接口,用来封装上下文对象。
下面是部分方法:
类型 | 方法名 | 说明 |
---|---|---|
void | setAttribute(String key, Object value) | 以key/value形式保存对象值 |
Object | getAttribute(String key) | 通过key获取对象值 |
String | getInitParameter(String path) | 返回上下文参数的值 。在xml中设置的init-param |
String | getContextPath() | |
String | getRealPath(String path) | 根据虚拟路径返回实际路径。返回带盘符的路径 |
getRequestDispatcher(“URL”) | ||
getResource |
-
getAttribute / setAttribute 通过理解我们知道,ServletContext类似一个全局变量,可以使用者两个方法实现多个用户之间数据共享
-
getInitParameter 单个Servlet可以在web.xml中配置初始参数,上下文环境当然也可以
<context-param>
<param-name>name<param-name/>
<param-value>value<param-value/>
</context-param>
- getRealPath 获取Web应用根目录实际路径,如getRealPath("/")返回诸如类似于
C:\workspace\project\train\test\learn\src\main\webapp\
请求方式get与post
Service方法:
-
可以处理get/post方式的请求。如果servlet中有Service方法,会【优先调用service】方法对请求进行处理,此时会==屏蔽掉doGet或doPost==。
doGet方法:
-
处理get方式的请求。在没有 service 方法的情况下如果是 get 方式的请求所调用的处理请求的方法
例:表单.jsp提交方式是get,但只有dopost方法,那么访问表单就会报05错误
doPost方法:
-
处理post方式的请求
注意:
-
如表单提交的method要与servlet所定义的方法一致。
-
如果在覆写的service方法中调用了父类的service方法
super.service(arg0, arg1)
,则service方法处理完后,会再次根据请求方式,再去执行doGet或doPost。所以,一般情况下我们是不在覆写的service中调用父类的service方法的,避免出现405错误。 而且,学了Service却不写doGet或doPost会使super.service无法寻找到doGet或doPost,从而报错405。所以我们干脆不写``super.service(`,这 时就可以不写doGet或doPost了。 -
405错误:原因:浏览器请求方式和servlet中的方法不匹配所造成的。解决:尽量使用service方法进行请求处理,并且不要再service方法中调用父类的service。
-
404错误:资源未找到。可能是请求地址中的[servlet的别名]或]虚拟项目名称]书写错误。
-
500错误:内部服务器错误。错误一:
java.lang.ClassNotFoundException: com.bjsxt.servlet.ServletMothod
。按照全类名找不到class。解决一:在web.xml中校验servlet类的全限定路径是否拼写错误。错误二:因为service方法体的代码执行错误导致。解决二:根据错误提示对service方法体中的代码进行错误更
代码示例
servletMethod.java
----------------servletMethod.java-----------
package com.bjsxt.servlet;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class ServletMethod extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
int i=5/0;
System.out.println("我是service");
//super.service(req, resp);//会调用doPost或doGet方法
}
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
System.out.println("我是doGet方法");
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
System.out.println("我是doPost方法");
}
}
method.jsp
------------method.jsp-----------------
<%@ page language="java" import="java.util.*" pageEncoding="utf-8"%>
<%
String path = request.getContextPath();
String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/";
%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<base href="<%=basePath%>">
<title>My JSP 'Method.jsp' starting page</title>
<meta http-equiv="pragma" content="no-cache">
<meta http-equiv="cache-control" content="no-cache">
<meta http-equiv="expires" content="0">
<meta http-equiv="keywords" content="keyword1,keyword2,keyword3">
<meta http-equiv="description" content="This is my page">
<!--
<link rel="stylesheet" type="text/css" href="styles.css">
-->
</head>
<body>
<form action="req" method="get"><!--相当于localhost:8080/ms2/req-->
用户名: <input type="text" name="uname" value="" /><br />
密码: <input type="text" name="pwd" value="" /><br />
爱好:<br />
<input type="checkbox" name="fav" value="1"/>唱歌<br />
<input type="checkbox" name="fav" value="2"/>跳舞<br />
<input type="checkbox" name="fav" value="3"/>游泳<br />
<input type="submit" value="登录" />
</form>
</body>
</html>
WEB.XML
--------------------WEB.XML-------------------------
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" id="WebApp_ID" version="2.5">
<!-- servlet配置 -->
<servlet>
<servlet-name>my</servlet-name>
<servlet-class>com.bjsxt.servlet.MyServlet</servlet-class>
</servlet>
<servlet>
<description>This is the description of my J2EE component</description>
<display-name>This is the display name of my J2EE component</display-name>
<servlet-name>ServletLife</servlet-name>
<servlet-class>com.bjsxt.servlet.ServletLife</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet>
<description>This is the description of my J2EE component</description>
<display-name>This is the display name of my J2EE component</display-name>
<servlet-name>ServletMethod</servlet-name>
<servlet-class>com.bjsxt.servlet.ServletMethod</servlet-class>
</servlet>
<servlet>
<description>This is the description of my J2EE component</description>
<display-name>This is the display name of my J2EE component</display-name>
<servlet-name>RequestServlet</servlet-name>
<servlet-class>com.bjsxt.servlet.RequestServlet</servlet-class>
</servlet>
<servlet>
<description>This is the description of my J2EE component</description>
<display-name>This is the display name of my J2EE component</display-name>
<servlet-name>ResponseServlet</servlet-name>
<servlet-class>com.bjsxt.servlet.ResponseServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>my</servlet-name>
<url-pattern>/my</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>ServletLife</servlet-name>
<url-pattern>/life</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>ServletMethod</servlet-name>
<url-pattern>/method</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>RequestServlet</servlet-name>
<url-pattern>/req</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>ResponseServlet</servlet-name>
<url-pattern>/resp</url-pattern>
</servlet-mapping>
</web-app>