Servlets

server applet 运行在服务器端的小程序

WebServlet

使用

public class MyServlet implements Servlet{
   ...
    @Override
    public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
        servletResponse.getWriter().println("hello world");
    }
    ...
}
<servlet>
    <servlet-name>MyServlet</servlet-name>
    <servlet-class>wang.ismy.web.MyServlet</servlet-class>
</servlet>
<servlet-mapping>
    <servlet-name>MyServlet</servlet-name>
    <url-pattern>/*</url-pattern>
</servlet-mapping>

原理

生命周期方法

被创建:执行init方法,只执行一次

* Servlet什么时候被创建?
            * 默认情况下,第一次被访问时,Servlet被创建
            * 可以配置执行Servlet的创建时机。
                * 在<servlet>标签下配置
                    1\. 第一次被访问时,创建
                        * <load-on-startup>的值为负数
                    2\. 在服务器启动时,创建
                        * <load-on-startup>的值为0或正整数

        * Servlet的init方法,只执行一次,说明一个Servlet在内存中只存在一个对象,Servlet是单例的
            * 多个用户同时访问时,可能存在线程安全问题。
            * 解决:尽量不要在Servlet中定义成员变量。即使定义了成员变量,也不要对修改值

提供服务:执行service方法,执行多次

* 每次访问Servlet时,Service方法都会被调用一次。

被销毁:执行destroy方法,只执行一次

* Servlet被销毁时执行。服务器关闭时,Servlet被销毁
        * 只有服务器正常关闭时,才会执行destroy方法。
        * destroy方法在Servlet被销毁之前执行,一般用于释放资源
sequenceDiagram
    客户端 ->> Servlet容器: 发送请求
    Servlet容器 ->> Servlet容器: 解析请求
    Servlet容器 ->> Servlet: 创建实例
    Servlet容器 ->> Servlet: 调用init()
    Servlet容器 ->> Servlet: 调用service()
    Servlet ->> Servlet容器: 输出响应
    Servlet容器 ->> 客户端: 返回响应
    Servlet容器 ->> Servlet: 调用destory()

Servlet3.0

@WebServlet("/*")

体系结构

批注 2019-08-09 093125

配置

路径定义规则:

Request

体系结构

request对象继承体系结构:    
        ServletRequest        --    接口
            |    继承
        HttpServletRequest    -- 接口
            |    实现
        org.apache.catalina.connector.RequestFacade 类(tomcat)

方法

请求转发

req.getRequestDispatcher("/404")
                .forward(req,resp);

共享数据

request域:代表一次请求的范围,一般用于请求转发的多个资源中共享数据

Response

方法

* 重定向的特点:redirect
                1\. 地址栏发生变化
                2\. 重定向可以访问其他站点(服务器)的资源
                3\. 重定向是两次请求。不能使用request对象来共享数据
            * 转发的特点:forward
                1\. 转发地址栏路径不变
                2\. 转发只能访问当前服务器下的资源
                3\. 转发是一次请求,可以使用request对象来共享数据

乱码问题

PrintWriter pw = response.getWriter();获取的流的默认编码是ISO-8859-1

//设置编码,是在获取流之前设置
response.setContentType("text/html;charset=utf-8");

ServletContext

获取:

获取MIME类型

System.out.println(getServletContext().getMimeType("a.jpg"));

域对象:共享数据

获取文件真实路径

Servlet 过滤器(Filter)

一般用于完成通用的操作。如:登录验证、统一编码处理、敏感字符过滤

@WebFilter
public class LoggingFilter implements Filter {
    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        //...
    }
}

也可以不适用注解使用如下xml配置

<filter>
    <filter-name>demo1</filter-name>
    <filter-class>wang.ismy.javaee.LoggingFilter</filter-class>
</filter>
<filter-mapping>
    <filter-name>demo1</filter-name>
    <!-- 拦截路径 -->
    <url-pattern>/*</url-pattern>
</filter-mapping>

执行流程

生命周期方法

拦截方式配置:资源被访问的方式

@WebFilter(value = "/*",dispatcherTypes = DispatcherType.ERROR)

过滤器链

事件监听器(Listener)

异步支持

@WebServlet(urlPatterns = "/hello",asyncSupported = true)
public class MyServlet extends HttpServlet {

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        AsyncContext async = req.startAsync();
        async.addListener(new AsyncListener() {
            @Override
            public void onComplete(AsyncEvent asyncEvent) throws IOException {
                asyncEvent.getSuppliedResponse().getWriter().write("jntm");
            }
            //...
        });
        new Thread(()->{
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            async.complete();
        }).start();
    }
}

非阻塞IO

AsyncContext async = req.startAsync();
ServletInputStream inputStream = req.getInputStream();
inputStream.setReadListener(new ReadListener() {
    @Override
    public void onDataAvailable() throws IOException {
        byte[] bytes = new byte[1024];
        while (inputStream.isReady() && inputStream.read(bytes)!=-1){
            System.out.println(new String(bytes));
        }
        async.complete();
    }
    @Override
    public void onAllDataRead() throws IOException {
        async.complete();
    }
    @Override
    public void onError(Throwable throwable) {
        throwable.printStackTrace();
        async.complete();
    }
});

WebFragment

可以对XML配置进行分区

安全

错误映射

<error-page>
    <error-code>404</error-code>
    <location>/404.html</location>
</error-page>
<error-page>
    <exception-type>java.lang.RuntimeException</exception-type>
    <location>/500.html</location>
</error-page>

文件上传

工作原理

Servlet

体系结构:

屏幕截图 2020-10-06 105912

ServletContexnt:贯穿请求的上下文

ServletConfig:传递参数集合

StandardWrapper.loadServlet() 方法创建Servlet实例

InstanceManager instanceManager = ((StandardContext)getParent()).getInstanceManager();
try {
    servlet = (Servlet) instanceManager.newInstance(servletClass;
...

StandardWrapper.initServlet() 调用Servlet.init()

servlet.init(facade);