Java学习-Tomcat&Servlet

1.Tomcat

1.1.Tomcat的安装以及启动

Tomcat的安装参考链接

  • 关于直接点击文件的启动和关闭:
    1. window环境下找到Tomcat的安装目录找到bin/startup.bat文件双击运行文件即可
    2. linux环境下找到Tomcat的安装目录找到bin/startup.sh文件双击运行文件即可
    3. 关闭文件根据不同的环境找到bin/shutdown不同后缀名的文件双击即可

1.2Tomcat部署项目方式

  1. 直接将项目放到webapps目录下即可:
    • /hello(项目名称):项目的访问路径–>虚拟目录
    • 简化部署:将项目打成一个war包,再将war包放置到webapps目录下。(war包会自动解压缩)
  2. 配置Tomcat安装目录下conf/server.xml文件:
    • 在标签体中配置
    • docBase:项目存放的路径
    • path:虚拟目录
  3. 在conf\Catalina\localhost创建任意名称的xml文件。在文件中编写
    • 虚拟目录:xml文件的名称

1.3 Tomcat于IDEA继承&创建web项目

  1. 首先我们配置IDEA的配置文件,先通过run->Edit Configurations:
    在这里插入图片描述

  2. 点击配置Tomcat的路径:
    在这里插入图片描述

3.在创建web项目,关联Tomcat.这样直接启动项目就启动了Tomcat服务器
在这里插入图片描述

  1. 创建好项目之后,我们可以设置项目的一些资源热更新:
    在这里插入图片描述

2. Servlet

2.1 Servlet的入门

  1. 概念:运行在服务器端的小程序

    • Servlet就是一个接口,定义了Java类被浏览器访问到(tomcat识别)的规则。
    • 将来我们自定义一个类,实现Servlet接口,复写方法。
  2. Servlet的快速入门:

    • 创建JavaEE项目
    • 定义一个类,实现Servlet接口
    • 实现接口中的抽象方法
    • 配置Servlet

示例代码:

package com.gy.web.servlet;

import javax.servlet.*;
import java.io.IOException;

/*
    servlet快速入门:
        1.创建JavaEE的项目
        2. 定义一个类,实现Servlet接口
        3.实现接口中的抽象方法
        4.配置Servlet
 */
public class ServletDemo1 implements Servlet {
    @Override
    public void init(ServletConfig servletConfig) throws ServletException {

    }

    @Override
    public ServletConfig getServletConfig() {
        return null;
    }

    //提供服务的方法
    @Override
    public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
        System.out.println("Hello Servlet");
    }

    @Override
    public String getServletInfo() {
        return null;
    }

    @Override
    public void destroy() {

    }
}

在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>
<!--        给servlet起个名字 都可以-->
        <servlet-name>demo1</servlet-name>
<!--        配置demo1对应的是哪个类(类名要配置全名)-->
        <servlet-class>com.gy.web.servlet.ServletDemo1</servlet-class>
    </servlet>

<!--    配置映射关系-->
    <servlet-mapping>
<!--        配置demo1珍格格名称将来可以被哪个路径访问到(也就是url-pattern配置的url参数)-->
        <servlet-name>demo1</servlet-name>
<!--        配置访问的资源路径 以 / 开头-->
        <url-pattern>/demo1</url-pattern>
    </servlet-mapping>
</web-app>

2.1.1 Servlet的执行原理:

  1. 当服务器接受到客户端浏览器的请求后,会解析请求URL路径,获取访问的Servlet的资源路径
  2. 查找web.xml文件,是否有对应的标签体内容。
  3. 如果有,则在找到对应的全类名
  4. tomcat会将字节码文件加载进内存,并且创建其对象
  5. 调用其方法

2.1.2 Servlet的方法和生命周期

  1. Servlet什么时候被创建?

    • 默认情况下,第一次被访问时,Servlet被创建
    • 可以配置执行Servlet的创建时机。
      • 在标签下配置
        • 第一次被访问时,创建的值为负数:<load-on-startup>-1</load-on-startup>
        • 在服务器启动时,创建 的值为0或正整数:<load-on-startup>1</load-on-startup>
  2. Servlet的init方法,只执行一次,说明一个Servlet在内存中只存在一个对象,Servlet是单例的

    • 多个用户同时访问时,可能存在线程安全问题。
    • 解决:尽量不要在Servlet中定义成员变量。即使定义了成员变量,也不要对修改值
  3. 提供服务:执行service方法,执行多次

    • 每次访问Servlet时,Service方法都会被调用一次。
  4. 被销毁:执行destroy方法,只执行一次

    • Servlet被销毁时执行。服务器关闭时,Servlet被销毁
    • 只有服务器正常关闭时,才会执行destroy方法。
    • destroy方法在Servlet被销毁之前执行,一般用于释放资源
public class ServletDemo1 implements Servlet {
    /**
     * servlet的初始化 只执行一次
     * @param servletConfig
     * @throws ServletException
     */
    @Override
    public void init(ServletConfig servletConfig) throws ServletException {

    }

    /**
     * 获取ServletConfig对像
     * ServletConfig: Servlet的配置对象
     * @return
     */
    @Override
    public ServletConfig getServletConfig() {
        return null;
    }

    /**
     * 提供服务的方法 执行servlet方法,执行多次
     * @param servletRequest
     * @param servletResponse
     * @throws ServletException
     * @throws IOException
     */
    @Override
    public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
        System.out.println("Hello Servlet");
    }

    /**
     * 获取Servlet的一些信息,版本、作者等等
     * @return
     */
    @Override
    public String getServletInfo() {
        return null;
    }

    /**
     * servlet被销毁的时候执行,只执行一次(正常关闭的情况下会执行,如果是非正常情况下是不会执行的)
     */
    @Override
    public void destroy() {

    }
}

2.2 Servlet3.0的注解配置

  1. Servlet3.0:
    • 好处:支持注解配置。可以不需要web.xml了。
    • 步骤:
      • 创建JavaEE项目,选择Servlet的版本3.0以上,可以不创建web.xml
      • 定义一个类,实现Servlet接口
      • 复写方法
      • 在类上使用@WebServlet注解,进行配置@WebServlet(“资源路径”) 如@WebServlet(urlPatterns = {"/demo"})因为value值可以代表urlPatterns()属性配置,而value值又可以省略不写,所以也可以写成@WebServlet( "/demo")
  • @WebServlet的代码:
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface WebServlet {
    String name() default "";

    String[] value() default {};

    String[] urlPatterns() default {};

    int loadOnStartup() default -1;

    WebInitParam[] initParams() default {};

    boolean asyncSupported() default false;

    String smallIcon() default "";

    String largeIcon() default "";

    String description() default "";

    String displayName() default "";
}

2.3 Servlet的体系结构

  1. Servlet – 接口 -> GenericServlet – 抽象类 ->HttpServlet – 抽象类
  2. GenericServlet:将Servlet接口中其他的方法做了默认空实现,只将service()方法作为抽象
    • 将来定义Servlet类时,可以继承GenericServlet,实现service()方法即可(不常用)
  3. HttpServlet:对http协议的一种封装,简化操作(简化了service()方法中需要判断请求方式的步骤)
  • 定义类继承HttpServlet
  • 复写doGet/doPost等请求方法
  1. Servlet相关配置:
    • urlpartten:Servlet访问路径:一个Servlet可以定义多个访问路径 : @WebServlet({"/d4","/dd4","/ddd4"})
    • 路径定义规则:
      • /xxx:路径匹配
      • /xxx/xxx:多层路径,目录结构
      • *.do:扩展名匹配
        示例代码:
  2. 继承GenericServlet类的书写方式
package com.gy.web.servlet;

import javax.servlet.GenericServlet;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.annotation.WebServlet;
import java.io.IOException;

@WebServlet("/demo3")
public class ServletDemo3 extends GenericServlet {

    @Override
    public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
        System.out.println("GenericServlet.....");
    }
}
  1. 继承HttpServlet的书写方式
package com.gy.web.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;

@WebServlet("/demo4")
public class ServletDemo4 extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        System.out.println("doGET........");
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        System.out.println("doPOST.......");
    }
}

//可以通过定义一个html文件制定访问路径,是/demo4 ,在设置post提交方式,就可以看到post方式的打印了,浏览器默认是get请求
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Login</title>
</head>
<body>
<form action="/Servlet_war_exploded/demo4" method="post">
    <input type="text" name="username">
    <input type="submit" value="提交表单">
</form>
</body>
</html>

3. HTTP

3.1 HTTP-基本介绍和请求消息数据格式

  1. 概念:Hyper Text Transfer Protocol 超文本传输协议(传输协议:定义了客户端和服务器端通信时,发送数据的格式)

    • 特点:
      • 基于TCP/IP的高级协议
      • 默认端口号:80
      • 基于请求/响应模型的:一次请求对应一次响应
      • 无状态的:每次请求之间相互独立,不能交互数据
    • 历史版本:
      • 1.0:每一次请求响应都会建立新的连接
      • 1.1:复用连接
  2. 请求数据的格式(请求消息:客户端发送给服务器端的数据)

    • 请求行
      • 格式: 请求方式 请求url 请求协议/版本(示例:GET /login.html HTTP/1.1)
      • 请求方式:
        • HTTP协议有7中请求方式,常用的有2种:
          • GET:
            • 请求参数在请求行中,在url后。
            • 请求的url长度有限制的
            • 不太安全
          • POST:
            • 请求参数在请求体中
            • 请求的url长度没有限制的
            • 相对安全
    • 请求头:客户端浏览器告诉服务器一些信息(格式: 请求头名称: 请求头值)
      • 常见的请求头:
        • User-Agent:浏览器告诉服务器,我访问你使用的浏览器版本信息
          • 以在服务器端获取该头的信息,解决浏览器的兼容性问题
        • Referer:http://localhost/login.html(告诉服务器,我(当前请求)从哪里来?)
          • 作用:1,房盗链(防止盗取链接);2.统计工作(比如:统计对应来源的访问次数)
    • 请求空行:空行,就是用于分割POST请求的请求头,和请求体的。
    • 请求体(正文):封装POST请求消息的请求参数的
  3. 字符串格式:
    POST /login.html HTTP/1.1
    Host: localhost
    User-Agent: Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:60.0) Gecko/20100101 Firefox/60.0
    Accept: text/html,application/xhtml+xml,application/xml;q=0.9,/;q=0.8
    Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2
    Accept-Encoding: gzip, deflate
    Referer: http://localhost/login.html
    Connection: keep-alive
    Upgrade-Insecure-Requests: 1

     	username=zhangsan	
    
  • 关于更详细的内容请参见下面的Request的内容

3.2 HTTP协议-响应消息

  • 响应消息:服务器端发送给客户端的数据

    1. 响应行:
      • 组成:协议/版本 响应状态码 状态码描述(HTTP/1.1 200 OK)
      • 响应状态码:服务器告诉客户端浏览器本次请求和响应的一个状态。
        • 状态码都是3位数字
        • 状态码的分类:
          • 1xx:服务器就收客户端消息,但没有接受完成,等待一段时间后,发送1xx多状态码
          • 2xx:成功。代表:200
          • 3xx:重定向。代表:302(重定向),304(访问缓存)
          • 4xx:客户端错误。代表:404(请求路径没有对应的资源);405:请求方式没有对应的doXxx方法
          • 5xx:服务器端错误。代表:500(服务器内部出现异常)
    2. 响应头:
      • 格式:头名称: 值
      • 常见的响应头:
        • Content-Type:服务器告诉客户端本次响应体数据格式以及编码格式
        • Content-disposition:服务器告诉客户端以什么格式打开响应体数据,值如下:
          • in-line:默认值,在当前页面内打开
          • attachment;filename=xxx:以附件形式打开响应体。文件下载
    3. 响应空行
    4. 响应体:传输的数据
  • 响应字符串格式

HTTP/1.1 200 OK
			Content-Type: text/html;charset=UTF-8
			Content-Length: 101
			Date: Wed, 06 Jun 2018 07:08:42 GMT
	
			<html>
			  <head>
			    <title>$Title$</title>
			  </head>
			  <body>
			  hello , response
			  </body>
			</html>
  • 关于更详细的内容请参见下面的Response的内容

4. Request

4.1 Request的原理和继承结构

  1. request对象和response对象的原理

    • request和response对象是由服务器创建的。我们来使用它们
      • tomacat服务器会根据url中的资源路径,创建对象的Servlet对象
      • tomcat服务器,会创建request和response,request对象中封装请求消息数据
      • tomcat将request和response两个对象传递给service方法,并且调用service方法
      • 程序员可以通过request对象获取请求消息数据,通过resopnse对象设置响应数据消息
      • 服务器在给浏览器做出响应前会从response对象中拿出程序员设置的响应消息数据
    • request对象是来获取请求消息,response对象是来设置响应消息
  2. request对象继承体系结构:
    ServletRequest – 接口
    | 继承
    HttpServletRequest – 接口
    | 实现
    org.apache.catalina.connector.RequestFacade 类(tomcat)

4.2Request的功能

4.2.1 获取请求消息数据

  1. 获取请求行数据:
    • 获取请求方式:String getMethod()
    • 获取虚拟目录(重点掌握):String getContextPath()
    • 获取Servlet路径: String getServletPath()
    • 获取get方式请求参数:String getQueryString()
    • 获取请求URI(统一资源标识符,重点掌握):String getRequestURI():
    • 获取请求URL(统一资源定位符):StringBuffer getRequestURL()
    • 获取协议及版本:String getProtocol()
    • 获取客户机的IP地址:String getRemoteAddr()

示例代码:

//访问地址http://localhost:8080/Servlet_war_exploded/requestDemo1?username=qqq&password=111

protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        //访问地址http://localhost:8080/Servlet_war_exploded/requestDemo1?username=qqq&password=111
        //1.获取请求方式
        String method = request.getMethod();
        System.out.println("method======" + method);//GET

        //2.获取虚拟目录
        String contextPath = request.getContextPath();
        System.out.println("contextPath=====" + contextPath);///Servlet_war_exploded

        //3获取Servlet路径
        String servletPath = request.getServletPath();
        System.out.println("servletPath=====" + servletPath);///requestDemo1

        //4.获取get请求的参数
        String queryString = request.getQueryString();
        System.out.println("queryString======" + queryString);//username=qqq&password=111

        //5.获取URI
        String requestURI = request.getRequestURI();
        System.out.println("requestURI====" + requestURI);///Servlet_war_exploded/requestDemo1

        //6.获取URL
        StringBuffer requestURL = request.getRequestURL();
        System.out.println("requestURL=====" + requestURL);//http://localhost:8080/Servlet_war_exploded/requestDemo1

        //7.获取协议和版本
        String protocol = request.getProtocol();
        System.out.println("protocol======" + protocol);//HTTP/1.1

        //8.获取客户的ip地址
        String remoteAddr = request.getRemoteAddr();
        System.out.println("remoteAddr======" + remoteAddr);//0:0:0:0:0:0:0:1  由于是本机访问
    }
  1. 获取请求头数据:
    • 通过请求头的名称获取请求头的值(重点掌握):String getHeader(String name)
    • 获取所有的请求头名称:Enumeration<String> getHeaderNames()

示例代码:

protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        //1.首先我们获取所有的请求头数据
        Enumeration<String> headerNames = request.getHeaderNames();//Enumeration枚举 迭代器
        while (headerNames.hasMoreElements()) {
            String s = headerNames.nextElement();
            System.out.println("s======" + s + "=====" + request.getHeader(s));
        }
        /*
           打印信息:
            s======host=====localhost:8080
            s======connection=====keep-alive
            s======upgrade-insecure-requests=====1
            s======user-agent=====Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/84.0.4147.125 Safari/537.36

            s======sec-fetch-site=====none
            s======sec-fetch-mode=====navigate
            s======sec-fetch-dest=====document
            s======accept-encoding=====gzip, deflate, br
            s======accept-language=====zh-CN,zh;q=0.9
            s======cookie=====JSESSIONID=2E04033102A078E92AD4447459435FD7; Idea-f0e63e3a=8b80cad2-1b62-443f-84c0-d46d9a597480

                */
            //s======accept=====text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9

        //获取Referer头
        String referer = request.getHeader("Referer");
        System.out.println("referer==========" + referer);
    }
  1. 获取请求体数据:
    • 请求体:只有POST请求方式,才有请求体,在请求体中封装了POST请求的请求参数
    • 获取流对象
      • BufferedReader getReader():获取字符输入流,只能操作字符数据
      • ServletInputStream getInputStream():获取字节输入流,可以操作所有类型数据(文件上传知识讲解)
    • 再从流对象中拿数据
      示例代码:
//使用post请求
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        //获取字符流
        BufferedReader reader = request.getReader(); //高效字符输入流
        String line;
        while ((line = reader.readLine()) != null) {
            System.out.println("line======" + line);
            //line======username=aaa&password=123456
        }

        //获取字节输入流
        //ServletInputStream inputStream = request.getInputStream();
    }

4.2.2 其他功能

  1. 获取请求参数通用方式:不论get还是post请求方式都可以使用下列方法来获取请求参数
    • String getParameter(String name):根据参数名称获取参数值
    • String[] getParameterValues(String name):根据参数名称获取参数值的数组
    • Enumeration getParameterNames():获取所有请求的参数名称
    • Map<String,String[]> getParameterMap():获取所有参数的map集合

通用方法示例代码:

protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        //通用方法
        //1.根据参数名获取参数值
        String username = request.getParameter("username");
        System.out.println("username====" + username);

        System.out.println("--------------");

        //2.根据参数名称获取参数值的数组
        String[] hobbies = request.getParameterValues("hobby");
        for (String str : hobbies) {
            System.out.println("str======" + str);
        }

        System.out.println("------------");
        //3获取所有请求参数数组
        Enumeration<String> parameterNames = request.getParameterNames();
        while (parameterNames.hasMoreElements()) {
            String s = parameterNames.nextElement();
            System.out.println(s);
        }

        System.out.println("----------------");
        //4.获取所有参数的mao集合
        Map<String, String[]> parameterMap = request.getParameterMap();
        Set<String> strings = parameterMap.keySet();
        for (String str : strings) {
            String[] strings1 = parameterMap.get(str);
            for (String str1 : strings1) {
                System.out.println(str + "====" + str1);
            }
        }
    }

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        doPost(request,response);
    }

测试过程中发现一个问题:get请求在输入中文的情况下,不会乱码,但是post请求在由中文的情况下,在读取数据的时候回产生乱码情况,如何解决?

  • get方式:tomcat 8 已经将get方式乱码问题解决了
  • post方式:会乱码 解决:在获取参数前,设置request的编码request.setCharacterEncoding("utf-8");,这里编码需要和你的访问客户端(这里只静态网页)编码一样
  1. 请求转发:一种在服务器内部的资源跳转方式

    • 步骤:
      • 通过request对象获取请求转发器对象:RequestDispatcher getRequestDispatcher(String path)
      • 使用RequestDispatcher对象来进行转发:forward(ServletRequest request, ServletResponse response)
    • 特点:
      • 浏览器地址栏路径不发生变化
      • 只能转发到当前服务器内部资源中。
      • 转发是一次请求
  2. 共享数据:

    • 域对象:一个有作用范围的对象,可以在范围内共享数据
    • request域:代表一次请求的范围,一般用于请求转发的多个资源中共享数据
    • 方法:
      • void setAttribute(String name,Object obj):存储数据
      • Object getAttitude(String name):通过键获取值
      • void removeAttribute(String name):通过键移除键值对
  • 示例代码
//第一个Servlet
@WebServlet("/requestDemo5")
public class RequestDemo5 extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        //1.设置一个共享数据
        request.setAttribute("username","test");

        //2.获取转发器对象,进行转发
        request.getRequestDispatcher("/requestDemo6").forward(request,response);
        System.out.println("demo5........");
    }

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        doPost(request,response);
    }
}


//第二个Servlet
@WebServlet("/requestDemo6")
public class RequestDemo6 extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        System.out.println("emo6.。。。。。。。。");
        //获取共享数据
        Object username = request.getAttribute("username");
        System.out.println("username=====" + username);
    }

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        doPost(request,response);
    }
}

5 阶段性案例

5.1 案例开发分析

  • 用户登录案例需求:
    1.编写login.html登录页面:username & password 两个输入框
    2.使用Druid数据库连接池技术,操作mysql,day14数据库中user表
    3.使用JdbcTemplate技术封装JDBC
    4.登录成功跳转到SuccessServlet展示:登录成功!用户名,欢迎您
    5.登录失败跳转到FailServlet展示:登录失败,用户名或密码错误

5.2 案例代码实现

  • 开发步骤:
  1. 创建项目,导入heml页面,配置文件,jar包
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <form action="/TestCase_war_exploded/loginServlet" method="post">
        用户名:<input type="text" name="username"> <br>
        密码:<input type="password" name="password"><br>

        <input type="submit" value="登录">

    </form>
</body>
</html>
  1. 创建数据库环境,可以使用mysql数据的图形操作软件创建数据库
CREATE DATABASE testcase;

USE testcase;

-- 创建一个user表 
CREATE TABLE user_tab(
id INT PRIMARY KEY auto_increment,
username VARCHAR(32) NOT NULL UNIQUE,
pwd VARCHAR(32) NOT NULL
);

-- 插入一条数据
INSERT INTO user_tab VALUE (NULL,'test','123');
SELECT * FROM user_tab;
  1. 创建数据连接池,工具类的使用
// 用户实例类
package com.gy.domain;

public class User {
    private int id;
    private String username;
    private String pwd;

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public String getPwd() {
        return pwd;
    }

    public void setPwd(String pwd) {
        this.pwd = pwd;
    }

    @Override
    public String toString() {
        return "User{" +
                "id=" + id +
                ", username='" + username + '\'' +
                ", pwd='" + pwd + '\'' +
                '}';
    }
}

//数据库工具类
package com.gy.util;

import com.alibaba.druid.pool.DruidDataSourceFactory;

import javax.sql.DataSource;
import java.io.IOException;
import java.io.InputStream;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.Properties;


public class JDBCUtils {
    private static DataSource ds;

    //记载数据库链接
    static {
        Properties properties = new Properties();
        InputStream inputStream = JDBCUtils.class.getClassLoader().getResourceAsStream("druid.properties");
        try {
            properties.load(inputStream);

            try {
                ds = DruidDataSourceFactory.createDataSource(properties);
            } catch (Exception e) {
                e.printStackTrace();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }

    }

    public static DataSource getDataSource() {
        return ds;
    }

    public static Connection getConnection() throws SQLException {
        return ds.getConnection();
    }

}
//JDBCTemple类
package com.gy.dao;

import com.gy.domain.User;
import com.gy.util.JDBCUtils;
import org.springframework.dao.DataAccessException;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.JdbcTemplate;

//实现user表的所有查询操作
public class UserDao {
    private JdbcTemplate template = new JdbcTemplate(JDBCUtils.getDataSource());

    public User longin(User user) {
        //这里需要进行数据库的查询操作
        String sql = "select * from user_tab where username = ? and pwd = ?";
        //这个方法 如果没有查询到会抛出异常,所以我们希望把这个异常抛出
        try {
            User user1 = template.queryForObject(sql, new BeanPropertyRowMapper<>(User.class), user.getUsername(), user.getPwd());
            return user1;
        } catch (DataAccessException e) {
            e.printStackTrace(); //这里可以记录日志
            return  null;
        }
    }
}

  1. 编写Servley类
//LoginServlet servler类
@WebServlet("/loginServlet")
public class LoginServlet extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        //1.设置请求的编码
        request.setCharacterEncoding("utf-8");
        //2.获取用户名和密码
        String username = request.getParameter("username");
        String pwd = request.getParameter("password");
        User user = new User();
        user.setUsername(username);
        user.setPwd(pwd);
        //3.去数据库查询表中数据
        User longin = new UserDao().longin(user);

        if (longin == null) {
            //表示登录失败
            request.getRequestDispatcher("/failServlet").forward(request,response);
        }else {
            //表示登录成功,首先需要设置共享数据用户名
            request.setAttribute("username",user.getUsername());
            request.getRequestDispatcher("/successServlet").forward(request,response);
        }
    }

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        doPost(request,response);
    }
}

//failServlet类
@WebServlet("/failServlet")
public class FailServlet extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        //告诉用户
        //设置编码
        response.setContentType("text/html;charset=utf-8");
        //输出
        response.getWriter().write("登录失败,用户名或密码错误!");
    }

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        doPost(request,response);
    }
}

//SuccessServlet
@WebServlet("/successServlet")
public class SuccessServlet extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        //获取共享数据用户名
        //设置编码
        response.setContentType("text/html;charset=utf-8");
        Object username = request.getAttribute("username");
        //输出
        response.getWriter().write("登录成功!" + username + ",欢迎您");
    }

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        doPost(request,response);
    }
}

5.3 案例-JavaBean工具类的使用

  • 使用commons-beanutils-1.8.0把map集合转成实例对象 jar包,首先导入jar包
  • Beanutils的简单运用
        request.setCharacterEncoding("utf-8");
        //2.获取用户名和密码
//        String username = request.getParameter("username");
//        String pwd = request.getParameter("password");
        User user = new User();
//        user.setUsername(username);
//        user.setPwd(pwd);
        Map<String, String[]> parameterMap = request.getParameterMap();
        //使用Beanutils来封装对象
        //3.去数据库查询表中数据
        try {
            BeanUtils.populate(user,parameterMap);
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }
  • BeanUtils工具类,简化数据封装:用于封装JavaBean的
    1. JavaBean:标准的Java类
      • 类必须被public修饰
      • 必须提供空参的构造器
      • 成员变量必须使用private修饰
      • 提供公共setter和getter方法
    2. 功能:封装数据
    3. 概念:
      • 成员变量:
      • 属性:setter和getter方法截取后的产物(例如:getUsername() --> Username–> username)
      • 成员变量和属性大多数情况下名称相同, BeanUtils操作是属性,而不是成员变量
    4. 方法:
      1. setProperty()
      2. getProperty()
      3. populate(Object obj , Map map):将map集合的键值对信息,封装到对应的JavaBean对象中

6. Response

6.1 Response的概述

  • 功能:设置响应消息
    1. 设置响应行
      • 格式:HTTP/1.1 200 ok
      • 设置状态码:setStatus(int sc)
    2. 设置响应头:setHeader(String name, String value)
    3. 设置响应体:
      • 使用步骤如下
        • 获取输出流:字符输出流PrintWriter getWriter();字节输出流ServletOutputStream getOutputStream()
        • 使用输出流,将数据输出到客户端浏览器

6.2 案例

6.2.1 案例_重定向

@WebServlet("/responseDemo1")
public class ResponseDemo1 extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        System.out.println("demo1-----------");
//        //1.设置状态码为302
//        response.setStatus(302);
//        //2.设置响应头的location
//        response.setHeader("location","/Servlet_war_exploded/responseDemo2");

        // 3.简单的设置重定向的方法
        response.sendRedirect("/Servlet_war_exploded/responseDemo2");
    }

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        doPost(request,response);
    }
}
@WebServlet("/responseDemo2")
public class ResponseDemo2 extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        System.out.println("demo2+++++++++");
    }

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        doPost(request, response);
    }
}
  1. 简单的设置重定向的方法:response.sendRedirect()

  2. 重定向(redirect)和转发(forward) 区别:

    • redirect地址栏会发生变化,而forward地址栏不会发生变化
    • redirect可以访问其他站点(服务器)的资源,而forward只能访问当前服务器下的资源
    • redirect是两次请求,不能使用request对象来共享数据,而forward是一次请求,可以使用request对象来共享数据
  3. 路径分类:

    • 相对路径:通过相对路径不可以确定唯一资源
      • 如:./index.html
      • 不以/开头,以.开头路径
      • 规则:找到当前资源和目标资源之间的相对位置关系
        • ./:当前目录
        • …/:后退一级目录
    • 绝对路径:通过绝对路径可以确定唯一资源
      • 如:http://localhost/day15/responseDemo2 /day15/responseDemo2
      • 以/开头的路径
      • 规则:判断定义的路径是给谁用的?判断请求将来从哪儿发出
        • 给客户端浏览器使用:需要加虚拟目录(项目的访问路径)
          • 建议虚拟目录动态获取:request.getContextPath()
          • , 重定向…
        • 给服务器使用:不需要加虚拟目录 如:转发路径

6.2.2服务器输出字符(字节)数据到浏览器

  1. 获取获取字符输出流,然后输出数据
  2. 乱码问题:
    • PrintWriter pw = response.getWriter();获取的流的默认编码是ISO-8859-1
    • 设置该流的默认编码
    • 告诉浏览器响应体使用的编码
@WebServlet("/responseDemo3")
public class ResponseDemo3 extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        System.out.println("responseDemo3......");
        //服务器输出的数据,在客户端显示可能会出现乱码的情况,那我们如何解决这个问题?
        //首先我们应该告诉客户端,数据传输的是什么格式,使用什么编码来解码.通过响应头中的content-type字段来告诉客户端 设置编码一定要在获取流对象之前设置
        // 解析的格式
        //第一种方法
        //response.setHeader("content-type","text/html;charset=utf-8");

        //第二种方法-简单的形式,设置编码,是在获取流之前设置
        response.setContentType("text/html;charset=utf-8");

        //首先获取字符输出流
        response.getWriter().write("hello 你好啊,中国 response");
        
		//获取字节流
        response.getOutputStream().write("hello 你好".getBytes());
    }

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        doPost(request,response);
    }
}

7.ServletContext对象

  1. 概念:代表整个web应用,可以和程序的容器(服务器)来通信
  2. 获取:
    • 通过request对象获取:request.getServletContext();
    • 通过HttpServlet获取:this.getServletContext();
  3. 功能:
    • 获取MIME类型:
      • MIME类型:在互联网通信过程中定义的一种文件数据类型
      • 格式: 大类型/小类型 text/html image/jpeg
      • 获取:String getMimeType(String file)
    • 域对象:共享数据
      • setAttribute(String name,Object value)
      • getAttribute(String name)
      • removeAttribute(String name)
      • ServletContext对象范围:所有用户所有请求的数据
    • 获取文件的真实(服务器)路径
      • 方法:String getRealPath(String path)

示例代码:

//1.获取servletContext的方法有两种
        //1.直接request获取
        ServletContext servletContext = request.getServletContext();

        //第二中直接获取httpServlet对象中的 servletContext对象
        ServletContext servletContext1 = this.getServletContext();

//1.获取servletContext中的功能
        //1.获取MIME类型:在互联网通信过程中定义的一种文件数据类型
        String file = "a.png";
        ServletContext servletContext = request.getServletContext();
        String mimeType = servletContext.getMimeType(file);
        System.out.println("mimeType=======" + mimeType);

        //2.ServlertContext也是一个域对象,可以共享数据,
        //只不过ServletContext对象范围:是所有用户共享数据,和request只能在一次请求中共享数据不一样
        //方法和request域的方法一样
 
        //3.获取文件的真实路径 方法:String getRealPath(String path)获取的是真实的在服务器上运行的路径   所有的资源文件都是放在服务器的web目录下
        //1.获取web文件下的a.text路径
        String realPath = servletContext.getRealPath("/b.text");
        System.out.println("realPath=====" + realPath);
        //获取WEB-INF文件下的c.text
        String realPath1 = servletContext.getRealPath("/WEB-INF/c.text");
        System.out.println("realPath1=====" + realPath1);

        //获取src文件下的a.text文件的路径。因为src目录下的文件将来都放在服务器的WEB-INF下的classes文件下
        String realPath2 = servletContext.getRealPath("/WEB-INF/classes/a.text");
        System.out.println("realPath2======" + realPath2);

8. 文件下载案例

  • 要求:

    1. 页面显示超链接
    2. 点击超链接后弹出下载提示框
    3. 完成图片下载
  • 分析:

    • 超链接指向的资源如果能够被浏览器解析,则在浏览器中展示,如果不能解析,则弹出下载提示框。不满足需求
    • 任何资源都必须弹出下载提示框
    • 使用响应头设置资源的打开方式:
      • content-disposition:attachment;filename=xxx只要设置这个头信息,任何资源都不会再在浏览器中默认打开,都是以附件的形式打开
  • 步骤:

    1. 定义页面,编辑超链接href属性,指向Servlet,传递资源名称filename
    2. 定义Servlet
      • 获取文件名称
      • 使用字节输入流加载文件进内存
      • 指定response的响应头: content-disposition:attachment;filename=xxx
      • 将数据写出到response输出流
  • html页面

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>下载</title>
</head>
<body>
<!--由于浏览器可以直接解析,所以不会弹出提示框下载-->
<a href="/Servlet_war_exploded/img/login_bg.png">下载图片</a>
<!--指定路径是某个servlet,然后传递参数 -->
<a href="/Servlet_war_exploded/servletContextDemo3?fileName=login_bg.png">下载图片,指向servlet传递文件名</a>

</body>
</html>
  • servlet里面的代码
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        /*
        1. 定义页面,编辑超链接href属性,指向Servlet,传递资源名称filename
		2. 定义Servlet
			1. 获取文件名称
			2. 使用字节输入流加载文件进内存
			3. 指定response的响应头: content-disposition:attachment;filename=xxx
			4. 将数据写出到response输出流
         */
        //1.获取请求参数
        String fileName = request.getParameter("fileName");

        //使用字节流输入流加载文件进内存
        //2.1找到这个文件的真实路径
        ServletContext servletContext = this.getServletContext();
        String realPath = servletContext.getRealPath("/img/" + fileName);
        //2.2用字节流关联
        FileInputStream inputStream = new FileInputStream(realPath);

        //3.设置头的信息,让文件以附件的形式打开
        //设置返回数据类型
        String mimeType = servletContext.getMimeType(fileName);
        response.setContentType(mimeType);

        //设置响应头的打开方式一附件的形式打开一附件的形式打开
        response.setHeader("content-disposition","attachment;filename="+fileName);

        //3.将输入流 读数据写出到输出流中
        ServletOutputStream outputStream = response.getOutputStream();
        byte[] bytes = new byte[1024 * 8];
        int length = 0;
        while ((length = inputStream.read(bytes)) != -1) {
            //把读取的数据写入到浏览器中
            outputStream.write(bytes,0,length);
        }

        inputStream.close();
    }

  • 但是上述存一个常见的问题,如果文件名称是中文名称解决思路:

    • 取客户端使用的浏览器版本信息
    • 根据不同的版本信息,设置filename的编码方式不同
  • 在设置响应头以附件形式打开之前设置文件名是中文的问题,也就是 在代码response.setHeader("content-disposition","attachment;filename="+fileName);之前设置

	 //解决中文文件名的问题
        //.1获取user-agent请求头
        String agent = request.getHeader("user-agent");
        //2.使用工具类编码文件名称
        fileName = DownLoadUtils.getFileName(agent,fileName);
        response.setHeader("content-disposition","attachment;filename="+fileName);
  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值