javaWeb基础二:Servlet(java前后端交互的技术)

您所在的位置:网站首页 写网页的步骤是什么 javaWeb基础二:Servlet(java前后端交互的技术)

javaWeb基础二:Servlet(java前后端交互的技术)

2024-07-10 05:47| 来源: 网络整理| 查看: 265

2. Servlet 2.1 定义

Servlet是sun公司提供的一门用于开发动态web资源的技术,可以实现和客户端的交互,接收客户端请求和给客户端返回响应。

Sun公司在其API中提供了一个servlet接口,用户若想开发一个动态web资源需要完成以下2个步骤:

编写一个Java类,实现servlet接口。把开发好的Java类部署到web服务器中。 2.2 Servlet接口

Servlet接口SUN公司定义了两个默认实现类,分别为:GenericServlet、HttpServlet。

​ HttpServlet指能够处理HTTP请求的servlet,它在原有Servlet接口上添加了一些与HTTP协议处理方法,它比Servlet接口的功能更为强大。因此开发人员在编写Servlet时,通常应继承这个类,而避免直接去实现Servlet接口。

​ HttpServlet在实现Servlet接口时,覆写了service方法,该方法体内的代码会自动判断用户的请求方式,如为GET请求,则调用HttpServlet的doGet方法,如为Post请求,则调用doPost方法。因此,开发人员在编写Servlet时,通常只需要覆写doGet或doPost方法,而不要去覆写service方法。

2.3 实现第一个Servlet

基础操作步骤:

编程工具上设置Tomcat配置。(有的是单独在项目中设置)

创建web项目。

以2020版本的idea为例:(先创建基础的java项目,再在项目右键,点击Add Frameworks Support 添加,选择web项目。)

注:不同的版本、编程软件也有不同的创建方式,有得可以直接创建web项目。

创建一个类,继承HttpServlet,重写service方法。

public class MyServlet extends HttpServlet { @Override protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { //使用getParameter方法接受key为str的value值。 String str = req.getParameter("str"); System.out.println("前端发送了一个请求"+str); //使用字符流打印返回浏览器一个响应。 resp.getWriter().print("hhhhh");//响应返回一个hhhh //注:可以直接返回HTML语言(js+css+html) //resp.getWriter().print("hhhh"); } }

​ 注:在eclipse和myeclipse上,类能够直接继承HttpServlet,因为实现了自动导包。(idea需要手动导入servlet-api.jar包,才能正常extends HttpServlet类)

​ servlet-api.jar包在Tomcat的lib目录下。

注册Servlet类(让web服务器识别。)

在web.xml文件中设置创建的Servlet类信息

MyServlet com.dream.servlet.MyServlet MyServlet /myservlet 编写前端页面。 欢迎来到web项目 点击发送请求 function fun01(){ location="myservlet?str=hello" }

在web.xml中设置进入项目默认访问的第一个文件。

这里设置为上面的welcome.html

welcome.html

拓展:前端发送请求的三种方式:(前两种都只能以get方式提交,而第三种能够选择提交方式)

超链接(标签)window.locationform表单(action,method(get/post)),使用submit按钮提交 解析过程:

​ Tomcat在加载Web应用时,就会把相应的web.xml文件中的数据读入到内存中。因此当Tomcat在解析web请求的时候,需要参考web.xml文件时,实际上只需要从内存中读取相关数据就可以了,不需要再到文件系统中读取web.xml。

图示:

在这里插入图片描述

2.4 Servlet运行过程

Servlet程序是由WEB服务器调用,web服务器收到客户端的Servlet访问请求后:

1,Web服务器首先检查是否已经装载并创建了该Servlet的实例对象。如果是,则直接执行第4步,否则,执行第2步。

2,装载并创建该Servlet的一个实例对象。

3,调用Servlet实例对象的init()方法。

4,创建一个用于封装HTTP请求消息的HttpServletRequest对象和一个代表HTTP响应消息的HttpServletResponse对象,然后调用Servlet的service()方法并将请求和响应对象作为参数传递进去。

5,WEB应用程序被停止或重新启动之前,Servlet引擎将卸载Servlet,并在卸载之前调用Servlet的destroy()方法。

测试:

public class MyServlet01 extends HttpServlet { public MyServlet01(){ System.out.println("MyServlet01调用构造方法,创建对象"); } @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { this.doPost(req,resp); } @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { System.out.println("MyServlet01接受到了来自前端的请求"); } @Override public void init(ServletConfig config) throws ServletException { System.out.println("MyServlet01进行初始化"); } @Override public void destroy() { System.out.println("MyServlet01销毁"); } }

在这里插入图片描述

2.4.1 Servlet生命周期

Servlet是一个供其他Java程序(Servlet引擎)调用的Java类,它不能独立运行,它的运行完全由Servlet引擎来控制和调度。

针对客户端的多次Servlet请求,通常情况下,服务器只会创建一个Servlet实例对象,也就是说Servlet实例对象一旦创建,它就会驻留在内存中,为后续的其它请求服务,直至web容器退出,servlet实例对象才会销毁。

在Servlet的整个生命周期内,Servlet的init方法只被调用一次。而对一个Servlet的每次访问请求都导致Servlet引擎调用一次servlet的service方法。对于每次访问请求,Servlet引擎都会创建一个新的HttpServletRequest请求对象和一个新的HttpServletResponse响应对象,然后将这两个对象作为参数传递给它调用的Servlet的service()方法,service方法再根据请求方式分别调用doXXX方法。

如果在元素中配置了一个元素,那么WEB应用程序在启动时,就会装载并创建Servlet的实例对象、以及调用Servlet实例对象的init()方法。

用途:为web应用写一个InitServlet,这个servlet配置为启动时装载,为整个web应用创建必要的公共数据。

MyServlet01 com.dream.servlet.MyServlet01 1

Servlet被创建的两种情况:

1.前端发送请求,tomcat服务器发现该Servlet没有对象时就创建

2.在web.xml中配置 1,该Servlet就会在项目启动时由Tomcat创建对象。

Servlet被销毁:

Tomcat关闭时调用destroy()销毁的方法

注:

一般情况下Servlet创建对象只创建一次,符合单例模式(在整个项目中该Servlet的对象是唯一),init() 初始化方法 也只调用一次,而doGet() 或 doPost() 每请求一次就会调用一次。

2.4.2 ServletConfig对象

在Servlet的配置文件中,可以使用一个或多个标签为servlet配置一些初始化参数。

当servlet配置了初始化参数后,web容器在创建servlet实例对象时,会自动将这些初始化参数封装到ServletConfig对象中,并在调用servlet的init方法时,将ServletConfig对象传递给servlet。进而,程序员通过ServletConfig对象就可以得到当前servlet的初始化参数信息。

阅读ServletConfig API,并举例说明该对象的作用:

获得字符集编码

获得数据库连接信息实际的Servlet开发中,可以直接通过getServletConfig()的到ServletConfig对象。

MyServlet01 com.dream.servlet.MyServlet01 code UTF-8

注:为键值对存在,放在servlet标签下,代表这个标签初始化的参数信息,在该servlet的init初始胡方法中能够获取到,使用示例:

@Override public void init(ServletConfig config) throws ServletException { String code = config.getInitParameter("code"); System.out.println("MyServlet01进行初始化,获得编码格式:"+code); } 2.4.3 使用@WebServlet注解

使用@WebServlet注解在Servlet类上,能够代替在web.xml配置文件中的一些相关设置,更加方便。

@WebServlet注解底层:

@Target({ElementType.TYPE}) //作用于类 @Retention(RetentionPolicy.RUNTIME) //运行时有效 @Documented public @interface WebServlet { //指定Servlet 的 name 属性,等价于 没有显式指定,则该 Servlet 的取值即为类的全限定名。 String name() default ""; //该属性等价于 urlPatterns 属性。两个属性不能同时使用。标签。 String[] value() default {}; String[] urlPatterns() default {}; //指定 Servlet 的加载顺序,等价于 标签。默认为-1即不在服务器启动时创建。 int loadOnStartup() default -1; // WebInitParam[] initParams() default {};// 配置初始化参数 注解数组见下 boolean asyncSupported() default false; String smallIcon() default ""; String largeIcon() default ""; String description() default ""; String displayName() default ""; } //键值对,表示指定一组 Servlet 初始化参数,等价于标签。 @Target({ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface WebInitParam { String name(); String value(); String description() default ""; }

简单使用:

@WebServlet( value = "/MyServlet", loadOnStartup = 1, initParams = { @WebInitParam(name ="code",value = "UTF-8") }) public class MyServlet extends HttpServlet { public MyServlet(){ System.out.println("MyServlet调用构造方法,创建对象"); } @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { this.doPost(req,resp); } @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { System.out.println("MyServlet接受到了来自前端的请求"); } @Override public void init(ServletConfig config) throws ServletException { String code = config.getInitParameter("code"); System.out.println("MyServlet进行初始化,获得编码格式:"+code); } @Override public void destroy() { System.out.println("MyServlet销毁"); } } 运行结果: MyServlet调用构造方法,创建对象 MyServlet进行初始化,获得编码格式:UTF-8 ...... MyServlet接受到了来自前端的请求 ...... MyServlet销毁 ...... Disconnected from server 2.5 线程安全问题

出现原因:多个客户端访问同一个Servlet中的资源时,有可能会出现线程安全问题

测试案例:

​ 在servlet中增加线程占用的时间(sleep方法),让多个浏览器去访问这个servlet,其中的数据读取就会出现脏读。

@WebServlet("/MyServlet") public class MyServlet extends HttpServlet{ //请求次数 int num; public MyServlet() { System.out.println("MyServlet被创建"); } protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { doPost(request, response); } protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { num++; try { Thread.sleep(5000); //线程睡眠5秒 } catch (InterruptedException e) { e.printStackTrace(); } response.getOutputStream().println(num); } }

在相差五秒内,前后使用两个浏览器访问这一个servlet,得到的请求次数(num)应该为:

先访问的页面出现1,而后访问的应该出现2。

但实际上两个页面都会显示2,这是因为在多线程访问一个servlet时,数据同时被两个线程处理,导致有些线程得到的数据不再准确。(在真正的项目中虽然没有sleep方法影响,servlet处理请求不会故意睡眠,但是在各种因素的影响下,我们还是不能保证数据的可靠性。)

处理方法:

1.将Servlet实现SingleThreadModel(已过时),因为当线程阻塞,就会创建新的Servlet对象(一般不使用,因为在这种情况下,servlet的对象不再是唯一的。)

2.利用线程锁机制, synchronized或lock

示例:在需要的处理请求的代码块上锁

@WebServlet("/MyServlet") public class MyServlet extends HttpServlet{ //请求次数 int num; //创建lock锁对象。 private Lock lock = new ReentrantLock(); public MyServlet() { System.out.println("MyServlet被创建"); } protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { doPost(request, response); } protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { lock.lock();//上锁 num++; try { Thread.sleep(5000); } catch (InterruptedException e) { e.printStackTrace(); } response.getOutputStream().println(num); lock.unlock();//解锁 } } 2.6 重定向和转发

​ 在前端页面,想要跳转到其他的页面或者是Servlet,可以直接通过超链接、提交表单、location三种实现,但是单纯两个页面之间的的跳转,是没有经过后端任何数据处理和数据传递的,这样的实现在大多数情况下是没有意义的。所以在需要数据处理和页面之间的数据传递时,常常会经过Servlet来实现。

Servlet跳转到某个页面/Servlet的两种方式为:重定向和转发。

重定向是通过响应(response)来完成的,而转发是通过请求(request)完成。

2.6.1 重定向 protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { //在Servlet中使用 重定向方式 跳转到page.html response.sendRedirect("page.html"); } 2.6.2 转发 protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { //在Servlet中使用 转发方式 跳转到page.html request.getRequestDispatcher("page.html").forward(request,response); }

注:请求转发时,需要将request和response一同转发。

2.6.3 重定向和转发的区别

浅显的说:

重定向是Servlet告诉浏览器,自己无法完成你的请求(第一次请求),并且通过响应告诉浏览器你想要完成这个请求应该去找谁,之后浏览器根据响应回来的信息(重定向的内容)重新给新的servlet或页面发送请求(第二次请求)。

转发则是Servlet知道自己无法完成浏览器的请求(一次请求),但是,我可以去找其他Servlet或页面帮你完成,而这些则不需要浏览器再操心。

(浏览器分别找重定向和转发借钱;

转发说:我虽然没有钱,但是我可以找别人借了,再借给你。

重定向说:我也没有钱,你找马云借吧,然后浏览器又去找马云借去了。(借了两次))

重定向转发前端发送请求的次数2次1次跳转到普通页面(项目web文件夹下的页面)okok跳转到外部页面的区别(如http://www.baidu.com)okno跳转到受保护页面的区别(WEB-INF里的页面)nook

解释:

1.转发无法跳转到外部页面的原因:当前服务器不能直接访问外部服务器。

2.重定向无法跳转到受保护页面的原因:浏览器不能直接访问WEB-INF内的资源

2.7 中文乱码问题

出现原因:

前端后端编码不一致

浏览器默认使用UTF-8码表进行编码 ,Servlet使用ISO-8859-1码表进行编码

传输和接收方编码不一致导致乱码的产生

2.7.1 Request(请求)乱码

Request请求分为post和get,分别有不同的解决方案

2.7.1.1 POST请求

在Servlet的doPost方法中给请求的参数设置编码格式。

示例:

protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { //先给请求中的参数设置编码格式 request.setCharacterEncoding("UTF-8"); //再通过getParameter获取数据。 }

注:

在执行 setCharacterEncoding()之前,不能执行任何 getParameter()操作。

通过 setCharacterEncoding 设置的编码方式只对 POST 方式提交的表单有效,对 GET 方式无效。

2.7.1.2 GET请求(Tomcat7.X版本)

解决方案一:将获取到的数据先采用ISO-8859-1的格式解码成字节数组,在通过UTF-8的编码格式重写编码成完整数据。

示例:

protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { String parameter1 = request.getParameter("parameter1"); parameter1 = new String(parameter1.getBytes("ISO-8859-1"),"UTF-8"); String parameter2 = request.getParameter("parameter2"); parameter2 = new String(parameter2.getBytes("ISO-8859-1"),"UTF-8"); }

解决方案二:在Tomcat根目录/conf/server.xml中设置编码格式

示例:

设置完成之后,在启动服务器的时候就会读取到URIEncoding的属性配置,然后再调用自己的一个setURIEncoding方法完成设置,形参的值为我们设置的UTF-8 。

底层setURIEncoding方法:

protected String URIEncoding = null; public void setURIEncoding(String URIEncoding) { this.URIEncoding = URIEncoding; setProperty("URIEncoding", URIEncoding); } 2.7.1.3 GET请求(Tomcat8.X版本)

Tomcat8.x的服务器在接收GET请求时,即使参数中有中文,也不会出现乱码,作者在底层设计上的一些改动

Tomcat的连接器组件(Connector) ,Connector是Tomcat中的一个重要的组件,它负责监听Tomcat收到的请求信息,并将这些请求信息传递给Servlet规范中所定义的Request,然后将转换后的请求交给Engine组件

去处理,最后将Engine返回的Response返回给客户端 。源码中我们可以看到, URIEncoding的默认值为UTF-8,所以在Tomcat8.x中,即使GET请求包含了中文的数据,也不会出现乱码了

public class Connector extends LifecycleMBeanBase { private Charset uriCharset = StandardCharsets.UTF_8; //查询Tomcat根目录/conf/catalina.properties配置文件中的属性 public static final boolean RECYCLE_FACADES = Boolean.parseBoolean(System.getProperty("org.apache.catalina.connector.RECYCLE_FACADES", "false")); public Connector() { this(null); } public Connector(String protocol) { setProtocol(protocol); ProtocolHandler p = null; try { Class clazz = Class.forName(protocolHandlerClassName); p = (ProtocolHandler) clazz.getConstructor().newInstance(); } catch (Exception e) { log.error(sm.getString( "coyoteConnector.protocolHandlerInstantiationFailed"), e); } finally { this.protocolHandler = p; } if (Globals.STRICT_SERVLET_COMPLIANCE) { uriCharset = StandardCharsets.ISO_8859_1; } else { uriCharset = StandardCharsets.UTF_8; } } } 2.7.2 Response(响应)乱码

单独设置响应内容的编码格式

protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { //设置响应内容的编码格式 response.setContentType("text/html;charset=UTF-8"); response.getWriter().println("哈喽!"); } 2.7.3 跳转到中文页面路径乱码 protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { System.out.println("MyServlet接受到了来自前端的请求"); //使用重定向的方式 跳转到 详情页面.html resp.sendRedirect(URLEncoder.encode("详情页面.html","UTF-8")); //使用转发的方式 跳转到 详情页面.html //req.getRequestDispatcher("详情页面.html").forward(req,resp); }

注:

1.通过转发的方式,Servlet可以识别中文,不需要其他改变,而重定向则需要如上述配置。

e) throws ServletException, IOException {

//设置响应内容的编码格式 response.setContentType("text/html;charset=UTF-8"); response.getWriter().println("哈喽!");

}

### 2.7.3 跳转到中文页面路径乱码 ```java protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { System.out.println("MyServlet接受到了来自前端的请求"); //使用重定向的方式 跳转到 详情页面.html resp.sendRedirect(URLEncoder.encode("详情页面.html","UTF-8")); //使用转发的方式 跳转到 详情页面.html //req.getRequestDispatcher("详情页面.html").forward(req,resp); }

注:

1.通过转发的方式,Servlet可以识别中文,不需要其他改变,而重定向则需要如上述配置。

2.不建议使用中文路径页面。



【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3