Servlet快速入门
Servlet概述
1.什么是Servlet
Servlet是javaweb的三大组件之一,它属于动态资源。Servlet的作用是处理请求,服务器会把接收到的请求交给Servlet来处理,在Servlet中通常需要:
- 接收请求数据
- 处理请求
- 完成响应
每个servlet都是唯一的,他们的请求是不同的
2.实现Servlet的方式(由我们自己来写)
实现Servlet有三种方式:
- 实现javax.servlet.Servlet接口
- 继承javax.servlet.GenericeServlet类
- 继承javax.servlet.http.HttpServlet类
通常我们去继承HttpServlet类来完成我们的Servlet
public class myServlet implements Servlet
{
/**
* 它是生命周期方法
* 它会在servlet对象创建之后马上执行,并只执行一次(出生之后)
*/
public void init(ServletConfig config) throws ServletException;
/**
* 可以用来获取Servlet的配置信息
*/
public ServletConfig getServletConfig();
/**
* 它是生命周期方法
* 它会被调用多次
* 每次处理请求都是在调用这个方法
*/
public void service(ServletRequest req,ServletResponse res)
throws ServletException,IOException;
/**
* 获取servlet的信息
*/
public String getServletInfo();
/**
* 它是生命周期方法
* 它会在Servlet被销毁之前调用,并且只会被调用一次
*/
public void destroy();
}
Servlet中的方法大多数不由我们来调用,而是由Tomcat来调用,并且Servlet的对象也不由我们来创建,由Tomcat来创建
特性:
- 单例:一个类只有一个对象,可能存在多个Servlet类
- 线程不安全,所以效率高
3.创建helloservlet应用
如何让浏览器访问Servlet
- 给Servlet指定一个Servlet路径(让Servlet与一个路径绑定在一起)
- 浏览器访问Servlet路径
给Servlet配置Servlet路径
-
需要在web.xml中对Servlet进行配置
<servlet> <servlet-name>XXX</servlet-name> <servlet-class>cn.itcast.web.servlet.AServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>XXX</servlet-name> <url-pattern>/xxxx</url-pattern> </servlet-mapping>
Servlet接口
1.Servlet的生命周期
void init(ServletConfig)
void service(ServiceRequest request,ServletResponse response)
void destroy()
1.1 Servlet的出生
服务器在Servlet第一次被访问时创建Servlet,或者在服务器启动时创建Servlet
一个Servlet类型,服务器只创建一个实例对象
1.2 Servlet服务
每次处理请求时都会被调用
1.3 Servlet的离去
临死之前(1次)
在关闭服务器的时候,服务器会去销毁Servlet,在销毁Servlet之前,服务器会先去调用Servlet的destroy()方法
2.Servlet接口相关类型
- ServletRequest:service()方法的参数,表示请求对象,它封装了所有与请求相关的数据,它是由服务器创建的
- ServletRespouse:service()方法的参数,表示响应对象,在service()方法中完成对客户端的响应需要使用这个对象
- ServletConfig:init()方法的参数,表示Servet配置对象,它对于Servet的配置信息,那对应web.xml文件中的 < servlet > 元素
2.1 ServletRequest和ServletRespouse
请求对象和响应对象,可以从ServletRequest对象中获取请求数据,可以使用ServletResponse对象完成响应。
2.2 ServletConfig
一个ServletConfig对象,对应一段web.xml中Servlet的配置信息
API:
- String getServletName(),获取的是< servlet-name >中的内容
- ServletContext getServletContext(),获取Servlet上下文对象
- String getInitParameter(String name),通过名称获取指定初始化参数的值
- Enumeration getInitParameterName(),获取所有初始化参数的名称
<servlet>
<servlet-name>xxx</servlet-name>
<servlet-class>cn.itcast.web.servlet.AServlet</servlet-class>
<init-param>
<param-name>p1</param-name>
<param-value>v1</param-value>
</init-param>
<init-param>
<param-name>p2</param-name>
<param-value>v2</param-value>
</init-param>
</servlet>
public void init(ServletConfig servletConfig) throws ServletException
{
/**
* 获取初始化参数
*/
System.out.println(servletConfig.getInitParameter("p1"));
System.out.println(servletConfig.getInitParameter("p2"));
/**
* 获取所有初始化参数的名称
*/
Enumeration e =servletConfig.getInitParameterNames();
while(e.hasMoreElements())
{
System.out.println(e.nextElement());
}
}
GenericeServlet
1.GenericeServlet概述
GenericServlet是Servlet接口的实现类,我们可以通过继承GenericServlet来编写自己的Servlet
2.GenericeServlet的init
@Override
public void init(ServletConfig servletConfig) throws ServletException {
//把tomcat传递的servletconfig赋值给本类的一个成员,其实就是把它给保存起来,方便在其他的方法中使用
this.config = servletConfig;
init();
}
/**
* 这个方法是自己创建的,不是Servlet接口中的,以便被子类覆写
*/
public void init() { }
3.实现了ServletConfig接口
/**
* 这个方法一定会在init()方法之后被调用
* init()调用后,奔雷的成员this.config已经有值了
* @return
*/
@Override
public ServletConfig getServletConfig()
{
return this.config;
}
public ServletContext getServletContext()
{
return config.getServletContext();
}
public String getServletName()
{
return config.getServletName();
}
public String getInitParameter(String name)
{
return config.getInitParameter(name);
}
HttpServlet
1.HttpServlet概述
2.HTTPServlet覆盖了service
- 强转两个参数为http协议相关的类型
- http相关的service会通过request得到当前请求的方式(GET/POST),根据请求方式再调用doGet()或doPost()方法
3.doGet()和doPost()
doGet()或doPost()方法由自己来写,如果没有覆盖且被调用了,就会出现405(客户端错误,表示不支持该种请求方式)
Servlet细节
1.Servlet与线程安全
一个类型的Servlet只有一个实例对象,线程不安全,工作效率高
我们不应该在Servet中创建便宜成员变量,因为可能会存在一个线程对这个成员变量进行写操作,另一个线程对这个成员变量进行读操作
解决措施:
- 不要在Servlet中创建成员,创建局部变量即可
- 可以创建无状态成员
- 可以创建有状态的成员,但状态必须为只读的(只能get,不能set)
2.让服务器在启动时就创建
默认情况下,服务器会在某个Servelt第一次收到请求时创建它,也可以在web.xml中对Servlet进行配置,使服务器启动时就创建Servlet
<servlet>
<servlet-name>hello1</servlet-name>
<servlet-class>cn.itcast.web.servlet.Heello1Servlet</servlet-class>
<load-on-startup>0</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>hello1</servlet-name>
<url-pattern>/hello1</url-pattern>
</servlet-mapping>
<servlet>
<servlet-name>hello2</servlet-name>
<servlet-class>cn.itcast.web.servlet.Heello2Servlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>hello2</servlet-name>
<url-pattern>/hello2</url-pattern>
</servlet-mapping>
在web.xml中配置< load-on-startup >,其中给出一个非负整数
创建顺序根据整数的大小,由小到大进行创建
3.< url-pattern >
< url-pattern >是< servlet-mapping >的子元素,用来指定Servlet的访问路径,即URL。它必须是以“/”开头
-
可以在< servlet-mapping >中给出多个< url-pattern >
- 一个Servlet绑定了两个URL,无论访问哪个都可以
- 在早期充当过滤器使用,现不这么用一般
-
可以在< url-pattern >中使用通配符(“*”),星号可以匹配任何URL前缀或后缀,使用通配符可以命名一个Servlet绑定一组URL
<url-pattern>/servlet/*</url-pattern>
:路径匹配<url-pattern>*.do</url-pattern>
:扩展名匹配<url-pattern>/*</url-pattern>
:匹配所有的URL
通配符要么为前缀,要么为后缀,不能出现在URL中间位置,也不能只有通配符。且一个URL最多只能出现一个通配符
通配符是一种模糊匹配URL的方式,匹配范围越广,优先级越低。
4.web.xml文件的继承(了解)
在Tomcat的conf/web.xml中的内容,相当于写到了每个项目的web.xml中,它是所有web.xml的父文件
<web-app xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
version="3.0">
<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>
<!-- The mapping for the default servlet -->
<servlet-mapping>
<servlet-name>default</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
<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 mappings for the JSP servlet -->
<servlet-mapping>
<servlet-name>jsp</servlet-name>
<url-pattern>*.jsp</url-pattern>
<url-pattern>*.jspx</url-pattern>
</servlet-mapping>
<session-config>
<session-timeout>30</session-timeout>
</session-config>
<mime-mapping>
<extension></extension>
<mime-type></mime-type>
</mime-mapping>
<welcome-file-list>
<welcome-file>index.html</welcome-file>
<welcome-file>index.htm</welcome-file>
<welcome-file>index.jsp</welcome-file>
</welcome-file-list>
</web-app>
- default:它的优先级最低,如果一个请求没有人处理,那么它来处理,显示404。即当访问路径不存在时,会执行该Servlet,在访问index.xml时也是在执行这个Servlet
- jsp:所有的jsp文件都要经过JspServlet的处理,将静态页面转换为动态的
- session的过期时间为30min
- mine类型:用来识别网络上资源的媒体类型,每一个mine类型都对应一个拓展名
- 在应用的web.xml在如果没有对< welcome-file-list >进行覆盖,那么默认主页为index.html,index.htm,index.jsp
5.Servlet与反射
Tomcat通过反射,使用字符串创造该类对象
Method m = c.getMethod("service",ServletRequest.class,ServletResponse.class);
m.invokie()
ServletContext
一个项目只有一个ServletContext对象,一般起名为application
我们可以在n多个Servlet中来获取这个唯一的对象,使用它可以给多个Servlet传递数据(降低耦合)
在Tomcat启动时创建,在Tomcat关闭时销毁
1.ServletContext概述
服务器会为每个应用创建一个ServletContext对象
ServletContext对象的创建是在服务器启动时完成的,销毁实在服务器关闭时完成的
ServletContext对象的作用是在整个Web应用的动态资源之间共享数据
2.获取ServletContext
在Servlet中获取ServletContext对象:
-
在void init(ServletConfig config)中:ServletContext context = config.getServletContext();
ServletConfig类的getServletContext()方法可以用来获取ServletContext对象
在GenericeServlet或HttpServlet中获取ServletContext对象
-
GenericServlet类有getServletContext()方法,所以可以直接使用this.getServletContext();或this.getServletConfig.getServletContext();
-
HttpSession中:getServletContext();
-
ServletContextEvent:getServletContext()
3.域对象的功能
- 域对象:在Servlet中传递数据,必须有存数据和取数据的功能,域对象内部其实有一个Map
- JavaWeb四大域对象:PageContext,ServletRequest,HTTPSession,ServletContext
void setAttribute(String name,Object value);
- 用来存储一个对象,也可以称之为存储一个域属性
- 如果多次调用该方法,并且使用相同的name,那么会覆盖上一次的值
Object getAttribute(String name);
- 用来获取ServletContext中的数据
- 在获取之前需要先存储
- 保存数据时要进行强制类型转换
void removeAttribute(String name);
- 用来移除ServletContext中的域属性
- 如果参数name指定的域属性不存在,那么本方法什么都不做
Enumeration getAttributeName();
- 获取所有域属性的名称
/**
* 演示向ServletContext中保存数据
*/
public class myServlet extends HttpServlet
{
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException
{
/**
* 1.获取ServletContext对象
* 2.调用其setAttribute()方法完成保存数据
*/
ServletContext application = this.getServletContext();
application.setAttribute("name","张三");
}
}
/**
* 演示从ServletContext中获取数据
*/
public class myServlet<name> extends HttpServlet
{
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException
{
/**
* 1.获取ServletContext对象
* 2.调用其getAttribute()方法完成获取数据
*/
ServletContext application = this.getServletContext();
String name = (String) application.getAttribute("name");
System.out.println(name);
}
}
4.获取应用初始化参数
- Servlet可以获取初始化参数,但是它是局部的参数,也就是说,一个Servlet只能获取自己的初始化参数,不能获取别人的,即初始化参数只为一个Servlet准备
- 可以配置公共的初始化参数,为所有Servlet而用,这需要使用ServletContext才能使用
<web-app>
<context-param>
<param-name>context-param</param-name>
<param-value>context-value</param-value>
</context-param>
</web-app>
public class myServlet extends HttpServlet
{
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException
{
/**
* 1.得到ServletContext
* 2.调用它getInitParameter(String)得到初始化参数
*/
ServletContext application = this.getServletContext();
String value = application.getInitParameter("context-param");
System.out.println(value);
}
}
5.获取资源相关方法
WebRoot - > WEB-INF - > lib - > b.txt
- > web.xml
- > a.txt
- > index.jsp
5.1 获取真实路径
可以使用ServletContext对象来获取web应用下的资源
public class myServlet extends HttpServlet
{
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException
{
/**
* 它得到的是有盘符的路径:F:/xxx/xxx/xx/……
*/
//获取a.txt的真实路径
String pathA = this.getServletContext().getRealPath("/a.txt");
//获取b.txt的真实路径
String pathB = this.getServletContext().getRealPath("/WEB-INF/b.txt")
}
}
5.2 获取资源流
可以把资源以输入流的方式获取
public class myServlet extends HttpServlet
{
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException
{
String path = this.getServletContext().getRealPath("/index.jap");
InputStream inputStream = new FileInputStream(path);
/**
* 获取资源的路径后,创建输入流对象
*/
InputStream inputStream =
this.getServletContext().getResourceAsStream("/index.jsp");
}
}
5.3 获取指定目录
可以使用ServletContext获取指定目录下所有资源路径
注意,本方法必须以“/”开头
/**
* 获取当前路径下所有资源的路径
*/
Set<String> paths = this.getServletContext().getResourcePaths("/WEB-INF");
System.out.println(paths);
6. 练习:访问量同居
一个项目中所有的资源被访问时都要对访问量进行累加
- 创建一个int类型的变量,用来保存访问量,然后把它保存到ServletContext的域中,这样可以保存所有的Servlet都可以访问到
- 最初时,ServletContext中没有保存访问量相关的属性
- 当本站第一次被访问时,创建一个变量,设置其值为1,保存到ServletContext中
- 当以后的访问时,就可以从ServletContext中获取这个变量然后在其基础上加1
public class myServlet extends HttpServlet
{
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException
{
/**
* 1.获取ServletContext对象
* 2.从ServletContext对象中获取名为count的属性
* 3.如果存在,给访问量加1,然后再保存回去
* 4.如果不存在,说明是第一次访问,向ServletContext中保存名为count的属性,值为1
*/
ServletContext application = this.getServletContext();
Integer count = (Integer)application.getAttribute("count");
if(count == null)
{
application.setAttribute("count",1);
}
else
{
application.setAttribute("name",count+1);
}
/**
* 向浏览器输出
*/
PrintWriter pw = resp.getWriter();
pw.print("<h1>" + count + "</h1>");
}
}
资源类路径下资源
获取类路径资源,类路径对一个javaweb项目而言,就是/WEB-INF/classess和/WEB-INF/lib/每个jar包
在src下创建的文件,除* .java编译为* .class,其它文件原封不动移到classes文件下
public class myServlet extends HttpServlet
{
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException
{
/**
* 1.得到ClassLoader
* >先得到Class,再得到ClassLoader
* 2.调用其getResourceAsStream(),得到一个InputStream
*/
ClassLoader classLoader = this.getClass().getClassLoader();
InputStream inputStreamA = classLoader.getResourceAsStream("a.txt");
//相对/classes
InputStream inputStreamB =
classLoader.getResourceAsStream("cn/itcast/servlet/b.txt");
Class c = this.getClass();
InputStream inputStreamAA = c.getResourceAsStream("/a.txt");
//相对classes下
InputStream inputStreamBB = c.getResourceAsStream("b.txt");
//相对于当前.class文件所在目录
InputStream inputStreamJsp = c.getResourceAsStream("/../../index.jsp");
/**
* 将流对象转换为字符串输出
*/
String s = IOUtils.toString(inputStreamA);
System.out.println(s);
}
}
BaseServlet
1.多个请求处理方法
-
我们希望在一个Servlet中可以有多个请求处理方法
-
客户端发送请求时,必须多给出一个参数,用来说明要调用的方法
请求处理方法的签名必须与service相同,即返回值和参数,以及声明的异常都相同
-
客户端必须传递名为method的参数
public abstract class BaseServlet extends HttpServlet
{
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException
{
/**
* 1.获取参数,用来识别用户想请求的方法
* 2.判断是哪一个方法,调用对应方法
*/
String methodName = req.getParameter("method");
if(methodName == null || methodName.trim().isEmpty())
{
throw new RuntimeException("没有传递method参数");
}
/**
* 得到方法名称,是否可以通过反射来调用方法
* 1.得到方法,通过方法名再得到Method类的对象
* * 需要得到Class,然后调用它的方法进行查询,得到Method
* * 我们要查询的是当前类的方法,所以我们需要得到当前类的class
*/
Class c = this.getClass(); //得到当前类的Class对象
Method method = null;
try
{
method = c.getMethod(methodName,
HttpServletRequest.class,HttpServletResponse.class);
}
catch (NoSuchMethodException e)
{
throw new RuntimeException("您要调用的方法" + methodName + "不存在");
}
/**
* 调用method表示的方法
*/
try
{
method.invoke(this,req,resp); //this.addUser(req,resp),method(this,req,resp)
}
catch (Exception e)
{
System.out.println("您调用的方法:" + methodName + ",它的内部出现了异常");
throw new RuntimeException(e);
}
}
}
public class myServlet extends BaseServlet
{
protected void addUser(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException
{
System.out.println("addUser()...");
}
protected void ediUser(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException
{
System.out.println("ediUser()...");
}
protected void delUser(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException
{
System.out.println("delUser()...");
}
}
2.转发和重定向
public abstract class BaseServlet extends HttpServlet
{
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException
{
String methodName = req.getParameter("method");
if(methodName == null || methodName.trim().isEmpty())
{
throw new RuntimeException("没有传递method参数");
}
Class c = this.getClass(); //得到当前类的Class对象
Method method = null;
try
{
method = c.getMethod(methodName,
HttpServletRequest.class,HttpServletResponse.class);
}
catch (NoSuchMethodException e)
{
throw new RuntimeException("您要调用的方法" + methodName + "不存在");
}
try
{
String result = (String) method.invoke(this,req,resp);
//this.addUser(req,resp),method(this,req,resp)
/**
* 获取请求处理方法执行后的返回的字符串,它表示转发或重定向的路径
* 帮他完成转发或重定向
*/
/**
* 如果用户返回的字符串为null,或为"",那么我们什么也不做
*/
if(result == null || result.trim().isEmpty())
{
return;
}
/**
* 查看返回的字符串中是否包含冒号,如果没有,表示转发
* 如果有,使用冒号分隔字符串,得到前缀和后缀
* 前缀如果是f,表示转发;如果是r,表示重定向
* 后缀是转发或重定向的路径
*/
if(result.contains(":"))
{
//使用冒号分隔字符串,得到前缀和后缀
int index = result.indexOf(":"); //获取冒号的位置
String s = result.substring(0,index); //截取前缀,表示操作
String path = result.substring(index+1); //截取后缀,表示路径
if(s.equalsIgnoreCase("r")) //如果前缀是r,那么重定向
{
resp.sendRedirect(req.getContextPath() + path);
}
else if(s.equalsIgnoreCase("f"))
{
req.getRequestDispatcher(path).forward(req,resp);
}
else
{
throw new RuntimeException("你指定的操作:" + s + ",当前版本还不支持");
}
}
else
{
req.getRequestDispatcher(result).forward(req,resp);
}
}
catch (Exception e)
{
System.out.println("您调用的方法:" + methodName + ",它的内部出现了异常");
throw new RuntimeException(e);
}
}
}
public class myServlet extends BaseServlet
{
//转发
protected String fun1(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException
{
System.out.println("fun1()...");
return "/index.jsp";
}
//重定向
protected String fun2(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException
{
System.out.println("fun2()...");
return "r:/index.jsp";
}
protected String fun3(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException
{
System.out.println("fun3()...");
return null;
}
}