Java web

JavaWeb Servlet

2018-12-28  本文已影响0人  dawsonenjoy

Servlet

即用于处理HTTP请求的类,该部分的类都处于javax.servlet下。

页面跳转方式

分为服务器跳转和客户端跳转,主要区别就是地址栏发生改变(前者不变,后者改变)

Servlet的三种创建方式
1.实现Servlet接口

主要实现里面的service方法,当客户端发起请求,就会执行该方法,举例:

package com.servlet;

import java.io.IOException;

import javax.servlet.Servlet;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;

public class ServletTest implements Servlet {

    public void destroy() {
        // 结束
    }

    public ServletConfig getServletConfig() {
        // TODO Auto-generated method stub
        return null;
    }

    public String getServletInfo() {
        return null;
    }

    public void init(ServletConfig arg0) throws ServletException {
        // 初始化
    }

    public void service(ServletRequest arg0, ServletResponse arg1)
            throws ServletException, IOException {
        System.out.println("aaa");
    }
}
2.继承GenericServlet类

该类实现了servlet接口的大部分方法,并提供了一些新方法,只需重写service即可,举例:

package com.servlet;

import java.io.IOException;

import javax.servlet.GenericServlet;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;

public class GenericTest extends GenericServlet {

    public void service(ServletRequest arg0, ServletResponse arg1)
            throws ServletException, IOException {
        System.out.println("service");
    }
}
3.继承HttpServlet类

该类继承于GenericServlet类,并提供了对于各种请求方式的处理方法,主要如下:
(1)doGet():处理get请求,是HTTP缺省方法
(2)doPost():处理post请求
(3)doPut():处理put请求
(4)doHead()
(5)doOptions()
(6)doDelete()
(7)doTrace()
一般都用前两种方法就行,举例:

package com.aaa;

import java.io.IOException;
import java.io.PrintWriter;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class Test extends HttpServlet {
    //处理get方法
    public void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        response.setContentType("text/html");
        PrintWriter out = response.getWriter();
        out.println("this is get method...");
        out.flush();
        out.close();
    }
    //处理post方法
    public void doPost(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        response.setContentType("text/html");
        PrintWriter out = response.getWriter();
        out.println("this is post method...");
        out.flush();
        out.close();
    }
}
WEB路由映射

servlet要想被外部浏览器访问到,则需要配置路由映射地址,主要有两种方式:传统的是通过配置WEB-INF下的web.xml文件;另一种是使用Servlet 3.0(Java EE 6.0)提供的@WebServlet()注解

1.web.xml路由配置

基本的配置格式如下:

//注册servlet
<servlet>
  <servlet-name>servlet名称</servlet-name>
  <servlet-class>servlet类的位置</servlet-class>  //该句本质是利用了反射机制(Class.forName("")来实例化)
  <load-on-startup>2</load-on-startup>  //可选,设置服务器启动时就实例化该servlet,里面是优先级数值,越小越优先,0是服务器启动的优先级
</servlet>

//设置servlet映射
<servlet-mapping>
  <servlet-name>和上面注册的servlet相同的名称</servlet-name>
  <url-pattern>/路由映射</url-pattern>
</servlet-mapping>

举例:

<servlet>
  <servlet-name>Servlet</servlet-name>
  <servlet-class>com.mvc.servlet.Servlet</servlet-class>
</servlet>
<servlet-mapping>
  <servlet-name>Servlet</servlet-name>
  <url-pattern>/Servlet</url-pattern>
</servlet-mapping>

设置映射路由时注意点:
(1)必须要在前面加/,有一种例外:*.xxx,此时前面不加/,代表只要结尾是.xxx请求都能够访问(其中/的优先级高于这种例外情况)
(2)一个servlet可以配置多个映射路由
(3)可以使用通配符,比如路由后面是xxx/*,则代表路由的最后面是xxx/加任意内容都能够映射
举例:

<servlet-mapping>
    <servlet-name>Test</servlet-name>
    <url-pattern>/Test</url-pattern>
</servlet-mapping>
<servlet-mapping>
    <servlet-name>Test</servlet-name>
    <url-pattern>/servlet/*</url-pattern>
</servlet-mapping>
<servlet-mapping>
    <servlet-name>Test</servlet-name>
    <url-pattern>*.done</url-pattern>
</servlet-mapping>

依次对应三种映射访问下面网址可以发现访问成功:

http://127.0.0.1:8080/XXX/Test
http://127.0.0.1:8080/XXX/servlet/asfa
http://127.0.0.1:8080/XXX/das.done
2.使用WebServlet注解

通过给要配置路由映射的Servlet加上@WebServlet(映射路由)的注解即可进行访问,举例:

@WebServlet("/abc")
public class Test extends HttpServlet {

    public void doGet(HttpServletRequest request, HttpServletResponse response)
            throws IOException {
        ServletOutputStream out = response.getOutputStream();
        out.print("/aaa");
    }
}

此时即可访问/abc的路由,如果要配置多个路由,可以传入一个数组,举例:

@WebServlet({"/abc", "/ccc"})

注:
查看源码可发现其本质也是加入了web.xml中需要配置的一些属性的注解

生命周期

servlet的生命周期遵循步骤:实例化(构造方法) -> init() -> doGet()/doPost() -> destory(),其中init()仅在第一次访问时该servlet时执行(即初始化),doGet()/doPost()在每次通过get/put方法访问该servlet时执行,destory()则在服务器关闭或者长时间不使用该servlet时执行,举例:

package com.servlet;

import java.io.IOException;
import java.io.PrintWriter;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class LifeCycle extends HttpServlet {
    
    public void init() throws ServletException {
        System.out.println("初始化...");
    }
    
    public void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        response.setContentType("text/html");
        response.setCharacterEncoding("utf-8");
        PrintWriter out = response.getWriter();
        out.println("处理get请求");
        out.flush();
        out.close();
    }

    public void doPost(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        System.out.println("处理post请求");
    }

    public void destroy() {
        System.out.println("结束");
        super.destroy();
    }
}

在web.xml中配置如下:

<servlet>
  <servlet-name>LifeCycle</servlet-name>
  <servlet-class>com.servlet.LifeCycle</servlet-class>
</servlet>

<servlet-mapping>
  <servlet-name>LifeCycle</servlet-name>
  <url-pattern>/servlet/LifeCycle</url-pattern>
</servlet-mapping>
线程安全问题

不要使用全局变量,使用局部变量

配置文件操作

对于配置文件web.xml,可以使用ServletConfig来操作,其下提供方法:getInitParameterNames()获取所有配置信息名称,getInitParameter()来获取配置信息内容。其中配置信息在web.xml的servlet里通过<init-param>里配置<param-name><param-value>即可,举例:

web.xml中:
<servlet>
    <servlet-name>Test</servlet-name>
    <servlet-class>com.Test</servlet-class>
    <init-param>
        <param-name>encoding</param-name>
        <param-value>utf-8</param-value>
    </init-param>
</servlet>
在servlet中操作时有以下几种方式:

(1)通过重写继承的public void init(ServletConfig config) throws ServletException方法获取config对象,从而进行调用,举例:

import java.io.IOException;

import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class Test extends HttpServlet {
    private ServletConfig config;
    public void init(ServletConfig config) throws ServletException {
        this.config = config;  //重写方法,获取config对象
    }
    
    public void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        
        System.out.println(config.getInitParameter("encoding"));    //utf-8
    }
}

(2)通过super获取父类的config对象,从而调用方法,举例:

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 Test extends HttpServlet {
    
    public void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        
        System.out.println(super.getInitParameter("encoding")); //调用父类的config,输出utf-8
    }
}

(3)通过调用自身的getServletConfig()方法获取config对象,然后调用方法,举例:

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 Test extends HttpServlet {
    
    public void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        System.out.println(this.getServletConfig().getInitParameter("encoding"));  //输出utf-8
    }
}
ServletContext

代表整个应用,域对象为当前应用,可以使多个servlet共享数据,通过getServletContext()获取
1.获取全局配置信息:getInitParameter()在ServletContext中使用该方法能够获取全局配置信息,即所有Servlet中的配置信息。在web.xml中配置全局信息通过在<context-param>标签里设置<param-name><param-value>即可。举例:
在web.xml中:

<context-param>
    <param-name>name</param-name>
    <param-value>aaa</param-value>
</context-param>

在Servlet中:

public class Test2 extends HttpServlet {

    public void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        System.out.println(this.getServletContext().getInitParameter("name"));
    }
}

2.获取资源路径:通过getRealPath(path)能够得到一个当前项目的绝对路径+path的路径,从而可以读取该项目下的文件内容,举例:

String path = this.getServletContext().getRealPath("/WEB-INF/1.txt");
//得到路径:C:\Program Files\Apache Software Foundation\Tomcat 7.0\webapps\NewTest\WEB-INF\1.txt
FileReader fr = new FileReader(new File(path));
int i;
while((i = fr.read())!=-1)
System.out.print((char)i);

3.全局键值对配置:通过setAttribute()添加键值对,getAttribute()获取键值对,removeAttribute()删除键值对,举例:
在Servlet1中:

import java.io.IOException;

import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class Test extends HttpServlet {
    int i = 0;
    public void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        ServletContext application = this.getServletContext();  //获取ServletContext对象,如果没有该属性值则为null
        application.setAttribute("name", i++);  //设置属性
    }
}

在Servlet2中:

public class Test2 extends HttpServlet {

    public void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        System.out.println(this.getServletContext().getAttribute("name"));  //获取全局的name属性
    }
}
Servlet转发

通过ServletContext下的getRequestDispatcher(路由地址)方法来获取一个RequsetDisapatcher对象,然后通过该对象的forward()方法进行转发,举例:

ServletContext application = this.getServletContext();
RequestDispatcher rd = application.getRequestDispatcher("/Test");  //转发到/Test路由的对象
rd.forward(request, response);  //将前面的请求和响应一起转发
转发和重定向区别

转发是直接从一个Servlet跳转到另一个Servlet,所以地址栏不变,并且可以传输数据,但是只能在该应用中转发,无法转发到其他如百度这样的地址中;而重定向是将要访问的地址返回给客户端让其再次去访问新地址,即两次都是客户端进行访问的,所以地址栏会发生变化,并且无法传输数据,但是没有地址限制

请求包含

通过include()可以实现请求包含,即把被包含的Servlet合并到原来的Servlet里一起执行,举例:

request.setAttribute("name", "aaa");
request.getRequestDispatcher("/Test2").include(request, response);
HttpResponse

对客户端请求的响应结果,由响应行、响应头、响应正文组成

响应行

里面由协议信息和响应状态码组成,举例:

HTTP/1.1 200 OK

其中可以通过setStatus(int)方法设置响应状态码

响应头

即服务端返回的response,主要提供了以下方法:
(1)sendRedirect(路由):请求重定向
(2)addHeader(name, value):添加响应头信息,因为响应头的键名可以重复,所以即使添加的信息名已存在,也不会被覆盖,举例:

response.addHeader("aaa", "111");
response.addHeader("aaa", "222");

结果返回的响应头如下:

aaa:111
aaa:222

(3)setHeader(name, value):设置响应头信息,和上面的不同,这个会将原来的内容覆盖,当不存在该键名时则会添加一个,举例:

response.setHeader("content-type", "text/html; charset=utf-8");

此时打开页面会默认用utf-8编码。
(4)addCookie():添加cookie信息
(5)Refresh():刷新页面

响应正文

即页面展示的内容,主要提供了以下方法:
(1)getWrite():字符输出流,返回一个PrintWriter对象
(2)getOutputStream():字节输出流,返回一个ServletOutputStream对象,和上面一个流对象不能并存,否则报错,使用举例:

public void doGet(HttpServletRequest request, HttpServletResponse response)
        throws ServletException, IOException {
    String path = this.getServletContext().getRealPath("/img/1.jpg");
    FileInputStream in = new FileInputStream(new File(path));
    ServletOutputStream out = response.getOutputStream();
    byte[] b = new byte[1024];
    int len = 0;
    while ((len = in.read(b)) != -1) {
        out.write(b, 0, len);
    }
}

结果会将图片WebRoot/img/1.jpg展示在页面中。
注:
向上面两个流对象里面写入的数据将存到response正文当中,最终被Servlet引擎读取并输出到客户端。并且service方法结束后,Servlet引擎也会检查是否调用了close()方法,如果没有则会自动将其关闭
(3)setCharacterEncoding():设置服务器编码
(4)setContentType():设置页面内容,这个方法和上面一个方法结合起来就相当于:

response.setHeader("content-type", "text/html; charset=编码");  //设置浏览器编码
常用响应头
文件下载

如果要下载文件则设置content-disposition响应头,举例:

public void doGet(HttpServletRequest request, HttpServletResponse response)
        throws ServletException, IOException {
    String path = this.getServletContext().getRealPath("/img/1.jpg");
    FileInputStream in = new FileInputStream(new File(path));
    ServletOutputStream out = response.getOutputStream();
    byte[] b = new byte[1024];
    int len = 0;
    String filename = URLEncoder.encode("图片1.jpg", "utf-8");  //避免文件名编码问题
    response.setHeader("content-disposition", "attachment;filename=" + filename);  //设置该响应头后会自动下载文件
    response.setHeader("content-type", "image/jpeg");  //页面类型为图片
    while ((len = in.read(b)) != -1) {
        out.write(b, 0, len);
    }
}
不使用缓存

设置pragma响应头和cache-control响应头,举例:

response.setHeader("pragma", "no-cache");
response.setHeader("cache-control", "no-cache");
response.setHeader("expires", "0");

这三种分别对应不同浏览器使用

定时刷新页面

设置refresh响应头,举例:

response.setHeader("refresh", "1");  //1秒自动刷新一次

如果需要刷新后跳转到某个页面,可以再设置第二个url值,举例:

response.setHeader("refresh", "1;url=http://www.baidu.com");  //1秒后跳转到别的页面
重定向

过程:
假如客户端发出访问地址1的请求,但是实际上资源在地址2,此时地址1则会返回重定向的地址2,然后客户端接收到了地址2以后再次发出访问地址2的请求。从中可以看出整个过程中地址1和地址2都是由客户端去访问的,而不是地址1去访问的地址2,所以在地址1返回重定向地址前,其会将所有的内容执行完。
实现:
设置响应状态码为302,并设置location响应头,举例:
Servlet中:

response.setStatus(302);
response.setHeader("location", "/NewTest/Test");  //跳转到url
System.out.println("这句会执行完再返回重定向地址");
HttpRequest

客户端发起的请求,由请求行、请求头和请求正文组成

请求行

(1)getMethod():获取请求方法
(2)getRequestURL():返回客户端发出请求的url
(3)getRequestURI():返回请求行的资源名称部分
(4)getContextPath():返回当前应用的虚拟目录
(5)getQueryString():返回请求行的参数部分

请求头

(1)getHeader(name):返回请求信息的值
(2)getHeaders(name):和上面功能一样,但是对于请求名对应多个值的上面的方法只能返回第一个,而这个方法能全部返回,是枚举类型
(3)getHeaderNames():返回请求头的所有name,是Enumeration对象,举例:

public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    Enumeration<String> x = request.getHeaderNames();
    while(x.hasMoreElements()){
        String e = x.nextElement();
        System.out.println(e + ":" + request.getHeader(e));
    }

(4)getAttribute(name):获取非表单属性,设置属性用setAttribute(name, value),删除属性用removeAttribute(name),一般用于Servlet转发的时候,举例:
Servlet1中:

request.setAttribute("name", "aaa");
request.getRequestDispatcher("/Test").forward(request, response);

Servlet2中:

System.out.println(request.getAttribute("name"));

(5)getParameter(name):获取表单的参数值,对于重名的(比如复选框)使用getParameterValues(),返回String数组
(6)getParameterNames():获取所有参数名,是Enumeration对象
(7)getParameterMap():获取所有的参数键值对,是Map<String, String[]>类型,举例:

public void doGet(HttpServletRequest request, HttpServletResponse response)
        throws ServletException, IOException {
    Map<String, String[]> map = request.getParameterMap();
    for(Map.Entry<String, String[]> m: map.entrySet()){
        System.out.println(m.getKey() + ":" + Arrays.toString(m.getValue()));
    }

(8)getRemoteAddr():获取客户端ip

请求数据编码问题

对于post和get请求的数据,往往需要设置编码来防止乱码,但两种请求对应的设置方法不同:
(1)post方法
通过setCharacterEncoding()方法解决post方法的编码问题,举例:
(2)get方法
通过将接受的数据通过String(name.getBytes(原编码), 转编码)解决,举例:

String name = request.getParameter("name");
String name = new String(name.getBytes(原编码), 转编码);
Cookie

客户端技术,其将数据存在客户端本地,但只能存字符串,且不能为中文。通过HttpRequest对象下的getCookies()方法可以获取Cookie对象,通过HttpResponse对象下的addCookie()方法可以添加cookie

常用方法

(1)getName():获取cookie名字
(2)setValue():修改内容(不能为中文)
(3)setMaxAge():设置保存时间(正数是秒,负数代表浏览器缓存,0代表删除),如果没设置保存时间,则关闭浏览器后即被删除
(4)setPath():设置路径,此时该cookie只有在该路径下才能读取,删除前也需要设置正确路径才能删除成功

使用举例
public void doGet(HttpServletRequest request, HttpServletResponse response)
        throws ServletException, IOException {
    Cookie cs[] = request.getCookies();
    for (Cookie c : cs) {
        System.out.println(c.getName() + ":" + c.getValue());
    }
    Cookie c = new Cookie("name", "aaa");
    c.setMaxAge(3600);  //1个小时有效
    response.addCookie(c);  //添加cookie
}
Session

服务端技术,在一个浏览器中独占一个Session,关闭浏览器即消失,可以存对象。通过HttpRequest对象下的getSession()方法可以获取HttpSession对象

常用方法

(1)setAttribute()/getAttribute()/removeAttribute():操作session里的属性
(2)setMaxInactiveInterval():设置有效时间,单位是秒,不设置默认30分钟后销毁
(3)invalidate():使会话无效,并取消任何和该对象的绑定
(4)isNew():是否为一个新的session
(5)getId():获取session的id

getSession()内部原理

(1)获取cookie中键为JSESSIONID的值
(2)若不存在则创建一个HttpSession对象,并分配一个唯一的SessionId,并添加一个键为JSESSIONID值为SessionId的cookie
(3)如果存在该cookie的值,则获取值以后从服务器内存中根据ID寻找该HttpSession对象,找到则继续服务,找不到(如执行了invalidate()或者session过期的情况)则继续执行(2)
注:
从上面可以看出session是基于cookie,所以如果把cookie禁用,那么一般session也就无法使用了

Session失效

(1)默认超过半小时自动过期
(2)执行invalidate()方法强制销毁
(3)超过setMaxInactiveInterval()方法设置的有效时间
(4)在web.xml中通过<session-config><session-timeout>标签设置失效时间(单位为分钟),举例:

<session-config>
    <session-timeout>1</session-timeout>  //1分钟后过期
</session-config>
Session数据保存

当服务器重启或者关闭时原来的Session内容将会消失,为了避免这种情况,可以给对应要保存状态的类继承Serializable接口,即可序列化,此时在重启前服务器会自动将数据进行序列化保存,从而避免了数据丢失的问题

上一篇下一篇

猜你喜欢

热点阅读