Tomcat和Servlet简单入门

Tomcat

tomcat是一个web服务器软件,说到web服务器软件,肯定免不了要提到Apache/Nginx,那tomcat和它们之间的区别是什么呢?

这里有必要先科普一个概念:

  • 静态资源:白话点就是用html、css、javascript写的那些东西,所有用户看到的代码都是一样的。
  • 动态资源:用servlet/jsp、php写的,动态的内容;动态内容需要先转换成静态才可以使用。

既然都是服务器软件,那它们必然都是能够绑定ip地址、监听端口并处理来自浏览器的HTTP请求。tomcat不仅能够做到这些,它还能够支持servlet和jsp,即所谓的动态内容。它能够根据你所做的配置,当用户访问指定url的时候,它会去加载特定的java类,在特定的类中有代码来处理页面内容。

Apache Tomcat and Nginx server, were created for two different tasks. NGINX is a free, open-source, high-performance HTTP server and reverse proxy, as well as an IMAP/POP3 proxy server and Apache Tomcat is an open source implementation of the Java Servlet, JavaServer Pages, Java Expression Language and Java WebSocket technologies. The Java Servlet, JavaServer Pages.

安装使用

  • 安装:直接去官网下载核心包,解压即可使用。卸载直接删除这个安装包即可。
  • 部署:直接将项目放到webapps目录下即可,如果是war还会自动解压

路径问题

因为这个问题困扰我很久,所以这里先提出来。我一般放置配置文件会放到三个地方:

  • 比如数据库配置文件,我会直接放到src目录下面,而src下的目录,会在编译之后放到WEB-INF/classes目录下。当然也可以通过classLoader来获取。
  • 直接放到web目录下,那可以直接通过/filename来找到。
  • 如果是放到web目录下的WEB-INF下,那么可以通过/WEB-INF/filename来找到

Servlet

简单来说它就是一个运行在服务器上的Java程序。

执行原理

当服务器接收到请求之后,它会解析url请求,然后通过web.xml文件,查询里面是否有<url-pattern>标签,如果有就会通过<servlet-class>这个标签找到全限定类名(到这里可以发现,其实我们只需要通过url能够让其找到对应的类即可,所以可以直接使用注解来简化操作),并且让tomcat将其加载进内存并创建对象。同时Tomcat也会根据获得的请求来创建request对象,封装好并将其传递给servlet的service方法。

生命周期

  1. 被创建:执行init方法,只执行一次。默认情况下是第一次访问的时候被创建,当然你可以配置<load-on-startup>标签的值为正整数让其在服务器启动的时候就创建。值得注意的是servlet使用的是单例模式,所以多线程访问会出现线程安全问题,所以尽量不要再servlet中定义成员变量。
  2. 提供服务:每次访问对应的url的时候,service方法都会执行一次。
  3. 被销毁:服务器正常关闭前,会执行这个方法,也是只执行一次。如果异常关闭就不会执行这个方法。

体系结构

servlet本身是一个接口,有一个抽象类叫GenericServlet继承了它,同时又有一个HttpServlet抽象类继承了generic。即这三者是爷爷-爸爸-儿子关系。

  • 由于servlet有5个方法要实现,而实际中我们一般会写service方法,所以GenericServlet这个抽象类继承了接口,并在其中实现了另外四个方法(空实现)。
  • 实际中我们一般使用的是HttpServlet,因为它能够判断用户的请求,并且根据不同的请求使用不同的方法。我们不在需要重写service()方法,而是需要去处理doGet等方法。

Request

这里将request请求分成了四部分:

  1. 请求行:GET /index.html HTTP/1.1
  2. 请求头:一些键值对组成的值
  3. 空行
  4. 请求体:username=aaa&password=bbb

Java中当然对这个提供了封装,具体是Tomcat会对收到的请求信息进行封装,然后传递给servlet中的service方法。下面是一些具体的方法:

  • String getMethod()
  • String getContextPath()
  • String getServletPath()
  • String getQueryString()
  • String getRequestURI()
  • StringBuffer getRequestURL()
  • String getProtocol()
  • String getRemoteIP()
  • String getHeader(String name)

这些方法一目了然,直接就可以获取到相应的信息。请求体中可以通过流来获取,分别用getReader和getInputStream来获取字符流和字节流。

为了获取参数值的方便,还提供了一些通用的方法来获取数据:

  • getParameter(String name):根据参数名字来获取对应的值。
  • getParameterMap():获取所有参数的map集合。

请求转发

假设你请求的url所对应的servlet无法完成任务,需要请求别的servlet来帮助一起完成,可以直接使用getRequestDispatcher.forward()方法来完成,如果需要携带数据,可以使用setAttribute(String name,Object obj)方法来传入,接收方可以用对应方法取出来。这里注意的是,由于是服务器内部处理的,所以客户端是完全感受不到的。

Response

同样也是分成了四个部分:

  1. 响应行:HTTP/1.1 200 OK,所以我们只需要设置一下状态码即可。setStatus(int sc)即可。
  2. 响应头:setHeader(String name,String value)
  3. 响应空行
  4. 响应体(大部分都是html的代码):也是获取流并进行操作。getWrite或者getOutputStream就能获取输出流,然后使用对应的方法即可。

重定向还有一个更简单的方法,setRedirect(String path)直接可以完成重定向任务。

还有值得注意的是,为了以后防止乱码,可以在获取流之前,加上一句response.setContentType("text/html,charset=utf-8");

ServletContext

这个是代表了整个web应用,可以用来和容器(即tomcat服务器)进行通信。比如能够获取MIME类型、共享数据、获取文件的真实路径。

  • 获取:this.getServletContext或者request.getServletContext都可以。因为这个对象代表的是整个web应用,所以仅有一个,而且获取的都是相同的。
  • 获取MIME类型:首先解释下,文件的对应类型恰好和服务器里的一个配置文件所对应,所以可以用方法调用直接获取。其实就是一个文件后缀对应一个MIME类型。
  • 数据共享:由于代表的是整个web应用,所以所有用户之间共享。
  • 获取文件真实路径:getRealPath()

会话

为了能够在一次会话范围内,即多次的请求之间来共享数据。

  • 客户端会话技术——cookie
  • 服务器端会话技术——session

两者的区别:

  • session没有大小限制,由于存在服务器端,比较安全。

这个技术相当于已经非常熟悉了,具体步骤比较简单:

  1. 创建cookie:new Cookie(String name,String value)
  2. 将cookie加入到response中:response.addCookie(Cookie c)
  3. 接下来的只需要每次获取request的时候获取下cookie即可。request.getCookies()

一些小tips:

  • 可以使用多个cookie吗?当然可以,创建多个,然后都加入进去即可。
  • cookie能够保存多长时间呢?默认的情况下浏览器关闭,cookie就没了;当然可以由服务器传递一个字段给客户端,让它保存得久一点:setMaxAge(int seconds);正数就是保存的秒数;负数就是默认值;如果是0的话就是删除cookie。
  • cookie在默认情况下,同一服务器下的项目之间是隔离的。因为默认情况下,cookie设置的是当前的虚拟目录,所以只需要cookie.setPath("/");,这样同一个tomcat服务器中不同的项目就可以共享了。
  • cookie可以通过设置域名,在多个服务器之间共享。例如:cookie.setDomain(".baidu.com");

session

和cookie对应,只不过它的数据是存放在服务器端,对应的对象是HttpSession

  1. 创建session:request.getSession
  2. 使用:session.setAttribute(String name, Object obj);

我们都知道,HTTP是无状态的,那么服务器是怎么知道来的请求是哪一个的呢?只有知道了来自同一个用户,才可以为其分配相同的session。其实session是依赖于cookie的。在第一次获取session的时候,那么在内存中就会新建一个session对象,然后有一个对应的sessionID对应它,然后就会在响应的时候把这个数据发还给客户端,形如这样:set-cookie:JSESSIONID=sessionID,之后客户端就能凭借这个cookie来找到这个session了。

小tips:

  • 客户端关闭,服务器不关闭,那么获取的session是否是同一个呢?默认情况下不是。但是我们可以通过设置cookie的存活时间,来让session也能活很久。
  • 客户端不关闭,服务器关闭,获取的session还是同一个吗?当然不是!!服务器都关了,内存里的对象当然也全部都没了。但是这样会引发一个问题,假设我的session时间是一个月,那我过几天再访问同一个服务器会发现自己的购物车里的内容没了。当然我们也有办法解决这个办法:session对象序列化到硬盘上就行了。之后反序列化就行了,分别叫session的钝化和活化。而且这是tomcat是自动完成的。
  • Session默认的生效时间是30分钟(在web.xml中可以修改),当然也可以调用对象的销毁方法
  • 和cookie不同,因为放在服务器上,所以理论上可以任意大。

过滤器

每次浏览器去访问服务器的时候,都会通过一个过滤器,一般用来做通用的处理,比如做编码处理等。

和servlet一样,也是一个接口,所以定义一个类来实现即可;同样的,也可以通过注解或者在web.xml中书写代码来完成配置。

执行原理

具体的流程是这样的:浏览器发送一个请求,如果这个请求匹配到了过滤器,那么就会先来到过滤器里,然后过滤器中会有放行的代码,之后就会到servlet去处理,处理完了之后还会再次通过过滤器,接着执行当时放行后的代码。所以前半部分关注的是request,后半部分关注的是response。

生命周期

一共有三个方法,分别是

  • init,服务器启动的时候就会执行,所以一般在这里加载资源。
  • destroy,服务器正常关闭的时候会执行,在这里释放资源。
  • doFilter,这里注意的是,需要在doFilter里面放行,否则就后面收不到了。

详细配置

路径拦截:

  • 直接配置特定资源,如/index.jsp
  • 配置指定目录,如/dir,这样这个目录下所有的servlet都会被拦截
  • 后缀名拦截:*.jsp

资源访问方式拦截,通过设置dispatcherType=资源类型,可以有下面几个:

  • REQUEST:默认值,即浏览器访问服务器资源
  • FORWARD:转发访问资源。
  • INCLUDE:包含访问资源
  • ERROR:错误跳转
  • ASYNC:异步访问资源

过滤器链

执行顺序:先过过滤器1,然后过过滤器2,回来的时候先过过滤器2,在过过滤器1。那么问题来了,怎么配置呢?

还是可以注解配置和web.xml配置,注解配置的直接对比过滤器的类名的字符串比较…(⊙﹏⊙),也就是aFilter会比bFilter先执行。而在web.xml中,看谁先定义就先拦截。