如何编写一个Servlet

本文详细介绍了Servlet的编写方式,包括实现Servlet接口、继承GenericServlet和HttpServlet的三种方法,并探讨了Servlet的生命周期,强调了单例特性和线程安全问题。此外,文章还讨论了Servlet的初始化、服务和销毁过程,以及ServletConfig接口和Servlet接口中的关键方法。最后,文章解释了如何通过URL映射访问Servlet,并比较了GET和POST请求的差异及其参数处理方式。

1.什么是servlet
servlet本身就是一种java类,这种java类可以提供web形式的访问(Java EE 规范)

2.怎么按照JavaEE的规范编写一个servlet
关键字    作用    说明
Servlet    接口    有五个抽象方法
GenericServlet    抽象类    有一个抽象方法
HttpServlet    抽象类    没有抽象方法
HttpServlet-继承->GenericServlet-实现->Servlet接口

所以编写一个servlet,有三种方式
第一种方式:
写一个类去实现接口servlet
最重要的是实现接口中的service方法
这个方法就是我们在访问servlet的时候被tomcat服务器调用的

第二种方式:
写一个类去继承父类GenericServlet
抽象类GenericServlet里面有一个抽象方法service,这个方法是servlet接口中的方法,所以GenericServlet只实现了Servlet接口中的四个抽象方法,还剩下这个service没有实现。同时,GenericServlet类中不但实现了Servlet接口中的init方法,而且还重载了一个无参的init()方法

源代码中两个init方法的实现:

            //tomcat服务器默认调用的是这个init方法
            @Override
            public void init(ServletConfig config) throws ServletException {
                this.config = config;
                this.init();
            }
            //用户需要重写的是这个init()方法
            public void init() throws ServletException{
                // NOOP by default
            }
第三种方式:
写一个类去继承父类HttpServlet
HttpServlet是一个抽象类,但是没有任何抽象方法
HttpServlet类中自定义了很多doXxxx方法,每一种方法都对应了浏览器发送请求的方法,一般常用的浏览器发请求方式为get和post,这两种方式分别对应了这个类中的doGet方法和doPost方法。
HttpServlet类中,有两个service方法

        //这个service方法Servlet接口中的
        void service(ServletRequest req, ServletResponse res){...}
        
        //这个service是HttpServlet中重载的方法
        void service(HttpServletRequest req, HttpServletResponse resp){...}
        

        源代码中俩个service方法的实现:
        //实现Servlet接口中的service方法
        @Override
        public void service(ServletRequest req, ServletResponse res)
            throws ServletException, IOException {

            HttpServletRequest  request;
            HttpServletResponse response;

            try {
                request = (HttpServletRequest) req;
                response = (HttpServletResponse) res;
            } catch (ClassCastException e) {
                throw new ServletException("non-HTTP request or response");
            }
            //调用重载之后的service方法
            service(request, response);
        }

        
        //HttpServlet类中重载的service方法
        protected void service(HttpServletRequest req, HttpServletResponse resp)
        throws ServletException, IOException {

            String method = req.getMethod();

            if (method.equals(METHOD_GET)) {
                long lastModified = getLastModified(req);
                if (lastModified == -1) {
                    // servlet doesn't support if-modified-since, no reason
                    // to go through further expensive logic
                    doGet(req, resp);
                } else {
                    long ifModifiedSince;
                    try {
                        ifModifiedSince = req.getDateHeader(HEADER_IFMODSINCE);
                    } catch (IllegalArgumentException iae) {
                        // Invalid date header - proceed as if none was set
                        ifModifiedSince = -1;
                    }
                    if (ifModifiedSince < (lastModified / 1000 * 1000)) {
                        // If the servlet mod time is later, call doGet()
                        // Round down to the nearest second for a proper compare
                        // A ifModifiedSince of -1 will always be less
                        maybeSetLastModified(resp, lastModified);
                        doGet(req, resp);
                    } else {
                        resp.setStatus(HttpServletResponse.SC_NOT_MODIFIED);
                    }
                }

            } else if (method.equals(METHOD_HEAD)) {
                long lastModified = getLastModified(req);
                maybeSetLastModified(resp, lastModified);
                doHead(req, resp);

            } else if (method.equals(METHOD_POST)) {
                doPost(req, resp);

            } else if (method.equals(METHOD_PUT)) {
                doPut(req, resp);

            } else if (method.equals(METHOD_DELETE)) {
                doDelete(req, resp);

            } else if (method.equals(METHOD_OPTIONS)) {
                doOptions(req,resp);

            } else if (method.equals(METHOD_TRACE)) {
                doTrace(req,resp);

            } else {
                //
                // Note that this means NO servlet supports whatever
                // method was requested, anywhere on this server.
                //

                String errMsg = lStrings.getString("http.method_not_implemented");
                Object[] errArgs = new Object[1];
                errArgs[0] = method;
                errMsg = MessageFormat.format(errMsg, errArgs);

                resp.sendError(HttpServletResponse.SC_NOT_IMPLEMENTED, errMsg);
            }
        }
3.servlet的生命周期
3.1 servlet是单例,在web项目运行期间,一个servlet只会创建一个对象
web项目本身就需要在多线程的环境中运行,tomcat服务器会提供这样的多线程环境,当浏览器发送一个请求,tomcat接收到这个请求之后会开启一个线程去处理这个请求

在这种环境下,由于servlet是单例,所以在servlet中声明的成员变量,就会有线程安全的问题。

所以我们应该尽量少的在servlet中定义成员变量

3.2 默认情况下
servlet对象是在用户第一次访问它的时候,由tomcat服务器来创建的(可以通过配置进行改变)。

3.3 servlet对象创建成功之后
tomcat服务器还会调用servlet里面的init(ServletConfig config),这个有参的init方法会调用无参的init()方法,程序员就可以重写这个无参的init()方法,对创建好的servlet对象进行初始化操作。

3.4 如果用户要访问这个servlet对象
那么tomcat服务器会调用这个servlet对象中的service方法,只不过service方法中又进行了方法的层层调用,最后调用到了我们重写的doGet或者是doPost方法

3.5 当servlet对象被销毁的时候
tomcat服务器会调用servlet里面的destory方法,程序员就可以重写这个方法,表示当对象销毁的时候需要做哪些额外的处理
servlet对象被销毁的情况
1.服务器【正常】关闭的时候
2.服务器重新加载web项目的时候(reloading)

3.6 可以通过修改web.xml文件中的配置,去改变servlet对象创建的时间
只要在<servlet>标签中,添加一个子标签<load-on-startup>就可以了
<load-on-startup>标签表示当前这个servlet需要在启动tomcat服务器期间就被创建出来

<load-on-startup>标签里面需要放一个正整数,数值的大小可以决定servlet对象被创建的先后顺序,数值越小就越先被创建。(如果有多个servlet对象需要在tomcat启动期间被创建的话)

4.Servlet接口中的方法(共有五个方法)
    //初始化servlet对象的时候被调用
    void     init(ServletConfig config)

    //销毁servlet对象的时候被调用
    void     destroy()

    //访问servlet对象的时候被调用
    void     service(ServletRequest req, ServletResponse res)
    
    //返回servlet相关信息,比如作者、版本、版权等
    //父类中(GenericServlet)默认返回一个空字符串 ""
    //如果需要的话,程序员可以自己重写这个方法
    String     getServletInfo()

    //返回ServletConfig对象
    ServletConfig     getServletConfig()
5.ServletConfig接口中的方法(共有四个方法)
ServletConfig接口的实现类对象,表示一个servlet在web.xml文件中的配置信息

    //返回servlet在web.xml文件中所配置的名字
    //也就是<servlet-name>这个标签中的值
    String     getServletName()

    //获得在web.xml中所配置的指定名字的参数值
    //在web.xml可以通过<init-param>标签给servlet传参
    String     getInitParameter(String name)

    //获得给当前servlet传的所有参数的名字
    Enumeration     getInitParameterNames()

    //获得ServletContext类型对象
    //ServletContext是web项目中非常重要的一个类型对象
    ServletContext     getServletContext()
6.servlet的访问
使用web.xml文件里面的这个<url-pattern>标签映射的路径,来访问servlet

6.1 在浏览器的地址栏中,直接输入servlet映射的路径来访问
这时候是get方式的访问,servlet中的doGet方法最终会被调用

6.2 在页面中,可以使用超链接来访问servlet
这时候是get方式的访问,servlet中的doGet方法最终会被调用

6.3 在页面中,可以使用表单发送请求去访问servlet
这时候默认情况下是get方式访问,但是可以通过表单里的属性值进行设置为get或者post方式

6.4 将来还可以使用javascript或者在ajax中发请求访问servlet
这个时候也可以进行设置使用get方式还是post方式进行访问servlet

6.5 其他的情况:
还可以使用标签语言来访问servlet

7.get方式的访问和post方式的访问
get方式访问
浏览器地址栏直接输入地址访问
超链接访问
<image src="">访问图片
外部js文件的引入
外部css文件的引入
表单提交数据method=“get”
在javascript代码中访问
在ajax中访问
使用jsp相关标签访问

post方式访问
表单提交数据method=“post”
ajax中设置本次请求为post方式

在http协议规范中,定义了四种访问方式(常见的)
get(查) post(改) put(增) delete(删)

http协议规范下的请求格式(分为四部分)
1部分: 请求行
2部分: 请求头部/消息报头
3部分: \r\n
4部分: 请求正文

get方式传参数 参数在uri后面(uri和url的区别)
GET /hello.html?name=tom HTTP1.1
key: value
key: value
key: value

\r\n

post方式传参 参数在请求正文中
POST /hello.html HTTP1.1
key: value
key: value
key: value

\r\n
name=tom

get和post的【传参】的安全性
get方式的参数由于是在地址栏中显示的,所以安全性低一些,post传参的时候参数在请求正文中,相对会安全一些。但是真正重要的数据在传的过程中还会更换协议http–>https,比如在网上支付的过程中

get和post在传参过程中参数长度的限制
get方式传参,参数长度是要看浏览器对地址栏中字符长度的限制,也就是要看浏览器对url地址的长度限制,我们只是把参数放到url后面了。url?参数=值
post方式参数,参数长度是要看服务器一次性最多能够接受并且处理多少数据

8.在servlet中,接受客户端传的参数
不管客户端是post方式还是get方式传参,只是参数存放的位置在传输过程有所变化,但是对于servlet接收参数来讲,俩种情况都是一样的接收

下面的测试是在这样的传参中进行的
http://127.0.0.1:8989/web_servlet/ParamServlet?name=tom&age=20&like=0&like=1

8.1 接收单一的值(一个参数名对应一个值)
        String name = request.getParameter("name");
        String age = request.getParameter("age");
        System.out.println(name);
        System.out.println(age);
8.2 一个参数名对应多个值(比如多选框)
        String[] like = request.getParameterValues("like");
        System.out.println(Arrays.toString(like));
8.3 获得本次传参中的所有参数名
        Enumeration<String> names = request.getParameterNames();
        while(names.hasMoreElements()){
            String str = names.nextElement();
            System.out.println(str);
        }
    打印结果:
    name
    age
    like
8.4 获得本次传参中的所有参数及对应的值
        Map<String, String[]> map = request.getParameterMap();
        for(String key:map.keySet()){
            System.out.println(key+" : "+Arrays.toString(map.get(key)));
        }
    打印结果:
    name : [tom]
    age  : [20]
    like : [0, 1]
9.客户端传参过程中,出现中文乱码
9.1 get方式传参,中文乱码
需要在tomcat服务器中server.xml文件中进行配置

    在<Connector>中加入新的属性URIEncoding="XXX"
    <Connector URIEncoding="UTF-8" connectionTimeout="20000" port="8989" protocol="HTTP/1.1" redirectPort="8443"/>
9.2 post方式传参,中文乱码
在使用request获取参数【之前】,先把request中的编码进行设置

        request.setCharacterEncoding("UTF-8");
        String name = request.getParameter("name");
        System.out.println(name);
9.3 servlet中使用io流写数据到浏览器,浏览器里面显示中文乱码
http协议规范中响应的格式为:
1部分 响应状态行
2部分 响应头部/消息报头
3部分 \r\n
4部分 响应正文

如果响应头部信息中没有设置编码,那么浏览器会默认使用简体中文(GBK)来解析响应中的内容

所以在使用io流之前,需要设置一下response中的编码,同时还要告诉浏览器本次响应内容的编码是什么
//设置response中的编码为UTF-8
response.setCharacterEncoding(“UTF-8”);
//设置响应头部,告诉浏览器响应内容编码为utf-8
response.setContentType(“text/html;charset=utf-8”);

9.4 Unicode(字符集)和UTF-8(编码规则)
Unicode符号范围 | UTF-8编码方式
十六进制 二进制
0000 0000-0000 007F | 0xxxxxxx
0000 0080-0000 07FF | 110xxxxx 10xxxxxx
0000 0800-0000 FFFF | 1110xxxx 10xxxxxx 10xxxxxx
0001 0000-0010 FFFF | 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值