JavaWeb

Servlet2.5

什么是Servlet

  1. ServletJavaEE规范之一.
  2. ServletJavaWeb三大组件之一.(Servlet, Filter,Listener)
  3. Servlet是运行在服务器上的一个Java程序, 处理Web客户端发送的请求以及返回响应信息给Web客户端;
  4. javax.servlet.Servlet 接口定义了Servlet的规范, 包括构造器,初始化方法, 处理请求与返回响应方法以及销毁方法等规范;

实现Servlet

  1. 实现javax.servlet.Servlet接口, 重写抽象方法;
package com.bryan.study;
import javax.servlet.*;
import java.io.IOException;

public class MyServlet implements Servlet {
    @Override
    public void init(ServletConfig servletConfig) throws ServletException {
    }
    @Override
    public ServletConfig getServletConfig() {
        return null;
    }
    @Override
    public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
		// 这个方法就是Servlet处理请求与返回响应的方法
    }
    @Override
    public String getServletInfo() {
        return null;
    }
    @Override
    public void destroy() {
    }
}
  1. WEB-INF文件夹下web.xml配置文件编写
<servlet>
    <!--定义Servlet的名称-->
    <servlet-name>MyServlet</servlet-name>
    <!--指定当前Servlet指向的类名-->
    <servlet-class>com.bryan.study.MyServlet</servlet-class>
	<!--指定当前Servlet加载时机, 默认为-1 表示访问时才实例化初始化, 大于0时, 则项目启动时就初始化, 按照此数值的大小顺序加载web.xml中所有此数值大于0的servlet-->
	<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
    <!--匹配Servlet的名称-->
    <servlet-name>MyServlet</servlet-name>
    <!--Web客户端访问Url的资源路径-->
    <url-pattern>/myServlet</url-pattern>
</servlet-mapping>

浏览器通过URL访问Servlet程序机制

访问路径http://localhost:8080/javaweb/myServlet解释
超文本传输协议HTTP:客户端和服务端进行数据传输的一种规则
IP地址localhost定位运行服务器的电脑地址
端口号:8080定位电脑中服务器运行的程序 这里就是Tomcat程序
项目路径/javaweb定位运行在Tomcat中的项目
资源路径/myServlet解析web.xml 文件, 根据url-pattern匹配 找到servlet-name, 根据servlet-name找到servlet-class, 然后实例化Servlet, 初始化Servlet, 最后执行Servlet.service(resquest, response);方法

Servlet生命周期

  1. 实例化: 调用Servlet的构造器
  2. 初始化: 调用Servlet.init(ServletConfig servletConfig)方法
  3. 处理请求与响应: 调用Servlet.service(ServletRequest servletRequest, ServletResponse servletResponse)方法
  4. 销毁: 调用Servlet.destroy()方法;
    其中 实例化和初始化默认只会在客户端第一次访问时被调用; 处理请求与响应在客户端每次的访问中都会被调用; 销毁方法在Tomcat容器停止时被调用;

Servlet类的继承体系在这里插入图片描述

请求分发

根据javax.servlet.http.HttpServlet源码可以看出 在重写父类的service()方法中 将请求和响应分别强制转换为HttpServletRequestHttpServletResponse, 然后调用了自身的重载protected void service(HttpServletRequest req, HttpServletResponse resp)方法, 在此方法中通过HttpServletRequest .getMethod()方法获取请求的方式, 通过不同的方式调用了不同的处理方法; 例如get请求调用了doGet()方法, post请求调用了doPost()方法;

public abstract class HttpServlet extends GenericServlet {
	public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException {
        if (req instanceof HttpServletRequest && res instanceof HttpServletResponse) {
            HttpServletRequest request = (HttpServletRequest)req;
            HttpServletResponse response = (HttpServletResponse)res;
            this.service(request, response);
        } else {
            throw new ServletException("non-HTTP request or response");
        }
    }
	
	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 = req.getDateHeader("If-Modified-Since");
                if (ifModifiedSince < lastModified) {
                    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);
        }

    }
}

那么实际开发中可以通过继承javax.servlet.http.HttpServlet类, 只需要重写对应的请求分发方法, 既可以处理不同的业务

public class MyHttpServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        // DO SOMETHING FROM GET METHOD REQUEST
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
         // DO SOMETHING FROM GET METHOD REQUEST
    }
}

ServletConfig

ServletConfig: 存储Servlet的配置信息, 在Servlet初始化时由Tomcat程序调用Servletinit()方法传入;
Servlet实例和ServletConfig实例都是由Tomcat负责创建初始化,Servlet默认是在客户端第一次访问时创建, 每一个Servlet实例都有一个属于自己的ServletConfig实例;

public interface Servlet {
	// 可以看到Servlet源码中 init方法的形参就是ServletConfig 
    void init(ServletConfig var1) throws ServletException;
    // 通过此方法获取当前Servlet的ServletConfig 
    ServletConfig getServletConfig();
    ....
}

public abstract class GenericServlet implements Servlet, ServletConfig, Serializable {
    private transient ServletConfig config;
	// 在GenericServlet 源码中 init方法将Tomcat传入的ServletConfig赋值给自己的属性private transient ServletConfig config
	public void init(ServletConfig config) throws ServletException {
        this.config = config;
        this.init();
    }
	// 通过此方法获取当前Servlet的ServletConfig, 直接返回了属性private transient ServletConfig config , 所以调用此方法时必须保证init()方法里将Tomcat传入的ServletConfig赋值给属性private transient ServletConfig config
	public ServletConfig getServletConfig() {
        return this.config;
    }
}

ServletConfig的作用:

  1. 可以获取Servlet程序的别名 servlet-name的值
  2. 获取初始化参数 init-param的值
  3. 获取ServletContext对象
public interface ServletConfig {
	// 获取web.xml文件中配置的servlet-name别名的值
    String getServletName();
	// 获取ServletContext对象实例
    ServletContext getServletContext();
	// 获取初始化参数 init-param的值
    String getInitParameter(String var1);
	
    Enumeration<String> getInitParameterNames();
}

ServletContext

ServletContext:Servlet上下文对象; 一个Web工程只有一个ServletContext对象实例. 在Web工程部署启动时被Tomcat实例化; 在Web工程停止时被销毁;
作用: 1. 获取web.xml中配置的上下文参数context-param; 2. 获取当前工程路径; 3.后驱工程部署后在服务器硬盘上的绝对路径;4. 像Map一样存取数据;

public class MyHttpServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        ServletConfig servletConfig = getServletConfig();
        ServletContext servletContext = servletConfig.getServletContext();
		// 获取上下文参数`context-param`
        Enumeration<String> initParameterNames = servletContext.getInitParameterNames();
        while (initParameterNames.hasMoreElements()){
            String paramName = initParameterNames.nextElement();
            String paramValue = servletContext.getInitParameter(paramName);
            System.out.println(paramName+":"+paramValue);
        }
        // 获取工程路径
        String contextPath = servletContext.getContextPath();
        System.out.println(contextPath);
        // 获取工程绝对路径
        String realPath = servletContext.getRealPath("/");
        System.out.println(realPath);
		
		// 设置属性值
		System.out.println("key被设置之前"+servletContext.getAttribute("key"));
		// 由于servletContext是在Tomcat启动项目时就被实例化, 那么第一次请求在servletContext没有设置key的值时,就会获取到空值, 在第一次请求servletContext设置了key的值后,可以获取到value值, 并且后面每次的请求时servletContext中将一直存在key的值; 
		// 由于一个web项目只有一个servletContext实例, 那么在其中一个Servlet对servletContext对象设置属性值后,其他Servlet也能获取到被设置的属性值;
        servletContext.setAttribute("key","value");

        System.out.println("key被设置之后"+servletContext.getAttribute("key"));
        super.doGet(req, resp);
    }
}

HttpServletResquest

每次请求,Tomcat服务器就会吧请求过来的Http协议信息解析封装到HttpServletRequest对象中. 然后传入Servletservice()方法中使用.

public class MyHttpServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        // 获取项目路径
        String reqContextPath = req.getContextPath();
        System.out.println(reqContextPath);
        // 获取参数字符串
        String queryString = req.getQueryString();
        System.out.println(queryString);
        // 获取 /项目路径/资源路径
        String requestURI = req.getRequestURI();
        System.out.println(requestURI);
        // 获取 访问路径 http://ip:port/项目路径/资源路径
        StringBuffer requestURL = req.getRequestURL();
        System.out.println(requestURL);
        String characterEncoding = req.getCharacterEncoding();
        System.out.println(characterEncoding);
        // get请求 参数中文乱码 可修改tomcat配置/conf/server.xml文件的配置 <Connector connectionTimeout="50000" port="8080" protocol="HTTP/1.1" redirectPort="8443" URIEncoding="UTF-8"/>
        String username1 = req.getParameter("username");
        System.out.println(username1);
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        
        // post请求参数中文乱码,可使用设置编码解决, 但是设置编码一定要在获取参数之前, 否则会设置失效
        req.setCharacterEncoding("utf-8");
        String username1 = req.getParameter("username");
        System.out.println(username1);
        String password = req.getParameter("password");
        System.out.println(password);
		// 设置属性
        req.setAttribute("key", "value");
        // 获取属性
        Object key = req.getAttribute("key");
        // 移除属性
        req.removeAttribute("key");
    }
}
请求转发

请求转发指的是服务器收到请求后,由服务器主动从一个资源跳转到另一个资源的操作;
特点: 1. 浏览器地址栏没有变化;2. 客户端总共只有一次请求; 3. 所有经过转发的Servlet共用一个Request; 4. 可以转发到WEB-INF目录下;5. 不可以转发访问工程以外的资源;
在这里插入图片描述

public class MyHttpServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
       	// 设置属性 
       	req.setAttribute("MyHttpServletKey","MyHttpServletValue");
        // 获取转发器 / 表示 "http://ip:port/项目路径/" 因此这里填写其他项目路径或访问外部链接,是无法形成转发
        RequestDispatcher requestDispatcher = req.getRequestDispatcher("/dispatcherServlet");
        req.setAttribute("MyHttpServletKey1","MyHttpServletValue1");
        // 服务器主动转发
        requestDispatcher.forward(req, resp);
    }
}
public class DispatcherServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        String username = req.getParameter("username");
        // 获取转发前设置的属性
        Object myHttpServletKey = req.getAttribute("MyHttpServletKey");
        Object myHttpServletKey1 = req.getAttribute("MyHttpServletKey1");
        System.out.println(username);
        System.out.println(myHttpServletKey);
        System.out.println(myHttpServletKey1);
    }
}

HttpServletResponse

HttpServletResponseTomcat创建并传递给Servlet使用.如果需要设置返回给客户端信息, 则通过HttpServletResponse来进行设置;
HttpServletResponse可以获取两种流, 一种字节流, 一般用来下载文件使用; 一种字符流, 用来回复客户端字符串信息; 两种流在同一个HttpServletResponse中只能选其一使用; 如果同时获取两种流,则程序将无法通过;

public class MyHttpServlet extends HttpServlet {
		// 解决响应中文乱码 方案一
		// 设置服务器响应编码
        //resp.setCharacterEncoding("UTF-8");
        // 告诉浏览器使用什么编码解析响应
        //resp.setHeader("Content-Type","text/html;charset=UTF-8");
		
		// 解决响应中文乱码 方案二
        // 此方法一定要在获取流对象之前才有效
        resp.setContentType("text/html;charset=UTF-8");

        // 一般用于下载文件
		// ServletOutputStream outputStream = resp.getOutputStream();
        // 两种流在同一个response中只能选其一使用
        PrintWriter writer = resp.getWriter();
        writer.write("您被转发了");
}
重定向

请求重定向: 客户端向服务器发送请求, 服务器响应客户端新地址.客户端继续访问新地址的操作;
特点:1. 浏览器地址栏发生变化;2. 客户端至少有两次请求; 3. 每次被访问的的Servlet不共用一个Request; 4. 不可以重定向到WEB-INF目录下;5. 可以重定向访问工程以外的资源;
在这里插入图片描述

public class MyHttpServlet extends HttpServlet {
	 @Override
     protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
		req.setAttribute("redireceKey","你猜你能接收到吗?");
		// 方案一
		// 设置状态码302 告诉浏览器需要重定向
		resp.setStatus(302);
		// 设置重定向新的地址
        resp.setHeader("Location","http://localhost:8080/javaweb/redirectServlet");
		
		// 方案二
		// resp.sendRedirect("http://localhost:8080/javaweb/redirectServlet")
     }
}
// 接受重定向信息
public class RedirectServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        resp.setContentType("text/html;charset=UTF-8");
        Object redireceKey = req.getAttribute("redireceKey");
        System.out.println(redireceKey); // 这里无法获取重定向之前被设置的属性值
        PrintWriter writer = resp.getWriter();
        writer.write("您被重定向了");
    }
}

JSP

JSP: Java Server Pages,是一种动态网页开发技术; 实际开发中直接使用ServletResponse返回HTML会造成开发与维护困难; 而JSP就是为了解决这一问题产生的技术;
JSP本质上是一个Servlet, 在客户端第一次访问JSP页面时, 可以发现Tomcat程序将JSP页面全部编写为同名.java文件,并编译为.class字节码文件;反编译这些文件可以看到这些.java文件就是JSP页面同名的类, 并且继承了org.apache.jasper.runtime.HttpJspBase; 而这个类刚好就继承了javax.servlet.http.HttpServlet;
因为JSP本质也是一个Servlet, 那么在实际开发中可以使用JAVA Servlet程序处理实际业务逻辑, 将需要在页面的展示的数据放入Request(req.setAttribute(key,value)), 通过请求转发(req.getRequestDispatcher("/xxx.jsp"))至JSP页面, 因为请求转发时所有的Servlet都共用一个Request, 那么在JSP页面中就可以从Request中直接获取数据(req.getAttribute("key"))展示即可; 这样就达到了视图层与业务层分离的思想, 代码开发与维护会相对方便; 同时为了防止客户端直接访问JSP页面, 通常JSP页面会放置在WEB-INF包下, 通过JAVAServlet程序请求转发来访问JSP页面;

Listener监听器

ListenerJavaEE的规范, 监听某种事物的变化, 然后通过回调函数, 对程序进行相应的处理;

ServletContextListener

ServletContext的监听器, 接口中定义了两个回调函数, 分别在ServletContext初始化被调用, ServletContext销毁时被调用;
使用监听器的步骤: 1. 提供监听器接口的实现类; 2. 重写回调函数, 处理相应的业务逻辑;3. web.xml配置文件中配置监听器;

//1. 提供监听器接口的实现类
public class MyServletContextListener implements ServletContextListener {
	// 2. 重写回调函数, 处理相应的业务逻辑
    @Override
    public void contextDestroyed(ServletContextEvent sce) {
    	// ServletContextEvent 被监听的事件
        ServletContext servletContext = sce.getServletContext();
        System.out.println(servletContext+"被销毁");
    }

    @Override
    public void contextInitialized(ServletContextEvent sce) {
        ServletContext servletContext = sce.getServletContext();
        System.out.println(servletContext+"被创建");
    }
}
<!-- 3. web.xml配置文件中配置监听器 -->
<listener>
    <listener-class>com.bryan.study.listener.MyServletContextListener</listener-class>
</listener>

Cookie

服务器通知客户端保存键值对信息的一种技术; 客户端存放Cookie后,每次请求都会携带Cookie发送给服务端;

// 基础Servlet 使用反射技术 分发不同的action
public class BaseServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        doAction(req,resp);
    }
    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        resp.setContentType("text/html;charset=UTF-8");
        doAction(req,resp);
    }
    protected void doAction(HttpServletRequest req, HttpServletResponse resp){
        String action = req.getParameter("action");
        if (action != null && action != "") {
            Class<? extends BaseServlet> clazz = this.getClass();
            Method method = null;
            try {
                method = clazz.getDeclaredMethod(action, HttpServletRequest.class, HttpServletResponse.class);
            } catch (NoSuchMethodException e) {
                e.printStackTrace();
            }
            if (method != null) {
                try {
                    method.invoke(this, req, resp);
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                } catch (InvocationTargetException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

// 客户端发送请求 htpp://localhost:8080/javaweb/cookieServlet?action=cookies
public class CookieServlet extends BaseServlet{
    public void cookies(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        // 创建cookie
        Cookie cookie = new Cookie("cookieName","cookieValue");
        cookie.setMaxAge(-1); // 设置cookie的存活时间 负数表示关闭浏览器就消失, 0表示立刻删除, 正数表示存活的秒数 默认是-1
        resp.addCookie(cookie);

        Cookie cookie1 = new Cookie("cookieName1","cookieValue1");
        cookie.setMaxAge(3600); // 设置cookie的存活时间 负数表示关闭浏览器就消失, 0表示立刻删除, 正数表示存活的秒数 默认是-1
        resp.addCookie(cookie1);

        // 获取cookie
        Cookie[] cookies = req.getCookies();
        Cookie getCookie = null;
        for (Cookie c : cookies) {
            if (c.getName().equals("cookieName")) {
                getCookie = c;
                break;
            }
        }
        if (null != getCookie) {
            // 删除
            getCookie.setMaxAge(0);
            resp.addCookie(getCookie);
        }
    }
}
设置网页记住登录名
public class LoginServlet extends BaseServlet{
    public void login(HttpServletRequest req, HttpServletResponse resp) throws IOException {
        String username = req.getParameter("username");
        String password = req.getParameter("password");
        if ("bryan".equals(username) && "123456".equals(password)) {
            Cookie cookie = new Cookie("username", username);
            cookie.setMaxAge(3600); // 登录成功后将登录名放进cookie并且设置失效时间
            resp.addCookie(cookie);
            resp.getWriter().write("登录成功");
        } else {
            resp.getWriter().write("登录失败");
        }
    }
}
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
    <base href="http://localhost:8080/javaweb/">
</head>
<body>
<form action="loginServlet" method="post">
    <input type="hidden" name="action" value="login">
    <!-- jsp页面 如果浏览器有cookie 则自动赋值 -->
    用户名:<input name="username" value="${cookie.username.value}"/><br />
    密  码:<input name="password" /><br>
    <button type="submit">提交</button>
</form>
</body>
</html>

SESSION

Session是用来维护客户端和服务器之间关联的一种技术, 每个客户端在服务器上有一个自己的Session会话;
Session是基于Cookie的, 如果浏览器的Cookie被禁用了, 那么将导致每次访问都需要创建新的Session
在这里插入图片描述

public class SessionServlet extends BaseServlet{
    public void session(HttpServletRequest req, HttpServletResponse resp) throws IOException {
        // 第一次请求时, 服务器会创建新的session, 并将session的ID放入key为JSESSIONID的cookie中
        HttpSession session = req.getSession();
		// 第一次请求时, 这个获取到的值一定是空的,  如果后面设置了此key的值,那么后续的请求在session没有过期的情况下就能获取到此key的值 
        Object k1 = session.getAttribute("k");
        System.out.println(k1);
		
        session.setAttribute("k","v");

        Object k = session.getAttribute("k");
        System.out.println(k);

        Cookie[] cookies = req.getCookies();
        for (Cookie c: cookies) {
            if (c.getName().equals("JSESSIONID")) {
                String value = c.getValue();
                System.out.println(value);
            }
        }

        // 移除
        // session.removeAttribute("k");
        // 设置有效时间 单位秒 负数表示永不失效 在Tomcat conf包下web.xml中有配置默认时间 1800秒 也就是30分钟
        // 如果想要修改某个项目的所有session默认过期时间 则可以在项目的web.xml文件中配置session-timeout
        // 如果只需修改某个单独的session过期时间, 则调用session.setMaxInactiveInterval(secondtime)方法 单位为秒
        // session.setMaxInactiveInterval(3600);
      	// 使session立刻失效
      	// session.invalidate();

        resp.setContentType("text/html;charset=UTF-8");
        resp.getWriter().write("SessionID="+session.getId() +",是否是新的Session:"+session.isNew()+", Session的有效期:"+session.getMaxInactiveInterval());
    }
}

Filter

过滤器: 拦截请求, 过滤响应; 是JavaEE的一个接口规范;在javaweb开发中,配置过滤器,可以统一拦截处理请求和响应;
生命周期: 1.初始化: Filter在项目启动是被Tomcat实例化, 并调用其自身的init()方法进行初始化; 2. 每一次请求时, 会调用Filter.doFilter()方法, 拦截请求; 3. 项目关闭时, Tomcat调用destory()方法销毁Filter;

<!--在web.xml配置文件中配置filter-->
<filter>
    <filter-name>MyFilter</filter-name>
    <filter-class>com.bryan.study.filter.MyFilter</filter-class>
</filter>
<filter-mapping>
    <filter-name>MyFilter</filter-name>
    <!--拦截的url匹配条件  其中/表示 http://ip:port/工程路径/; *表示全匹配-->
    <!--可以是 / , /* , *.html, /abc.* 等等-->    
    <url-pattern>/*</url-pattern>
    <url-pattern>/</url-pattern>
    <url-pattern>*.html</url-pattern>
    <url-pattern>*.jsp</url-pattern>    
</filter-mapping>
/** 实现Filter接口,并重写抽象方法*/
public class MyFilter implements Filter {
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        // 获取配置的filter名称
        String filterName = filterConfig.getFilterName();
        System.out.println(filterName+"被初始化");
    }

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        HttpServletRequest request = (HttpServletRequest) servletRequest;
        HttpServletResponse response = (HttpServletResponse) servletResponse;
        HttpSession session = request.getSession();
        Object username = session.getAttribute("username");
        System.out.println("前置");
		// 放行
        filterChain.doFilter(servletRequest,servletResponse);
        response.addCookie(new Cookie("key","value"));
        System.out.println("后置");
    }

    @Override
    public void destroy() {
        System.out.println("被销毁");
    }
}
多个Filter的执行机制

多个Filter的执行顺序根据项目web.xml中配置的Filter顺序从上向下执行;
在这里插入图片描述

Servlet3.0

Servlet3.0相较于Servlet2.5增加注解配置, 不在需要web.xml配置文件; 增加Servlet异步处理支持; 对文件上传API的简化支持;

@WebServlet

@WebServlet 修饰Servlet类,用于部署Servlet类的相关配置;

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface WebServlet {
	// 指定Servlet的名称 相当于 web.xml中的 servlet-name
    String name() default "";
	// 指定Servlet的匹配路径 相当于 web.xml中的 url-pattern
    String[] value() default {};
	// 指定Servlet的匹配路径 相当于 web.xml中的 url-pattern 
	// value属性与urlPatterns属性二选一使用 不可同时使用
    String[] urlPatterns() default {};
	// 指定Servlet的实例化时机 默认-1 指定第一次请求时实例化, 大于0时表示在项目启动时实例化, 且所有配置大于0的Servlet按照此数值大小顺序实例化
    int loadOnStartup() default -1;
	// 指定Servlet初始化参数 相当于web.xml中<init-param></init-param>
    WebInitParam[] initParams() default {};
	// 指定Servlet是否支持异步 默认是false 表示同步处理
    boolean asyncSupported() default false;
    String smallIcon() default "";
    String largeIcon() default "";
    String description() default "";
    String displayName() default "";
}
@WebServlet(
        name="myAnnotationServlet",
        // 指定servlet url-pattern
        value="/myAnnotationServlet", 
        // 指定初始化参数
        initParams={@WebInitParam(name="username",value = "bryan"), @WebInitParam(name="password",value = "123456")}, 
        // 指定servlet的加载时机
        loadOnStartup = 1 
)
public class MyAnnotationServlet extends BaseServlet {
    public void annotationServlet(HttpServletRequest req, HttpServletResponse resp) throws IOException {
        resp.setContentType("text/html;charset=UTF-8");
        resp.getWriter().write("使用了注解定义Servlet");
    }
}

@WebFilter

@WebFilter修饰Filter类,用于部署Filter类的相关配置;

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface WebFilter {
    String description() default "";
    String displayName() default "";
    // 指定拦截器的初始化参数 相当于web.xml的init-param
    WebInitParam[] initParams() default {};
	// 指定Filter的名称 相当于 web.xml的 filter-name
    String filterName() default "";
    String smallIcon() default "";
    String largeIcon() default "";
	// 指定Filter拦截的具体的Servlet的名称集合
    String[] servletNames() default {};
	// 指定Filter拦截的路径 相当于 web.xml中的url-pattern
    String[] value() default {};
	// 指定Filter拦截的路径 相当于 web.xml中的url-pattern
    String[] urlPatterns() default {};
	// 指定Filter拦截请求的类型, 默认是DispatcherType.REQUEST
    DispatcherType[] dispatcherTypes() default {DispatcherType.REQUEST};
	// 指定拦截器是否支持异步
    boolean asyncSupported() default false;
}
@WebFilter(value = "/*", 
	dispatcherTypes = DispatcherType.FORWARD, // 表示只拦截被Servlet转发过来的请求
	filterName = "myAnnotationFilter")
public class MyAnnotationFilter implements Filter {
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {

    }
    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {

    }
    @Override
    public void destroy() {

    }
}

@WebListener

@WebListener修饰Listener类,用于部署Listener类的相关配置;

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface WebListener {
    String value() default "";
}
@WebListener
public class MyAnnotationContextListener implements ServletContextListener {

    @Override
    public void contextDestroyed(ServletContextEvent sce) {
        ServletContext servletContext = sce.getServletContext();
        System.out.println(servletContext+"被销毁");
    }
    @Override
    public void contextInitialized(ServletContextEvent sce) {
        ServletContext servletContext = sce.getServletContext();
        String username = servletContext.getInitParameter("username");
        System.out.println(servletContext+"被创建");
    }
}

注解配置与文件配置同时存在情况分析

  1. 对于Servlet配置, 如果一个Servlet类同时被@WebServlet修饰, 又在web.xml中配置了<servlet></servelt>; @WebServletvalueweb.xml中的url-pattern不同时, 那么相当于这个Servlet有多个url-pattern访问路径;@WebServletvalueweb.xml中的url-pattern相同时, 项目将无法启动, 不支持两者相同;
  2. 对于Filter配置, 如果一个Filter类同时被@WebFilter修饰, 又在web.xml中配置了<filter></filter>;无论url-pattern是否相同,Tomcat会实例化两个Filter, 也就是认为@WebFilterweb.xml中是不同的Filter;
  3. Listener才用了两种方式同时注册, 则仅仅相当于一个Listener;

Servlet异步处理

  1. 声明Servlet,增加asyncSupported属性,开启异步支持;
  2. 通过request获取异步上下文AsyncContext;
  3. 调用 AsyncContext.start()方法 并传入实现Runnable接口的实例 启动子线程;
  4. 调用AsyncContext.complete()方法 通知子线程完成;
// 1. 配置asyncSupported = true 表示当前Servlet支持异步
@WebServlet(value = "/asyncServlet", asyncSupported = true)
public class AsyncServlet extends BaseServlet {
    public void async(HttpServletRequest req, HttpServletResponse resp) throws Exception {
        resp.setContentType("text/html;charset=UTF-8");
        PrintWriter writer = resp.getWriter();
        writer.write(Thread.currentThread().getName()+"主线程Servlet开始<br>");
        writer.write(resp.toString());
		// 2. 通过HttpServletRequest 获取AsyncContext
        AsyncContext ac = req.startAsync();
        // 设置响应超时时间 如果业务处理逻辑时间超过此设定的时间,还没有调用ac.complete(),将有可能发生异常
        ac.setTimeout(100000);
        // 设置监听器
        ac.addListener(new MyAsyncListener());
        // 3. 调用 AsyncContext.start()方法 并传入实现Runnable接口的实例 启动子线程
        ac.start(new AsyncSupportedService(ac));
        writer.write(Thread.currentThread().getName()+"主线程Servlet结束<br>");
    }
}

public class AsyncSupportedService implements Runnable {
    private AsyncContext ac;
    public AsyncSupportedService(AsyncContext ac) {
        this.ac = ac;
    }
    @Override
    public void run() {
        try {
            ServletResponse response = ac.getResponse();
            PrintWriter writer = response.getWriter();
            writer.write(Thread.currentThread().getName()+"子线程开始<br>");
            writer.write(response.toString());
            int sum = 0;
            for (int i = 0; i <= 10 ; i++) {
                sum += i;
                System.out.println(i);
                Thread.sleep(1000);
            }
            writer.write("SUM="+sum+"<br>");
            writer.write(Thread.currentThread().getName()+"子线程结束<br>");
            // 4. 调用AsyncContext.complete()方法 通知子线程完成
            ac.complete();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

动态注册三大组件

使用ServletContextListener监听器, 在项目启动时通过ServletContextEvent获取ServletContext, 调用ServletContext的相关API动态注册三大组件;

@WebListener
public class MyServletContextListener implements ServletContextListener {
    @Override
    public void contextInitialized(ServletContextEvent sce) {
        ServletContext servletContext = sce.getServletContext();

        // 获取动态注册器 动态注册Servlet
        ServletRegistration.Dynamic dynamicServlet = servletContext.addServlet("dynamicServlet", DynamicServlet.class);
        dynamicServlet.addMapping("/dynamicServlet"); // 添加url-pattern
        dynamicServlet.setLoadOnStartup(2); // 添加Servlet实例化机制
        dynamicServlet.setInitParameter("username","bryan"); // 设置初始化参数
        dynamicServlet.setAsyncSupported(false); // 设置是否支持异步
        // 动态注册过滤器
        FilterRegistration.Dynamic dynamicFilter = servletContext.addFilter("dynamicFilter", DynamicFilter.class);
       	// dynamicFilter.addMappingForServletNames(); 	
        // 设置url-pattern
        dynamicFilter.addMappingForUrlPatterns(EnumSet.of(DispatcherType.REQUEST),false,"/*");  
        dynamicFilter.setInitParameter("password","123456");  
        // 动态注册监听器
        servletContext.addListener(DynamicListener.class);

    }

}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值