servlet3.x 续集,进一步了解

xiaoxiao2021-02-28  29

servlet3.x 续集,进一步了解

​ 今天对servlet3.0做一点点的补充,主要是针对注解式 和原有的配置式的一个对比,让从以前的配置式同学更好的到现在的注解式。

servlet3.x 续集,进一步了解 1 异步请求支持 1.1 如何实现1.2 一起看代码 2 当web.xml 与配置同时存在3 文件上传

1 异步请求支持

​ 在将异步请求之前,让我们先了解一下,为什么要支持异步,及以前servlet 工作的过程

正常的servlet请求流程

在这种阻塞式的请求下,当请求数据过多,并且后服务处理逻辑复杂,需要的相应时间比较就,比如大数据量的导出, 导致Tomcat 假死的情况,无法处理请求。

异步请求

从对比中可以发现,异步时,servlet主线程在接收请求之后,创建子线程,然后主线程就结束了,子线程的处理过程就与主线程无关,这是,servlet就可以在很短的时间内接收一个新的请求。当然,我们这里只是将servlet的异步处理,在特别大的并发访问下, 后台服务对请求的接收也是有限的,我们不讨论。

1.1 如何实现

​ 下面让我们看看 如何实现servlet 的异步请求。

​ AsyncContext 对象, servlet 提供的异步容器对象,当需要此对象时,必须告知容器此Servlet支持异步处理,

​ 如果使用@WebServlet来标注,则可以设置其asyncSupported为true

​ 如果使用web.xml设置Servlet,则可以在中设置标签为true:

​ AsyncContext 是一个接口,他可以通过request 的 startAsync

​ AsyncContext 在调用了startAsync()方法取得AsyncContext对象之后,此次请求的响应会被延后,并释放容器分配的线程

​ AsyncContext 是一个容器,可以getRequest()、getResponse()方法取得请求、响应对象 。

​ AsyncContext 还提供了complete()或dispatch()方法,对子线程进行终止 或者跳转到指定路径,同时释放所有的 资源信息 request 、response

​ AsyncContext 还提供了超时设置setTimeOut(), 当超时时间到达时,会自动停止异步子线程,返回请求,同时释放所有的 资源信息 request 、response。

所以AsyncContext 是异步的关键,既可以在主线程启动子线程,同时设置超时时间,又可以在子线程中,对请求对象进行共享传递,还可以对子线程进行终止,返回response。

//无参方法,直接利用原有的请求与响应对象来创建AsyncContext public AsyncContext startAsync() throws IllegalStateException { return request.startAsync(); } //可以传入自行创建的请求、响应封装对象 public AsyncContext startAsync(ServletRequest servletRequest, ServletResponse servletResponse) throws IllegalStateException { return request.startAsync(servletRequest, servletResponse); }

1.2 一起看代码

主线程

@WebServlet(name = "asyncServlet", urlPatterns = "/asyncServlet", asyncSupported = true) public class MyAsyncServlet 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.write("this is a asyncServelt <br>"); System.out.println(Thread.currentThread().getName()+ "主线程开始...."); out.write( "主线程开始运行.....<br>"); //经过测试 发现req.getAsyncContext() 一直是null, 可能需要进行特殊设置 // req.getAsyncContext().start(new MyAsyncThread(out)); AsyncContext ac = req.startAsync(); MyAsyncThread th = new MyAsyncThread(ac); ac.start(th); System.out.println(Thread.currentThread().getName()+ "主线程结束...."); out.write("主线程运行结束.....<br>"); } }

子线程

public class MyAsyncThread implements Runnable { private AsyncContext ac; //将AsyncContext 对象通过构造器传入子线程,便可以恭喜请求内容 public MyAsyncThread(AsyncContext ac) { this.ac = ac; } @Override public void run() { int sum = 0; try { PrintWriter out = ac.getResponse().getWriter(); out.print("子线程开始运行.....<br>"); out.print("sum = " + sum); for (int i = 0; i < 10; i++) { System.out.println("i = " + i ); Thread.sleep(1000); sum += i; } out.print("sum = " + sum + "<br>"); out.print("子线程运行结束.....<br>"); // 结束子线程,进行数据返回 //ac.complete(); //结束子线程同时,将数据返回至指定的跳转页面,相当与 RequestDispathcher的include() ac.dispatch("/index.jsp"); } catch (InterruptedException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } } }

==总结:==

​ 对上面的整个测试结果来看,我们主要使用异步的目的是为了尽快释放主线程,释放servlet资源,以增加访问能力。

​ 那在代码中,==我们是否能够或者说需要调用多个ac.start(th); 方法,启动多个线程呢?==我认为是不需要的,因为servlet异步是为了释放资源,而不是使用多个线程去完成业务规则。而且同一个request的 AsyncContext是相同的,只要调用complete方法,资源都会被释放,所以多个start 多个线程没有意义。

​ 所以我觉得,当时设计AsyncContext 初衷就是为了封装子线程资源,达到资源共享,资源销毁的目的,然后将请求做分离。 有点类似FutureTask 封装Callable实现,获取返回值的目的一样。

​ 在测试中,我们可以知道,如果子线程中没有释放response资源,用户页面就一直在等待,所以,如果有场景需要及时返还,在使用AsyncContext 时,就不要使用response ,而是让资源在主线程中直接释放掉。

2 当web.xml 与配置同时存在

​ 在servlet3.0 中,编程式方式可以取代之前的配置式,但是在3.0中,其实文件与注解可以同时存在,我们来比较下如果两个同时存在会有哪些问题

​ web.xml

<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_3_1.xsd" version="3.1"> <!-- version 需要是3.x以上, 描述符需要是web-app_3_x.xsd 以上版本 --> </web-app>

Servlet 共存

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_3_1.xsd" version="3.1"> <!--注册servlet--> <!--servlet 名称 MySerlvet--> <!--url /MySerlvet--> <servlet> <servlet-name>MySerlvet</servlet-name> <servlet-class>com.Henry.servlet.MySerlvet</servlet-class> </servlet> <servlet-mapping> <servlet-name>MySerlvet</servlet-name> <url-pattern>/MySerlvet</url-pattern> </servlet-mapping> </web-app>

代码注册

//相同servlet名称 MySerlvet //Url /MySerlvet //写法1 @WebServlet("/MySerlvet") //写法2 @WebServlet(name = "MySerlvet" ,urlPatterns = "/MySerlvet") public class MySerlvet extends HttpServlet { @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { PrintWriter out = resp.getWriter(); out.write(" this is a servlet "); } }

==结论==

​ 当@WebServlet(“/MySerlvet”)使用这种写法的时候, 如果web.xml 中,存在一个相同的 url-pattern 却不指定servlet-name时,系统启动时报错

​ 当@WebServlet(name = “MySerlvet” ,urlPatterns = “/MySerlvet”)使用 这种写法时,制定了servlet-name时, 如果web.xml中存在相同的servlet名称,则以web.xml为主

​ 原因其实是因为,两个servlet 不同的servlet,虽然路径可以包含,但url-pattern 是不允许使用一样的,

当@WebServlet(“/MySerlvet”) 其实是没有指定名称,系统会默认指定一个名称,此时就出现了两个servlet名称不同但是 url-pattern却相同的情况,所以报错

Filter共存

Web.xml

<filter> <filter-name>myFilter</filter-name> <filter-class>com.Henry.filter.MyFilter</filter-class> </filter> <filter-mapping> <filter-name>myFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping>

代码

@WebFilter(filterName = "myFilter",urlPatterns = "/*") public class MyFilter 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("========after============"); filterChain.doFilter(servletRequest,servletResponse); System.out.println("========befor============="); } @Override public void destroy() { } }

==结论==

​ 其实与servlet是相同的, 当 @WebFilter(filterName = “myFilter”,urlPatterns = “/*”) 指定了名称,如果web.xml中存在相同名称的filter, 则以web.xml为主。 如果是@WebFilter(“/”) 这种,那两个filter是可以同时存在的。因为Filter 本么有URL的冲突

Listener 共存

这个没什么好进行测试的, 直接可以获取到结论, @WebListener 只有一个属性,所以如果当@WebListener 和web.xml 存在相同的Listener 配置,以web.xml内容生效

3 文件上传

在servlet3.0中,request增加了对文件上传处理MulitPart请求的支持。

在定义的servlet中增加@MultipartConfig 注解,同时form的enctype=”multipart/form-data” method=”post”

<html> <head> <title>$Title$</title> </head> <body> <form action="${pageContext.request.contextPath}/uploadFile" method="post" enctype="multipart/form-data"> 文件<input type="file" name="text"/> <input type="submit" value="上传"/> </form> </body> </html> @WebServlet("/uploadFile") @MultipartConfig //表示支持文件上传 处理MulitPart请求 public class fileServlet extends HttpServlet{ @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { String path = this.getServletContext().getRealPath("/text"); Part part = req.getPart("text"); part.write(path + "/xxx.txt"); } }

这样就可以文件上传了,通过获取request.getPart 或者getParts方法。

唯一不好的地方就是无法获取到对应的文件实际名称,需要更加hearder 去截取文件名称,不算方便

转载请注明原文地址: https://www.6miu.com/read-2450288.html

最新回复(0)