Servlet入门实践

最后更新于:2022-04-01 14:17:17

> 本文主要介绍servlet,包括入门到升入,基本上可以对servlet有一个很好的认识; ### 1servlet介绍:   Servlet(Server Applet),全称Java Servlet,未有中文译文。是用Java编写的服务器端程序。其主要功能在于交互式地浏览和修改数据,生成动态Web内容。狭义的Servlet是指Java语言实现的一个接口,广义的Servlet是指任何实现了这个Servlet接口的类,一般情况下,人们将Servlet理解为后者。 **手把手教你建立一个继承servlet的类:** 1. 在webcontent点击右键选择other,选择servlet,这里就建成了servlet的类 2. 重写doGet和doPost方法 ~~~ public class TestServlet extends HttpServlet { private static final long serialVersionUID = 1L; protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { response.setCharacterEncoding("utf-8"); PrintWriter out = response.getWriter(); out.println("hello peace"); } protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { doGet(request, response); } } ~~~ 1. 修改web.xml或者加上webServlet注解: 修改web.xml:在根标签下加入一下代码 ~~~ <!-- 配置一个servlet --> <!-- servlet的配置 --> <servlet> <!-- servlet的内部名称,自定义。尽量有意义 --> <servlet-name>TestServlet</servlet-name> <!-- servlet的类全名: 包名+简单类名 --> <servlet-class>com.rlovep.servlet.TestServlet</servlet-class> <!-- servlet的映射配置 --> </servlet> <servlet-mapping> <!-- servlet的内部名称,一定要和上面的内部名称保持一致!! --> <servlet-name>TestServlet</servlet-name> <!-- servlet的映射路径(访问servlet的名称) --> <url-pattern>/TestServlet</url-pattern> </servlet-mapping> ~~~ **或者在类定义上加上webServlet的注解(注意两者只能有一个)** 注意:1.不要在web.xml根标签中指定metadata-complete=”true”.2。不要在web.xml中配置servlet **修改后的servlet类:** ~~~ @WebServlet("/TestServlet")//设置servlet的访问路径为/TestServlet;或者这样写@WebServlet(name="TestServlet",urlPatterns={"/TestServlet"}) public class TestServlet extends HttpServlet { private static final long serialVersionUID = 1L; protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { response.setCharacterEncoding("utf-8"); PrintWriter out = response.getWriter(); out.println("hello peace"); } protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { doGet(request, response); } } ~~~ 1. 访问[http://localhost:8080/HttpSer/TestServlet](http://localhost:8080/HttpSer/TestServlet)即可看到结果。 ### 2servlet的路径映射: ~~~ <url-pattern>/TestServlet</url-pattern>或者:@WebServlet(urlPatterns={"/TestServlet"}) ~~~ <table border="1"><tbody><tr><td>匹配方式</td> <td>url-pattern</td> <td>地址栏</td> </tr><tr><td>精确匹配</td> <td>/TestServlet<br/>/TestServlet/test</td> <td>http://localhost:8080/HttpSer/TestServlet<br/>http://localhost:8080/HttpSer/TestServlet/test</td> </tr><tr><td>模糊匹配</td> <td>/*<br/>/TestServlet/*<br/>/*.do</td> <td>http://localhost:8080/HttpSer/任意路径<br/>http://localhost:8080/HttpSer/TestServlet/任意路径<br/>http://localhost:8080/HttpSer/任意路径.do</td> </tr></tbody></table> **注意** 1)url-pattern要么以 / 开头,要么以*开头。 例如, TestServlet是非法路径。 2)不能同时使用两种模糊匹配,例如 /TestServlet/*.do是非法路径 3)当有输入的URL有多个servlet同时被匹配的情况下:   3.1 精确匹配优先。(长的最像优先被匹配)   3.2 以后缀名结尾的模糊url-pattern优先级最低!!! ### 3servlet生命周期: 1. 生命周期的引入: Servlet的生命周期: servlet类对象什么时候创建,什么时候调用什么方法,什么时候销毁。 以前的对象: new Student(); stu.study(); stu=null; Servlet程序的生命周期由tomcat服务器控制的!!!! 1. Servlet重要的四个生命周期方法: 构造方法: 创建servlet对象的时候调用。默认情况下,第一次访问servlet的时候创建servlet对象 只调用1次。证明servlet对象在tomcat是单实例的。 init方法: 创建完servlet对象的时候调用。只调用1次。 service方法: 每次发出请求时调用。调用n次。 destroy方法: 销毁servlet对象的时候调用。停止服务器或者重新部署web应用时销毁servlet对象。只调用1次。 1. 代码演示: ~~~ @WebServlet(name="TestServlet",urlPatterns={"/TestServlet"}) public class TestServlet extends HttpServlet { private static final long serialVersionUID = 1L; int i=0; /** * 1.构造方法,只被调用一次 */ public TestServlet() { super(); System.out.println("构造方法>>>>"); } /** * 2.init初始化方法,在构造方法后只被调用一次 * 有参数的init方法会调用init方法;;一般覆盖无参数的init方法; */ @Override public void init() throws ServletException { // TODO Auto-generated method stub super.init(); System.out.println("inti>>>>"); } /** * 3.service方法,发生请求和响应时调用的方法,次数不限 * 一般是重写doget和dopost,此去只是方便演示 */ @Override protected void service(HttpServletRequest arg0, HttpServletResponse arg1) throws ServletException, IOException { System.out.println("service>>>" +i++); } /** * 4.destory方法,只有在停止服务器和重新部署web应用时调用 */ @Override public void destroy() { System.out.println("destory"); super.destroy(); } } #输出结果:http://localhost:8080/HttpSer/TestServlet。连续访问四次,得到下面的结果: 构造方法>>>> inti>>>> service>>>0 service>>>1 service>>>2 service>>>3 Oct 11, 2015 8:53:52 PM org.apache.catalina.core.StandardContext reload INFO: Reloading Context with name [/HttpSer] has started destory ~~~ 1. 伪代码演示: ~~~ Tomtcat内部代码运行: 1)通过映射找到到servlet-class的内容,字符串: com.rlovep.serlvet.TestServlet 2)通过反射构造TestServlett对象 2.1 得到字节码对象 Class clazz = class.forName(" com.rlovep.serlvet.TestServlet"); 2.2 调用无参数的构造方法来构造对象 Object obj = clazz.newInstance(); ---1.servlet的构造方法被调用 3)创建ServletConfig对象,通过反射调用init方法 3.1 得到方法对象 Method m = clazz.getDeclareMethod("init",ServletConfig.class); 3.2 调用方法 m.invoke(obj,config); --2.servlet的init方法被调用 4)创建request,response对象,通过反射调用service方法 4.1 得到方法对象 Methodm m =clazz.getDeclareMethod("service",HttpServletRequest.class,HttpServletResponse.class); 4.2 调用方法 m.invoke(obj,request,response); --3.servlet的service方法被调用 5)当tomcat服务器停止或web应用重新部署,通过反射调用destroy方法 5.1 得到方法对象 Method m = clazz.getDeclareMethod("destroy",null); 5.2 调用方法 m.invoke(obj,null); --4.servlet的destroy方法被调用 ~~~ 1. 图像演示如下: ![1](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-04-13_570e001204b1a.png "") ### 4自动加载servlet: 默认情况下,第一次访问servlet的时候创建servlet对象。如果servlet的构造方法或init方法中执行了比较多的逻辑代码,那么导致用户第一次访问sevrlet的时候比较慢。 改变servlet创建对象的时机: 提前到加载web应用的时候!!! 在servlet的配置信息中,加上一个或者加上注解(loadOnStartup=1)即可: ~~~ <servlet> <!-- servlet的内部名称,自定义。尽量有意义 --> <servlet-name>TestServlet</servlet-name> <!-- servlet的类全名: 包名+简单类名 --> <servlet-class>com.rlovep.serlvet.TestServlet</servlet-class> <load-on-startup>1</load-on-startup> </servlet> 或者: @WebServlet(name="TestServlet",loadOnStartup=1,urlPatterns={"/TestServlet"}) ~~~ ### 5多用户问题: 注意: servlet对象在tomcat服务器是单实例多线程的。可以有多个用户; 因为servlet是多线程的,所以当多个线程同时访问了servlet的共享数据,如成员变量,可能会引发线程安全问题。 解决办法: 1)把使用到共享数据的代码块进行同步(使用synchronized关键字进行同步) 2)建议在servlet类中尽量不要使用成员变量。如果确实要使用成员,必须同步。而且尽量缩小同步代码块的范围。(哪里使用到了成员变量,就同步哪里!!),以避免因为同步而导致并发效率降低。 ### 6Servlet常用对象: servlet:有几个比较有用的对象: ~~~ 1.HttpServletRequest 请求对象 http协议中已讲 2.HttpServletResponse 响应对象 http协议中已讲 3.ServletConfig servlet配置对象 4.ServletContext Servlet的上下文对象。对整个web应用有效 5.HttpSession 会话对象,当一个用户向服务器发送第一个请求时,服务器为其建立一个session,并为此session创建一个标识号; ~~~ ### ServletConfig对象介绍: ServletConfig对象: 主要是用于加载servlet的初始化参数。在一个web应用可以存在多个ServletConfig对象(一个Servlet对应一个ServletConfig对象) 可以直接从getServletConfig方法;或者自己在有参的init中获得 . 在web.xml中创建参数:一样有两种方法: ~~~ <servlet> <!-- servlet的内部名称,自定义。尽量有意义 --> <servlet-name>TestConfig</servlet-name> <!-- servlet的类全名: 包名+简单类名 --> <servlet-class>com.rlovep.serlvet.TestConfig</servlet-class> <init-param> <param-name>name</param-name> <param-value>peace</param-value> </init-param> </servlet> 或者: @WebServlet(urlPatterns={"/TestConfig"}, initParams={@WebInitParam(name="driver",value="com.mysql*") ,@WebInitParam(name="url",value="jdbc*"), @WebInitParam(name="user",value="root"), @WebInitParam(name="pass",value="123456")}) ~~~ 测试如下: ~~~ protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { ServletConfig servletConfig = getServletConfig(); //得到servlet的名字 System.out.println(servletConfig.getServletName()); //根据参数名获取参数值 String name=servletConfig.getInitParameter("user"); System.out.println(name+">>>>>"); //获取所有参数名称 Enumeration<String> names = servletConfig.getInitParameterNames(); while(names.hasMoreElements()){ String s=names.nextElement(); System.out.println(s+"="+servletConfig.getInitParameter(s)); } } ~~~ ### ServletContext对象介绍: **1. 引入** ServletContext对象 ,叫做Servlet的上下文对象。表示一个当前的web应用环境。一个web应用中只有一 个ServletContext对象。 **2. 对象引用的得到** 创建时机:加载web应用时创建ServletContext对象。 得到对象: 从ServletConfig对象的getServletContext方法得到或者直接调用ServletContext servletContext = getServletContext(); **3. 核心api** ~~~ java.lang.String getContextPath() --得到当前web应用的路径 java.lang.String getInitParameter(java.lang.String name) --得到web应用的初始化参数 java.util.Enumeration getInitParameterNames() void setAttribute(java.lang.String name, java.lang.Object object) --域对象有关的方法 java.lang.Object getAttribute(java.lang.String name) void removeAttribute(java.lang.String name) RequestDispatcher getRequestDispatcher(java.lang.String path) --转发(类似于重定向) java.lang.String getRealPath(java.lang.String path) --得到web应用的资源文件 java.io.InputStream getResourceAsStream(java.lang.String path) ~~~ **4. 演示如下** ~~~ ServletContext servletContext = getServletContext(); //得到当前web应用路径 System.out.println("路径:"+servletContext.getContextPath()); //根据参数名获得参数值 System.out.println("AAA="+servletContext.getInitParameter("AAA")); //获取所有参数名称 Enumeration<String> names = servletContext.getInitParameterNames(); while(names.hasMoreElements()){ String s=names.nextElement(); System.out.println(s+":"+servletContext.getInitParameter(s)); } //设置域对象,整个web应用有效 servletContext.setAttribute("name","peace"); servletContext.setAttribute("age", "23"); //获得域对象 System.out.println("name"+servletContext.getAttribute("name")); System.out.println("age"+servletContext.getAttribute("age")); //删除域对象 servletContext.removeAttribute("age"); System.out.println("age"+servletContext.getAttribute("age")); ~~~ ### 7.转发和重定向; ### 域对象介绍: 域对象:作用是用于保存数据,获取数据。可以在不同的动态资源(servlet)之间共享数据。 案例: ~~~ #通过重定向,使用实体内容传递数据,一般只能存储字符 Servlet1 传数据:name=eric response.sendRedirect("/Servlet2?name=eric") Servlet2接收: String request.getParameter("name"); #通过域对象传递:可以传递任何数据; ServletContext就是一个域对象,上面有介绍怎么用 保存数据:void setAttribute(java.lang.String name, java.lang.Object object) 获取数据: java.lang.Object getAttribute(java.lang.String name) 删除数据: void removeAttribute(java.lang.String name) ServletContext域对象:作用范围在整个web应用中有效!!! 所有域对象: HttpServletRequet 域对象 测试; ServletContext域对象 HttpSession 域对象 PageContext域对象 ~~~ ### 重定向: **重定向是服务器告诉浏览器,重新请求另一页面,请求信息丢失更新** a)地址栏会改变,变成重定向到地址。 b)重定向可以跳转到当前web应用,或其他web应用,甚至是外部域名网站。 c)不能在重定向的过程,把数据保存到request中。 测试如下: ~~~ 1.建立一个TestRedect servlet: protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { /** * 保存数据到request域对象 */ request.setAttribute("name", "rose"); //重定向 /** * 注意:可以跳转到web应用内,或其他web应用,甚至其他外部域名。 */ //request域数据会丢失 response.sendRedirect("/HttpSer/GetData"); //重定向到外部域名: //response.sendRedirect("www.baidu.com"); } 2.建立一个GetData的servlet protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { PrintWriter out = response.getWriter(); out.println("hello"); System.out.println("获得域对象"+request.getAttribute("name")); } ~~~ **现象如下:** 1.地址栏从[http://localhost:8080/HttpSer/TestRedect](http://localhost:8080/HttpSer/TestRedect)变为[http://localhost:8080/HttpSer/GetData](http://localhost:8080/HttpSer/GetData); 2.request域对象数据丢失:获得域对象null ### 转发: **转发是服务器将请求信号封装后转发到另一个servlet页面,请求信息会保存** a)地址栏不会改变 b)转发只能转发到当前web应用内的资源 c)可以在转发过程中,可以把数据保存到request域对象中 测试如下: ~~~ 1.建立一个TestRedect servlet: protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { /** * 保存数据到request域对象 */ request.setAttribute("name", "rose"); //转发 /** * 注意:不能转发当前web应用以外的资源。 */ /*RequestDispatcher rd = this.getServletContext().getRequestDispatcher("/GetDataServlet"); rd.forward(request, response);*/ this.getServletContext(). getRequestDispatcher("/GetData").forward(request, response); } 2.建立一个GetData的servlet protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { PrintWriter out = response.getWriter(); out.println("hello"); System.out.println("获得域对象"+request.getAttribute("name")); } ~~~ **现象如下:** 1.地址栏没有变化 2.request域对象数据没有丢失:获得域对象rose 来自一条小鲨鱼(rlovep.com) [代码下载](https://github.com/wpeace1212/BlogSource/tree/master/HttpSer/src/com/rlovep/serlvet)
';