Servlet 学习笔记(二)

Servlet 核心接口和类

在 Servlet 体系结构中,除了实现 Servlet 接口,还可以通过继承 GenericServlet 或者 HttpServlet 类完成编写

Servlet 接口

在 Servlet API 中最重要的是 Servlet 接口,搜索 Servlet 都会直接或者间接与该接口发生联系

该接口有以下五个方法:

  • void init(ServletConfig servletConfig)
  • ServletConfig getServletConfig()
  • void service(ServletRequest servletRequest, ServletResponse servletResponse)
  • String getServletInfo()
  • void destroy()

主要用到的方法是 service 方法

GenericServlet

GenericServlet 使编写 Servlet 变得更加容易,它提供了对 Servlet 接口方法的简单实现,只需重写 service 方法即可

例子:

public class GenServlet extends GenericServlet {
    @Override
    public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
        System.out.println("my first genericServlet");
    }
}

HttpServlet

HttpServlet 继承自 GenericServlet,是 GenericServlet 的进一步扩展

它是一个抽象类,其子类必须至少重写以下方法之一:

  • doGet,用于 HTTP Get 请求
  • doPost,用于 HTTP Post 请求
  • doPut,用于 HTTP Put 请求
  • doDelete,用于 HTTP Delete 请求
    在这里插入图片描述

Servlet 两种创建方式

实现 Servlet 接口

该方式比较麻烦,需要实现 Servlet 接口的所有方法

public class MyServlet 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("my first servlet web project");
        System.out.println(new Date());
    }

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

    @Override
    public void destroy() {
    }
}

继承 HttpServlet 抽象类(推荐)

在这里插入图片描述

常见错误

在这里插入图片描述

Servlet 两种配置方式

使用 web.xml 配置文件

在这里插入图片描述

其中 url-pattern 的定义方式有:
在这里插入图片描述
load-on-startup 表示对应 servlet 加载的时机
在这里插入图片描述

使用注解(Servlet 3.0 之后可以使用)

在 Servelet 实现类上方加上注解

@WebServlet

在这里插入图片描述
常用属性如下
在这里插入图片描述

Servlet 应用

request 对象

在 Servlet 中处理客户端的 GET/POST 请求的具体内容都包含在 request 对象中
在这里插入图片描述

get 和 post 的区别

get 请求

  • get 提交的数据会放在 URL 之后,以 ? 分割 URL 和传输数据,参数之间通过 & 相连
  • get 方式明文传递,数据量小,不安全
  • 效率高,浏览器默认请求方式就是 GET
  • 对应 Servlet 方法是 doGet

post 请求

  • post 方法把提交的数据放在 HTTP 包的 Body 中
  • 密文传输,数据量大,安全
  • 效率不如 get
  • 对应 Servlet 方法是 doPost

request 主要方法

在这里插入图片描述

request 应用

  1. 编写注册页面
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>注册页面</title>
</head>
<body>
    <form action="/servletDemo1_war_exploded/rs" method="get">
        用户名:<input type="text" name="username"/><br/>
        密码:<input type="password" name="password"/><br/>
        <input type="submit" value="注册"/>
    </form>
</body>
</html>

  1. 编写对应的 Servlet 类
@WebServlet(value = "/rs")
public class RegisterServlet extends HttpsServlet{
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        // 获取用户请求发送的数据
        String username = req.getParameter("username");
        String password = req.getParameter("password");

        System.out.println("提交的数据:" + username + "\t" + password);
    }
}
  1. 测试

运行项目,输入路径
http://localhost:8080/servletDemo1_war_exploded/register.html

填写表单,输出结果为
在这里插入图片描述
同时,浏览器的路径变为
在这里插入图片描述

get 中文乱码问题

在这里插入图片描述
解决方案:
在这里插入图片描述

post 中文乱码问题

在这里插入图片描述
在这里插入图片描述

response 对象

response 对象用于响应客户端请求并向客户端输出信息

在这里插入图片描述

主要方法
在这里插入图片描述
案例
在这里插入图片描述
在上一个应用加上以下内容
在这里插入图片描述
然后提交 post 请求,输出结果为
在这里插入图片描述
输出结果为乱码,这是因为
在这里插入图片描述

中文乱码解决

  1. 设置服务端响应的编码格式
  2. 设置客户端响应内容的头内容的文件类型以及编码格式
resp.setCharacterEncoding("utf-8"); // 设置服务器编码方式
resp.setHeader("Content-Type", "text/html;charset=utf-8");  // 设置客户端解析方式

上面的方式要复杂一点,还有更简洁的方式可以同时设置服务端的编码格式和客户端响应的文件类型及响应时的编码格式

resp.setContentType("text/html;charset=utf-8");

在这里插入图片描述

Servlet + JDBC

  1. 创建数据库
    在这里插入图片描述
  2. 添加 jar 包到 lib 文件夹中,add as library
    在这里插入图片描述
  3. 创建配置文件
    在这里插入图片描述
  4. 创建实体层、dao 层、service 层、utils 工具类

utils 工具类

public class DbUtils {
    private static DruidDataSource dataSource;
    private static final ThreadLocal<Connection> THREAD_LOCAL = new ThreadLocal<>();

    // 初始化
    static {
        Properties properties = new Properties();
        InputStream inputStream = DbUtils.class.getResourceAsStream("/database.properties");
        try {
            properties.load(inputStream);
            dataSource = (DruidDataSource) DruidDataSourceFactory.createDataSource(properties);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    // 获取当前数据库连接
    public static Connection getConnection() {
        Connection connection = THREAD_LOCAL.get();
        try {
            if (connection == null) {
                connection = dataSource.getConnection();
                THREAD_LOCAL.set(connection);
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }
        return connection;
    }

    // 开启事务
    public static void begin() {
        Connection connection = null;
        try {
            connection = getConnection();
            connection.setAutoCommit(false);
        } catch (SQLException throwables) {
            throwables.printStackTrace();
        }
    }

    // 提交事务
    public static void commit() {
        Connection connection = null;
        try {
            connection = getConnection();
            connection.commit();
        } catch (SQLException throwables) {
            throwables.printStackTrace();
        } finally {
            closeALl(connection, null, null);
        }
    }

    // 事务回滚
    public static void rollback() {
        Connection connection = null;
        try {
            connection = getConnection();
            connection.rollback();
        } catch (SQLException throwables) {
            throwables.printStackTrace();
        } finally {
            closeALl(connection, null, null);
        }
    }

    // 关闭连接
    public static void closeALl(Connection connection, Statement statement, ResultSet resultSet) {
        try {
            if (resultSet != null) {
                resultSet.close();
            }
            if (statement != null) {
                statement.close();
            }
            if (connection != null) {
                connection.close();
                THREAD_LOCAL.remove();
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }

}

实体层,主要为数据库表对应属性以及属性对应的 set 和 get 方法以及构造器
在这里插入图片描述
dao 层接口和实现类

public interface UserDao {
    public int insert(User user);
    public int delete(String username);
    public int update(User user);
    public User select(String username);
    public List<User> selectAll();
}
public class UserDaoImpl implements UserDao {

    private QueryRunner queryRunner = new QueryRunner();

    @Override
    public int insert(User user) {
        return 0;
    }

    @Override
    public int delete(String username) {
        return 0;
    }

    @Override
    public int update(User user) {
        return 0;
    }

    @Override
    public User select(String username) {
        String sql = "select * from t_user where username = ?;";
        try {
            User user = queryRunner.query(DbUtils.getConnection(), sql, new BeanHandler<>(User.class), username);
            return user;
        } catch (SQLException e) {
            e.printStackTrace();
        }
        return null;
    }

    @Override
    public List<User> selectAll() {
        String sql = "select * from t_user;";
        try {
            List<User> list = queryRunner.query(DbUtils.getConnection(), sql, new BeanListHandler<>(User.class));
            return list;
        } catch (SQLException e) {
            e.printStackTrace();
        }
        return null;
    }
}

service 层接口和实现类

public interface UserService {

    public User login(String username, String password);

    public List<User> showAllUser();
}
public class UserServiceImpl implements UserService {

    private UserDao userDao = new UserDaoImpl();

    @Override
    public User login(String username, String password) {
        User res = null;
        try {
            DbUtils.begin();
            User user = userDao.select(username);
            if (user != null) {
                if (user.getPassword().equals(password)) {
                    res = user;
                }
            }
            DbUtils.commit();
        } catch (Exception e) {
            DbUtils.rollback();
            e.printStackTrace();
        }
        return res;
    }

    @Override
    public List<User> showAllUser() {
        List<User> res = null;
        try {
            DbUtils.begin();
            res = userDao.selectAll();
            DbUtils.commit();
        } catch (Exception e) {
            DbUtils.rollback();
            e.printStackTrace();
        }
        return res;
    }
}
  1. 创建 servlet 类

显示所有用户的 servlet 类

@WebServlet(value = "/showall")
public class ShowAllUserServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        resp.setContentType("text/html;charset=UTF-8");

        UserService userService = new UserServiceImpl();
        List<User> users = userService.showAllUser();

        PrintWriter writer = resp.getWriter();
        if (users != null) {
            // 响应给客户端结果页面
            writer.println("<html>");
            writer.println("<head>");
            writer.println("<meta charset='UTF-8'>");
            writer.println("<title>结果页面</title>");
            writer.println("</head>");
            writer.println("<body>");
            writer.println("<table>");
            writer.println("    <tr>");
            writer.println("        <td>username</td>");
            writer.println("        <td>password</td>");
            writer.println("        <td>phone</td>");
            writer.println("        <td>address</td>");
            writer.println("    </tr>");
            for (User user : users) {
                writer.println("    <tr>");
                writer.println("        <td>" + user.getUsername() + "</td>");
                writer.println("        <td>" + user.getPassword() + "</td>");
                writer.println("        <td>" + user.getPhone() + "</td>");
                writer.println("        <td>" + user.getAddress() + "</td>");
                writer.println("    </tr>");
            }
            writer.println("</table>");
            writer.println("</body>");
            writer.println("</html>");
        } else {
            // 响应给客户端结果页面
            writer.println("<html>");
            writer.println("<head>");
            writer.println("<meta charset='UTF-8'>");
            writer.println("<title>结果页面</title>");
            writer.println("</head>");
            writer.println("<body>");
            writer.println("<h3>当前没有用户!</h3>");
            writer.println("</body>");
            writer.println("</html>");
        }
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        doGet(req, resp);
    }
}

在刚刚的 Servlet 中,业务逻辑和效果显示在同一个类中,就会产生设计问题,不符合单一职能原则,也不利于维护

应开进行分离
在这里插入图片描述
于是可以将刚刚的 ShowAllUserServlet 分为

  • ShowAllUserController 用来处理业务逻辑
    在这里插入图片描述

  • ShowAllAdminJSP 用来显示页面(代码过多不展示)

不过这时候会出现问题

  1. 如何从业务逻辑 Servlet 跳转到显示页面 Servlet
  2. 显示页面要展示的数据在业务逻辑 Servlet 中,那么显示页面 Servlet 如何获取数据?

这时候就可以使用转发解决第一个问题

转发

转发用在服务器端,将请求发送给服务器端的其他资源,以共同完成一次请求的处理

可以通过以下代码实现页面跳转

req.getRequestDispatcher(url).forward(req, resp);

其中 url 表示要转发的路径

注意,forward 跳转是服务器内部跳转
在这里插入图片描述

数据传递

forward 表示一次请求,是在服务器内部跳转,可以共享同一次 request 作用域中的数据

  • request 作用域:拥有存储数据的容器,作用范围是该次请求内有效(一次请求可以多次转发)。所以可以将数据存入 request,然后在请求过程中的任意位置进行获取
    在这里插入图片描述

  • 存数据

request.setAttribute(key, value);
  • 取数据
request.getAttribute(key);

于是,之前的 Servlet 为
在这里插入图片描述
在这里插入图片描述

转发特点

在这里插入图片描述

重定向

在这里插入图片描述
可以在 Servlet 中通过以下方法实现重定向

response.sendRedirect(URI);

在这里插入图片描述
在这里插入图片描述

数据传递

使用重定向时,地址栏发生改变,属于两次请求
在这里插入图片描述
可以看到重定向只能传递字符串类型数据

重定向特点

在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

三更鬼

谢谢老板!

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值