Servlet总结

1.JavaEE

JavaEE、Servlet、Jsp、Web Container、tomcat这几者之间的关系是什么?

JavaEE是企业级应用开发规范,它本身并不是一种技术,而是多种技术解决方案的集合(即多种技术组件的集合),其中Servlet、JSP是JavaEE的两个主要组件。而Servlet、JSP程序需要放入Web容器中才能运行,Web Container、tomcat就是常见的容器。

2.Tomcat

目录

  1. bin目录,存放运行Tomcat需要的命令文件,例如启动和关闭Tomcat的命令

    win:startup.bat、shutown.bat

    linux:startup.sh、shutdown.sh

  2. conf目录,存放Tomcat相关的配置文件,其中最重要的是server.xml,例如可以在文件中配置Tomcat启动后监听的端口号

  3. lib目录,存放项目运行时需要的jar包,例如,servlet-api.jar

  4. logs目录,存放Tomcat运行时的日志文件

  5. temp目录,存放Tomcat运行期间可能产生的临时文件

  6. webapps目录,部署项目的位置

  7. work目录,编译JSP页面后所存放的文件

虚拟目录

  1. 在server.xml localhost标签下新增

    弊端:修改srver.xml后需要重启服务器

  2. 在conf/Catalina/localhost目录下,新建映射名.xml,在xml中加入

    不写path,path是xml名,不需重启服务器即可发布

3.HTTP

1.概念

hype text transport protocol超文本传输协议,规定了浏览器与服务器(万维网)之间通信的规则,是应用层协议。

2.交互
  1. 在浏览器地址栏输入URL,按下回车
  2. 浏览器向DNS服务器发送域名解析请求并获得目的ip地址和端口号
  3. 根据目的ip和端口号建立TCP连接
  4. 浏览器向服务器发送请求数据
  5. 服务器将网页内容响应给浏览器
  6. 浏览器将得到的数据,解析并显示
  7. 通信完成,断开TCP连接
3.请求内容

请求行,request line
消息报头(请求头),request head
请求正文(请求体),request body

4.响应内容

响应状态行,response status line
消息报头(响应头),response head
响应正文,response body

5.常见响应状态码
状态码 描述
200 请求成功,已正常处理完毕
301 永久重定向
302 临时重定向
400 客户端内部错误
401 客户端请求没有经过授权
403 请求拒绝,没有访问权限IP被封)
404 资源不存在
500 服务器内部错误

4. Servlet

按照Servlet规范要求,编写一个Servlet程序,只需实现javax.servlet.Servlet接口

1. javax.servlet.Servlet接口

它是Servlet技术的核心接口,所有的Servlet程序都必须是这个接口的实现类

package javax.servlet;

import java.io.IOException;

public interface Servlet {
    //初始化servlet对象
    void init(ServletConfig var1) throws ServletException;
	//返回ServletConfig对象,该对象封装了Servlet的配置信息
    ServletConfig getServletConfig();
	//访问servlet对象的时候被调用(最重要的方法)
    void service(ServletRequest var1, ServletResponse var2) throws ServletException, IOException;
	//返回servlet的相关信息,比如作者、版本、版权等等
    String getServletInfo();
	//销毁servlet对象
    void destroy();
}

2. javax.servlet.GenericServlet

一个抽象类,实现了Servlet接口,但是没有实现(没重写方法体)最重要的service()方法

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//

package javax.servlet;

import java.io.IOException;
import java.io.Serializable;
import java.util.Enumeration;

public abstract class GenericServlet implements Servlet, ServletConfig, Serializable {
    private static final long serialVersionUID = 1L;
    private transient ServletConfig config;

    public GenericServlet() {
    }

    public void destroy() {
    }

    public String getInitParameter(String name) {
        return this.getServletConfig().getInitParameter(name);
    }

    public Enumeration<String> getInitParameterNames() {
        return this.getServletConfig().getInitParameterNames();
    }

    public ServletConfig getServletConfig() {
        return this.config;
    }

    public ServletContext getServletContext() {
        return this.getServletConfig().getServletContext();
    }

    public String getServletInfo() {
        return "";
    }

    public void init(ServletConfig config) throws ServletException {
        this.config = config;
        this.init();
    }

    public void init() throws ServletException {
    }

    public void log(String message) {
        this.getServletContext().log(this.getServletName() + ": " + message);
    }

    public void log(String message, Throwable t) {
        this.getServletContext().log(this.getServletName() + ": " + message, t);
    }

    public abstract void service(ServletRequest var1, ServletResponse var2) throws ServletException, IOException;

    public String getServletName() {
        return this.config.getServletName();
    }
}

3.javax.servlet.HttpServlet

它是一个抽象类,但没有抽象方法

继承了GenericServlet,对于客户端的请求进行了细分:

  1. DELETE
  2. HEAD
  3. GET
  4. POST
  5. OPTIONS
  6. PUT
  7. TRACE

对于GET请求,使用doGet()方法进行处理。对于POST请求,使用doPost()方法进行处理

由此,我们只需编写子类继承HttpServlet并重写方法即可。

4.web.xml配置
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
         version="4.0">
    <!-- servlet标签给Tomcat配置servlet程序 -->
    <servlet>
        <!-- 给servlet-name程序起一个别名 -->
        <servlet-name>servletTest</servlet-name><--!第三步 -->
        <!--servlet程序的全类名-->
        <servlet-class>top.roud.servlet.doServlet2</servlet-class><--!第四步 -->
        <!-- 表示多个servlet请求时此servlet的优先级,数字越小越优先执行 -->
        <load-on-startup>1</load-on-startup>
        <!-- 存放初始化参数 -->
        <init-param>
        	<param-name>username</param-name>
            <param-value>tom</param-value>
        </init-param>
    </servlet>
    <servlet-mapping>
        <!-- 通过地址找到servlet别名,再找到servlet程序 -->
        <servlet-name>servletTest</servlet-name><--!第二步 -->
        <!-- url-pattern 标签配置访问地址-->
        <url-pattern>/do</url-pattern><--!第一步 -->
    </servlet-mapping>
</web-app>

以上配置的效果与在Servlet类中注解的效果一致!!!

@WebServlet("/do")
5.生命周期

服务器创建servlet对象并自动调用service()方法

单例模式,存在线程安全问题,所以servlet程序尽量不使用成员变量

  1. 执行Servlet构造器方法

  2. 执行init()初始化方法

    以上两步只在在第一次访问的时候创建Servlet对象会调用

  3. 执行service()方法,每次访问都会调用,每次客户向服务器发出请求时,服务器就会调用这个方法,这个方法会判断请求方式然后进行下一步执行

protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    String method = req.getMethod();
    long lastModified;
    if (method.equals("GET")) {
        lastModified = this.getLastModified(req);
        if (lastModified == -1L) {
            this.doGet(req, resp);
        } else {
            long ifModifiedSince;
            try {
                ifModifiedSince = req.getDateHeader("If-Modified-Since");
            } catch (IllegalArgumentException var9) {
                ifModifiedSince = -1L;
            }

            if (ifModifiedSince < lastModified / 1000L * 1000L) {
                this.maybeSetLastModified(resp, lastModified);
                this.doGet(req, resp);
            } else {
                resp.setStatus(304);
            }
        }
    } else if (method.equals("HEAD")) {
        lastModified = this.getLastModified(req);
        this.maybeSetLastModified(resp, lastModified);
        this.doHead(req, resp);
    } else if (method.equals("POST")) {
        this.doPost(req, resp);
    } else if (method.equals("PUT")) {
        this.doPut(req, resp);
    } else if (method.equals("DELETE")) {
        this.doDelete(req, resp);
    } else if (method.equals("OPTIONS")) {
        this.doOptions(req, resp);
    } else if (method.equals("TRACE")) {
        this.doTrace(req, resp);
    } else {
        String errMsg = lStrings.getString("http.method_not_implemented");
        Object[] errArgs = new Object[]{method};
        errMsg = MessageFormat.format(errMsg, errArgs);
        resp.sendError(501, errMsg);
    }

}
public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException {
    HttpServletRequest request;
    HttpServletResponse response;
    try {
        request = (HttpServletRequest)req;
        response = (HttpServletResponse)res;
    } catch (ClassCastException var6) {
        throw new ServletException(lStrings.getString("http.non_http"));
    }

    this.service(request, response);
}

​ 4.执行destroy()销毁方法,在Web工程停止的时候调用

6.常用方法
//设置请求、响应的编码格式
request.setCharacterEncoding("UTF-8");
response.setCharacterEncoding("UTF-8"); 
response.setContextType("text/plain;charset=UTF-8");

//接收参数(单值、多值)
request.getParameter("参数名,及属性name的值");
///param?like=0&like=1
request.getParameter("like");
///param?name=tom&age=20&like=0&like=1
request.getParameterNames();
//获取参数名及值的映射
request.getParameterMap();

//写回数据
PrintWriter out = response.getWriter();
out.println("你好,程序员");

//HttpServletRequest接口下
//常用和路径相关的方法
getContextPath();//获取项目的根路径(默认是项目名)
getServletPath();//获取当前访问的Servlet地址
getRequestURL();//获取当前请求的URL地址
getRequestURI();//获取当前请求的URI地址

//ServletContext接口下
getContextPath();//获取项目的根路径(默认是项目名)
getRealPath(String Path);//获取一个资源在服务器中的绝对路径
getResourcePaths(String Path);//获取一个路径下面的所有资源
7.请求转发及重定向

请求转发

request.getRequestDispatcher(request,response);

image

request.setAttribute("testAttribute", new String[]{"proper", "xxx"});
request.getRequestDispatcher("/hello4").forward(request, response);

请求转发的特点:

  1. 使用request完成

  2. url不变,是同一次请求

  3. 共享request域中的数据

  4. 可以转发到WEB-INF目录下(java Web应用下的安全目录,所谓安全是指客户端无法访问,服务端可以访问)

  5. 常用于页面控制,例如对失效或被冻结账户判断,再进行相应转发

如果request中有数据,在跳转到的下一个资源中是可以拿到这个数据,因为只有一个request请求对象

由于服务器跳转是把请求和响应在服务器内部中进行转发,所以浏览器的地址栏中的地址是不受影响的,始终还是第一次发出请求的地址,其实浏览器根本就不知道服务器内部的请求转发情况

在设置跳转的资源地址的时候,路径的前面一般要加上/,例如 String path = "/hello.html";

响应重定向

image

//通过后面拼接参数传递数据
response.sendRedirect("other?name=tom&age=20");

使用response完成

重定向的本质是把新的资源路径返回给浏览器,让浏览器向这个新地址发送一个新请求

此处是利用了响应的状态码302和响应头信息中的Location字段来完成

如果request中有数据,重定向后在新的资源中是拿不到这个数据的,因为重定向会发出新的请求,

但是数据在上一个老的请求中

由于重定向让浏览器发出新的请求,所以浏览器地址栏中的地址会变成新请求的地址

在设置重定向的资源地址的时候,路径的前面一般不加/

例如, String path = "hello.html"

8.路径

URL与URI的区别:

URL:统一资源定位符,不仅能标识资源,还能定位资源

URI:统一资源标识符

如URL:http://127.0.0.1:80/servlet/hello.html

此时URI:servlet/hello.html

**斜杠/ ** (非常重要)

如 /hello/hello.jsp

除第一个斜杠,其余斜杠代表路径分隔

第一个斜杠的含义

1.客户端(浏览器)中

第一个斜杠代表端口号后的斜杠 http://ip:port/

此时/a.html代表http://ip:port/a.html

由于响应重定向是发送给客户端,所以前面一般加斜杠

2.服务端中,第一个斜杠表示项目名后的斜杠

如http://ip:port/servlet/

<!-- 当前url:http://127.0.0.1:8989/servlet-test/servlet/path-->
<a href="hello.html">点我</a>
<!-- 此连接为相对路径,此时path与hello.html在同一目录下,点击超链接后跳转至http://127.0.0.1:8989/servlet-test/servlet/hello-->


<a href="../hello2.html">点我2</a>
<!-- 此连接为相对路径,此时servlet与hello2.html在同一目录下,点击超链接后跳转至http://127.0.0.1:8989/servlet-test/hello2.html-->

HTML中标签,在HTML页面中添加base标签后,这个页面的所有相对路径,就不再是相对于地址栏中的路径了,而是相对于base标签中设置的路径

9. 修改路径

默认情况下,项目部署后,项目路径(ContextPath)就是项目的名字,也可以根据情况对该路径进行修改。

例如,把ContextPath修改为/

此时项目的ContextPath为 /servlet ,可以在这里直接修改为 /

启动后,原来是这样访问的路径:http://127.0.0.1:8989/servlet/index.html

现在可以这样来访问:http://127.0.0.1:8989/index.html

10.乱码

POST

//解决传参乱码
request.setCharacterEncoding("UTF-8");
response.setCharacterEncoding("UTF-8");
//解决写回数据乱码
resp.setContentType("text/plain;charset=UTF-8");
//或者
resp.setContentType("text/html;charset=UTF-8");
//或者
resp.setContentType("application/json;charset=UTF-8");

GET

//在server.xml种设置
<Connector URIEncoding="UTF-8" connectionTimeout="20000" port="8989" protocol="HTTP/1.1" redirectPort="8443"/>
//打散后组装
String name = request.getParameter("name");
name = new String(name.getByte("ISO8859-1","UTF-8"));
11.范围对象
接口类型 对象名 描述
javax.servlet.http.HttpServletRequest request 同一个请求范围内
javax.servlet.http.HttpSession session 在同一个会话内
javax.servlet.ServletContext application 在同一个项目内
  1. request对象

    request、response是方法参数,可以直接使用,只在一次请求内有效

  2. session对象

    创建: 客户端与服务器无有效会话情况下,客户端访问服务器

    销毁:超时自动销毁、session.invalidate()

    1. 一个session是指一个客户端与服务器的会话,并不是所有客户端共享
    2. request.getSession()方法第一次被调用会创建session对象,session.invalidate()调用会销毁session
    3. session超时的话也会自动销毁,默认超时时间为30分钟,其中超时时间是客户端不与服务器交互的时间
    4. 一个会话包含多次请求,所以放在session中的数据,可以被多个请求所共享

    正常关闭服务器session会被序列化保存

    session与cookie的区别

    session数据保存于服务端,生命周期仅为C/S无交互时间的30分钟,相对安全。应用场景:登录验证,如验证码

    cookie数据保存于客户端,生命周期长(不被删除即存在),不安全完全可见,cookie为会话追踪技术

    应用场景:判断用户是否登录过网站,以便下次登录可以直接登录,如果我们删除cookie,则每次登录必须重新填写登录的相关信息

    3.application

    启动服务器的时候,applicaion就会被创建,关闭服务器时,application就会被销毁

    每个项目在运行期间,有且只有一个application与之对应

11.2范围对象存储数据
//存
request.setAttribute("key","value");
session.setAttribute("key","value");
application.setAttribute("key","value");
//取
request.getAttribute("key");
session.getAttribute("key");
application.getAttribute("key");
//删除
request.removeAttribute("key");
session.removeAttribute("key");
application.removeAttribute("key");
12. 会话追踪
12.1 无状态访问

HTTP的访问是无状态访问,当前的访问是不会知道之前访问的状态的。也就是说,http协议不会帮我们保存访问记录及痕迹。但是,有些时候我们需要记录之前的访问状态。这时候就需要使用到会话追踪技术。

cookie是常用到的一种会话追踪技术,cookie是浏览器中用来存储信息的一种本地文件,浏览器再向服务器发送请求的时候,可以根据服务器的地址,将对应的cookie中的信息,携带到请求头中,一起发送给服务器。

新建cookie对象,让response携带cookie发送给浏览器,浏览器会将cookie对象内容保存到本地对应的一个cookie文件中

//创建cookie对象
Cookie c1 = new Cookie("name","tom");
Cookie c2 = new Cookie("msg","hello");
//设置cookie的有效时间
c1.setMaxAge(60*60*24*365);
c2.setMaxAge(60*60*24*365*10);
//把cookie放到response里面
response.addCookie(c1);
response.addCookie(c2);

服务器读取cookie

servlet中,可以使用request对象拿到从浏览器发送过来的cookie数据

Cookie[] cookies = request.getCookies();
//遍历数组 拿出key和value
for(Cookie c:cookies){
  String key = c.getName();
  String value = c.getValue();
  System.out.println(key+" : "+value);
}
12.3 cookie和session

在浏览器的cookie的文件中,有一个默认的名字:JSESSIONID之后,浏览器发送请求的时候,会把之前保存在cookie文件中的JSESSIONID的值,传给服务器。服务器通过这个JESSIONID的值,就能够知道服务器内存中是哪一个session对象,和当前这个客户端对应。

第二次访问页面时候,cookie会附带请求头(request)给服务器。服务器识别到这个cookie了。就会根据这个找对应的session,找到后先判断是否过期,没有过期就去读取session文件的配置

12.4 URL重写

当用户把浏览器的cookie功能禁用之后,浏览器在发请求的时候,响应(response)把cookie带回来,但是浏览器无法接收,这其中就包括最重要的JSESSIONID。

此时可以使用URL重写来达到会话追踪的效果。

String url = response.encodeURL("url"); 

注意,重写后的url和原来的url比较,其实就是在url后面拼接上了JSESSIONID的值,以便点击链接
的时候可以把JSESSIONID的值也传过来,从而达到会话追踪的效果

5. Servlet规范三大组件

三大组件:Servlet接口、Listener接口、filter接口

6.Filter

在一个请求去访问某个资源的时候,filter(过滤器)可以在这个请求,访问到这个资源之前,进行拦截,然后做出一系列的处理,例如编码转换信息过滤权限判断登录验证等等,最后filter再决定,是否要让这个请求去访问那个目标资源。

package javax.servlet;
import java.io.IOException;
public interface Filter {
  //过滤器对象初始化时调用的方法
  public void init(FilterConfig filterConfig) throws ServletException;
 
  //当过滤器拦截到请求的时候,会调用这个doFilter方法进行处理
  public void doFilter(ServletRequest request, ServletResponse response,
      FilterChain chain) throws IOException, ServletException;
  //过滤器对象销毁时调用的方法
  public void destroy();
}

filterChain过滤器链:在一个wed应用中,可以开发多个filter,这些filter组合起来称为过滤器链

web服务器根据Filter在web.xml文件中的注册顺序(mapping的配置顺序)决定先调用哪个filter,依次调用后面的过滤器。如果没有下一个过滤器,调用目标资源

如果是注解,执行顺序按注解中映射路径的首字母顺序执行

image

默认情况下,服务器内部跳转的请求,就无法被拦截,除非进行配置

<filter-mapping>
  <filter-name>checkFilter</filter-name>
  <url-pattern>/test</url-pattern>
  <dispatcher>FORWARD</dispatcher>
</filter-mapping>

除此之外,还有其他的拦截类型,了解知道即可。重点关注的时候REQUEST和FORWARD俩种

Filter的生命周期

filtter的创建由服务器负责,web创建的时候就会创建filter实例化对象,并调用init方法进行初始化(filter创建和init方法执行都只进行一次)

每次filter进行拦截的时候,doFilter方法都会执行

当服务器关闭的时候,Filter对象会被销毁

权限拦截

...
//用户登录验证界面
//验证成功
request.getSession().setAttribute("user",user);
//拦截器
public class doFilter implements Filter{
    public void init(FilterConfig filterConfig) throws ServletException {

    }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        //将ServletRequest转为HttpServletRequest
        HttpServletRequest req= (HttpServletRequest) request;
        HttpSession session = req.getSession();
        LoginUser loginUser = (LoginUser) session.getAttribute("User");
        if(loginUser!=null){
            //放行到下一个拦截器或放行到资源
            chain.doFilter(request,response);
        }else{
            response.setContentType("application/json;charset=UTF-8");
            PrintWriter writer = response.getWriter();
            writer.println("未登录");

        }
    }

    @Override
    public void destroy() {

    }
}
<filter>
    <filter-name>loginFiler</filter-name>
    <filter-class>top.roud.Filter.LoginFilter</filter-class>
</filter>
<filter-mapping>
    <filter-name>loginFiler</filter-name>
    <!--对于访问/JSP目录下所有资源的请求都进行拦截-->
    <url-pattern>/JSP/*</url-pattern>
</filter-mapping>

7. Listener

观察者模式:一个被观察者所发出的信息,可能会影响到多个观察者。

被观察者与观察者之间是一对多的关系,当被观察者的状态发生改变时,通知所有的所有的观察者

7.1分类

监听对象为范围对象,根据监听类型分类

不同功能的监听器,需要实现不同的接口,可以大致分为以下几种监听器类型:

1、监听 request、Session、application 的创建和销毁,这些接口分别为:
ServletRequestListener
HttpSessionListener
ServletContextListener

2、监听request、session、application三个对象中属性变化,这些接口分别为:

ServletRequestAttributeListener
HttpSessionAttributeListener
ServletContextAttributeListener

3、监听Session对象里面存放着的其他对象,这些接口分别为:

HttpSessionBindingListener
HttpSessionActivationListener

7.2 使用

1.监听 request、Session、application 的创建和销毁

import javax.servlet.ServletContext;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
import javax.servlet.ServletRequestEvent;
import javax.servlet.ServletRequestListener;
import javax.servlet.annotation.WebListener;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import javax.servlet.http.HttpSessionEvent;
import javax.servlet.http.HttpSessionListener;
@WebListener
public class ListenerTest implements HttpSessionListener, ServletContextListener,
ServletRequestListener {
    //参数为监听到的事件
// 创建 session
    public void sessionCreated(HttpSessionEvent se) {
        HttpSession session = se.getSession();
        System.out.println("新创建一个session, ID为: " + session.getId());
    }
    // 销毁 session
    public void sessionDestroyed(HttpSessionEvent se) {
        HttpSession session = se.getSession();
        System.out.println("销毁一个session, ID为: " + session.getId());
    }
    // 加载 application
    public void contextInitialized(ServletContextEvent sce) {
        ServletContext servletContext = sce.getServletContext();
        System.out.println("即将启动" + servletContext.getContextPath());
    }
    // 卸载 application
    public void contextDestroyed(ServletContextEvent sce) {
        ServletContext servletContext = sce.getServletContext();
        System.out.println("即将关闭" + servletContext.getContextPath());
    }
    // 创建 request
    public void requestInitialized(ServletRequestEvent sre) {
    HttpServletRequest request = (HttpServletRequest)
        sre.getServletRequest();
        String uri = request.getRequestURI();
        uri = request.getQueryString() == null ? uri : (uri + "?"+request.getQueryString());
        request.setAttribute("dateCreated", System.currentTimeMillis());
        System.out.println("IP " + request.getRemoteAddr() + " 请求 " + uri);
    }
    // 销毁 request
    public void requestDestroyed(ServletRequestEvent sre) {
    HttpServletRequest request = (HttpServletRequest)
        sre.getServletRequest();
        long time = System.currentTimeMillis() - (Long)
        request.getAttribute("dateCreated");
        System.out.println(request.getRemoteAddr() + "请求处理结束, 用时" + time +"毫秒. ");
    }
}

注意,这里的监听器使用了注解配置,也可以使用xml配置,在web.xml中

<listener>
    <listener-class>com.briup.listener.TestListener</listener-class>
</listener>

2.监听request、session、application三个对象中属性变化

import javax.servlet.annotation.WebListener;
import javax.servlet.http.HttpSession;
import javax.servlet.http.HttpSessionAttributeListener;
import javax.servlet.http.HttpSessionBindingEvent;
@WebListener
public class SessionAttributeListenerTest implements HttpSessionAttributeListener
{
    //当属性发生修改事件会发生
// 添加属性
    public void attributeAdded(HttpSessionBindingEvent se) {
    //HttpSession session = se.getSession();
        String name  = se.getName();
        Object value = se.getValue();
        System.out.println("新建session属性:" + name + ", 值为:" + value);
    }
    // 删除属性
    public void attributeRemoved(HttpSessionBindingEvent se) {
    //HttpSession session = se.getSession();
        String name = se.getName();
        Object value = se.getValue();
        System.out.println("删除session属性:" + name + ", 值为:" + value);
    }
    // 修改属性
    public void attributeReplaced(HttpSessionBindingEvent se) {
    HttpSession session = se.getSession();
        String name = se.getName();
        Object oldValue = se.getValue();
        Object newValue = session.getAttribute(name);
        System.out.println("修改session属性:" + name + ", 原值:" + oldValue + ",
        新值:" + newValue);
    }
}

8. error-page

在web.xml中,可以使用 定制错误的返回页面
例如,如果出现404情况,则跳转到/404.html页面中

<error-page>
  <error-code>404</error-code>
  <location>/404.html</location>
</error-page>

也可以指定servlet中出现的异常情况:
例如,当servlet程序的时候,如果出现了以下类型异常,则跳转到not_found.html页面

<error-page>
  <error-type>java.io.FileNotFoundException</error-type>
  <location>/not_found.html</location>
</error-page>

9. WEB-INF

WEB-INF是Java的WEB应用中的安全目录。

安全目录指的是客户端无法访问,只有服务端可以访问的目录。

例如,如果将hello.html页面存放在WEB-INF下,那么浏览器中是无法通过以下地址【直接】访问到页

面的:

http://127.0.0.1:8989/servlet-test/WEB-INF/hello.html

但是,在servlet中是可以通过服务器内部跳转,来访问到WEB-INF目录下的资源的。

所以,浏览器可以访问servlet,让servlet再通过服务器内部跳转的方式,来访问WEB-INF下的

hello.html,从而提高页面的安全性,因为在servlet中可以对本次请求进行验证,通过后跳转到WEB-

INF下的页面,否则返回错误信息。

10. 上传

在不同版本的servlet中,完成上传的方式不同,servlet3.0及之前的版本,完成上传较为麻烦(了解即可),servlet3.1版本完成上传较为

简单。

servlet3.0之前上传需要注意事项:

  1. 需要导入两个包:commons-fileupload-1.4.jar,commons-io-2.8.0.jar

  2. 页面表单一定要是post方式提交

  3. 表单编码类型设置为:enctype="multipart/form-data",默认是 application/x-www-form-urlencoded

  4. 上传成功的时候,上传的文件可以在项目中的目录,也可以在系统目录

  5. 设置了表单的enctype="multipart/form-data" 就【无法】通过HttpServletRequest对象的
    getParameter方法获取请求参数值。

servlet3.1上传需要注意事项:

  1. 不需要导入额外的jar包,使用servlet提供的@MultipartConfig注解和javax.servlet.http.Part接口类型
    对象即可
  2. 页面表单一定要是post方式提交
  3. 表单编码类型设置为:enctype="multipart/form-data",默认是 application/x-www-form-urlencoded
  4. 上传成功的时候,上传的文件可以在项目中的目录,也可以在系统目录
  5. 设置了表单的enctype="multipart/form-data" 【也可以】通过HttpServletRequest对象的
    getParameter方法获取请求参数值

servlet3.1实现:

  1. 导jar包

    idea:动态WEB项目,在WED-INF下新建lib目录,将jar文件放入其中,file->Project Structure->Modules->Dependencies->+->...

  2. 编写页面upload.html

    <!DOCTYPE html>
    <html>
    <head>
    <meta charset="UTF-8">
    <title>上传</title>
    </head>
        <body>
            <h1>hello world</h1>
            <h2>上传</h2>
            <form action="upload" method="post" enctype="multipart/form-data">
                <input type="text" name="username" /><br>
                <input type="file" name="file" /><br>
                <input type="submit" value="上传" />
            </form>
        </body>
    </html>
    
  3. 编写servlet

    import javax.servlet.ServletException;
    import javax.servlet.annotation.MultipartConfig;
    import javax.servlet.annotation.WebServlet;
    import javax.servlet.http.HttpServlet;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    import javax.servlet.http.Part;
    /**
    * servlet3.1的上传操作
    */
    @WebServlet("/upload2")//注解映射路径
    @MultipartConfig(location = "D:\\briup\\upload")//注解上传文件保存的位置
    public class UpLoadServlet2 extends HttpServlet {
        private static final long serialVersionUID = 1L;
        protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
            //可以使用filter来进行编码设置
            //request.setCharacterEncoding("UTF-8");
            //response.setCharacterEncoding("UTF-8");
            String username = request.getParameter("username");
            //获取上传的文件
            Part part = request.getPart("file");
            //获取上传文件的名字
            //servlet3.1的方法
            String fileName = part.getSubmittedFileName();
            System.out.println(username+" 上传了文件:"+fileName);
            //避免上传的文件重复,文件名前面加上时间戳,part.write(“文件名”);
            part.write(System.currentTimeMillis()+"-"+fileName);
    }
    }
    
    

    @MultipartConfig注解参数

    image

Part 接口的方法:

image

11. 下载

  1. 编写download.html文件

    <!DOCTYPE html>
    <html>
    <head>
    <meta charset="UTF-8">
    <title>下载</title>
    </head>
        <body>
            <h1>hello world</h1>
            <h2>servlet下载</h2>
            <a href="download?fileName=我的文档.txt">我的文档</a><br>
        </body>
    </html>
    
  2. 编写downloadServlet

    import java.io.File;
    import java.io.FileInputStream;
    import java.io.IOException;
    import javax.servlet.ServletException;
    import javax.servlet.ServletOutputStream;
    import javax.servlet.annotation.WebServlet;
    import javax.servlet.http.HttpServlet;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    @WebServlet("/download")
    public class DownLoadServlet extends HttpServlet {
    private static final long serialVersionUID = 1L;
    private String path = "D:\\briup\\upload";
    @Override
    protected void doGet(HttpServletRequest request,
    HttpServletResponse response) throws ServletException, IOException {
    	// 获得用户需要下载文件的名字,"我的文档.txt"
        String fileName = request.getParameter("fileName");
        // 如果是下载文件的目录在项目中,可以通过该方法获得该下载文件目录的绝对路径
        // String realPath = getServletContext().getRealPath(path);
        // 响应类型设置为下载:application/x-download
        response.setContentType("application/x-download");
        // 下载文件时显示文件的保存名称
        String fileDisplay = "briup_"+fileName;
        // 下载文件名的中文编码转换 因为写回去的时候是字节流
        // 不设置会出现下载文件的中文名字乱码
        fileDisplay = new String(fileDisplay.getBytes("utf-8"),"ISO-8859-1");
        // 设置响应头部信息(http协议中的固定设置方式)
        response.addHeader("Content-Disposition",
        "attachment;filename="+fileDisplay);
        FileInputStream in = null;
        try {
            // 获取servlet中是输出流
            ServletOutputStream out = response.getOutputStream();
            // 使用IO流读取需要下载的文件
            File file = new File(path,fileName);
            in = new FileInputStream(file);
            // 读取文件后再写回给浏览器
            byte[] b = new byte[1024];
            int len = -1;
            while ((len = in.read(b)) != -1) {
            out.write(b, 0, len);
        }
            in.close();
        } catch (Exception e) {
            e.printStackTrace();
        }finally {
            if(in!=null) {
            in.close();
        }
        }
        //注意下载之后就不能使用request跳转了,因为response已经返回了
        }
    }
    

补充:

request.getQueryString(); 获取查询字符串

http://localhost/test.do?a=b&c=d&e=f 得到 :a=b&c=d&e=f

posted @ 2021-10-14 19:13  热心邻居老王  阅读(73)  评论(0编辑  收藏  举报