Java零基础教学文档servlet(2)

【Servlet】

1.工程结构

在这里插入图片描述

2.J2EE平台

Java EE,Java平台企业版(Java Platform Enterprise Edition),之前称为Java 2 Platform, Enterprise Edition (J2EE),2018年3月更名为Jakarta EE。是Sun公司为企业级应用推出的标准平台。Java平台共分为三个主要版本Java EE、Java SE和Java ME。

Sun公司在1998年发表JDK1.2版本的时候,使用了新名称Java 2 Platform,即“Java2平台”,修改后的JDK称为Java 2 Platform Software Developing Kit,即J2SDK。并分为标准版(Standard Edition,J2SE),企业版(Enterprise Edition,J2EE),微型版(MicroEdition,J2ME)。J2EE便由此诞生。

2005年6月,JavaOne大会召开,SUN公司公开Java SE 6。此时,Java的各种版本已经更名以取消其中的数字“2”:J2EE更名为Java EE, J2SE更名为Java SE,J2ME更名为Java ME。

随着Java技术的发展,J2EE平台得到了迅速的发展,成为Java语言中最活跃的体系之一。现如今,J2EE不仅仅是指一种标准平台,它更多的表达着一种软件架构和设计思想。

2.1 J2EE平台组件

Java EE是一系列技术标准所组成的平台(一系列规范),包括:

1.Applet - Java Applet

2.EJB - 企业级JavaBean(Enterprise Java Beans)

3.JAAS - Java Authentication and Authorization Service

4.JACC - J2EE Authorization Contract for Containers

5.JAF - Java Beans Activation Framework

6.JAX-RPC - Java API for XML-Based Remote Procedure Calls

7.JAX-WS - Java API for XML Web Services

8.JAXM - Java API for XML Messaging

9.JAXP - Java XML解析API(Java API for XML Processing)

10.JAXR - Java API for XML Registries

11.JCA - J2EE连接器架构(J2EE Connector Architecture)

12.JDBC - Java数据库联接(Java Database Connectivity)

13.JMS - Java消息服务(Java Message Service)

14.JMX - Java Management

15.JNDI - Java名称与目录接口(Java Naming and Directory Interface)

16.JSF - Java Server Faces

17.JSP - Java服务器页面(Java Server Pages)

18.JSTL - Java服务器页面标准标签库(Java Server Pages Standard Tag Library)

19.JTA - Java事务API(Java Transaction API)

20.JavaMail

21.Servlet - Java Servlet API

22.StAX - Streaming APIs for XML Parsers

23.WS - Web Services

3.servlet

Servlet(Server Applet),全称Java Servlet。是用Java编写的服务器端程序。其主要功能在于交互式地浏览和修改数据,生成动态Web内容。狭义的Servlet是指Java语言实现的一个接口,广义的Servlet是指任何实现了这个Servlet接口的类,一般情况下,人们将Servlet理解为后者。

Servlet运行于支持Java的应用服务器中。从实现上讲,Servlet可以响应任何类型的请求,但绝大多数情况下Servlet只用来扩展基于HTTP协议的Web服务器。

最早支持Servlet标准的是JavaSoft的Java Web Server。此后,一些其它的基于Java的Web服务器开始支持标准的Servlet。

3.1 Servlet的作用

Servlet是服务端的一个小程序,程序其作用只有2个:

3.1.1 获取客户的请求信息
Servlet获取客户端的请求信息,基于ServletRequest对象,ServletRequest的子接口: HttpServletRequest。

HttpServletRequest这个接口包含:客户端的请求参数,客户端的请求头信息。

3.1.2 向客户端做出响应
Servlet向客户端做出响应,基于ServletResponse对象,ServletResponse的子接口:HttpServletResponse。

HttpServletResponse这个接口:获取输出流(字节流,字符流),向客户端返回响应头信息。

3.2 Servlet的使用方式

3.2.1 创建Servlet的3个步骤
步骤

导入servlet-api.jar

在tomcat的lib文件夹下也有此jar包,开发过程中为防止报错需要导入,实际运行时需要移除此jar包避免版本冲突。

自定义类继承HttpServlet 继承后,重写doGet、doPost方法。

配置虚拟路径 1)可以通过注解配置 2)通过xml形式配置

3.2.2 使用注解形式
JavaWeb开发中,Servlet开发配置相对繁琐。在web3.0之后,提供一些简化web配置的数据,@WebServlet就可以取代在web.xml中对servlet的配置。

package com.powernode.servlet;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

/**
 * 内容: JavaWeb
 */
//@WebServlet(urlPatterns= {"/login.html","/a.html","/login.php","/login.aspx"})
@WebServlet("/LoginServlet")  //简写
public class LoginServlet extends HttpServlet {

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        System.out.println("哈哈,我是get请求");
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        System.out.println("哈哈,我是post请求");
    }
}

3.2.3 使用xml形式

向服务器注册这个Servlet,servlet是要提供服务的

1.声明/定义servlet类

2.servlet提供服务访问方式

package com.powernode.servlet;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

/**
 * 内容: JavaWeb
 */
public class LoginServlet extends HttpServlet {

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        System.out.println("哈哈,我是get请求");
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        System.out.println("哈哈,我是post请求");
    }
}

<?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_4_0.xsd"
         version="4.0">
    <!--通过xml形式配置servlet的虚拟路径-->
    <servlet>
        <servlet-name>LoginServlet</servlet-name>
        <servlet-class>com.powernode.servlet.LoginServlet</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>LoginServlet</servlet-name>
        <url-pattern>/login.html</url-pattern>
    </servlet-mapping>
</web-app>

3.2.4 两种配置形式区别

平时开发中能用注解形式则用注解形式。但如果类是第3方的,由于我们不能去修改第3方的源代码,无法在类上添加注解,则使用xml形式最为合适。例如:后期SpringMVC框架中的核心类DispatcherServlet就是使用的xml形式作为配置。

3.2.5 通过Servlet输出html标签等富文本信息

package com.powernode.servlet;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
/**
 * 内容: JavaWeb
 */
@WebServlet("/Demo1Servlet")
public class Demo1Servlet extends HttpServlet {

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //在服务器端与浏览器端建立输出流管道
        PrintWriter pw = resp.getWriter();
        pw.write("<h1 style='color:red'>我是servlet输出</h1>");
        pw.close();

        /*ServletOutputStream sos = resp.getOutputStream();
        String str = "<h1 style='color:red'>我是servlet输出</h1>";
        sos.write(str.getBytes("UTF-8"));
        sos.close();*/
    }
    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {

    }
}

3.3 为什么使用tomcat加载类,而不是使用JDK自带的加载器加载

JDK自带的类加载器3个,双亲委派.bootstrap类加载器,加载JDK核心类.Ext 扩展类加载器,加载默认ext目录相关类.application类加载器加载当前项目的类.双亲委派加载机制,避免重复加载,其次为了安全.因为开发者可以自定义类的包名,如果定义一个与系统包名同名的类,自定义的类会覆盖系统的类,可能到导致系统不安全.但是如果tomcat也使用双亲为加载机制加载,会导致如果多个web项目,其中类的名臣完全相同,但是实现不同就出现覆盖.tomcat通过打破双亲委派加载机制,做到了类在应用级别隔离.

3.4 Servlet的生命周期(重点)

Servlet的生命周期,是指Servlet的实例的创建到到销毁的过程。

代码示例:

package com.powernode.servlet;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;


public class LifeServlet extends HttpServlet {
    /**
     *  init 方法 只 执行了1遍
     *  初始化Servlet 对象的方法
     * @throws ServletException
     */
    @Override
    public void init() throws ServletException {
        System.out.println(this);
        System.out.println("我是 init 方法");
    }

    /**
     *  service 方法 执行多次
     *  每次有请求时都会执行
     * @param req    就是封装了所有请求信息的对象
     * @param resp   与请求对应的做出响应的对象
     * @throws ServletException
     * @throws IOException
     */
    @Override
    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        System.out.println(this);
        System.out.println("我是 service 方法");
    }

    /**
     *  当服务器 停止时  destroy 方法执行
     */
    @Override
    public void destroy() {
        System.out.println(this);
        System.out.println("我是 destroy 方法");
    }
}





package com.powernode.servlet;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;


public class LifeServlet extends HttpServlet {
    /**
     *  init 方法 只 执行了1遍
     *  初始化Servlet 对象的方法
     * @throws ServletException
     */
    @Override
    public void init() throws ServletException {
        System.out.println(this);
        System.out.println("我是 init 方法");
    }

    /**
     *  service 方法 执行多次
     *  每次有请求时都会执行
     * @param req    就是封装了所有请求信息的对象
     * @param resp   与请求对应的做出响应的对象
     * @throws ServletException
     * @throws IOException
     */
    @Override
    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        System.out.println(this);
        System.out.println("我是 service 方法");
    }

    /**
     *  当服务器 停止时  destroy 方法执行
     */
    @Override
    public void destroy() {
        System.out.println(this);
        System.out.println("我是 destroy 方法");
    }
}

Tomcat服务器在加载servlet类时,默认是不会创建对象的.只有在servlet实际被使用时,才会去创建对象,tomcat提供了init方法,供开发者进行对象属性的设置,所以该方法被称之为对象初始化方法.

每次访问时,具体的访问业务,tomcat会将访问的数据进行封装,封装为HttpServletRequest对象和HttpServletResponse对象,然后调用service方法.

当tomcat服务器停止时,又会去调用destroy方法.开发者,在这个方法中释放资源,留遗书.

init方法执行一次,如果在配置中配置load-on-startup,则tomcat在启动时就会创建servlet对象,调用init方法.

service方法,会执行多次,只有有请求就会执行service方法.请求数据会被封装到HttpServletRequest对象中,响应数据会被封装到HttpServletResponse对象中。

destroy,servlet对象销毁方法,当tomcat服务器停止时调用.可以释放资源.

3.5 Servlet线程安全问题

Servlet本质是单例模式,如果存在多个并发请求,所有请求使用同一个对象,如果请求中使用了对象的成员变量,则可能出现数据不一致的情况,servlet是非线程安全的.

解决方案:

  1. 不要在servlet使用成员变量.

  2. 使用ThreadLocal,线程局部变量存储当前线程自己的数据.

3.6 Servlet初始化参数配置

可以在web.xml中配置servlet初始化时的参数,也可以在注解中配置(鸡肋).在xml中配置示例如下:

<servlet>
    <servlet-name>life</servlet-name>
    <servlet-class>com.powernode.servlet.ServletLife</servlet-class>
    <!-- 配置servlet 初始化参数 -->
    <init-param>
        <param-name>paramKey1</param-name>
        <param-value>paramValue1</param-value>
    </init-param>
    <init-param>
        <param-name>paramKey2</param-name>
        <param-value>paramValue2</param-value>
    </init-param>
    <!-- 启动时创建servlet对象 -->
    <load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
    <servlet-name>life</servlet-name>
    <url-pattern>/lifeServlet</url-pattern>
</servlet-mapping>



@Override
public void init() throws ServletException {
    System.out.println("init执行下了!!!....");
    System.out.println("当前对象为:"+this);
    // servlet 对象
    // 配置信息
    ServletConfig config = this.getServletConfig();
    // 所有的初始化配置参数的名称
    Enumeration<String> initParameterNames = config.getInitParameterNames();
    while ( initParameterNames.hasMoreElements()){
        // 获取参数名称
        String name = initParameterNames.nextElement();

        String value = config.getInitParameter(name);

        System.out.println(name +" : " + value);
    }
    // 根据参数名获取对应的参数值
    Enumeration<String> names = this.getInitParameterNames();
    while ( names.hasMoreElements()){
        // 获取参数名称
        String name = names.nextElement();

        String value = this.getInitParameter(name);

        System.out.println(name +" : " + value);
    }
}





注解配置示例,仅供了解.

@WebServlet(value  ="/initServlet",initParams = {
        @WebInitParam(name = "paramName1",value = "paramValue1"),
        @WebInitParam(name = "paramName2",value = "paramValue2"),
} )
public class ServletInitParam extends HttpServlet {


}

3.7 Servlet的Api

3.7.1 Servlet的请求Api
3.7.1.1 获取请求行数据

protected void reqLine(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    // 获取请求的方法  : GET  POST  DELETE  PUT  OPTION
    String method = req.getMethod();
    // 获取请求的资源路径
    String uri = req.getRequestURI();
    // 获取请求的URL地址
    StringBuffer url = req.getRequestURL();
    // 获取请求的协议
    String protocol = req.getProtocol();

    System.out.println(method);
    System.out.println(uri);
    System.out.println(url);
    System.out.println(protocol);

}

3.7.1.2 请求头数据

/**
 * 获取请求头信息
 *
 * @param req
 * @param resp
 * @throws ServletException
 * @throws IOException
 */
protected void reqHeader(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    // 获取所有的请求头信息的头
    Enumeration<String> headerNames = req.getHeaderNames();
    while (headerNames.hasMoreElements()) {
        // 获取具体的请求头名称
        String headerName = headerNames.nextElement();
        // 根据请求头获取对应的值
        String headerValue = req.getHeader(headerName);
        System.out.println(headerName + ":" + headerValue);
    }
    // 如果比较核心的请求头:
    // content-type 请求头数据
    String contentType = req.getContentType();
    //content-length 请求头数据
    int contentLength = req.getContentLength();
    System.out.println("content-type:" + contentType);
    System.out.println("content-length:" + contentLength);
}

3.7.1.3 获取网络信息

/**
 * 获取网络信息
 *
 * @param req
 * @param resp
 * @throws ServletException
 * @throws IOException
 */
protected void reqNet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    // 协议
    String protocol = req.getProtocol();
    //端口
    int localPort = req.getLocalPort();
    // 远程主机
    String remoteHost = req.getRemoteHost();
    // 远程主机的端口
    int remotePort = req.getRemotePort();

    System.out.println(protocol);
    System.out.println(localPort);
    System.out.println(remoteHost);
    System.out.println(remotePort);
}

3.7.1.4 请求参数

/**
 * 请求参数
 *  在HTTP协议中 默认的传输格式 ISO-8859-1  程序使用的是UTF-8的编码.并且tomcat 的 post 请求处理数据的格式 也是ISO-8859-1的编码.
 *  两种编码不一致导致乱码。注意: GET请求使用的UTF-8编码.在Tomcat 7.0之后
 *  如果乱码要进行转码
 * @param req
 * @param resp
 * @throws ServletException
 * @throws IOException
 */
protected void reqParam(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    // 设置默认的编码格式
    req.setCharacterEncoding("UTF-8");
    // 获取请求参数的Map 集合
    // 参数的name 做为key
    // 参数的值作为value
    // 所有的请求参数 默认字符串类型 由于 name 可能对应的多个值 使用数组存储 (复选框时  name 可能对应多个值)
    Map<String, String[]> parameterMap = req.getParameterMap();
    parameterMap.forEach((k, v) -> {
        System.out.println(k + " : " + Arrays.toString(v));
    });
    // 根据名称获取值  获取指定数据
    String username = req.getParameter("username");
    String password = req.getParameter("password");
    String sex = req.getParameter("sex");
    String[] gfs = req.getParameterValues("gf");
    String city = req.getParameter("city");
    String comment = req.getParameter("comment");

    // 转码:
    // 按照 ISO-8859-1 进行转码  还原
    //byte[] bytes = username.getBytes(StandardCharsets.ISO_8859_1);
    // 使用新的编码格式转字符串
    //username = new String(bytes, StandardCharsets.UTF_8);
    System.out.println("username:" + username);
    System.out.println("password:" + password);
    System.out.println("sex:" + sex);
    System.out.println("gfs:" + Arrays.toString(gfs));
    System.out.println("city:" + city);
    System.out.println("comment:" + comment);
}

3.7.1.5 Servlet内部转发

Servlet内部转发是指当前servlet将请求和响应信息移交其他的servlet进行处理.对于请求方而言,只需要请求一次,但是实际上经历多个具体servlet(具体次数要看实际的转发次数).浏览器的URL地址不会发生变化,并且servlet之间可以进行参数传递操作.

这种内部转发的方式,由于将请求对象进行了传递,在实际开发中使用的比较多的.但是又由于URL地址没有发生变化,所以一旦浏览器进行刷新操作,之前所有相关的程序会重新执行.首先会导致无意义资源消耗,其次可能导致脏数据.

能不使用内部转发就不要使用内部转发.

/**
 *  服务器转发
 *      服务器转发 是指 处理客户端请求时,当前本该处理的程序不进行完整的处理,而是将请求移交给其他程序处理,这种方式就叫服务器内部转发
 *      例如:
 *      张三去银行办业务 本该在3号窗口办理业务
 *          1. 张三去3号窗口
 *          2. 3号窗口业务员在办理张三的业务时,突然要上个厕所.
 *          3. 3号业务叫X号业务继续协助办理业务
 *          4. x号业务员完成整个业务
 *      这种形式就是内部转发
 *      对于张三而言,只进行一次挂号.  张三经历业务员当前 2个
 * @param req
 * @param resp
 * @throws ServletException
 * @throws IOException
 */
protected void forward(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    System.out.println("3号业务员....");
    System.out.println("3号业务员去厕所....");
    // 服务器内部转发
    // 将请求 移交给  servlet04.do
    // req
    // resp   张三的请求对象 和 响应对象
    req.getRequestDispatcher("servlet04.do").forward(req,resp);

}



package com.powernode.servlet;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Arrays;

@WebServlet("/servlet04.do")
public class Servlet04  extends HttpServlet {

    @Override
    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        // 设置编码格式
        req.setCharacterEncoding("UTF-8");
        System.out.println(" 4号业务员 ");
        System.out.println("4 号业务员办理业务");
        // 根据名称获取值  获取指定数据
        String username = req.getParameter("username");
        String password = req.getParameter("password");
        String sex = req.getParameter("sex");
        String[] gfs = req.getParameterValues("gf");
        String city = req.getParameter("city");
        String comment = req.getParameter("comment");
        System.out.println("username:" + username);
        System.out.println("password:" + password);
        System.out.println("sex:" + sex);
        System.out.println("gfs:" + Arrays.toString(gfs));
        System.out.println("city:" + city);
        System.out.println("comment:" + comment);
        // 添加数据库操作
        //......
    }
}



<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<!--
    get 请求参数会显示在URL上面,明文显示
    post请求参数会封装在HTTP协议内部
    post 请求相对于get请求安全一些.
    不论get还是post请求都是明文传输,HTTP协议也是不安全的协议.
    一般而言,修改时才用post请求,查询时使用get请求.
-->
<form action="param.do" method="post">
    <p>用户名:<input name="username" /></p>
    <p>密码:<input name="password" /></p>
    <p>性别:
        <input name="sex" type="radio" value="1" /><input name="sex" type="radio" value="2" /></p>
    <p>女朋友:
        <input name="gf" type="checkbox" value="韩梅梅" /> 韩梅梅
        <input name="gf" type="checkbox" value="Lily" /> Lily
        <input name="gf" type="checkbox" value="Lucy" />Lucy
    </p>
    <p>城市:
        <select name="city">
            <option value="武汉">武汉</option>
            <option value="上海">上海</option>
            <option value="深圳">深圳</option>
        </select>
    </p>
    <p>简介:
        <textarea name="comment"></textarea>
    </p>
    <button>提交</button>
</form>
</body>
</html>

3.7.2 Servlet的响应Api

在servlet中,请求对象和响应对象是成对出现的.哪怕没有数据的响应数据,tomcat服务器会先为每一个请求对象创建相应的响应对象,只是响应内容默认空的.在实际操作中,只需要向响应对象中提供数据即可.

3.7.2.1 响应对象的核心Api
HttpServletResponse核心api就是获取输出流.由于输出的数据可能是字符串也可能是字节.提供两个方法,但是注意,两个输出流实际使用只能使用其中的一个.

package com.powernode.servlet;

import javax.servlet.ServletException;
import javax.servlet.ServletOutputStream;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.PrintWriter;

/**
 *  响应数据的Api
 */
@WebServlet("/resp.do")
public class ResponseApiServlet extends HttpServlet {

    @Override
    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        // 返回字符数据
//        text(req,resp);
        // 返回字节数据
        bytes(req,resp);
    }

    /**
     *  返回字符数据
     * @param req
     * @param resp
     * @throws ServletException
     * @throws IOException
     */
    protected void text(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        // 设置数据内容的编码格式 通知浏览器 按照这种编码进行解析
        resp.setContentType("text/html;charset=utf-8");
        // 设置将字符串转byte的编码
        resp.setCharacterEncoding("UTF-8");
        // 获取字符输出流
        PrintWriter writer = resp.getWriter();
        writer.println("Hello 字符流!");
        writer.flush();
        writer.close();
    }

    protected void bytes(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        ServletOutputStream out = resp.getOutputStream();
        // 文件对象
        File file = new File("E://imgs/2.png");
        FileInputStream fis = new FileInputStream(file);
        // 定义变量接收数据
        byte[] b = new byte[1024];
        int len = -1;
        while ((len = fis.read(b) ) != -1){
            out.write(b,0,len);
        }
        out.flush();
        out.close();
        fis.close();
    }
}

3.7.2.2 重定向和内部转发

protected void redirect(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    // 重定向
    // resp.sendRedirect("success.html");
    // 内部转发
    req.getRequestDispatcher("success.html").forward(req,resp);
    /**
     *  重定向和内部转发对比
     *      相同点 :
     *          都能实现页面的跳转
     *      不同点:
     *          内部转发 :
     *              1. URL地址栏 不会发生改变
     *              2. 浏览器请求一次
     *              3. 可以将request 和 response对象进行传递
     *
     *          重定向 :
     *              1. URL地址栏会发生改变
     *              2. 浏览器请求多次
     *              3. 不能传递request 和 response对象进行传递
     *
     *      结论 : 如果要进行参数传递则使用内部转发,否则都建议使用重定向.因为重定向的URL地址栏发生了变化,刷新浏览器时不会造成不必要的请求.
     *      不会产生不必要的服务器负担.
     */
}

3.7.2.3 向响应头添加数据

protected void respHeader(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    // 通过 addHeader 向响应头中添加数据
    resp.addHeader("token", DateUtil.today());
    resp.sendRedirect("success.html");
}

3.8 html与servlet相互跳转总结

描述

html跳html

a标签、js(location.href=’xxx’)、form表单的action属性

html跳servlet

a标签、js(location.href=’xxx’)、form表单的action属性

servlet跳html

重定向:response.sendRedirect(“/页面路径”);

转发:request.getRequestDispatcher(“/views/demo3.jsp”).forward(request,response);

servlet跳servlet

推荐重定向

3.8.1 练习1
demo1.html---->点击a标签----->demo2.html------>OneServlet------>TwoServlet----->demo3.html

3.8.2 练习2
demo1.html-------->不做任何点击跳------->demo2.html------->点击a标签------>demo3.html----->

不做任何点击----->OneServlet---------->TwoServlet------->demo4.html----->点击button按钮---->跳demo1.html

3.9 用户登录案例

用户在界面输入用户名和密码,servlet接收用户输入的用户名和密码,与数据库中的用户信息进行对比,如果用户名和密码都正确.跳转到登录成功页面,否则返回登录页面.

3.9.1 技术点
前端界面:HTML.数据存储:mysql.访问数据库使用JDBC.接收用户数据使用servlet.

3.9.2 前期准备
3.9.2.1 数据库准备
创建数据库erp

创建用户表user

CREATE TABLE `user` (

  `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '用户ID',

  `username` varchar(20) NOT NULL COMMENT '登录名',

  `password` varchar(20) NOT NULL COMMENT '登录密码',

  `realname` varchar(20) NOT NULL COMMENT '用户姓名',

  `img` varchar(100) DEFAULT NULL COMMENT '用户头像',

  PRIMARY KEY (`id`)

) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8mb4;

3.9.3 导入相关jar包
mysql驱动包
在这里插入图片描述

3.9.4 引入相关的工具类
3.9.4.1 数据库配置文件
#驱动
jdbc.driverClassName=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/erp?useUnicode=true&useSSL=false&characterEncoding=UTF-8
jdbc.username=root
jdbc.password=123456
3.9.4.2 BaseDao

package com.powernode.dao;

import com.powernode.util.DBUtils;
import com.powernode.util.PageInfo;

import java.lang.reflect.Field;
import java.sql.*;
import java.util.ArrayList;
import java.util.List;

/**
 * JDBC工具类:旨在为客户提供更加便捷的操作数据库的方式
 */
public class BaseDao {
    /**
     * 修改:可以执行添加、删除、修改单条操作
     * @param sql
     * @param params
     * @return
     */
    public int executeUpdate(String sql,Object...params){
        Connection conn = null;
        PreparedStatement st = null;
        int i=0;
        try {
            //参数校验
            if(sql==null || "".equals(sql.trim())){
                return 0;
            }
            //获取连接
            conn = DBUtils.getConn();
            //创建Statement对象
            st = conn.prepareStatement(sql);
            //给sql赋值参数
            for (int n=0;params!=null && params.length>=1 &&n<params.length;n++){
                st.setObject(n+1,params[n]);
            }
            //向mysql发送sql语句,接受结果
            i = st.executeUpdate();
        } catch (Exception throwables) {
            throwables.printStackTrace();
        }finally {
            //释放资源
            DBUtils.close(null,st,conn);
        }
        return i;
    }

    /**
     * 批量删除
     * @param sql
     * @param params
     * @return
     */
    public int executeBatchDel(String sql,Object...params){
        Connection conn = null;
        PreparedStatement st = null;
        int[] arr = null;
        try {
            //基本参数校验
            if(sql==null || "".equals(sql.trim())){
                return 0;
            }
            //获取连接
            conn = DBUtils.getConn();
            //在同一个事务中执行
            conn.setAutoCommit(false);
            st = conn.prepareStatement(sql);
            //给参数赋值
            for (int i = 0;params!=null && params.length>=1 && i < params.length; i++) {
                st.setObject(1,params[i]);
                st.addBatch();
            }
            //执行sql
            arr = st.executeBatch();//[0,0,0,0]
        } catch (Exception throwables) {
            throwables.printStackTrace();
        } finally {
            try {
                conn.setAutoCommit(true);
            } catch (SQLException throwables) {
                throwables.printStackTrace();
            }
            //释放资源
            DBUtils.close(null,st,conn);
        }
        return arr.length;
    }

    /**
     * 查询多条记录
     * @param sql
     * @param c
     * @param params
     * @param <T>
     * @return
     */
    public <T> List<T> executeQueryList(String sql, Class<T> c ,Object...params){
        Connection conn= null;
        PreparedStatement st = null;
        ResultSet rs = null;
        List<T> dataList = new ArrayList<>();//{}
        try {
            //参数校验
            if(sql==null || "".equals(sql.trim())){
                return dataList;
            }
            //获取Connection连接
            conn = DBUtils.getConn();
            //创建PrepareStatement对象
            st = conn.prepareStatement(sql);
            for (int i = 0;params!=null && params.length>=1 && i < params.length; i++) {
                st.setObject(i+1,params[i]);
            }
            //发送sql,接受结果
            rs = st.executeQuery();
            //获取查询数据的字段个数
            ResultSetMetaData metaData = rs.getMetaData();
            int columnCount = metaData.getColumnCount();
            for(;rs.next();){//取记录的次数
                //创建一个c字节码的对象,用来装一条记录
                T t = c.newInstance();
                for (int i = 1; i <=columnCount ; i++) {
                    //获取某个字段名称
                    String columnName = metaData.getColumnName(i);
                    //获取字段对应的值
                    Object value = rs.getObject(columnName);
                    //内部for全部执行完了,则代表一条记录取出来了
                    Field field = c.getDeclaredField(columnName);
                    field.setAccessible(true);
                    field.set(t,value);
                    field.setAccessible(false);
                }
                //将对象装入到List中
                dataList.add(t);
            }
        } catch (Exception throwables) {
            throwables.printStackTrace();
        }finally {
            DBUtils.close(rs,st,conn);
        }
        return dataList;
    }

    /**
     * 查询单条记录
     * @param sql
     * @param c
     * @param param
     * @param <T>
     * @return
     */
    public <T> T executeQueryOne(String sql,Class<T> c,Object...param){
        return executeQueryList(sql,c,param).size()==0?null:executeQueryList(sql,c,param).get(0);
    }

    /**
     * 分页查询
     * @param sql 基本分页sql,不用写limit
     * @param pageNum 页码
     * @param pageSize 每页显示的数据条数
     * @param c   某一条数据的类型
     * @param params  分页太哦建
     * @param <T>
     * @return
     */
    public <T> PageInfo<T> executeQueryPage(String sql,int pageNum,int pageSize,Class<T> c,Object...params){
        Connection conn=null;
        PreparedStatement st = null;
        ResultSet rs = null;
        PageInfo<T> pageInfo = new PageInfo<>();
        try {
            //校验参数
            if(sql==null || "".equals(sql.trim())){
                return pageInfo;
            }
            if(pageNum<=0 || pageSize<=0){
                return pageInfo;
            }
            if(c==null){
                return pageInfo;
            }
            //去除;
            sql = sql.replaceAll(";","");
            //获取数据库连接:Connection对象
            conn = DBUtils.getConn();
            //准备sql语句
            //创建PrepareStatement对象  (此处有个;的小bug)
            String newSql = sql + " limit "+(pageNum-1)*pageSize+","+pageSize+"";
            st = conn.prepareStatement(newSql);
            //给sql占位符?赋值
            for (int i = 0;params!=null && params.length>=1 && i < params.length; i++) {
                st.setObject(i+1,params[i]);
            }
            //执行sql,处理结果
            rs = st.executeQuery();
            //数据表头信息
            ResultSetMetaData metaData = rs.getMetaData();
            //获取查询sql语句中字段的个数
            int columnCount = metaData.getColumnCount();
            for (;rs.next();){
                //创建一个对象,用于接收一条记录
                T t = c.newInstance();
                for (int i = 1; i <=columnCount ; i++) {
                    //获取字段名
                    String columnName = metaData.getColumnName(i);
                    //获取字段值
                    Object columnValue = rs.getObject(columnName);
                    //将对应字段的值装入到实体类对象中去
                    Field field = c.getDeclaredField(columnName);
                    field.setAccessible(true);
                    field.set(t,columnValue);
                    field.setAccessible(false);
                }
                //将某一条记录放入到List集合中去
                //往pageInfo对象中添加分页数据
                pageInfo.getData().add(t);
            }
            //往pageInfo对象中添加总记录数
            long total = executeQueryCount(sql);
            pageInfo.setTotal(total);
            //往pageInfo对象中添加总页数
            long temp = total % pageSize;//3%2=1
            int pages = temp==0? (int)(total/pageSize): (int)Math.ceil((double)total/(double)pageSize);
            pageInfo.setPages(pages);
        } catch (Exception throwables) {
            throwables.printStackTrace();
        } finally {
            //释放资源
            DBUtils.close(rs,st,conn);
        }
        return pageInfo;
    }

    /**
     * 统计满足sql条件的总记录数
     * @param sql
     * @return
     */
    private long executeQueryCount(String sql){
        long total = 0;
        Connection conn=null;
        PreparedStatement st = null;
        ResultSet rs = null;
        try {
            conn = DBUtils.getConn();
            String newSql = "select count(*) from ("+sql+") t";
            st = conn.prepareStatement(newSql);
            rs = st.executeQuery();
            for (;rs.next();){
               total = rs.getLong("count(*)");
            }
            return total;
        } catch (Exception throwables) {
            throwables.printStackTrace();
        } finally {
            DBUtils.close(rs,st,conn);
        }
        return total;
    }
}

3.9.4.3 DButil

package com.powernode.util;

import java.io.InputStream;
import java.sql.*;
import java.util.Properties;

/**
 * 自定义JDBC工具类:1)提供获取链接Connection的方法   2)关闭资源的方法  3)实现jdbc配置软编码
 */
public final class DBUtils {
    /**
     * 驱动名称
     */
    private static String driverClassName;
    /**
     * 链接信息
     */
    private static String url;
    /**
     * 用户名
     */
    private static String username;
    /**
     * 用户密码
     */
    private static String password;

    static {
        try {
            //关联.properties配置文件
            Properties prop = new Properties();
            InputStream ins = DBUtils.class.getClassLoader().getResourceAsStream("db.properties");
            prop.load(ins);
            //读取.properties配置文件的属性值
            driverClassName = prop.getProperty("jdbc.driverClassName");
            url = prop.getProperty("jdbc.url");
            username = prop.getProperty("jdbc.username");
            password = prop.getProperty("jdbc.password");
            Class.forName(driverClassName);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 获取连接
     *
     * @return
     */
    public static Connection getConn() {
        Connection conn = null;
        try {
            conn = DriverManager.getConnection(url, username, password);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return conn;
    }

    /**
     * 关闭资源
     *
     * @param acs
     */
    public static void close(AutoCloseable acs) {
        try {
            if (acs != null) {
                acs.close();
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 关闭资源
     *
     * @param rs
     * @param st
     * @param conn
     */
    public static void close(ResultSet rs, Statement st, Connection conn) {
        try {
            if (rs != null)
                rs.close();
            if (st != null)
                st.close();
            if (conn != null)
                conn.close();
        } catch (SQLException throwables) {
            throwables.printStackTrace();
        }
    }
}

3.9.4.4 PageInfo

package com.powernode.util;

import java.util.ArrayList;
import java.util.List;

/**
 * 此实体类对象中封装的是分页相关数据信息: 分页数据、总记录数、页码等
 */
public class PageInfo<T> {

    /**
     * 分页数据
     */
    private List<T> data = new ArrayList<>();

    /**
     * 满足sql条件的总记录数
     */
    private long total;

    /**
     * 总页数
     */
    private int pages;

    public PageInfo() {
    }

    public PageInfo(List<T> data, long total, int pages) {
        this.data = data;
        this.total = total;
        this.pages = pages;
    }

    public List<T> getData() {
        return data;
    }

    public void setData(List<T> data) {
        this.data = data;
    }

    public long getTotal() {
        return total;
    }

    public void setTotal(long total) {
        this.total = total;
    }

    public int getPages() {
        return pages;
    }

    public void setPages(int pages) {
        this.pages = pages;
    }

    @Override
    public String toString() {
        return "PageInfo{" +
                "data=" + data +
                ", total=" + total +
                ", pages=" + pages +
                '}';
    }
}

3.9.5 数据操作类-dao

3.9.5.1 UserDao

package com.powernode.dao;

import com.powernode.domain.User;

/**
 *  用户数据操作类
 */
public class UserDao extends BaseDao{

    /**
     *  根据用户名和密码查询用户
     * @param username
     * @param password
     * @return
     */
    public  User login(String username, String password){
        String sql = "select id,username,password,realname,img from user where username = ? and password = ?";
        User user = super.executeQueryOne(sql, User.class, username, password);
        return user;
    }
}
 

3.9.6 数据控制类-servlet
3.9.6.1 UserServlet

package com.powernode.servlet;

import cn.hutool.core.util.ObjectUtil;
import com.powernode.dao.UserDao;
import com.powernode.domain.User;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

@WebServlet("/user.do")
public class UserServlet extends HttpServlet {


    @Override
    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        // 获取页面数据
        String username = req.getParameter("username");
        String password = req.getParameter("password");
        UserDao userDao = new UserDao();
        User user = userDao.login(username, password);
        /*
        *  可能user 对象存在  可能不存在
        *   存在 说明登录成功  跳转到成功页面
        *   不存在是说明 用户名或者密码错误  返回 登录页面
        * */
        if (ObjectUtil.isNull(user)){
            System.err.println("用户名或者密码错误!");
            // 跳转到登录页面
            resp.sendRedirect("index.html");
            return;
        }
        System.out.println("登录成功!");
        resp.sendRedirect("success.html");
    }
}

3.9.7 前端页面
3.9.7.1 登录页面-index.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>登录</title>
</head>
<body>
<form action="user.do" method="post">
    <p>用户名:<input name="username"/></p>
    <p>密码:<input name="password"/></p>
    <button>登录</button>
</form>
</body>
</html>

3.9.7.2 登录成功页面-success.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
登录成功!
</body>
</html>
  • 25
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值