servlet3.0

Servlet3.0

Servlet 3.0的注解

Servlet3.0规范中允许Servlet、Filter与Listener三大组件时使用注解,而不用在web.xml进行注册了。Servlet3.0规范允许Web项目没有web.xml配置文件

Servlet注解

Servlet3.0规范中使用 @WebServlet() 注解来注册当前的Servlet类。该注解具有多个属性,常用属性的类型与意义如下表所示:

序号属性名属性类型属性说明
1urlPatternsString[]相当于< url-pattern>的值
2valueString[]与urlPatterns 意义相同,但此属性名可以省略。不能与urlPatterns 同时使用
3nameString相当于< servlet-name>的值
4loadOnStartupint相当于< loadOnStartup>的值,默认值为-1
5initParamsWebInitParam[]WebInitParam也是个注解,name相当于< param-name>,value相当于< param-value>

对应如下:

  <servlet>
    <servlet-name></servlet-name>
    <servlet-class></servlet-class>
    <init-param>
      <param-name></param-name>
      <param-value></param-value>
    </init-param>
    <load-on-startup></load-on-startup>
  </servlet>
  <servlet-mapping>
    <servlet-name></servlet-name>
    <url-pattern></url-pattern>
    <url-pattern></url-pattern>
    <url-pattern></url-pattern>
  </servlet-mapping>

简单验证,输入some,可以跳转:

@WebServlet("/some")
public class ServletAnotion extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        System.out.println("你好啊");
    }
}

完整版!

@WebServlet(urlPatterns = {"/some", "/xxx", "/jjj"},     //属性urlpattern与属性value功能,只能使用一个,不能同时使用
        name = "some_servlet",      //设置servletname
        initParams = {@WebInitParam(name = "key1", value = "value1"),
                @WebInitParam(name = "key2", value = "value2")},
        loadOnStartup = 2    //启动时创建当前servlet实例,默认为-1
)
public class ServletAnotion extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //获取当前servletname
        ServletConfig servletConfig = getServletConfig();
        String servletName = servletConfig.getServletName();
        System.out.println(servletName);

        //获取初始话参数
        Enumeration<String> initParameterNames = servletConfig.getInitParameterNames();
        while (initParameterNames.hasMoreElements()) {
            String name = initParameterNames.nextElement();
            String value = servletConfig.getInitParameter(name);
            System.out.println(name + " " + value);
        }
    }
}
Filter注解

Servlet3.0规范中使用 @WebFilter() 注解过滤器类。该注解具有多个属性,常用属性的类型与意义如下表所示:

序号属性名属性类型属性说明
1urlPatternsString[]相当于< url-pattern>的值
2valueString[]与urlPatterns 意义相同,但此属性名可以省略。不能与urlPatterns 同时使用
3servletNamesString[]相当于< servlet-name>的值
4filterNameString相当于< filter-name>的值
5dispatcherTypesDispatcherType[]DispatcherType是个枚举类,里面有FORWARD,INCLUDE,REQUEST,ASYNC,ERROR

对应如下:

  <filter>
    <filter-name></filter-name>
    <filter-class></filter-class>
  </filter>
  <filter-mapping>
    <filter-name></filter-name>
    <url-pattern></url-pattern>
    
    <servlet-name></servlet-name>
    <dispather></dispather>
  </filter-mapping>

java代码:

//@WebFilter("/*")
//@WebFilter(servletNames = "some_servlet")
@WebFilter(value = "/*", dispatcherTypes = DispatcherType.FORWARD)
public class FilterAnotion implements Filter {
    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        System.out.println("before");
        filterChain.doFilter(servletRequest, servletResponse);
        System.out.println("after");
    }

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {

    }

    @Override
    public void destroy() {

    }
}
Listener注解

Servlet3.0规范中使用 @WebListener() 注解监听器类

相当于:

  <listener>
    <listener-class></listener-class>
  </listener>

java代码:

@WebListener
public class ListenerAnotion implements ServletContextListener {
    @Override
    public void contextInitialized(ServletContextEvent servletContextEvent) {
        System.out.println("应用启动");
    }

    @Override
    public void contextDestroyed(ServletContextEvent servletContextEvent) {

    }
}

servlet3.0的web.xml

注册servlet
若对于servlet采用了两种方式同时进行注册,则需要注意:

  1. 若两种方式的url-pattern值相同,则应用无法启动
  2. 若两种方式的url-pattern值不同,那么相当于servlet具有两个url-pattern
  <servlet>
    <servlet-name></servlet-name>
    <servlet-class></servlet-class>
    <init-param>
      <param-name></param-name>
      <param-value></param-value>
    </init-param>
    <load-on-startup></load-on-startup>
  </servlet>
  <servlet-mapping>
    <servlet-name></servlet-name>
    <url-pattern></url-pattern>
    <url-pattern></url-pattern>
    <url-pattern></url-pattern>
  </servlet-mapping>

注册Filter
若对于Filter采用了两种方式同时进行注册,则需要注意:
无论url-pattern的值是否相同,其都是作为两个独立的Filter出现的

  <filter>
    <filter-name></filter-name>
    <filter-class></filter-class>
  </filter>
  <filter-mapping>
    <filter-name></filter-name>
    <url-pattern></url-pattern>
    
    <servlet-name></servlet-name>
    <dispather></dispather>
  </filter-mapping>

注册Listener
若对于Listener采用了两种方式同时进行注册,其仅仅相当于一个Listener

  <listener>
    <listener-class></listener-class>
  </listener>

特别注意
在这里插入图片描述
注意:
< web-app/>中的属性metadata-complete的值若为true,则表示对三大组件的注册方式,只有web.xml中的注册起作用,将忽略注解的注册;若为false,则表示两种注册方式同时起作用。其值默认为false

Servlet3.0的文件上传

以前的文件操作很麻烦,需要去导第三方的jar包来做,servlet3.0提供了文件的上传

html页面:
记得开头要<%@ page isELIgnored=“false” %>

<form action="${pageContext.request.contextPath}/uploadServlet" method="post" enctype="multipart/form-data">
    文件:<input type="file" name="photo"><br>
    <input type="submit" value="上传">
</form>

java代码:

@WebServlet("/uploadServlet")
@MultipartConfig    //表明当前Servlet可以处理Multipart请求
public class Servlet2ANotion extends HttpServlet {
    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //获取服务器保存上传文件的目录路径
        ServletConfig servletConfig = getServletConfig();
        String realPath = servletConfig.getServletContext().getRealPath("/image");
        //从请求中获取Multipart请求中的上传文件“部分”对象
        Part part = req.getPart("photo");
        //获取指定的头部属性
        String header = part.getHeader("Content-Disposition");
        //解析出原始文件名
        int index = header.lastIndexOf("=");
        String fileName = header.substring(index + 2, header.length() - 1);
        //完成文件上传
        part.write(realPath + "/" + fileName);
    }
}

Servlet3.0的异步处理

为什么要使用Servlet异步处理

在这里插入图片描述
如果不采用异步,要么响应太久,要么不显示子线程的内容

不采用的代码案例:
下面这段代码就是主线程执行完成后,不显示子线程的内容
加上那个Thread.sleep后,12秒后全都显示了

@WebServlet(urlPatterns = "/some")
public class SomeServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        resp.setContentType("text/html; charset=UTF-8");
        PrintWriter out = resp.getWriter();
        out.println("主线程开始运行");

        //启动一个耗时的运算过程(子线程)
        ComputeThread ct = new ComputeThread(out);
        Thread subThread = new Thread(ct);
        subThread.start();

//        try {
//            Thread.sleep(12000);
//        } catch (InterruptedException e) {
//            e.printStackTrace();
//        }

        out.println("主线程运行结束");
    }
}
public class ComputeThread implements Runnable {
    private PrintWriter out;

    public ComputeThread(PrintWriter out) {
        super();
        this.out = out;
    }

    @Override
    public void run() {
        out.println("子线程----运行");
        int sum = 0;
        try {
            for (int i = 0; i < 10; i++) {
                System.out.println("i =" + i);
                sum += i;
                Thread.sleep(1000);
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        out.println("sum  = " + sum);
        out.println("子线程----结束");
    }
}
Servlet异步代码的引入

在注解上加入asyncSupported=true表示当前servlet支持异步处理
通过异步上下文对象来达成目的

//asyncSupported=true表示当前servlet支持异步处理
@WebServlet(urlPatterns = "/some",asyncSupported = true)
public class SomeServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        resp.setContentType("text/html; charset=UTF-8");
        PrintWriter out = resp.getWriter();
        out.println("主线程开始运行 <br>");

        //获取异步上下文对象
        AsyncContext ac = req.startAsync();

        //启动一个耗时的运算过程(子线程)
        ComputeThread ct = new ComputeThread(ac);

        //开启异步上下文对象
        ac.start(ct);

        out.println("主线程运行结束 <br>");
    }
}
public class ComputeThread implements Runnable {
    private AsyncContext ac;  //异步上下文对象

    public ComputeThread(AsyncContext ac) {
        super();
        this.ac = ac;
    }

    @Override
    public void run() {
        try {
            PrintWriter out = ac.getResponse().getWriter();
            out.println("子线程----运行 <br>");
            int sum = 0;
            for (int i = 0; i < 10; i++) {
                System.out.println("i =" + i);
                sum += i;
                Thread.sleep(1000);
            }
            out.println("sum  = " + sum + " <br>");
            out.println("子线程----结束 <br>");

        } catch (IOException e) {
            e.printStackTrace();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

但是:还是要等待很久才能出现,那么如何解决呢?看下一段

Servlet基本异步处理的实现

对于上面的异步上下文对象ac的结束方式有三种:

  1. 在异步子线程中使用ac.complete()方法:该方法用户结束异步操作,并将与当前异步对象相关的request和response对象销毁
  2. 在异步子线程中使用ac.dispath()方法:该方法在结束异步操作的同时,会将参数所指定的页面内容包含到当前异步对象相关的标准输出流中。
    其执行效果相当于RequestDispather对象的include()方法的执行效果
  3. 在异步Servlet主线程中设置ac的超时时限,当超时时限到达时,异步对象及其相关的request和response对象销毁‘

在异步子线程中使用ac.complete()方法:
ac.complete

    @Override
    public void run() {
        try {
            PrintWriter out = ac.getResponse().getWriter();
            out.println("子线程----运行 <br>");
            int sum = 0;
            for (int i = 0; i < 10; i++) {
                System.out.println("i =" + i);
                sum += i;
                Thread.sleep(1000);
            }
            out.println("sum  = " + sum + " <br>");
            out.println("子线程----结束 <br>");

            //通知主线程异步子线程执行完毕
            ac.complete();
        } catch (IOException e) {
            e.printStackTrace();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

在异步子线程中使用ac.dispath()方法
ac.dispatch

    @Override
    public void run() {
        try {
            PrintWriter out = ac.getResponse().getWriter();
            out.println("子线程----运行 <br>");
            int sum = 0;
            for (int i = 0; i < 10; i++) {
                System.out.println("i =" + i);
                sum += i;
                Thread.sleep(1000);
            }
            out.println("sum  = " + sum + " <br>");
            out.println("子线程----结束 <br>");

            //通知主线程异步子线程执行完毕
            ac.dispatch("/Upload.jsp");
        } catch (IOException e) {
            e.printStackTrace();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

在异步Servlet主线程中设置ac的超时时限
ac.setTimeout

    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        resp.setContentType("text/html; charset=UTF-8");
        PrintWriter out = resp.getWriter();
        out.println("主线程开始运行 <br>");

        //获取异步上下文对象
        AsyncContext ac = req.startAsync();

        //启动一个耗时的运算过程(子线程)
        ComputeThread ct = new ComputeThread(ac);

        //设置异步上下文对象的超时时间
        ac.setTimeout(3000);

        //开启异步上下文对象
        ac.start(ct);

        out.println("主线程运行结束 <br>");
    }
Servlet异步处理的应用

下面的案例,子线程并没有跑完,但是主线程好了后,后台仍然再跑子线程,这时候我们跳转,如果子线程执行完成,将数据存入session中,jsp页面显示的就是子线程跑完的结果

主线程:

//asyncSupported=true表示当前servlet支持异步处理
@WebServlet(urlPatterns = "/some", asyncSupported = true)
public class SomeServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        resp.setContentType("text/html; charset=UTF-8");
        PrintWriter out = resp.getWriter();
        out.println("主线程开始运行 <br>");

        //获取异步上下文对象
        AsyncContext ac = req.startAsync();

        //启动一个耗时的运算过程(子线程)
        ComputeThread ct = new ComputeThread(ac);

        //设置异步上下文对象的超时时间
        ac.setTimeout(3000);

        //开启异步上下文对象
        ac.start(ct);

        out.println("主线程运行结束 <br>");
    }
}

子线程:

public class ComputeThread implements Runnable {
    private AsyncContext ac;  //异步上下文对象

    public ComputeThread(AsyncContext ac) {
        super();
        this.ac = ac;
    }

    @Override
    public void run() {
        try {
            HttpServletRequest request = (HttpServletRequest) ac.getRequest();
            HttpSession session = request.getSession();

            int sum = 0;
            for (int i = 0; i < 10; i++) {
                System.out.println("i =" + i);
                sum += i;
                Thread.sleep(1000);
            }
            session.setAttribute("sum", sum);

        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

jsp页面:

<%
    Integer sum = (Integer) session.getAttribute("sum");
    if (sum == 45) {
        out.print("注册成功");
    } else {
        out.print("注册失败");
    }
%>
Servlet异步监视器

异步监视器的使用,在主线程调用 ac.addListener(new AsyncListener())方法,然后在子线程添加上ac.complete()方法。

主线程:

    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        resp.setContentType("text/html; charset=UTF-8");
        PrintWriter out = resp.getWriter();
        out.println("主线程开始运行 <br>");

        //获取异步上下文对象
        AsyncContext ac = req.startAsync();

        //启动一个耗时的运算过程(子线程)
        ComputeThread ct = new ComputeThread(ac);

        //设置异步上下文对象的超时时间
        ac.setTimeout(3000);

        ac.addListener(new AsyncListener() {
            @Override
            public void onComplete(AsyncEvent asyncEvent) throws IOException {
                System.out.println("异步操作已经完成");
            }

            @Override
            public void onTimeout(AsyncEvent asyncEvent) throws IOException {

            }

            @Override
            public void onError(AsyncEvent asyncEvent) throws IOException {

            }

            @Override
            public void onStartAsync(AsyncEvent asyncEvent) throws IOException {

            }
        });

        //开启异步上下文对象
        ac.start(ct);

        out.println("主线程运行结束 <br>");
    }

子线程:

    public void run() {
        try {
            HttpServletRequest request = (HttpServletRequest) ac.getRequest();
            HttpSession session = request.getSession();

            int sum = 0;
            for (int i = 0; i < 10; i++) {
                System.out.println("i =" + i);
                sum += i;
                Thread.sleep(1000);
            }
            session.setAttribute("sum", sum);

            ac.complete();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
Servlet异步处理的典型应用

主线程:

//asyncSupported=true表示当前servlet支持异步处理
@WebServlet(urlPatterns = "/some", asyncSupported = true)
public class SomeServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        resp.setContentType("text/html; charset=UTF-8");
        PrintWriter out = resp.getWriter();
        //获取异步上下文对象
        AsyncContext ac = req.startAsync();

        //启动一个耗时的运算过程(子线程)
        ComputeThread ct = new ComputeThread(ac);

        //设置异步上下文对象的超时时间
        ac.setTimeout(3000);

        //开启异步上下文对象
        ac.start(ct);

        out.println("检查结果是否正确 <br>");
    }
}

子线程:

public class ComputeThread implements Runnable {
    private AsyncContext ac;  //异步上下文对象

    public ComputeThread(AsyncContext ac) {
        super();
        this.ac = ac;
    }

    @Override
    public void run() {
        try {
            HttpServletRequest request = (HttpServletRequest) ac.getRequest();
            HttpSession session = request.getSession();

            int sum = 0;
            for (int i = 0; i < 10; i++) {
                System.out.println("i =" + i);
                sum += i;
                Thread.sleep(1000);
            }
            String message = "注册失败";
            if (sum == 45) {
                message = "注册成功";
            }

            //将分析结果存放到Session域
            session.setAttribute("message", message);

        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

jsp页面:

${ message }

Servlet3.0的组件可插性

所谓组件可插性是指,项目支持将打为jar包的servlet、filter、listener直接插入到正在运行的web项目中。当然,这些jar包中同时包含有相应的配置文件

Servlet3.0的文件三大组件动态注册

Servlet3.0对于ServletContext进行了功能增强,可以对Servlet、Filter及Listener进行动态注册。所谓动态注册是指,web应用在运行过程中通过代码对Servlet、Filter或 Listener进行注册。

为了系统安全考虑,这个动态注册是有限制的:只能在应用启动时进行,而不能在应用运行过程中的进行注册。这个应用启动时间点,可以通过ServletContextListener监听器来把握。

动态注册Servlet

ServletContext的几个方法:

  • addServlet():动态注册Servlet
  • setInitParameter():为Servlet添加动态参数
  • addMapping():为Servlet指定url

使用监听器动态注册Servlet代码:

@WebListener
public class MyServletListener implements ServletContextListener {
    @Override
    public void contextInitialized(ServletContextEvent servletContextEvent) {
        //获取ServletContext
        ServletContext servletContext = servletContextEvent.getServletContext();

        //动态注册Servlet
        //下面两个变量的值一般从配置文件中读取来的
        String servletName="someservlet";
        String className="com.study.SomeServlet2";
        ServletRegistration.Dynamic srd = servletContext.addServlet(servletName, className);

        //为Servlet添加动态参数
        srd.setInitParameter("key1","value1");
        srd.setInitParameter("key2","value2");

        //为Servlet指定url
        srd.addMapping("/some1");
    }

    @Override
    public void contextDestroyed(ServletContextEvent servletContextEvent) {

    }
}

servlet页面:
可以发现能正常获取到各种值

public class SomeServlet2 extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        resp.setContentType("text/html; charset=UTF-8");
        PrintWriter out = resp.getWriter();
        out.println("SomeServlet2已经被动态注册");

        ServletConfig servletConfig = getServletConfig();
        Enumeration<String> names = servletConfig.getInitParameterNames();
        while (names.hasMoreElements()) {
            String name = names.nextElement();
            String value = servletConfig.getInitParameter(name);
            out.println(name + " = " + value);
        }
    }
}

动态注册Filter

ServletContext的几个方法:

  • addFilter():动态注册Filter
  • setInitParameter():为Filter添加动态参数
  • addMappingForUrlPatterns():为Filter设置过滤类型,过滤路径等

使用监听器动态注册Filter代码:

@WebListener
public class MyServletListener implements ServletContextListener {
    @Override
    public void contextInitialized(ServletContextEvent servletContextEvent) {
        //获取ServletContext
        ServletContext servletContext = servletContextEvent.getServletContext();

        //动态注册Filter
        //下面两个变量的值一般从配置文件中读取来的
        String FilterName="someFilter";
        String FilterclassName="com.study.Some";
        FilterRegistration.Dynamic frd = servletContext.addFilter(FilterName, FilterclassName);

        //为Filter添加动态参数
        frd.setInitParameter("key1","value1");
        frd.setInitParameter("key2","value2");

        //配置当前的Filter的映射信息
        frd.addMappingForUrlPatterns(EnumSet.of(DispatcherType.REQUEST),true,"/*");
    }

    @Override
    public void contextDestroyed(ServletContextEvent servletContextEvent) {

    }
}

Filter代码:

public class Some implements Filter {
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {

    }

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        System.out.println("动态注册Filter成功");
        filterChain.doFilter(servletRequest, servletResponse);
    }

    @Override
    public void destroy() {

    }
}

动态注册Listener

ServletContext的几个方法:

  • addListener():动态注册Listener

使用监听器动态注册Listener代码:

@WebListener
public class MyServletListener implements ServletContextListener {
    @Override
    public void contextInitialized(ServletContextEvent servletContextEvent) {
        //获取ServletContext
        ServletContext servletContext = servletContextEvent.getServletContext();

        //动态注册Listener
        //下面这个变量的值一般从配置文件中读取来的
        String ListenerclassName="com.study.SomeListener";
        servletContext.addListener(ListenerclassName);
    }

    @Override
    public void contextDestroyed(ServletContextEvent servletContextEvent) {

    }
}

Listener代码:

public class SomeListener implements ServletRequestListener {
    @Override
    public void requestDestroyed(ServletRequestEvent servletRequestEvent) {

    }

    @Override
    public void requestInitialized(ServletRequestEvent servletRequestEvent) {
        System.out.println("注册成功");
    }
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值