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

  1. 给Servlet指定一个Servlet路径(让Servlet与一个路径绑定在一起)
  2. 浏览器访问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中创建便宜成员变量,因为可能会存在一个线程对这个成员变量进行写操作,另一个线程对这个成员变量进行读操作

解决措施:

  1. 不要在Servlet中创建成员,创建局部变量即可
  2. 可以创建无状态成员
  3. 可以创建有状态的成员,但状态必须为只读的(只能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。它必须是以“/”开头

  1. 可以在< servlet-mapping >中给出多个< url-pattern >

    • 一个Servlet绑定了两个URL,无论访问哪个都可以
    • 在早期充当过滤器使用,现不这么用一般
  2. 可以在< 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.多个请求处理方法

  1. 我们希望在一个Servlet中可以有多个请求处理方法

  2. 客户端发送请求时,必须多给出一个参数,用来说明要调用的方法

    请求处理方法的签名必须与service相同,即返回值和参数,以及声明的异常都相同

  3. 客户端必须传递名为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;
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值