JavaWeb基础学习笔记10——Servlet

目录

1、Servlet概念

2、Servlet快速入门

3、Servlet执行原理

4、Servlet中的生命周期

5、Servlet 3.0

6、IDEA与Tomcat相关配置

7、Servlet的体系结构

7.1、GenericServlet -- 抽象类

7.2、HttpServlet -- 抽象类

8、Servlet相关配置

9、Request对象

9.1、Request和Response原理

9.2、Request对象继承体系结构

9.3、Request的功能

9.3.1、获取请求消息数据

9.3.2、其他功能

9.3.3、案例:用户登录

9.3.4、BeanUtils工具类

10、Response对象

10.1、Response的功能

10.1.1、设置响应消息数据

10.1.2、案例1:完成重定向

10.1.3、案例2:服务器输出字符数据到浏览器

10.1.4、案例3:服务器输出字节数据到浏览器

10.1.5、案例4:验证码

11、ServletContext对象

11.1、ServletContext对象的概念

11.2、ServletContext对象的功能

11.3、案例:文件下载


1、Servlet概念

Servlet(server applet),运行在服务器端的小程序。Servlet就是一个接口,定义了Java类被浏览器访问到(被Tomcat识别)的规则。

将来我们自定义一个类,实现Servlet接口,重写方法。

2、Servlet快速入门

步骤:

1、创建JavaEE项目(参考学习笔记9);

2、定义一个类,如果IDEA无法自动导入Servlet,那么依次点击File -> Project Structure -> Libraries -> + 号 -> 添加 servlet-api.jar 即可。

3、实现接口中的抽象方法:

package web.servlet;

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

/**
 * Servlet快速入门
 */
public class ServletDemo1 implements Servlet {
    @Override
    public void init(ServletConfig servletConfig) throws ServletException {

    }

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

    /**
     * 提供服务的方法
     * @param servletRequest
     * @param servletResponse
     * @throws ServletException
     * @throws IOException
     */
    @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() {

    }
}

4、配置Servlet:在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_4_0.xsd"
         version="4.0">

<!--    配置Servlet-->
    <servlet>
        <servlet-name>demo1</servlet-name>
        <servlet-class>web.servlet.ServletDemo1</servlet-class>
    </servlet>

    <servlet-mapping>
        <servlet-name>demo1</servlet-name>
        <url-pattern>/demo1</url-pattern>
    </servlet-mapping>
</web-app>

3、Servlet执行原理

1、当服务器接收到客户端浏览器的请求后,会解析请求的URL路径,获取访问到Servlet的资源路径;

2、查找web.xml文件是否有对应的<url-pattern>标签体内容;

3、如果有,则再找到对应的<servlet-class>的全类名;

4、tomcat会将字节码文件加载进内存,并创建其对象;

5、调用其方法。

4、Servlet中的生命周期

1、被创建:执行init方法,只执行一次。

        *Servlet什么时候被创建?

                默认情况下,第一次被访问时,Servlet被创建。

                可以配置执行Servlet的创建时机。在<servlet>标签下配置:

<!--    配置Servlet-->
    <servlet>
        <servlet-name>demo2</servlet-name>
        <servlet-class>web.servlet.ServletDemo2</servlet-class>

        <!--    执行Servlet的创建时机
            1、第一次被访问时创建;<load-on-startup>的值为负数(默认-1)
            2、在服务器启动时,创建:<load-on-startup>的值为0或正整数(一般0~10之间)
        -->
        <load-on-startup>5</load-on-startup>
    </servlet>

    <servlet-mapping>
        <servlet-name>demo2</servlet-name>
        <url-pattern>/demo2</url-pattern>
    </servlet-mapping>

        *Servlet的init方法,只执行一次,说明一个Servlet在内存中只存在一个对象,Servlet是单例的。多个用户同时访问时,可能存在线程安全问题。解决方案:尽量不要在Servlet中定义成员变量。即使定义了成员变量,也不要对其修改。

2、提供服务:执行service方法,执行多次。

        *每次访问Servlet时,Servlet方法都会被调用一次。

3、被销毁:执行destroy方法,执行一次。

        *Servlet被销毁时执行。服务器关闭时,Servlet被销毁。

        *只有服务器正常关闭时,才会执行destroy方法。

        *destroy方法在Servlet被销毁之前执行,一般用于释放资源。

package web.servlet;

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

/**
 * Servlet方法
 */
public class ServletDemo2 implements Servlet {
    /**
     * 初始化方法
     * 在Servlet被创建时执行,只会执行一次
     * @param servletConfig
     * @throws ServletException
     */
    @Override
    public void init(ServletConfig servletConfig) throws ServletException {
        System.out.println("init......");
    }

    /**
     * 获取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("service....");
    }

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

    /**
     * 销毁方法
     * 在服务器正常关闭,Servlet被杀死时执行,执行一次
     */
    @Override
    public void destroy() {
        System.out.println("destroy....");
    }
}

5、Servlet 3.0

好处:支持注解配置,可以不需要web.xml了。

步骤:

1、创建JavaEE项目,选择Servlet的版本3.0以上,可以不创建web.xml;

2、定义一个类,实现Servlet接口;

3、重写方法;

4、在类上使用@WebServlet注解,进行配置。

@WebServlet("资源路径")

6、IDEA与Tomcat相关配置

1、IDEA会为每一个Tomcat部署的项目单独建立一份配置文件。查看控制台的log:

Using CATALINA_BASE:   "C:\Users\Administrator\AppData\Local\JetBrains\IntelliJIdea2021.1\tomcat\85c2d8c2-188c-445c-a915-35ce35309b0e"

2、工作空间项目 和 Tomcat部署的web项目。

        *Tomcat真正访问的是"Tomcat部署的web项目"(out文件夹),该项目对应着“工作空间项目”的web目录下的所有资源。

        *WEB-INF目录下的资源不能被浏览器直接访问。

3、断点调试:使用 “小虫子” 启动(debug 启动)。

7、Servlet的体系结构

Servlet -- 接口。有一个实现类GenericServlet。

7.1、GenericServlet -- 抽象类

GenericServlet:将Servlet接口中的其它方法做了空实现,只将service()方法作为抽象。将来定义Servlet类时,可以继承GenericServlet,实现service()方法即可。

7.2、HttpServlet -- 抽象类

HttpServlet:对http协议的一种封装,简化操作。HttpServlet是GenericServlet的子类。

1、定义类继承HttpServlet;

2、重写doGet() / doPost() 方法。

package 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("/demo3")
public class ServletDemo3 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");
    }
}

8、Servlet相关配置

1、urlpattern:Servlet访问路径。

        *一个Servlet可以定义多个访问路径:

package 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({"/d4","/dd4","/ddd4"})
public class ServletDemo4 extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        System.out.println("demo4...");
    }
}

        *路径的定义规则:① /xxx;② /xxx/xxx:多层路径,目录结构;③ *.xxx。

package 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;

/**
 * Servlet路径配置
 */
//@WebServlet({"/d4","/dd4","/ddd4"})
//@WebServlet("/user/demo4")
//@WebServlet("/*")
//@WebServlet("/user/*") //*代表任意字符串
@WebServlet("*.do") //localhost/demo4.do
public class ServletDemo4 extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        System.out.println("demo4...");
    }
}

9、Request对象

9.1、Request和Response原理

1、Request和Response对象是由服务器创建的,我们来使用它们;

2、Request对象是来获取请求消息,Response对象是来设置响应消息。

9.2、Request对象继承体系结构

ServletRequest -- 接口

        ^ 继承

HttpServletRequest -- 接口

        ^ 实现

org.apache.catalina.connector.RequestFacade@76fbbdc4 -- 实现类

9.3、Request的功能

9.3.1、获取请求消息数据

1、获取请求行数据:

请求行:
GET /ServletProject/demo?name=zhangsan HTTP/1.1


方法:
-获取请求方式:GET
String getMethod();

-获取虚拟目录:/ServletProject
String getContextPath();

-获取Servlet路径:/demo
String getServletPath();

-获取get方式的请求参数:name=zhangsan
String getQueryString();

-获取请求URI:/ServletProject/demo
String getRequestURI(); // /ServletProject/demo
StringBuffer getRequestURL(); // http://localhost/ServletProject/demo
    URI:统一资源标识符(范围更大)
    URL:统一资源定位符

-获取协议及版本:HTTP/1.1
String getProtocol();

-获取客户机的IP地址
String getRemoteAddr();

注:快速创建Servlet的方法:File -> Project Structure -> Facets -> Source Roots 打勾。创建时直接点击 New -> Servlet 即可。

package web.request;

import javax.servlet.*;
import javax.servlet.http.*;
import javax.servlet.annotation.*;
import java.io.IOException;
import java.net.URI;

/**
 * 演示Request对象获取请求行数据
 */
@WebServlet("/requestDemo1")
public class RequestDemo1 extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        //-获取请求方式:GET
        //String getMethod();
        String method = request.getMethod();
        System.out.println(method);

        //-获取虚拟目录:/ServletProject
        //String getContextPath();
        String contextPath = request.getContextPath();
        System.out.println(contextPath);

        //-获取Servlet路径:/demo
        //String getServletPath();
        String servletPath = request.getServletPath();
        System.out.println(servletPath);

        //-获取get方式的请求参数:name=zhangsan
        //String getQueryString();
        String queryString = request.getQueryString();
        System.out.println(queryString);

        //-获取请求URI:/ServletProject/demo
        //String getRequestURI(); // /ServletProject/demo
        String requestURI = request.getRequestURI();
        System.out.println(requestURI);
        //StringBuffer getRequestURL(); // http://localhost/ServletProject/demo
        StringBuffer requestURL = request.getRequestURL();
        System.out.println(requestURL);

        //-获取协议及版本:HTTP/1.1
        //String getProtocol();
        String protocol = request.getProtocol();
        System.out.println(protocol);

        //-获取客户机的IP地址
        //String getRemoteAddr();
        String remoteAddr = request.getRemoteAddr();
        System.out.println(remoteAddr);

    }

    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

    }
}

2、获取请求头数据:

方法:
-通过请求头的名称获取请求头的值
String getHeader(String name);

-获取所有的请求头名称
Enumeration<String> getHeaderName();
package web.request;

import javax.servlet.*;
import javax.servlet.http.*;
import javax.servlet.annotation.*;
import java.io.IOException;
import java.util.Enumeration;

@WebServlet("/requestDemo2")
public class RequestDemo2 extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException {
        //演示获取请求头数据
        //-获取所有的请求头名称
        //Enumeration<String> getHeaderName();
        Enumeration<String> headerNames = request.getHeaderNames();

        while(headerNames.hasMoreElements()) {
            String name = headerNames.nextElement();
            //-通过请求头的名称获取请求头的值
            //String getHeader(String name);
            String value = request.getHeader(name);
            System.out.println(name+"----"+value);

        }

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

        //获取请求头数据 user-agent
        String agent = request.getHeader("user-agent"); //不区分大小写
        //判断agent的浏览器版本
        if(agent.contains("Chrome")) {
            //谷歌
            System.out.println("谷歌来了。。。");
        }else if(agent.contains("Firefox")) {
            //火狐
            System.out.println("火狐来了...");
        }

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

        //获取请求头数据 referer
        String referer = request.getHeader("referer");
        System.out.println(referer); //http://localhost/ServletProject/login.html
        //防盗链
        if(referer != null) {
            if(referer.contains("ServletProject")) {
                //正常访问
                System.out.println("播放电影...");
                response.setContentType("text/html;charset=utf-8");
                response.getWriter().write("播放电影...");
            }else{
                //盗链
                System.out.println("想看电影吗?来优酷吧..");
                response.setContentType("text/html;charset=utf-8");
                response.getWriter().write("想看电影吗?来优酷吧..");
            }
        }
    }

    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

    }
}

3、获取请求体数据:

请求体:只有POST请求方式才有请求体,在请求体中封装了POST请求的请求参数
步骤:
1、获取流对象
    BufferedReader getReader(); //获取字符输入流,只能操作字符数据
    ServletInputStream getInputStream(); //获取字节输入流,可以操作所有类型的数据
        *在文件上传知识点后讲解
2、再从流对象中拿数据
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>注册页面</title>
</head>
<body>

<form action="/ServletProject/requestDemo3" method="post">
  <input type="text" placeholder="请输入用户名" name="username"><br />
  <input type="text" placeholder="请输入密码" name="password"><br />
  <input type="submit" value="注册">
</form>

</body>
</html>
package web.request;

import javax.servlet.*;
import javax.servlet.http.*;
import javax.servlet.annotation.*;
import java.io.BufferedReader;
import java.io.IOException;

@WebServlet("/requestDemo3")
public class RequestDemo3 extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

    }

    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        //获取请求消息体 -- 请求参数
        //1、获取字符流
        BufferedReader br = request.getReader();
        //2、读取数据
        String line = null;
        while((line = br.readLine()) != null) {
            System.out.println(line);
        }
    }
}

9.3.2、其他功能

-获取请求参数通用方式(兼容GET和POST,不论get还是post请求方式都可以用下列方法获取请求参数)
String getParameter(String name); //根据参数名称获取参数值 
    如:username=zhangsan&password=123
String getParameterValues(String name); //根据参数名称获取参数值的数组
    如:hobby=xx&hobby=game
Enumeration<String> getParameterNames(); //获取所有请求的参数名称
Map<String, String[]> getParameterMap(); //获取所有参数键值对的集合
*常见问题:中文乱码问题
    get方式:tomcat8已经将get方式乱码问题解决了
    post方式:会乱码。
        解决:在获取参数前,设置request的编码:request.setCharacterEncoding("utf-8");

-请求转发:一种在服务器内部的资源跳转方式
步骤:
    第一步:通过request对象获取请求转发器对象
        RequestDispatcher getRequestDispatcher(String path);
    第二步:使用RequestDispatcher对象进行转发
        forward(ServletRequest request, ServletResponse response);
特点:
    1、浏览器地址栏路径不发生变化;
    2、只能转发到当前服务器内部资源中;
    3、转发是一次请求。

-共享数据
*域对象:一个有作用范围的对象,可以在范围内共享数据。
*request域:代表一次请求的范围,一般用于请求转发的多个资源中共享数据。
*方法:
    1、void setAttribute(String name, Object obj); //存储数据
    2、Object getAttribute(String name); //通过键获取值
    3、void removeAttribute(String name); //通过键移除键值对

-获取ServletContext对象
方法:
    ServletContext getServletContext(); //获取ServletContext对象
package web.request;

import javax.servlet.*;
import javax.servlet.http.*;
import javax.servlet.annotation.*;
import java.io.IOException;
import java.util.Enumeration;
import java.util.Map;
import java.util.Set;

@WebServlet("/requestDemo4")
public class RequestDemo4 extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        //get 获取请求参数
        //String getParameter(String name); //根据参数名称获取参数值
//        String username = request.getParameter("username");
//        System.out.println("get");
//        System.out.println(username);

        //String getParameterValues(String name); //根据参数名称获取参数值的数组

        //Enumeration<String> getParameterNames(); //获取所有请求的参数名称
        //Map<String, String[]> getParameterMap(); //获取所有参数键值对的集合

        this.doPost(request, response);
    }

    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        //解决中文乱码问题:设置流的编码
        request.setCharacterEncoding("utf-8");
        //post 获取请求参数
        //String getParameter(String name); //根据参数名称获取参数值
        String username = request.getParameter("username");
        System.out.println("post");
        System.out.println(username);
        System.out.println("------------");

        //String getParameterValues(String name); //根据参数名称获取参数值的数组
        String[] hobbies = request.getParameterValues("hobby");
        for(String hobby : hobbies) {
            System.out.println(hobby);
        }
        //Enumeration<String> getParameterNames(); //获取所有请求的参数名称
        Enumeration<String> parameterNames = request.getParameterNames();
        while(parameterNames.hasMoreElements()) {
            String name = parameterNames.nextElement();
            System.out.println(name);
            String value = request.getParameter(name);
            System.out.println(value);
            System.out.println("---");
        }

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

        //Map<String, String[]> getParameterMap(); //获取所有参数键值对的集合
        Map<String, String[]> parameterMap = request.getParameterMap();
        //遍历
        Set<String> keySet = parameterMap.keySet();
        for (String name : keySet) {
            //根据键获取值
            String[] values = parameterMap.get(name);
            System.out.println(name);
            for(String value : values) {
                System.out.println(value);
            }
            System.out.println("---");
        }
    }
}
package web.request;

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

@WebServlet("/requestDemo5")
public class RequestDemo5 extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        this.doPost(request, response);
    }

    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        System.out.println("demo5被访问了");
        //转发到demo5a资源
        request.getRequestDispatcher("/requestDemo5a").forward(request, response);
    }
}
package web.request;

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

@WebServlet("/requestDemo5")
public class RequestDemo5 extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        this.doPost(request, response);
    }

    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        System.out.println("demo5被访问了");
        //存储数据到request域中
        request.setAttribute("msg", "hello");

        //转发到demo5a资源
        request.getRequestDispatcher("/requestDemo5a").forward(request, response);
    }
}


package web.request;

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

@WebServlet("/requestDemo5a")
public class RequestDemo5a extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        this.doPost(request, response);
    }

    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        System.out.println("demo5a被访问了");
        //获取数据
        Object msg = request.getAttribute("msg");
        System.out.println(msg);
    }
}

9.3.3、案例:用户登录

需求:

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

分析:

开发步骤:

  1. 创建项目,导入html页面,配置文件和jar包;
  2. 创建数据库环境;
    USE mydatabase;
    
    CREATE TABLE USER(
    	id INT PRIMARY KEY AUTO_INCREMENT,
    	username VARCHAR(32) UNIQUE NOT NULL,
    	PASSWORD VARCHAR(32) NOT NULL
    );
  3. 创建包domain,创建User类。
    package domain;
    
    /**
     * 用户的实体类
     */
    public class User {
        private int id;
        private String username;
        private String password;
    
        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 getPassword() {
            return password;
        }
    
        public void setPassword(String password) {
            this.password = password;
        }
    
        @Override
        public String toString() {
            return "User{" +
                    "id=" + id +
                    ", username='" + username + '\'' +
                    ", password='" + password + '\'' +
                    '}';
        }
    }
  4. 创建包dao,创建UserDao类,提供login方法。
    package 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;
    
    /**
     * JDBC工具类,使用Druid连接池
     */
    public class JDBCUtils {
        private static DataSource ds;
    
        static {
            try {
                //1、加载配置文件
                Properties pro = new Properties();
                //使用ClassLoader加载配置文件,获取字节输入流
                InputStream is = JDBCUtils.class.getClassLoader().
                        getResourceAsStream("druid.properties");
                pro.load(is);
    
                //2、初始化连接池对象
                ds = DruidDataSourceFactory.createDataSource(pro);
            } catch (IOException e) {
                e.printStackTrace();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    
        /**
         * 获取连接池对象
         */
        public static DataSource getDataSource() {
            return ds;
        }
    
        /**
         * 获取连接Connection对象
         * @return
         */
        public static Connection Connection() throws SQLException {
            return ds.getConnection();
        }
    }
    
    
    /****************************************************************************/
    package dao;
    
    import domain.User;
    import org.springframework.dao.DataAccessException;
    import org.springframework.jdbc.core.BeanPropertyRowMapper;
    import org.springframework.jdbc.core.JdbcTemplate;
    import util.JDBCUtils;
    
    /**
     * 操作数据库中User表的类
     */
    public class UserDao {
        //声明JDBCTemplate对象共用
        private JdbcTemplate template = new JdbcTemplate(JDBCUtils.getDataSource());
    
        /**
         * 登陆方法
         * @param loginUser 只有用户名和密码
         * @return user包含用户全部数据,没有查询到,返回null
         */
        public User login(User loginUser) {
            try {
                //1、编写sql
                String sql = "select * from user where username = ? and password = ?";
                //2、调用query方法
                User user = template.queryForObject(sql,
                        new BeanPropertyRowMapper<User>(User.class),
                        loginUser.getUsername(), loginUser.getPassword());
    
                return user;
            } catch (DataAccessException e) {
                e.printStackTrace(); //记录日志
                return null;
            }
        }
    }
  5. 创建web.servlet包,编写LoginServlet类。

    注意:login.html中form表单的action路径的写法:
    "/虚拟目录/Servlet的资源路径"
    
    /*****************************************************************************/
    package web.servlet;
    
    import dao.UserDao;
    import domain.User;
    
    import javax.servlet.*;
    import javax.servlet.http.*;
    import javax.servlet.annotation.*;
    import java.io.IOException;
    
    @WebServlet("/loginServlet")
    public class LoginServlet extends HttpServlet {
        @Override
        protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
            //1、设置编码
            request.setCharacterEncoding("utf-8");
            //2、获取请求参数
            String username = request.getParameter("username");
            String password = request.getParameter("password");
            //3、封装user对象
            User loginUser = new User();
            loginUser.setUsername(username);
            loginUser.setPassword(password);
            //4、调用UserDao的login方法
            UserDao dao = new UserDao();
            User user = dao.login(loginUser);
            //5、判断user
            if(user == null) {
                //登录失败
                request.getRequestDispatcher("/failServlet").
                        forward(request,response);
            }else{
                //登录成功
                //存储数据
                request.setAttribute("user", user);
                //转发数据
                request.getRequestDispatcher("/successServlet").
                        forward(request,response);
            }
        }
    
        @Override
        protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
            this.doGet(request, response);
        }
    }
    
    
    
    /*****************************************************************************/
    package web.servlet;
    
    import javax.servlet.*;
    import javax.servlet.http.*;
    import javax.servlet.annotation.*;
    import java.io.IOException;
    
    @WebServlet("/failServlet")
    public class FailServlet extends HttpServlet {
        @Override
        protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
            this.doPost(request, response);
        }
    
        @Override
        protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
            //给页面写一句话
            //设置页面编码
            response.setContentType("text/html;charset=utf-8");
            //输出
            response.getWriter().write("登录失败,用户名或密码错误!");
        }
    }
    
    
    /*****************************************************************************/
    package web.servlet;
    
    import domain.User;
    
    import javax.servlet.*;
    import javax.servlet.http.*;
    import javax.servlet.annotation.*;
    import java.io.IOException;
    
    @WebServlet("/successServlet")
    public class SuccessServlet extends HttpServlet {
        @Override
        protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
            this.doPost(request, response);
        }
    
        @Override
        protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
            //获取request域中共享的user对象
            User user = (User) request.getAttribute("user");
    
            if(user != null) {
                //给页面写一句话
                //设置页面编码
                response.setContentType("text/html;charset=utf-8");
                //输出
                response.getWriter().write("登录成功!" + user.getUsername() + "欢迎您!");
            }
        }
    }

9.3.4、BeanUtils工具类

BeanUtils是用于封装JavaBean的,可以简化数据封装。

JavaBean:标准的Java类。

JavaBean的规范(定义要求):
    1、类必须被public修饰;
    2、必须提供空参的构造器;
    3、成员变量必须使用private修饰;
    4、提供公共setter()和getter()方法。

JavaBean的功能:
    封装数据。

概念:
    成员变量:
    属性:setter()和getter()方法截取后的产物。
        如:getUsername() --> Username --> username 

JavaBean中的方法:
    setProperty(); //设置属性值
    getProperty(); //获得属性值
    populate(Obejct obj, Map map); //封装数据。将map集合的键值对信息,封装到对应的JavaBean对象中
package test;

import domain.User;
import org.apache.commons.beanutils.BeanUtils;
import org.junit.Test;

import java.lang.reflect.InvocationTargetException;

public class BeanUtilsTest {
    @Test
    public void test() {
        User user = new User();
        try {
            //username是属性值而不是成员变量名
            BeanUtils.setProperty(user, "username", "zhangsan");
            System.out.println(user);

            //username为属性值,而不是成员变量名
            String username = BeanUtils.getProperty(user, "username");
            System.out.println(username);
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException | NoSuchMethodException e) {
            e.printStackTrace();
        }
    }
}

10、Response对象

10.1、Response的功能

10.1.1、设置响应消息数据

Response对象用来设置响应消息。

1、设置响应行

1、格式:
    HTTP/1.1 200 OK
2、设置状态码
    setStatus(int sc);

2、设置响应头

1、方法
    setHeader(String name, String value);

3、设置响应体

使用步骤:
1、获取输出流
    *字符输出流:PrintWriter getWriter();
    *字节输出流:ServletOutputStream getOutputStream();
2、使用输出流,将数据输出到浏览器中

10.1.2、案例1:完成重定向

重定向:资源跳转的方式。

1、代码实现:
//1、设置状态码为302
//        response.setStatus(302);
//        //2、设置响应头location
//        response.setHeader("location", "/ServletProject/responseDemo2");
        //简单的重定向方法
        response.sendRedirect("/ServletProject/responseDemo2");

2、重定向的特点:
    1、地址栏发生变化;
    2、重定向可以访问其它站点(服务器)的资源;
    3、重定向是两次请求,不能使用request对象来共享数据。
*转发的特点:
    1、转发地址栏路径不变;
    2、转发只能访问当前服务器下的资源;
    3、转发是一次请求,可以使用request对象来共享数据。

3、路径写法:
    *路径的分类:
        ① 相对路径:通过相对路径不可以确定唯一资源。
            * ./index.html 
            *不以 / 开头,以 . 开头的路径。
            *规则:找到当前资源和目标资源之间的相对位置关系。
        ② 绝对路径:通过绝对路径可以确定唯一资源。
            *如:http://localhost/ServletProject/demo  /ServletProject/demo 
            *以 / 开头的路径
            *规则:判断定义的路径是给谁用的?判断请求将来从哪发出?
                给客户端浏览器使用:需要加虚拟目录(项目的访问路径)。
                    建议虚拟目录动态获取:String request.getContextPath();
                    使用情况:<a>,<form>,重定向,...
                给服务器使用:不需要加虚拟目录。
                    使用情况:转发路径。
package web.response;

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

/**
 * 重定向
 */
@WebServlet("/responseDemo1")
public class ResponseDemo1 extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        this.doPost(request, response);
    }

    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        System.out.println("demo1...");
        //访问responseDemo1,会自动跳转到/responseDemo2资源
        //1、设置状态码为302
//        response.setStatus(302);
//        //2、设置响应头location
//        response.setHeader("location", "/ServletProject/responseDemo2");

        //动态获取虚拟目录
        String contextPath = request.getContextPath();
        //简单的重定向方法
        response.sendRedirect(contextPath+"/responseDemo2");
        //response.sendRedirect("http://www.baidu.com"); //重定向至其它服务器资源
    }
}
package web.response;

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

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

    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        System.out.println("demo2...");
    }
}
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>相对路径</title>
</head>
<body>

<p>相对路径:找到当前资源和目标资源之间的相对位置关系。</p>
<p>
    当前资源:location.html
    http://localhost/ServletProject/location.html
</p>
<p>
    目标资源:location.html
    http://localhost/ServletProject/responseDemo2
</p>
<!-- ../ :代表向上退一级 -->
<!-- ./ :代表当前目录-->
<a href="./responseDemo2">
  responseDemo2
</a>

</body>
</html>

10.1.3、案例2:服务器输出字符数据到浏览器

步骤:
    1、获取字符输出流;
    2、输出数据。

注意:
    乱码问题:
        1、PrintWriter pw = response.getWriter(); 获取的流的默认编码是ISO-8859-1
        2、设置该流的默认编码;
        3、告诉浏览器响应体使用的编码。简单的形式设置编码(在获取流之前设置):
            response.setContentType("text/html;charset=utf-8");
package web.response;

import javax.servlet.*;
import javax.servlet.http.*;
import javax.servlet.annotation.*;
import java.io.IOException;
import java.io.PrintWriter;

@WebServlet("/responseDemo3")
public class ResponseDemo3 extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        this.doPost(request,response);
    }

    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        //获取流对象之前设置流的默认编码:ISO-8859-1设置为GBK
        //response.setCharacterEncoding("utf-8");
        //告诉浏览器,服务器发送的消息体数据的编码,建议浏览器使用该编码解码
        response.setHeader("content-type", "text/html;charset=utf-8");
        //简单形式来设置i编码
        //response.setContentType("text/html;charset=utf-8");

        //获取字符输出流
        PrintWriter pw = response.getWriter();
        //输出数据
        //pw.write("Hello response");
        pw.write("<h1>你好 response<h1>");
    }
}

10.1.4、案例3:服务器输出字节数据到浏览器

步骤:
    1、获取字节输出流;
    2、输出数据
package web.response;

import javax.servlet.*;
import javax.servlet.http.*;
import javax.servlet.annotation.*;
import java.io.IOException;
import java.nio.charset.StandardCharsets;

@WebServlet("/responseDemo4")
public class ResponseDemo4 extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        this.doPost(request,response);
    }

    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        //设置编码格式
        response.setContentType("text/html;charset=utf-8");

        //1、获取字节输出流
        ServletOutputStream sos = response.getOutputStream();
        //2、输出数据
        sos.write("你好".getBytes("utf-8"));
    }
}

10.1.5、案例4:验证码

1、验证码的本质:就是一张图片。
2、目的:防止恶意表单注册。
package web.servlet;

import javax.imageio.ImageIO;
import javax.servlet.*;
import javax.servlet.http.*;
import javax.servlet.annotation.*;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.util.Random;

@WebServlet("/checkCodeServlet")
public class CheckCodeServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        this.doPost(request,response);
    }

    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        int width = 100;
        int height = 50;
        //1、创建一个对象,在内存中代表图片(验证码图片对象)
        BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);

        //2、美化图片
        //2.1、填充背景色
        Graphics g = image.getGraphics(); //画笔对象
        g.setColor(Color.pink); //设置画笔颜色
        g.fillRect(0,0,width,height);
        //2.2、画边框
        g.setColor(Color.blue);
        g.drawRect(0,0,width-1,height-1);

        String str = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
        //生成随机角标
        Random rand = new Random();
        //2.3、写验证码
        for (int i = 1; i <= 4 ; i++) {
            int index = rand.nextInt(str.length());
            //获取字符
            char ch = str.charAt(index); //随机字符
            g.setColor(Color.blue);
            g.drawString(ch+"",width/5*i,height/2);
        }
        //2.4、画干扰线
        g.setColor(Color.green);
        for (int i = 0; i < 10; i++) {
            //随机生成坐标点
            int x1 = rand.nextInt(width);
            int x2 = rand.nextInt(width);
            int y1 = rand.nextInt(height);
            int y2 = rand.nextInt(height);
            g.drawLine(x1,y1,x2,y2);
        }

        //3、将图片输出到页面展示
        ImageIO.write(image, "jpg", response.getOutputStream());
    }
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>验证码</title>
</head>
<body>

<img id="checkCode" src="/ServletProject/checkCodeServlet"/>
<a id="change" href="">看不清?换一张</a>

<script>
    /**
     * 分析:点击超链接或者图片,需要换一张
     * 1、给超链接和图片绑定单击事件
     * 2、重新设置图片的src属性值
     */
    window.onload = function () {
        //1、获取图片对象
        var img = document.getElementById("checkCode");
        //2、绑定单击事件
        img.onclick = function () {
            //加时间戳,防止一直访问浏览器缓存的同一张图片
            var date = new Date().getTime();
            img.src = "/ServletProject/checkCodeServlet?1" + date;
        }
    }
</script>

</body>
</html>

11、ServletContext对象

11.1、ServletContext对象的概念

概念:ServletContext代表整个web应用,可以和程序的容器(服务器)来通信。

11.2、ServletContext对象的功能

1、获取
    *通过resuest对象获取:
        getServletContext();
    *通过HttpServlet来获取:
        this.getServletContext();

功能:
    1、获取MIME类型;
        *MIME类型:在互联网通信过程中定义的一种文件数据类型。
            格式:大类型/小类型 如:text/html、image/jpeg
        *获取:String getMimeType(String file);
    2、域对象:共享数据;
        setAttribute(String name, Object value);
        getAttribute(String name);
        removeAttribute(String name);
        *ServletContext对象范围:所有用户所有请求的数据。
    3、获取文件的真实(服务器)路径:
        方法:String getRealPath("参数");
package web.servletcontext;

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

@WebServlet("/servletContextDemo1")
public class ServletContextDemo1 extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        this.doPost(request,response);
    }

    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        //ServletContext对象的获取
        //方式1:通过resuest对象获取:
        // getServletContext();
        ServletContext context1 = request.getServletContext();

        //方式2:通过HttpServlet来获取:
        // this.getServletContext();
        ServletContext context2 = this.getServletContext();

        System.out.println(context1);
        System.out.println(context2);
        System.out.println(context1 == context2); //true
    }
}
package web.servletcontext;

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

@WebServlet("/servletContextDemo2")
public class ServletContextDemo2 extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        this.doPost(request,response);
    }

    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        //*MIME类型:在互联网通信过程中定义的一种文件数据类型。
        //格式:大类型/小类型 如:text/html、image/jpeg
        //        *获取:String getMimeType(String file);
        //1、通过HttpServlet获取ServletContext
        ServletContext context = this.getServletContext();
        //2、定义文件名称
        String filename = "a.jpg"; //MIME type : image/jpeg
        //3、获取MIME类型
        String mimeType = context.getMimeType(filename);
        System.out.println(mimeType);
    }
}
package web.servletcontext;

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

@WebServlet("/servletContextDemo3")
public class ServletContextDemo3 extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        this.doPost(request,response);
    }

    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        //通过HttpServlet获取ServletContext
        ServletContext context = this.getServletContext();
        //设置数据
        context.setAttribute("msg","haha");
    }
}
package web.servletcontext;

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

@WebServlet("/servletContextDemo4")
public class ServletContextDemo4 extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        this.doPost(request,response);
    }

    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        //通过HttpServlet获取ServletContext
        ServletContext context = this.getServletContext();
        //获取数据
        Object msg = context.getAttribute("msg");
        System.out.println(msg);
    }
package web.servletcontext;

import javax.servlet.*;
import javax.servlet.http.*;
import javax.servlet.annotation.*;
import java.io.File;
import java.io.IOException;

@WebServlet("/servletContextDemo5")
public class ServletContextDemo5 extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        this.doPost(request,response);
    }

    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        //通过HttpServlet获取ServletContext
        ServletContext context = this.getServletContext();
        //获取文件的服务器路径
        String realPath1 = context.getRealPath("/b.txt"); //web目录下的资源访问
        System.out.println(realPath1);
        File file1 = new File(realPath1);//获取文件的服务器路径

        String realPath2 = context.getRealPath("/WEB_INF/c.txt"); //web/WEB_INF下的资源访问
        System.out.println(realPath2);
        File file2 = new File(realPath2);

        String realPath3 = context.getRealPath("/WEB_INF/classes/a.txt"); //web/WEB_INF/classes(src)目录下的资源访问
        System.out.println(realPath3);
        File file3 = new File(realPath3);
    }
}

11.3、案例:文件下载

文件下载需求:

  1. 页面显示超链接;
  2. 点击超链接后弹出下载提示框;
  3. 完成图片文件下载。

分析:

  • 超链接指向的资源如果能够被浏览器解析,则在浏览器中展示,如果不能解析,则弹出下载提示框。不满足需求。
  • 任何资源都必须弹出下载提示框。
  • 使用响应头设置资源的打开方式:content-disposition:attachment;filename=xxx 。

步骤:

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

问题:

中文文件名问题:
    解决思路:
        1、获取客户端使用的浏览器版本信息;
        2、根据不同的版本信息,设置filename的编码方式不同。
package util;

//import sun.misc.BASE64Encoder;

import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;

public class DownLoadUtils {

    public static String getFileName(String agent, String filename) throws UnsupportedEncodingException {
        if(agent.contains("MSIE")) {
            //IE浏览器
            filename = URLEncoder.encode(filename, "utf-8");
            filename = filename.replace("+"," ");
//        }else if(agent.contains("Firefox")) {
//            //火狐浏览器
//            BASE64Encoder base64Encoder = new BASE64Encoder();
//            filename = "=?utf-8?B?" + base64Encoder.encode(filename.getBytes("utf-8")+"?=");
        }else {
            //其它浏览器
            filename = URLEncoder.encode(filename,"utf-8");
        }
        return filename;
    }
}

/***********************************************************************/
import javax.servlet.annotation.*;
import java.io.FileInputStream;
import java.io.IOException;

@WebServlet("/downloadServlet")
public class DownloadServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        this.doPost(request,response);
    }

    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        //1、获取请求参数:文件名称
        String filename = request.getParameter("filename");

        //2、使用字节输入流加载文件进内存
        //2.1、找到文件服务器路径
        ServletContext servletContext = this.getServletContext();
        String realPath = servletContext.getRealPath("/img/" + filename);
        //2.2、用字节流关联
        FileInputStream fis = new FileInputStream(realPath);


        //3、设置response的响应头
        //3.1、设置响应头类型:content-type
        String mimeType = servletContext.getMimeType(filename); //获取文件的Mime类型
        response.setHeader("content-type", mimeType);
        //3.2、设置响应头打开方式:content-disposition
        //解决中文文件名问题
        //3.3、获取user-agent请求头
        String agent = request.getHeader("user-agent");
        //3.4、使用工具类方法编码文件名即可
        filename = DownLoadUtils.getFileName(agent, filename);
        //3.5、设置文件为附件形式下载
        response.setHeader("content-disposition","attachment;filename="+filename);

        //4、将输入流写出到输出流中
        ServletOutputStream sos = response.getOutputStream();
        byte[] buff = new byte[1024 * 8];
        int len = 0;
        //fis.read(buff):从该输入流读取最多buff.length个字节的数据到一个字节数组,返回实际读取到的字节数据个数
        while((len = fis.read(buff)) != -1) {
            sos.write(buff,0,len); //字节流不需要刷新
        }
        fis.close(); //字节输入流需要手动关闭,释放资源
    }
}
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>下载案例</title>
</head>
<body>

<a href="/ServletProject/img/1.jpg">图片1</a>

<hr />

<a href="/ServletProject/downloadServlet?filename=1.jpg">图片</a>

</body>
</html>

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值