Servlet-学习笔记

Servlet-学习笔记,第1张

Servlet-学习笔记 Servlet-授课 一、 Servlet 1.1 Servlet概述

Servlet是SUN公司提供的一套规范,名称就叫Servlet规范,它也是JavaEE规范之一。我们可以像学习Java基础一样,通过API来学习Servlet。这里需要注意的是,在我们之前JDK的API中是没有Servlet规范的相关内容,需要使用JavaEE的API。目前在Oracle官网中的最新版本是JavaEE8,该网址中介绍了JavaEE8的一些新特性。当然,我们可以通过访问官方API,学习和查阅里面的内容。

打开官方API网址,在左上部分找到javax.servlet包,在左下部分找到Servlet,如下图显示:

Servlet的API官网
通过阅读API,我们得到如下信息:
第一:Servlet是一个运行在web服务端的java小程序
第二:它可以用于接收和响应客户端的请求
第三:要想实现Servlet功能,可以实现javax.servlet.Servlet接口,继承GenericServlet或者HttpServlet
第四:核心方法:service(), 每次请求都会执行service方法
第五:Servlet还支持配置
具体请看下图:

1.2 Servlet入门 1.2.1 Servlet使用步骤 搭建web环境流程

一、新建项目-选择Maven-Next


点击Finish

结构目录如下:


配置本地服务器




点 Fix

点击运行,就可以运行index.jsp文件。

如果想修改默认的index.jsp页面可以在web.xml中添加


        /admin.jsp
 

基本框架搭建完成,要需要Servlet,需要我们导入依赖包
1、在pom.xml中加入


        
            javax.servlet
            servlet-api
            2.5
        
    

2、使用Servlet有三种方法:
1、实现Servlet接口,2、继承HttpServlet(经常使用), 3、继承GenericServlet(三者的关系如下)

第一种方法:实现Servlet接口

** 我们先使用实现Servlet接口的方法:需要重写的方法很多,其余都不管,我们只注重service方法即可。**

3、在web.xml加入配置
每个servlet都需要在web.xml进行声明和映射。否则无法使用。

    
		
        sutdent
		
        com.lvmanba.servlet.StudentServlet
    

    
		
        sutdent
		
        /sutdent
    

启动Tomcat,访问设置的路径

运行流程分析:

第二种方法:继承GenericServlet

如果是要使用GenericServlet,新增一个servlet,只要继承GenericServlet即可,重写方法只有一个。要想访问,同样也在web.xml配置一组声明和映射即可。

第三种方法:继承HttpServlet

继承的时候没有提示需要重写方法, 我们可以通过alt+insert来选择 OverrideMethods来选择doGet和doPost方法。
如果不重写任何方法,出现了访问错误,状态码是405。提示信息是:方法不允许。需要重写里面的doGet和doPost方法来接收get方式和post方式的请求

最后在xml加入一组配置:既声明和映射。

1.2.2 Servlet执行过程分析

我们通过浏览器发送请求,请求首先到达Tomcat服务器,由服务器解析请求URL,然后在部署的应用列表中找到我们的应用。接下来,在我们的应用中找应用里的web.xml配置文件,在web.xml中找到FirstServlet的配置,找到后执行service方法,最后由FirstServlet响应客户浏览器。整个过程如下图所示:

一句话总结执行过程:
浏览器——>Tomcat服务器——>我们的应用——>应用中的web.xml——>FirstServlet——>响应浏览器

1.2.3 Servlet实现三种方法分析

我们在实现Servlet功能时,可以选择以下三种方式:
第一种:实现Servlet接口,接口中的方法必须全部实现。
​ 使用此种方式,表示接口中的所有方法在需求方面都有重写的必要。此种方式支持最大程度的自定义。
第二种:继承GenericServlet,service方法必须重写,其他方可根据需求,选择性重写。
​ 使用此种方式,表示只在接收和响应客户端请求这方面有重写的需求,而其他方法可根据实际需求选择性重写,使我们的开发Servlet变得简单。但是,此种方式是和HTTP协议无关的。
第三种:继承HttpServlet,它是javax.servlet.http包下的一个抽象类,是GenericServlet的子类。如果我们选择继承HttpServlet时,只需要重写doGet和doPost方法,不要覆盖service方法。
​ 使用此种方式,表示我们的请求和响应需要和HTTP协议相关。也就是说,我们是通过HTTP协议来访问的。那么每次请求和响应都符合HTTP协议的规范。请求的方式就是HTTP协议所支持的方式(目前我们只知道GET和POST,而实际HTTP协议支持7种请求方式,GET POST PUT DELETE TRACE OPTIONS HEAD )。

1.3 Servlet使用细节 1.3.1 Servlet的生命周期

对象的生命周期,就是对象从生到死的过程,即:出生——活着——死亡。既是对象创建到销毁的过程。
出生:请求第一次到达Servlet时,对象就创建出来,并且初始化成功。只出生一次,就放到内存中。
活着:服务器提供服务的整个过程中,该对象一直存在,每次只是执行service方法。
死亡:当服务停止时,或者服务器宕机时,对象消亡。
Servlet对象只会创建一次,销毁一次。所以,Servlet对象只有一个实例。如果一个对象实例在应用中是唯一的存在,那么我们就说它是单实例的,即运用了单例模式。

public class OtherServlet extends HttpServlet {
    @Override
    public void init() throws ServletException { 
        System.out.println("对象出生了");  //只执行一次,常驻内存
    }
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        System.out.println("我是HttpSerlet...");
    }
    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        doGet(req,resp);
    }
    @Override
    public void destroy() {
        System.out.println("对象销毁了!"); //tomcat停止的时候才执行
    }
}
1.3.2 Servlet的线程安全

由于Servlet运用了单例模式,即整个应用中只有一个实例对象,所以我们需要分析这个唯一的实例中的类成员是否线程安全。接下来,我们来看下面的的示例:

public class ServletDemo04 extends HttpServlet{
    //1.定义用户名成员变量
    //private String username = null;

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        String username = null;
        //synchronized (this) {
            //2.获取用户名
            username = req.getParameter("username");
            try {
                Thread.sleep(3000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            //3.获取输出流对象
            PrintWriter pw = resp.getWriter();
            //4.响应给客户端浏览器
            pw.print("welcome:" + username);
            //5.关闭
            pw.close();
        //}
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        doGet(req,resp);
    }
}

启动两个浏览器,输入不同的参数,访问之后发现输出的结果都是一样,所以出现线程安全问题。

通过上面的测试我们发现,在Servlet中定义了类成员之后,多个浏览器都会共享类成员的数据。其实每一个浏览器端发送请求,就代表是一个线程,那么多个浏览器就是多个线程,所以测试的结果说明了多个线程会共享Servlet类成员中的数据,其中任何一个线程修改了数据,都会影响其他线程。因此,我们可以认为Servlet它不是线程安全的。

分析产生这个问题的根本原因,其实就是因为Servlet是单例,单例对象的类成员只会随类实例化时初始化一次,之后的 *** 作都是改变,而不会重新初始化。

解决这个问题也非常简单,就是在Servlet中定义类成员要慎重。如果类成员是共用的,并且只会在初始化时赋值,其余时间都是获取的话,那么是没问题。如果类成员并非共用,或者每次使用都有可能对其赋值,那么就要考虑线程安全问题了,把变量定义到doGet或者doPost方法里面去就可以了,或者加上同步。

解决的方法1:

//private String username=null;
@Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        String username=null;  //将变量放在方法中
        //接收浏览器的参数
        username = req.getParameter("username");
        try {
            Thread.sleep(30000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        //将用户名响应给浏览器
        PrintWriter pw = resp.getWriter();
        pw.println("welcome:"+username);
        pw.close();
    }

解决的方法2:

private String username = null;
        @Override
        protected void doGet (HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
            synchronized (this){  
                //接收浏览器的参数
                username = req.getParameter("username");
                try {
                    Thread.sleep(30000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                //将用户名响应给浏览器
                PrintWriter pw = resp.getWriter();
                pw.println("welcome:" + username);
                pw.close();
            }
        }
1.3.3 Servlet的映射的三种方式

Servlet支持三种映射方式,以达到灵活配置的目的。

第一种:指名道姓的方式

​ 此种方式,只有和映射配置一模一样时,Servlet才会接收和响应来自客户端的请求。
​ 例如:映射为:/servletDemo5
​ 访问URL:http://localhost:8585/servlet_demo/servletDemo5

第二种:/开头+通配符的方式

​ 此种方式,只要符合目录结构即可,不用考虑结尾是什么。
​ 例如:映射为:/servlet @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @documented public @interface WebServlet { //1、指定Servlet的名称。 相当于xml配置中标签下的 String name() default ""; //2、用于映射Servlet访问的url映射: 相当于xml配置时的 String[] value() default {}; //3、相当于xml配置时的 String[] urlPatterns() default {}; //4、用于配置Servlet的启动时机:相当于xml配置的 int loadOnStartup() default -1; //5、用于配置Servlet的初始化参数:相当于xml配置的 WebInitParam[] initParams() default {}; //6、用于配置Servlet是否支持异步:相当于xml配置的 boolean asyncSupported() default false; //7、用于指定Servlet的小图标 String smallIcon() default ""; //8、用于指定Servlet的大图标 String largeIcon() default ""; //9、用于指定Servlet的描述信息 String description() default ""; //10、用于指定Servlet的显示名称 String displayName() default ""; } 4.2.2 手动创建容器 1)前置说明

在使用Servlet3.1版本的规范时,脱离了web.xml进行注解开发,它除了支持使用注解的配置方式外,还支持纯手动创建Servlet容器的方式。要想使用的话,必须遵循它的编写规范。它是从Servlet3.0规范才开始引入的,加入了一个新的接口:

package javax.servlet;

import java.util.Set;


public interface ServletContainerInitializer {
     
    void onStartup(Set> c, ServletContext ctx) throws ServletException;
}

同时可以利用@HandlesTypes注解,把要加载到onStartup方法中的类字节码传入进来,@HandlesTypes源码如下:

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface HandlesTypes {

    
    Class[] value();
}
2)编写步骤

第一步:创建工程,并移除web.xml

第二步:编写Servlet

public class ServletDemo1 extends HttpServlet {

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        doPost(req,resp);
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        System.out.println("Servlet Demo1 Annotation manual");
    }
}

第三步:创建初始化容器的类,并按照要求配置

public class MyServletContainerInitializer implements ServletContainerInitializer {

    @Override
    public void onStartup(Set> c, ServletContext ctx) throws ServletException {
       
    }
}

在脱离web.xml时,要求在src目录下包含一个meta-INF目录,位置和及字母都不能改变,且严格区分大小写。在目录中创建一个名称为javax.servlet.ServletContainerInitializer的文件,里面写实现了ServletContainerInitializer接口的全限定类名。如下图所示:

第四步:编写注册Servlet的代码

第五步:测试

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/zaji/5686822.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-12-17
下一篇 2022-12-17

发表评论

登录后才能评论

评论列表(0条)

保存