文章目录
- 视频对应资料
- Tomcat服务器
- 下载与安装
- 关于Tomcat服务器的目录
- 启动Tomcat
- 实现一个最基本的web应用(这个web应用中没有java小程序)
- 静态资源与动态资源
- 模拟Servlet本质
- Servlet规范
- 开发一个带有Servlet(Java小程序)的webapp(重点)
- 关于JavaEE的版本
- 解决Tomcat服务器在DOS命令窗口中的乱码问题(控制台乱码)
- 向客户端相应内容
- 在Servlet中连接数据库
- 在集成开发环境当中开发Servlet程序
- Servlet对象的生命周期
- 适配器设计模式Adapter实现Servlet接口
- ServletConfig
- ServletContext
- 缓存机制
- HTTP协议
- 模板方法设计模式
- HttpServlet源码分析
- 关于一个web站点的欢迎页面
- 关于WEB-INF目录
- HttpServletRequest接口详解
- 使用纯Servlet做一个单表的CRUD操作
- 在一个web应用中应该如何完成资源的跳转
- 将oa项目中的资源跳转修改为合适的跳转方式
- Servlet注解,简化配置
- 通过反射获取注解
- 使用模板方法设计模式优化oa项目
- 分析使用纯粹Servlet开发web应用的缺陷
- JSP
- 我的第一个JSP程序
- JSP概述
- JSP的基础语法
- JSP中直接编写文字
- JSP的page指令
- JSP中编写java程序
- <% java语句; %>
- <%! %>
- JSP的输出语句 <%= %>
- JSP中的注释
- JSP基础语法总结
- 使用Servlet + JSP完成oa项目的改造
- oa系统实现登录功能
- session
- web应用的session机制
- session的实现原理
- 域对象
- session改造oa项目
- Cookie
- cookie实现一下十天内免登录功能
- JSP的指令
- JSP的九大内置对象
- EL表达式
- EL表达式概述
- EL表达式语法
- EL表达式的使用
- JSTL标签库
- JSTL标签库概述
- 使用JSTL标签库的步骤
- JSTL标签的原理
- jstl中的核心标签库core当中常用的标签
- 使用EL与JSTL改造OA项目
- Filter过滤器
- 使用过滤器改造OA项目。
- Listener监听器
- 实现oa项目中当前登录在线的人数。
- MVC架构模式
视频地址:动力节点最新JavaWeb视频教程,javaweb零基础入门到精通IDEA版https://www.bilibili.com/video/BV1Z3411C7NZ?p=1&vd_source=93ab990b9131a1b90943874a5448830a
资料链接:https://pan.baidu.com/s/1y-Dm0dGjQQOvARFBmGiG1w?pwd=1234
提取码:1234
【Tomcat服务器版本与支持Java版本的对应关系:https://tomcat.apache.org/whichversion.html】
【Tomcat官网下载地址:https://tomcat.apache.org/download-10.cgi】
下载完成后,直接选择合适的位置将下载的压缩包解压即可,解压就是安装。
startup.bat
,通过它可以启动Tomcat服务器。
http://ip地址:端口号/webapp名/资源文件名
http://127.0.0.1:8080/oa/index.html
我们在浏览器上直接输入一个URL,然后回车。这个动作和超链接一样吗?既然是一样的,我们完全可以使用超链接。
<a href="/oa/login.html">user login2a>
<a href="/oa/test/debug/d.html">d pagea>
DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Documenttitle>
head>
<body>
<table>
<thead>
<th>noth>
<th>usernameth>
thead>
<tbody>
<tr>
<td>1td>
<td>Tomtd>
tr>
<tr>
<td>2td>
<td>Michiletd>
tr>
<tr>
<td>3td>
<td>Janetd>
tr>
<tr>
<td>4td>
<td>Marrytd>
tr>
<tr>
<td>5td>
<td>Jacktd>
tr>
tbody>
table>
body>
html>
充当SUN公司的角色,制定Servlet规范,javax.servlet.Servlet接口,Webapp的开发者编写的Servlet类都要实现该接口
package javax.servlet;
/*
我们现在充当的角色时SUN公司
SUN公司把Servlet接口/规范制定出来
*/
public interface Servlet{
// 一个专门提供服务的方法
void service();
}
充当Tomcat服务器的开发者
package org.apache;
import java.util.Scanner;
import java.util.Properties;
import java.io.FileReader;
import javax.servlet.Servlet;
// 充当Tomcat服务器的开发者
public class Tomcat{
public static void main(String[] args) throws Exception{
System.out.println("Tomcat服务器启动成功,开始接收用户的访问。");
// 简单的使用Scanner来模拟一下用户的请求
// 用户访问服务器是通过浏览器上的“请求路径”
// 也就是说用户请求路径不同,后台执行的Servlet不同。
/*
/userList UserListServlet
/login UserLoginServlet
/bank BankServlet
......
*/
System.out.print("请输入您的访问路径:");
Scanner s = new Scanner(System.in);
// 用户的请求路径 /bbbb
String key = s.nextLine(); // Tomcat服务器已经获取到了用户的请求路径了。
// Tomcat服务器应该通过用户的请求路径找对应的XXXServlet
// 请求路径和XXXServlet之间的关系应该由谁指定呢?
// 对于Tomcat服务器来说需要解析配置文件
FileReader reader = new FileReader("web.properties");
Properties pro = new Properties();
pro.load(reader);
reader.close();
// 通过key获取value
String className = pro.getProperty(key);
// 通过反射机制创建对象
Class clazz = Class.forName(className);
Object obj = clazz.newInstance(); // obj的类型对于Tomcat服务器开发人员来说不知道。
// 但是Tomcat服务器的开发者知道,你写的XXXXServlet一定实现了Servlet接口
Servlet servlet = (Servlet)obj;
servlet.service();
}
}
充当Webapp的开发者
BankServlet implements Servlet
package com.bjpowernode.servlet;
import javax.servlet.Servlet;
// 充当的角色发生了改变:webapp开发者
// 只要是我们webapp开发者写的XXXServlet都要实现Servlet接口
public class BankServlet implements Servlet{
public void service(){
System.out.println("BankServlet's service...");
}
}
UserListServlet implements Servlet
package com.bjpowernode.servlet;
import javax.servlet.Servlet;
// 充当的角色发生了改变:webapp开发者
public class UserListServlet implements Servlet{
public void service(){
System.out.println("UserListServlet's service...");
}
}
UserLoginServlet implements Servlet
package com.bjpowernode.servlet;
import javax.servlet.Servlet;
// 充当的角色发生了改变:webapp开发者
public class UserLoginServlet implements Servlet{
public void service(){
System.out.println("UserLoginServlet's service...");
}
}
编写配置文件,请求资源路径和相应Servlet类的对应关系有配置文件给出
/aaaa=com.bjpowernode.servlet.UserListServlet
/bbbb=com.bjpowernode.servlet.UserLoginServlet
/cccc=com.bjpowernode.servlet.BankServlet
通过分析,对于我们javaweb程序员来说,我们只需要做两件事,(1)编写一个类实现Servlet接口;(2)将编写的类配置到配置文件中,在配置文件中:指定 请求路径 和 类名 的关系。
注意:
这个配置文件的文件名不能乱来。固定的。
这个配置文件的存放路径不能乱来。固定的。
文件名、文件路径都是SUN公司制定的Servlet规范中的明细。
// 从该段代码可以看出
FileReader reader = new FileReader("web.properties");
Properties pro = new Properties();
pro.load(reader);
reader.close();
严格意义上来说Servlet其实并不是简单的一个接口:
Tomcat服务器要遵循Servlet规范。JavaWEB程序员也要遵循这个Servlet规范。这样Tomcat服务器和webapp才能解耦合。
第一步:在webapps目录下新建一个目录,起名crm(这个crm就是webapp的名字)。当然,也可以是其它项目,比如银行项目,可以创建一个目录bank,办公系统可以创建一个oa。
第二步:在webapp的根下新建一个目录:WEB-INF
第三步:在WEB-INF目录下新建一个目录:classes
第四步:在WEB-INF目录下新建一个目录:lib
第五步:在WEB-INF目录下新建一个文件:web.xml
<web-app xmlns="https://jakarta.ee/xml/ns/jakartaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="https://jakarta.ee/xml/ns/jakartaee
https://jakarta.ee/xml/ns/jakartaee/web-app_5_0.xsd"
version="5.0"
metadata-complete="true">
web-app>
第六步:编写一个Java程序,这个小Java程序也不能随意开发,这个小java程序必须实现Servlet接口。
第七步:编译我们编写的HelloServlet
package com.bjpowernode.servlet;
// 在Tomcat9以及Tomcat9之前的版本中使用javax.servlet这个包
import jakarta.servlet.Servlet;
import jakarta.servlet.ServletException;
import jakarta.servlet.ServletRequest;
import jakarta.servlet.ServletResponse;
import jakarta.servlet.ServletConfig;
import java.io.IOException;
import java.io.PrintWriter;
public class HelloServlet implements Servlet{
// 5个方法
public void init(ServletConfig config) throws ServletException{
}
public void service(ServletRequest request,ServletResponse response)
throws ServletException , IOException{
// 向控制台打印输出
System.out.println("My First Servlet, Hello Servlet");
}
public void destroy(){
}
public String getServletInfo(){
return "";
}
public ServletConfig getServletConfig(){
return null;
}
}
第八步:将以上编译之后的HelloServlet.class文件拷贝到WEB-INF\classes目录下。
javac -d . HelloServlet.java
第九步:在web.xml文件中编写配置信息,让“请求路径”和“Servlet类名”关联在一起。
<web-app xmlns="https://jakarta.ee/xml/ns/jakartaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="https://jakarta.ee/xml/ns/jakartaee
https://jakarta.ee/xml/ns/jakartaee/web-app_5_0.xsd"
version="5.0"
metadata-complete="true">
<servlet>
<servlet-name>fdsafdsagfdsafdsaservlet-name>
<servlet-class>com.bjpowernode.servlet.HelloServletservlet-class>
servlet>
<servlet-mapping>
<servlet-name>fdsafdsagfdsafdsaservlet-name>
<url-pattern>/fdsa/fd/saf/d/sa/fd/sa/fdurl-pattern>
servlet-mapping>
web-app>
第十一步:打开浏览器,在浏览器地址栏上输入一个url,这个URL必须是:
浏览器上编写的路径太复杂,可以使用超链接。(非常重要:html页面只能放到WEB-INF目录外面。)
DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Documenttitle>
head>
<body>
<a href="/crm/fdsa/fd/saf/d/sa/fd/sa/fd">HelloServeleta>
body>
html>
以后不需要我们编写main方法了。tomcat服务器负责调用main方法,Tomcat服务器启动的时候执行的就是main方法。我们javaweb程序员只需要编写Servlet接口的实现类,然后将其注册到web.xml文件中,即可。
总结一下:一个合法的webapp目录结构应该是怎样的?
webapproot
|------WEB-INF
|------classes(存放字节码)
|------lib(第三方jar包)
|------web.xml(注册Servlet)
|------html
|------css
|------javascript
|------image
....
浏览器发送请求,到最终服务器调用Servlet中的方法,是怎样的一个过程?(以下这个过程描述的很粗糙。其中还有很多步骤我省略了。)
将CATALINA_HOME/conf/logging.properties文件中的内容修改如下:
// Windows DOS命令窗口的编码为GBK
java.util.logging.ConsoleHandler.encoding = GBK
package com.bjpowernode.servlet;
import javax.servlet.Servlet;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.ServletConfig;
import java.io.IOException;
import java.io.PrintWriter;
public class HelloServlet implements Servlet{
// 5个方法
public void init(ServletConfig config) throws ServletException{
}
public void service(ServletRequest request,ServletResponse response)
throws ServletException , IOException{
// 向控制台打印输出
System.out.println("My First Servlet, Hello Servlet");
// 设置响应的内容类型是普通文本或html代码
// 需要在获取流对象之前设置,有效。
// 响应到浏览器出现中文乱码,可以指定字符集text/html;charset=UTF-8
response.setContentType("text/html");
// 怎么将一个信息直接输出到浏览器上?
// 需要使用ServletResponse接口:response
// response表示响应:从服务器向浏览器发送数据叫做响应。
PrintWriter out = response.getWriter();
// 设置响应的内容类型时不要在获取流之后设置。
//response.setContentType("text/html");
out.print("Hello Servlet, You are my first servlet!");
// 浏览器是能够识别html代码的,那我们是不是应该输出一段HTML代码呢?
out.print("hello servlet,你好Servlet
");
// 这是一个输出流,负责输出字符串到浏览器
// 这个输出流不需要我们刷新,也不需要我们关闭,这些都由Tomcat来维护。
/*
out.flush();
out.close();
*/
}
public void destroy(){
}
public String getServletInfo(){
return "";
}
public ServletConfig getServletConfig(){
return null;
}
}
Servlet是Java程序,所以在Servlet中完全可以编写JDBC代码连接数据库。
在一个webapp中去连接数据库,需要将驱动jar包放到WEB-INF/lib目录下。(com.mysql.cj.jdbc.Driver 这个类就在驱动jar包当中。)
注册驱动,新版本中建议使用com.mysql.cj.jdbc.Driver,com.mysql.jdbc.Driver已过时。(对应Mysql的版本为8.0+)
package com.bjpowernode.servlet;
import jakarta.servlet.Servlet;
import jakarta.servlet.ServletException;
import jakarta.servlet.ServletRequest;
import jakarta.servlet.ServletResponse;
import jakarta.servlet.ServletConfig;
import java.io.IOException;
import java.io.PrintWriter;
import java.sql.*;
public class StudentServlet implements Servlet{
public void init(ServletConfig config) throws ServletException{
}
public void service(ServletRequest request,ServletResponse response)
throws ServletException , IOException{
// 响应到浏览器出现中文乱码,可以指定字符集text/html;charset=UTF-8
response.setContentType("text/html");
PrintWriter out = response.getWriter();
// 编写JDBC代码,连接数据库,查询所有学生信息。
Connection conn = null;
PreparedStatement ps = null;
ResultSet rs = null;
try{
// 注册驱动 (com.mysql.jdbc.Driver,这个已过时。)
// 新版本中建议使用:com.mysql.cj.jdbc.Driver驱动。
Class.forName("com.mysql.cj.jdbc.Driver");
// 获取连接
String url = "jdbc:mysql://localhost:3306/mydatabase";
String user = "root";
String password = "123456";
conn = DriverManager.getConnection(url,user,password);
// 获取预编译的数据库操作对象
String sql = "select id,name from account";
ps = conn.prepareStatement(sql);
// 执行SQL
rs = ps.executeQuery();
// 处理查询结果集
while(rs.next()){
String no = rs.getString("id");
String name = rs.getString("name");
//System.out.println(no + "," + name);
out.print(no + "," + name + "
");
}
}catch(Exception e){
e.printStackTrace();
}finally{
// 释放资源
if(rs != null){
try{
rs.close();
}catch(Exception e){
e.printStackTrace();
}
}
if(ps != null){
try{
ps.close();
}catch(Exception e){
e.printStackTrace();
}
}
if(conn != null){
try{
conn.close();
}catch(Exception e){
e.printStackTrace();
}
}
}
}
public void destroy(){
}
public String getServletInfo(){
return "";
}
public ServletConfig getServletConfig(){
return null;
}
}
将相应的jar包放到WEB-INF/lib目录下
编译生成字节码文件
<web-app xmlns="https://jakarta.ee/xml/ns/jakartaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="https://jakarta.ee/xml/ns/jakartaee
https://jakarta.ee/xml/ns/jakartaee/web-app_5_0.xsd"
version="5.0"
metadata-complete="true">
<servlet>
<servlet-name>fdsafdsagfdsafdsaservlet-name>
<servlet-class>com.bjpowernode.servlet.HelloServletservlet-class>
servlet>
<servlet-mapping>
<servlet-name>fdsafdsagfdsafdsaservlet-name>
<url-pattern>/fdsa/fd/saf/d/sa/fd/sa/fdurl-pattern>
servlet-mapping>
<servlet>
<servlet-name>jdbc_testservlet-name>
<servlet-class>com.bjpowernode.servlet.StudentServletservlet-class>
servlet>
<servlet-mapping>
<servlet-name>jdbc_testservlet-name>
<url-pattern>/jdbc_testurl-pattern>
servlet-mapping>
web-app>
class StudentServlet implements Servlet
package com.javaweb.servlet;
import javax.servlet.*;
import java.io.IOException;
public class StudentServlet 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 {
}
@Override
public String getServletInfo() {
return null;
}
@Override
public void destroy() {
}
}
package com.javaweb.servlet;
import javax.servlet.*;
import java.io.IOException;
import java.io.PrintWriter;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
public class StudentServlet implements Servlet {
@Override
public void init(ServletConfig servletConfig) throws ServletException {
}
@Override
public ServletConfig getServletConfig() {
return null;
}
@Override
public void service(ServletRequest request, ServletResponse response)
throws ServletException, IOException {
// 响应到浏览器出现中文乱码,可以指定字符集text/html;charset=UTF-8
response.setContentType("text/html;charset=UTF-8");
PrintWriter out = response.getWriter();
// 编写JDBC代码,连接数据库,查询所有学生信息。
Connection conn = null;
PreparedStatement ps = null;
ResultSet rs = null;
try{
// 注册驱动 (com.mysql.jdbc.Driver,这个已过时。)
// 新版本中建议使用:com.mysql.cj.jdbc.Driver驱动。
Class.forName("com.mysql.cj.jdbc.Driver");
// 获取连接
String url = "jdbc:mysql://localhost:3306/mydatabase";
String user = "root";
String password = "285013 ";
conn = DriverManager.getConnection(url,user,password);
// 获取预编译的数据库操作对象
String sql = "select id,name from account";
ps = conn.prepareStatement(sql);
// 执行SQL
rs = ps.executeQuery();
// 处理查询结果集
while(rs.next()){
String no = rs.getString("id");
String name = rs.getString("name");
//System.out.println(no + "," + name);
out.print(no + "," + name + "
");
}
}catch(Exception e){
e.printStackTrace();
}finally{
// 释放资源
if(rs != null){
try{
rs.close();
}catch(Exception e){
e.printStackTrace();
}
}
if(ps != null){
try{
ps.close();
}catch(Exception e){
e.printStackTrace();
}
}
if(conn != null){
try{
conn.close();
}catch(Exception e){
e.printStackTrace();
}
}
}
}
@Override
public String getServletInfo() {
return null;
}
@Override
public void destroy() {
}
}
<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-name>studentServletservlet-name>
<servlet-class>com.javaweb.servlet.StudentServletservlet-class>
servlet>
<servlet-mapping>
<servlet-name>studentServletservlet-name>
<url-pattern>/servlet/studenturl-pattern>
servlet-mapping>
web-app>
DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>student pagetitle>
head>
<body>
<a href="/xmm/servlet/student">student lista>
body>
html>
子标签,在该子标签中填写整数,越小的整数优先级越高。 <servlet>
<servlet-name>aservletservlet-name>
<servlet-class>com.bjpowernode.javaweb.servlet.AServletservlet-class>
<load-on-startup>1load-on-startup>
servlet>
<servlet-mapping>
<servlet-name>aservletservlet-name>
<url-pattern>/aurl-pattern>
servlet-mapping>
AServlet无参数构造方法执行了
AServlet's init method execute!
AServlet's service method execute!
AServlet's service method execute!
AServlet's destroy method execute!
package com.bjpowernode.javaweb.servlet;
import jakarta.servlet.*;
import java.io.IOException;
public class AServlet implements Servlet {
// 无参数构造方法
public AServlet() {
System.out.println("AServlet无参数构造方法执行了");
}
// 程序员手动提供Servlet类的有参数的构造方法,会怎么样?
/*public AServlet(String s){
}*/
// init被翻译为初始化
// init方法只执行一次
// 在AServlet对象第一次被创建之后执行。
// init方法通常是完成初始化操作的。
// init方法在执行的时候AServlet对象已经被创建出来了。
@Override
public void init(ServletConfig servletConfig) throws ServletException {
System.out.println("AServlet's init method execute!");
}
// service方法:是处理用户请求的核心方法。
// 只要用户发送一次请求,service方法必然会执行一次。
// 发送100次请求,service方法则执行100次。
@Override
public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
System.out.println("AServlet's service method execute!");
}
// destroy方法也是只执行一次。
// Tomcat服务器在销毁AServlet对象之前会调用一次destroy方法
// destroy方法在执行的时候,AServlet对象的内存还没有被销毁。即将被销毁。
// destroy方法中可以编写销毁前的准备。
// 比如说,服务器关闭的时候,AServlet对象开启了一些资源,这些资源可能是流,可能是数据库连接
// 那么,关闭服务器的时候,要关闭这些流,关闭这些数据库连接,那么这些关闭资源的代码就可以写到destroy方法当中。
@Override
public void destroy() {
System.out.println("AServlet's destroy method execute!");
}
@Override
public ServletConfig getServletConfig() {
return null;
}
@Override
public String getServletInfo() {
return null;
}
}
package com.bjpowernode.javaweb.servlet;
import jakarta.servlet.*;
import java.io.IOException;
/**
* 编写一个标准通用的Servlet,起名:GenericServlet
* 以后所有的Servlet类都不要直接实现Servlet接口了。
* 以后所有的Servlet类都要继承GenericServlet类。
* GenericServlet 就是一个适配器。
*/
public abstract class GenericServlet implements Servlet {
@Override
public void init(ServletConfig config) throws ServletException{
}
@Override
public ServletConfig getServletConfig() {
return null;
}
/**
* 抽象方法,这个方法最常用。所以要求子类必须实现service方法。
* @param servletRequest
* @param servletResponse
* @throws ServletException
* @throws IOException
*/
public abstract void service(ServletRequest servletRequest, ServletResponse servletResponse)
throws ServletException, IOException;
@Override
public String getServletInfo() {
return null;
}
@Override
public void destroy() {
}
}
public class Tomcat {
public static void main(String[] args){
// .....
// Tomcat服务器伪代码
// 创建LoginServlet对象(通过反射机制,调用无参数构造方法来实例化LoginServlet对象)
Class clazz = Class.forName("com.bjpowernode.javaweb.servlet.LoginServlet");
Object obj = clazz.newInstance();
// 向下转型
Servlet servlet = (Servlet)obj;
// 创建ServletConfig对象
// Tomcat服务器负责将ServletConfig对象实例化出来。
// 多态(Tomcat服务器完全实现了Servlet规范)
ServletConfig servletConfig = new org.apache.catalina.core.StandardWrapperFacade();
// 调用Servlet的init方法
servlet.init(servletConfig);
// 调用Servlet的service方法
// ....
}
}
package com.bjpowernode.javaweb.servlet;
import jakarta.servlet.*;
import java.io.IOException;
/**
* 编写一个标准通用的Servlet,起名:GenericServlet
* 以后所有的Servlet类都不要直接实现Servlet接口了。
* 以后所有的Servlet类都要继承GenericServlet类。
* GenericServlet 就是一个适配器。
*/
public abstract class GenericServlet implements Servlet {
// 成员变量
// 将ServletConfig对象保存以便其他方法可以使用
private ServletConfig config;
/**
* init方法中的ServletConfig对象是小猫咪创建好的。
* 这个ServletConfig对象目前在init方法的参数上,属于局部变量。
* 那么ServletConfig对象肯定以后要在service方法中使用,怎么才能保证ServletConfig对象在service方法中能够使用呢?
*/
// 防止子类重写该方法,将该方法声明为final,提供一个单独的init方法是供子类重写的,在该方法中调用允许子类重写的init方法
@Override
public final void init(ServletConfig config) throws ServletException {
//System.out.println("servletConfig对象,小猫咪创建好的:" + config);
this.config = config;
// 调用init()方法
this.init();
}
/**
* 这个init方法是供子类重写的。
*/
public void init(){
}
@Override
public ServletConfig getServletConfig() {
return config;
}
/**
* 抽象方法,这个方法最常用。所以要求子类必须实现service方法。
* @param servletRequest
* @param servletResponse
* @throws ServletException
* @throws IOException
*/
public abstract void service(ServletRequest servletRequest, ServletResponse servletResponse)
throws ServletException, IOException;
@Override
public String getServletInfo() {
return null;
}
@Override
public void destroy() {
}
}
transient 表示修饰的属性不可序列化
package com.bjpowernode.javaweb.servlet;
import jakarta.servlet.*;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Enumeration;
/**
* ServletConfig
* 1. ServletConfig是什么?
* jakarta.servlet.ServletConfig
* 显然ServletConfig是Servlet规范中的一员。
* ServletConfig是一个接口。(jakarta.servlet.Servlet是一个接口。)
* 2. 谁去实现了这个接口呢? WEB服务器实现了
* public class org.apache.catalina.core.StandardWrapperFacade implements ServletConfig {}
* 结论:Tomcat服务器实现了ServletConfig接口。
* 思考:如果把Tomcat服务器换成jetty服务器,输出ServletConfig对象的时候,还是这个结果吗?
* 不一定一样,包名类名可能和Tomcat不一样。但是他们都实现了ServletConfig这个规范。
* 3. 一个Servlet对象中有一个ServletConfig对象。(Servlet和ServletConfig对象是一对一。)
* 100个Servlet,就应该有100个ServletConfig对象。
* 4. ServletConfig对象是谁创建的?在什么时候创建的?
* Tomcat服务器(WEB服务器)创建了ServletConfig对象。
* 在创建Servlet对象的时候,同时创建ServletConfig对象。
* 5. ServletConfig接口到底是干啥的?有什么用呢?
* Config是哪个单词的缩写?
* Configuration
* ServletConfig对象被翻译为:Servlet对象的配置信息对象。
* 一个Servlet对象就有一个配置信息对象。
* 两个Servlet对象就有两个配置信息对象。
*
* 6. ServletConfig对象中到底包装了什么信息呢?
*
* configTest
* com.bjpowernode.javaweb.servlet.ConfigTestServlet
*
* ServletConfig对象中包装的信息是:
* web.xml文件中 标签的配置信息。
*
* Tomcat小猫咪解析web.xml文件,将web.xml文件中 标签中的配置信息自动包装到ServletConfig对象中。
*
* 7. ServletConfig接口中有哪些方法?
*
* configTest
* com.bjpowernode.javaweb.servlet.ConfigTestServlet
*
*
* driver
* com.mysql.cj.jdbc.Driver
*
*
* url
* jdbc:mysql://localhost:3306/bjpowernode
*
*
* user
* root
*
*
* password
* root1234
*
*
* 以上 标签中的 是初始化参数。这个初始化参数信息会自动被小猫咪封装到ServletConfig对象当中。
* 8. ServletConfig接口中有4个方法:
* 第1个方法:
* public String getInitParameter(String name);
* 第2个方法:
* public Enumeration getInitParameterNames();
* 第3个方法:
* public ServletContext getServletContext();
* 第4个方法:
* public String getServletName();
*
* 以上的4个方法,在自己编写的Servlet类当中也可以使用this去调用。(这个Servlet继承了GenericServlet)
*/
public class ConfigTestServlet extends GenericServlet {
@Override
public void service(ServletRequest request, ServletResponse response) throws ServletException, IOException {
response.setContentType("text/html");
PrintWriter out = response.getWriter();
// 获取ServletConfig对象
ServletConfig config = this.getServletConfig();
// 输出该对象
// org.apache.catalina.core.StandardWrapperFacade@aea0d43
out.print("ServletConfig对象是:" + config.toString());
out.print("
");
// 获取
String servletName = config.getServletName();
out.print("" +servletName+"");
out.print("
");
// 通过ServletConfig对象的两个方法,可以获取到web.xml文件中的初始化参数配置信息。
// java.util.Enumeration getInitParameterNames() 获取所有的初始化参数的name
Enumeration<String> initParameterNames = config.getInitParameterNames();
// 遍历集合
while(initParameterNames.hasMoreElements()) { // 是否有更多元素
String parameterName = initParameterNames.nextElement(); // 取元素
String parameterVal = config.getInitParameter(parameterName); // 通过name获取value
out.print(parameterName + "=" + parameterVal);
out.print("
");
}
// java.lang.String getInitParameter(java.lang.String name) 通过初始化参数的name获取value
/*String driver = config.getInitParameter("driver");
out.print(driver);*/
// ServletConfig的四个方法在自己编写的Servlet类当中也可以使用this去调用。(这个Servlet继承了GenericServlet,GenericServlet实现了ServletConfig接口)
// 实际上获取一个Servlet对象的初始化参数,可以不用获取ServletConfig对象。直接通过this也可以。
Enumeration<String> names = this.getInitParameterNames();
while(names.hasMoreElements()){
String name = names.nextElement();
String value = this.getInitParameter(name);
// 打印到后台
System.out.println(name + "=" + value);
}
}
}
标签中的配置信息。(web.xml文件中servlet的配置信息)public String getInitParameter(String name); // 通过初始化参数的name获取value
public Enumeration<String> getInitParameterNames(); // 获取所有的初始化参数的name
public ServletContext getServletContext(); // 获取ServletContext对象
public String getServletName(); // 获取Servlet的name
// 怎么获取ServletContext对象呢?
// 第一种方式:通过ServletConfig对象获取ServletContext对象。
ServletContext application = config.getServletContext();
// 输出
out.print("
" + application); //org.apache.catalina.core.ApplicationContextFacade@19187bbb
// 第二种方式:通过this也可以获取ServletContext对象。
ServletContext application2 = this.getServletContext();
out.print("
" + application2); //org.apache.catalina.core.ApplicationContextFacade@19187bbb
package com.bjpowernode.javaweb.servlet;
import jakarta.servlet.*;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Enumeration;
/**
* ServletContext
* 1. ServletContext是什么?
* ServletContext是接口,是Servlet规范中的一员。
* 2. ServletContext是谁实现的?
* Tomcat服务器(WEB服务器)实现了ServletContext接口。
* public class org.apache.catalina.core.ApplicationContextFacade implements ServletContext {}
* 3. ServletContext对象是谁创建的?在什么时候创建的?
* ServletContext对象在WEB服务器启动的时候创建。
* ServletContext对象是WEB服务器创建的。
* 对于一个webapp来说,ServletContext对象只有一个。
* 一个 标签对应一个ServletContext对象
* ServletContext对象在服务器关闭的时候销毁。
* 4. ServletContext怎么理解?
* context是什么意思?
* Servlet对象的环境对象。(Servlet对象的上下文对象。)
* ServletContext对象其实对应的就是整个web.xml文件。
* 50个学生,每个学生都是一个Servlet,这50个学生都在同一个教室当中。那么这个教室就相当于ServletContext对象。
* 放在ServletContext对象当中的数据,所有Servlet一定是共享的。
* 比如:一个教室中的空调是所有学生共享的,一个教室中的语文老师是所有学生共享的。
* Tomcat是一个容器,一个容器当中可以放多个webapp,一个webapp对应一个ServletContext对象。
*/
public class AServlet extends GenericServlet {
@Override
public void service(ServletRequest request, ServletResponse response) throws ServletException, IOException {
response.setContentType("text/html");
PrintWriter out = response.getWriter();
// 获取ServletContext对象
ServletContext application = this.getServletContext();
out.print("ServletContext对象是:" + application + "
");
}
}
public String getInitParameter(String name); // 通过初始化参数的name获取value
public Enumeration<String> getInitParameterNames(); // 获取所有的初始化参数的name
<context-param>
<param-name>pageSizeparam-name>
<param-value>10param-value>
context-param>
<context-param>
<param-name>startIndexparam-name>
<param-value>0param-value>
context-param>
// 获取上下文的初始化参数
Enumeration<String> initParameterNames = application.getInitParameterNames();
while(initParameterNames.hasMoreElements()){
String name = initParameterNames.nextElement();
String value = application.getInitParameter(name);
out.print(name + "=" + value + "
");
}
// 获取应用的根路径(非常重要),因为在java源代码当中有一些地方可能会需要应用的根路径,这个方法可以动态获取应用的根路径
// 在java源码当中,不要将应用的根路径写死,因为你永远都不知道这个应用在最终部署的时候,起一个什么名字。
public String getContextPath();
//String contextPath = application.getContextPath();
// 获取context path (获取应用上下文的根)
String contextPath = application.getContextPath();
out.print(contextPath + "
");
// 获取文件的绝对路径(真实路径)
public String getRealPath(String path);
// 获取文件的绝对路径
// 后面的这个路径,加了一个“/”,这个“/”代表的是web的根
//String realPath = application.getRealPath("/index.html"); // 可以
// 你不加“/”,默认也是从根下开始找。
//String realPath = application.getRealPath("index.html"); // 不加“/”也可以
//out.print(realPath + "
");
// C:\Users\Administrator\IdeaProjects\javaweb\out\artifacts\servlet04_war_exploded\common\common.html
String realPath = application.getRealPath("/common/common.html");
out.print(realPath + "
");
// 通过ServletContext对象也是可以记录日志的
public void log(String message);
// 记录日志信息外,还记录异常信息,控制台不会发送异常,只是记录异常信息
public void log(String message, Throwable t);
// 这些日志信息记录到哪里了?
// localhost.2021-11-05.log
// IDEA工具的Tomcat服务器是根据下载的Tomcat服务器生成的副本
// Tomcat服务器的logs目录下都有哪些日志文件?
//catalina.2021-11-05.log 服务器端的java程序运行的控制台信息。
//localhost.2021-11-05.log ServletContext对象的log方法记录的日志信息存储到这个文件中。
//localhost_access_log.2021-11-05.txt 访问日志
// log
// 这个日志会自动记录到哪里呢?
// CATALINA_HOME/logs目录下。
//application.log("大家好,我是动力节点杜老师,欢迎大家和我一起学习Servlet规范!");
int age = 17; // 17岁
// 当年龄小于18岁的时候,表示非法,记录日志
if(age < 18) {
application.log("对不起,您未成年,请绕行!", new RuntimeException("小屁孩,快走开,不适合你!"));
}
// ServletContext对象还有另一个名字:应用域(后面还有其他域,例如:请求域、会话域)
// 如果所有的用户共享一份数据,并且这个数据很少的被修改,并且这个数据量很少,可以将这些数据放到ServletContext这个应用域中
// 为什么是所有用户共享的数据? 不是共享的没有意义。因为ServletContext这个对象只有一个。只有共享的数据放进去才有意义。
// 为什么数据量要小? 因为数据量比较大的话,太占用堆内存,并且这个对象的生命周期比较长,服务器关闭的时候,这个对象才会被销毁。大数据量会影响服务器的性能。占用内存较小的数据量可以考虑放进去。
// 为什么这些共享数据很少的修改,或者说几乎不修改?
// 所有用户共享的数据,如果涉及到修改操作,必然会存在线程并发所带来的安全问题。所以放在ServletContext对象中的数据一般都是只读的。
// 数据量小、所有用户共享、又不修改,这样的数据放到ServletContext这个应用域当中,会大大提升效率。因为应用域相当于一个缓存,放到缓存中的数据,下次在用的时候,不需要从数据库中再次获取,大大提升执行效率。
// 存(怎么向ServletContext应用域中存数据)
public void setAttribute(String name, Object value); // map.put(k, v)
// 取(怎么从ServletContext应用域中取数据)
public Object getAttribute(String name); // Object v = map.get(k)
// 删(怎么删除ServletContext应用域中的数据)
public void removeAttribute(String name); // map.remove(k)
// 准备数据
User user = new User("jack", "123");
// 向ServletContext应用域当中存储数据
application.setAttribute("userObj", user);
// 取出来
//Object userObj = application.getAttribute("userObj");
// 输出到浏览器
//out.print(userObj + "
");
jakarta.servlet.Servlet(接口)【爷爷】
jakarta.servlet.GenericServlet implements Servlet(抽象类)【儿子】
jakarta.servlet.http.HttpServlet extends GenericServlet(抽象类)【孙子】
我们以后编写的Servlet要继承HttpServlet类。
GET /servlet05/getServlet?username=lucy&userpwd=1111 HTTP/1.1 请求行
Host: localhost:8080 请求头
Connection: keep-alive
sec-ch-ua: "Google Chrome";v="95", "Chromium";v="95", ";Not A Brand";v="99"
sec-ch-ua-mobile: ?0
sec-ch-ua-platform: "Windows"
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/95.0.4638.54 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
Sec-Fetch-Site: same-origin
Sec-Fetch-Mode: navigate
Sec-Fetch-User: ?1
Sec-Fetch-Dest: document
Referer: http://localhost:8080/servlet05/index.html
Accept-Encoding: gzip, deflate, br
Accept-Language: zh-CN,zh;q=0.9
空白行
请求体
POST /servlet05/postServlet HTTP/1.1 请求行
Host: localhost:8080 请求头
Connection: keep-alive
Content-Length: 25
Cache-Control: max-age=0
sec-ch-ua: "Google Chrome";v="95", "Chromium";v="95", ";Not A Brand";v="99"
sec-ch-ua-mobile: ?0
sec-ch-ua-platform: "Windows"
Upgrade-Insecure-Requests: 1
Origin: http://localhost:8080
Content-Type: application/x-www-form-urlencoded
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/95.0.4638.54 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
Sec-Fetch-Site: same-origin
Sec-Fetch-Mode: navigate
Sec-Fetch-User: ?1
Sec-Fetch-Dest: document
Referer: http://localhost:8080/servlet05/index.html
Accept-Encoding: gzip, deflate, br
Accept-Language: zh-CN,zh;q=0.9
空白行
username=lisi&userpwd=123 请求体
)HTTP/1.1 200 ok 状态行
Content-Type: text/html;charset=UTF-8 响应头
Content-Length: 160
Date: Mon, 08 Nov 2021 13:19:32 GMT
Keep-Alive: timeout=20
Connection: keep-alive
空白行
响应体
from get servlet
from get servlet
package com.bjpowernode.template1;
public class Student {
/**
* 这个方法描述学生的一天
*/
public void day(){
// 和Teacher的算法相同。
qiChuang();
xiShu();
chiZaoCan();
doSome();
chiWanFan();
shuiJiao();
}
public void qiChuang(){
System.out.println("起床");
}
public void xiShu(){
System.out.println("洗漱");
}
public void chiZaoCan(){
System.out.println("吃早餐");
}
public void doSome(){
System.out.println("学生上学,学习");
}
public void chiWanFan(){
System.out.println("吃晚饭");
}
public void shuiJiao(){
System.out.println("睡觉");
}
}
package com.bjpowernode.template1;
/**
* 存在的问题:
* 第一:算法没有得到重复的使用。
* 第二:代码没有得到复用。
*/
public class Teacher {
/**
* 这个方法描述老师的一天
*/
public void day(){
// 和Student的算法相同。
qiChuang();
xiShu();
chiZaoCan();
doSome();
chiWanFan();
shuiJiao();
}
public void qiChuang(){
System.out.println("起床");
}
public void xiShu(){
System.out.println("洗漱");
}
public void chiZaoCan(){
System.out.println("吃早餐");
}
public void doSome(){
System.out.println("老师正在课堂上授课,教授学生知识");
}
public void chiWanFan(){
System.out.println("吃晚饭");
}
public void shuiJiao(){
System.out.println("睡觉");
}
}
package com.bjpowernode.template2;
/**
* Teacher和Student都是Person
* 1. Person就是模板方法设计模式当中的模板类。
* 2. day()方法就是模板方法设计模式当中的模板方法。
*/
public abstract class Person { // 模板类通常是抽象类。
// 模板方法
// 添加了final之后,这个方法无法被覆盖,这样核心的算法也可以得到保护。
// 模板方法定义核心的算法骨架,具体的实现步骤可以延迟到子类当中去实现。
// 核心算法一方面是得到了保护,不能被改变。另外一方面就是算法得到了重复使用。
// 另外代码也得到了服用,因为算法中某些步骤的代码是固定的。这些固定的代码不会随着子类的变化而变换,这一部分代码可以写到模板类当中。
public final void day(){
// 第一步
qiChuang();
// 第二步
xiShu();
// 第三步
chiZaoCan();
// 第四步
doSome();
// 第五步
chiWanFan();
// 第六步
shuiJiao();
}
// 其中的某些步骤,不会随着子类的变化而变化,这些代码可以写到父类中,得到代码复用。
public void qiChuang(){
System.out.println("起床");
}
public void xiShu(){
System.out.println("洗漱");
}
public void chiZaoCan(){
System.out.println("吃早餐");
}
// 这一步是要做,但是具体这一步怎么做,子类说了算。
public abstract void doSome();
public void chiWanFan(){
System.out.println("吃晚饭");
}
public void shuiJiao(){
System.out.println("睡觉");
}
}
package com.bjpowernode.template2;
public class Student extends Person {
public void doSome(){
System.out.println("学生上学,学习");
}
}
package com.bjpowernode.template2;
public class Teacher extends Person{
public void doSome(){
System.out.println("老师正在课堂上授课,教授学生知识");
}
}
public class HelloServlet extends HttpServlet {
// 用户第一次请求,创建HelloServlet对象的时候,会执行这个无参数构造方法。
public HelloServlet() {
}
//override 重写 doGet方法
//override 重写 doPost方法
}
public abstract class GenericServlet implements Servlet, ServletConfig,
java.io.Serializable {
// 用户第一次请求的时候,HelloServlet对象第一次被创建之后,这个init方法会执行。
public void init(ServletConfig config) throws ServletException {
this.config = config;
this.init();
}
// 用户第一次请求的时候,带有参数的init(ServletConfig config)执行之后,会执行这个没有参数的init()
public void init() throws ServletException {
// NOOP by default
}
}
// HttpServlet模板类。
public abstract class HttpServlet extends GenericServlet {
// 用户发送第一次请求的时候这个service会执行
// 用户发送第N次请求的时候,这个service方法还是会执行。
// 用户只要发送一次请求,这个service方法就会执行一次。
@Override
public void service(ServletRequest req, ServletResponse res)
throws ServletException, IOException {
HttpServletRequest request;
HttpServletResponse response;
try {
// 将ServletRequest和ServletResponse向下转型为带有Http的HttpServletRequest和HttpServletResponse
request = (HttpServletRequest) req;
response = (HttpServletResponse) res;
} catch (ClassCastException e) {
throw new ServletException(lStrings.getString("http.non_http"));
}
// 调用重载的service方法。
service(request, response);
}
// 这个service方法的两个参数都是带有Http的。
// 这个service是一个模板方法。
// 在该方法中定义核心算法骨架,具体的实现步骤延迟到子类中去完成。
protected void service(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
// 获取请求方式
// 这个请求方式最终可能是:""
// 注意:request.getMethod()方法获取的是请求方式,可能是七种之一:
// GET POST PUT DELETE HEAD OPTIONS TRACE
String method = req.getMethod();
// 如果请求方式是GET请求,则执行doGet方法。
if (method.equals(METHOD_GET)) {
long lastModified = getLastModified(req);
if (lastModified == -1) {
// servlet doesn't support if-modified-since, no reason
// to go through further expensive logic
doGet(req, resp);
} else {
long ifModifiedSince;
try {
ifModifiedSince = req.getDateHeader(HEADER_IFMODSINCE);
} catch (IllegalArgumentException iae) {
// Invalid date header - proceed as if none was set
ifModifiedSince = -1;
}
if (ifModifiedSince < (lastModified / 1000 * 1000)) {
// If the servlet mod time is later, call doGet()
// Round down to the nearest second for a proper compare
// A ifModifiedSince of -1 will always be less
maybeSetLastModified(resp, lastModified);
doGet(req, resp);
} else {
resp.setStatus(HttpServletResponse.SC_NOT_MODIFIED);
}
}
} else if (method.equals(METHOD_HEAD)) {
long lastModified = getLastModified(req);
maybeSetLastModified(resp, lastModified);
doHead(req, resp);
} else if (method.equals(METHOD_POST)) {
// 如果请求方式是POST请求,则执行doPost方法。
doPost(req, resp);
} else if (method.equals(METHOD_PUT)) {
doPut(req, resp);
} else if (method.equals(METHOD_DELETE)) {
doDelete(req, resp);
} else if (method.equals(METHOD_OPTIONS)) {
doOptions(req,resp);
} else if (method.equals(METHOD_TRACE)) {
doTrace(req,resp);
} else {
//
// Note that this means NO servlet supports whatever
// method was requested, anywhere on this server.
//
String errMsg = lStrings.getString("http.method_not_implemented");
Object[] errArgs = new Object[1];
errArgs[0] = method;
errMsg = MessageFormat.format(errMsg, errArgs);
resp.sendError(HttpServletResponse.SC_NOT_IMPLEMENTED, errMsg);
}
}
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException{
// 报405错误
String msg = lStrings.getString("http.method_get_not_supported");
sendMethodNotAllowed(req, resp, msg);
}
protected void doPost(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
// 报405错误
String msg = lStrings.getString("http.method_post_not_supported");
sendMethodNotAllowed(req, resp, msg);
}
}
/*
通过以上源代码分析:
假设前端发送的请求是get请求,后端程序员重写的方法是doPost,子类没有实现doGet方法,会执行父类的doGet方法,父类的doGet方法会报405错误
假设前端发送的请求是post请求,后端程序员重写的方法是doGet
会发生什么呢?
发生405这样的一个错误。
405表示前端的错误,发送的请求方式不对。和服务器不一致。不是服务器需要的请求方式。
通过以上源代码可以知道:只要HttpServlet类中的doGet方法或doPost方法执行了,必然405.
怎么避免405的错误呢?
后端重写了doGet方法,前端一定要发get请求。
后端重写了doPost方法,前端一定要发post请求。
这样可以避免405错误。
这种前端到底需要发什么样的请求,其实应该后端说了算。后端让发什么方式,前端就得发什么方式。
有的人,你会看到为了避免405错误,在Servlet类当中,将doGet和doPost方法都进行了重写。
这样,确实可以避免405的发生,但是不建议,405错误还是有用的。该报错的时候就应该让他报错。
如果你要是同时重写了doGet和doPost,那还不如你直接重写service方法好了。这样代码还能
少写一点。
*/
page1/page2/page.html
<welcome-file-list>
<welcome-file>page1/page2/page.htmlwelcome-file>
<welcome-file>login.htmlwelcome-file>
welcome-file-list>
<welcome-file-list>
<welcome-file>index.htmlwelcome-file>
<welcome-file>index.htmwelcome-file>
<welcome-file>index.jspwelcome-file>
welcome-file-list>
public class WelcomeServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
response.setContentType("text/html");
PrintWriter out = response.getWriter();
out.print("welcome to bjpowernode!
");
}
}
<servlet>
<servlet-name>welcomeServletservlet-name>
<servlet-class>com.bjpowernode.javaweb.servlet.WelcomeServletservlet-class>
servlet>
<servlet-mapping>
<servlet-name>welcomeServletservlet-name>
<url-pattern>/fdsa/fds/a/fds/af/ds/af/dsafdsafdsaurl-pattern>
servlet-mapping>
HttpServletRequest是一个接口,全限定名称:jakarta.servlet.http.HttpServletRequest
HttpServletRequest接口是Servlet规范中的一员。
HttpServletRequest接口的父接口:ServletRequest
public interface HttpServletRequest extends ServletRequest {}
HttpServletRequest接口的实现类谁写的? HttpServletRequest对象是谁给创建的?
通过测试:org.apache.catalina.connector.RequestFacade 实现了 HttpServletRequest接口
public class RequestFacade implements HttpServletRequest {}
测试结果说明:Tomcat服务器(WEB服务器、WEB容器)实现了HttpServletRequest接口,还是说明了Tomcat服务器实现了Servlet规范。而对于我们javaweb程序员来说,实际上不需要关心这个,我们只需要面向接口编程即可。我们关心的是HttpServletRequest接口中有哪些方法,这些方法可以完成什么功能!!!!
HttpServletRequest对象中都有什么信息?都包装了什么信息?
request和response对象的生命周期?
HttpServletRequest接口中有哪些常用的方法?
怎么获取前端浏览器用户提交的数据?
Map<String,String[]> getParameterMap() 这个是获取Map
Enumeration<String> getParameterNames() 这个是获取Map集合中所有的key
String[] getParameterValues(String name) 根据key获取Map集合的value
String getParameter(String name) 根据key获取value这个一维数组当中的第一个元素。这个方法最常用。
// 以上的4个方法,和获取用户提交的数据有关系。
思考:如果是你,前端的form表单提交了数据之后,你准备怎么存储这些数据,你准备采用什么样的数据结构去存储这些数据呢?
前端提交的数据格式:username=abc&userpwd=111&aihao=s&aihao=d&aihao=tt
我会采用Map集合来存储:
Map<String,String>
key存储String
value存储String
这种想法对吗?不对。
如果采用以上的数据结构存储会发现key重复的时候value覆盖。
key value
---------------------
username abc
userpwd 111
aihao s
aihao d
aihao tt
这样是不行的,因为map的key不能重复。
Map<String, String[]>
key存储String
value存储String[]
key value
-------------------------------
username {"abc"}
userpwd {"111"}
aihao {"s","d","tt"}
注意:前端表单提交数据的时候,假设提交了120这样的“数字”,其实是以字符串"120"的方式提交的,所以服务器端获取到的一定是一个字符串的"120",而不是一个数字。(前端永远提交的是字符串,后端获取的也永远是字符串。)
手工开发一个webapp。测试HttpServletRequest接口中的相关方法。
先测试了4个常用的方法,获取请求参数的四个方法。
Map<String,String[]> parameterMap = request.getParameterMap();
Enumeration<String> names = request.getParameterNames();
String[] values = request.getParameterValues("name");
String value = request.getParameter("name");
package com.bjpowernode.javaweb.servlet;
import jakarta.servlet.*;
import jakarta.servlet.http.*;
import java.io.*;
import java.util.*;
/*
username=zhangsan&userpwd=123&interest=s&interest=d
Map
key value
---------------------------
"username" {"zhangsan"}
"userpwd" {"123"}
"interest" {"s", "d"}
总结一下:request接口中四个非常重要的方法。
Map parameterMap = request.getParameterMap();
Enumeration names = request.getParameterNames();
String[] values = request.getParameterValues("name");
String value = request.getParameter("name");
*/
public class RequestTestServlet extends HttpServlet{
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws IOException,ServletException{
// 面向接口编程:HttpServletRequest接口。
// 获取前端提交的数据
// 前端会提交什么数据呢?
// username=zhangsan&userpwd=123&interest=s&interest=d
// 以上的数据会被小猫咪封装到request对象中。
// 获取参数Map集合
Map<String,String[]> parameterMap = request.getParameterMap();
// 遍历Map集合(获取Map集合中所有的key,遍历)
Set<String> keys = parameterMap.keySet();
Iterator<String> it = keys.iterator();
while(it.hasNext()){
String key = it.next();
//System.out.println(key);
// 通过key获取value
String[] values = parameterMap.get(key);
/*
username=[Ljava.lang.String;@7cce40b4
userpwd=[Ljava.lang.String;@7453f0b9
interest=[Ljava.lang.String;@4063ebb5
*/
//System.out.println(key + "=" + values);
// 遍历一维数组
System.out.print(key + "=");
for(String value : values){
System.out.print(value + ",");
}
// 换行
System.out.println();
}
// 直接通过getParameterNames()这个方法,可以直接获取这个Map集合的所有key
Enumeration<String> names = request.getParameterNames();
while(names.hasMoreElements()){
String name = names.nextElement();
System.out.println(name);
}
// 直接通过name获取value这个一维数组。
String[] usernames = request.getParameterValues("username");
String[] userpwds = request.getParameterValues("userpwd");
String[] interests = request.getParameterValues("interest");
// 遍历一维数组
for(String username : usernames){
System.out.println(username);
}
for(String userpwd : userpwds){
System.out.println(userpwd);
}
for(String interest : interests){
System.out.println(interest);
}
// 通过name获取value这个一维数组的第一个元素
// 这个方法使用最多,因为这个一维数组中一般只有一个元素。
String username = request.getParameter("username");
String userpwd = request.getParameter("userpwd");
//String interest = request.getParameter("interest");
// 既然是checkbox,调用方法:request.getParameterValues("interest")
String[] interests2 = request.getParameterValues("interest");
// 获取的都是一维数组当中的第一个元素。
System.out.println(username);
System.out.println(userpwd);
//System.out.println(interest);
for(String interest : interests2){
System.out.println(interest);
}
}
}
request对象实际上又称为“请求域”对象。
应用域对象是什么?
ServletContext (Servlet上下文对象。)
什么情况下会考虑向ServletContext这个应用域当中绑定数据呢?
你见过哪些缓存技术呢?
ServletContext当中有三个操作域的方法:
void setAttribute(String name, Object obj); // 向域当中绑定数据。
Object getAttribute(String name); // 从域当中根据name获取数据。
void removeAttribute(String name); // 将域当中绑定的数据移除
// 以上的操作类似于Map集合的操作。
Map<String, Object> map;
map.put("name", obj); // 向map集合中放key和value
Object obj = map.get("name"); // 通过map集合的key获取value
map.remove("name"); // 通过Map集合的key删除key和value这个键值对。
“请求域”对象
“请求域”对象要比“应用域”对象范围小很多。生命周期短很多。请求域只在一次请求内有效。
一个请求对象request对应一个请求域对象。一次请求结束之后,这个请求域就销毁了。
请求域对象也有这三个方法:
void setAttribute(String name, Object obj); // 向域当中绑定数据。
Object getAttribute(String name); // 从域当中根据name获取数据。
void removeAttribute(String name); // 将域当中绑定的数据移除
请求域和应用域的选用原则?
跳转
转发(一次请求)
// 这样做可以吗?
// 在AServlet当中new一个BServlet对象,然后调用BServlet对象的doGet方法,把request对象传过去。
// 这个代码虽然可以实现功能,但是Servlet对象不能自己由程序员来new。自己new的Servlet对象生命周期不受Tomcat服务器的管理。
/*BServlet bServlet = new BServlet();
bServlet.doGet(request, response);*/
// 使用Servlet当中的转发机制。
// 执行了AServlet之后,跳转到BServlet。(这个资源跳转可以使用转发机制来完成。)
// 怎么转发?代码怎么写?
// 第一步:获取请求转发器对象
// 相当于把"/b"这个路径包装到请求转发器当中,实际上是把下一个跳转的资源的路径告知给Tomcat服务器了。
RequestDispatcher dispatcher = request.getRequestDispatcher("/b");
// 第二步:调用请求转发器RequestDispatcher的forward方法。进行转发。
// 转发的时候:这两个参数很重要。request和response都是要传递给下一个资源的。
dispatcher.forward(request, response);
// 一行代码搞定转发。
request.getRequestDispatcher("/b").forward(request, response);
两个Servlet怎么共享数据?
转发的下一个资源必须是一个Servlet吗?
// 转发到一个Servlet,也可以转发到一个HTML,只要是WEB容器当中的合法资源即可。
request.getRequestDispatcher("/test.html").forward(request, response);
关于request对象中两个非常容易混淆的方法:
// uri?username=zhangsan&userpwd=123&sex=1
String username = request.getParameter("username");
// 之前一定是执行过:request.setAttribute("name", new Object())
Object obj = request.getAttribute("name");
// 以上两个方法的区别是什么?
// 第一个方法:获取的是用户在浏览器上提交的数据。
// 第二个方法:获取的是请求域当中绑定的数据。
HttpServletRequest接口的其他常用方法:
// 获取客户端的IP地址
String remoteAddr = request.getRemoteAddr();
// request.getRemoteHost(); 获取远程主机地址
// request.getRemotePort(); 获取端口
// get请求在请求行上提交数据。
// post请求在请求体中提交数据。
// 设置请求体的字符集。(显然这个方法是处理POST请求的乱码问题。这种方式并不能解决get请求的乱码问题。)
// Tomcat10之后,request请求体当中的字符集默认就是UTF-8,不需要设置字符集,不会出现乱码问题。
// Tomcat9前(包括9在内),如果前端请求体提交的是中文,后端获取之后出现乱码,怎么解决这个乱码?执行以下代码。
request.setCharacterEncoding("UTF-8");
// 在Tomcat9之前(包括9),响应中文也是有乱码的,怎么解决这个响应的乱码?
response.setContentType("text/html;charset=UTF-8");
// 在Tomcat10之后,包括10在内,响应中文的时候就不在出现乱码问题了。以上代码就不需要设置UTF-8了。
// 注意一个细节
// 在Tomcat10包括10在内之后的版本,中文将不再出现乱码。(这也体现了中文地位的提升。)
// get请求乱码问题怎么解决?
// get请求发送的时候,数据是在请求行上提交的,不是在请求体当中提交的。
// get请求乱码怎么解决
// 方案:修改CATALINA_HOME/conf/server.xml配置文件
<Connector URIEncoding="UTF-8" />
// 注意:从Tomcat8之后,URIEncoding的默认值就是UTF-8,所以GET请求也没有乱码问题了。
// 获取应用的根路径
String contextPath = request.getContextPath();
// 获取请求方式
String method = request.getMethod();
// 获取请求的URI
String uri = request.getRequestURI(); // /aaa/testRequest
// 获取servlet path,不带项目名
String servletPath = request.getServletPath(); // /testRequest
使用纯粹的Servlet完成单表【对部门的】的增删改查操作。(B/S结构的。)
第一步:准备一张数据库表。(sql脚本)
# 部门表
drop table if exists dept;
create table dept(
deptno int primary key,
dname varchar(255),
loc varchar(255)
);
insert into dept(deptno, dname, loc) values(10, 'XiaoShouBu', 'BEIJING');
insert into dept(deptno, dname, loc) values(20, 'YanFaBu', 'SHANGHAI');
insert into dept(deptno, dname, loc) values(30, 'JiShuBu', 'GUANGZHOU');
insert into dept(deptno, dname, loc) values(40, 'MeiTiBu', 'SHENZHEN');
commit;
select * from dept;
第二步:准备一套HTML页面(项目原型)
DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>欢迎使用OA系统title>
head>
<body>
<a href="./list.html">查看部门列表a>
body>
html>
DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>部门列表页面title>
head>
<body>
<h1 align="center">部门列表h1>
<hr>
<table align="center" border="1px solid black" width="50%">
<thead>
<tr>
<th>序号th>
<th>部门编号th>
<th>部门名称th>
<th>操作th>
tr>
thead>
<tbody>
<tr>
<td>1td>
<td>10td>
<td>销售部td>
<td>
<a href="">删除a>
<a href="edit.html">修改a>
<a href="detail.html">详情a>
td>
tr>
<tr>
<td>1td>
<td>10td>
<td>销售部td>
<td>
<a href="">删除a>
<a href="edit.html">修改a>
<a href="detail.html">详情a>
td>
tr>
<tr>
<td>1td>
<td>10td>
<td>销售部td>
<td>
<a href="">删除a>
<a href="edit.html">修改a>
<a href="detail.html">详情a>
td>
tr>
tbody>
table>
<hr>
<a href="add.html">新增部门a>
body>
html>
DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>新增部门title>
head>
<body>
<h1>新增部门h1>
<hr>
<form action="./list.html" method="post">
部门编号:<input type="text" name="dptno"><br>
部门名称:<input type="text" name="dname"><br>
部门位置:<input type="text" name="loc"><br>
<input type="submit" value="新增">
form>
body>
html>
DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>修改部门title>
head>
<body>
<h1>修改部门h1>
<hr >
<form action="list.html" method="get">
部门编号<input type="text" name="deptno" value="20" readonly /><br>
部门名称<input type="text" name="dname" value="销售部"/><br>
部门位置<input type="text" name="loc" value="北京"/><br>
<input type="submit" value="修改"/><br>
form>
body>
html>
DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>部门详情title>
head>
<body>
<h1>部门详情h1>
<hr>
<p>部门编号:20p>
<p>部门名称:销售部p>
<p>部门位置:北京p>
<input type="button" value="后退" onclick="window.history.back()">
body>
html>
第三步:分析我们这个系统包括哪些功能?
第四步:在IDEA当中搭建开发环境
创建一个webapp(给这个webapp添加servlet-api.jar和jsp-api.jar到classpath当中。)
向webapp中添加连接数据库的jar包(mysql驱动)
JDBC的工具类
package cw.javaweb.oa;
import java.sql.*;
import java.util.ResourceBundle;
public class DBUtil {
// 静态变量:在类加载时执行。
// 并且是有顺序的。自上而下的顺序。
// 属性资源文件绑定
private static ResourceBundle bundle = ResourceBundle.getBundle("resources.jdbc");
// 根据属性配置文件key获取value
private static String driver = bundle.getString("driver");
private static String url = bundle.getString("url");
private static String user = bundle.getString("user");
private static String password = bundle.getString("password");
static {
// 注册驱动(注册驱动只需要注册一次,放在静态代码块当中。DBUtil类加载的时候执行。)
try {
// "com.mysql.jdbc.Driver" 是连接数据库的驱动,不能写死。因为以后可能还会连接Oracle数据库。
// 如果连接oracle数据库的时候,还需要修改java代码,显然违背了OCP开闭原则。
// OCP开闭原则:对扩展开放,对修改关闭。(什么是符合OCP呢?在进行功能扩展的时候,不需要修改java源代码。)
// Class.forName("com.mysql.cj.jdbc.Driver");
Class.forName(driver);
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
/**
* 私有化构造器防止工具类被实例化
*/
private DBUtil() {
}
/**
* 获取数据库连接对象
* @return
* @throws SQLException
*/
public static Connection getConnection() throws SQLException {
return DriverManager.getConnection(url, user, password);
}
/**
* 关闭数据库连接资源
* @param conn
* @param stmt
* @param rs
*/
public static void close(Connection conn, Statement stmt, ResultSet rs) {
if (rs != null) {
try {
rs.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (stmt != null) {
try {
stmt.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (conn != null) {
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
第五步:实现第一个功能:查看部门列表
我们应该怎么去实现一个功能呢?
第一:先修改前端页面的超链接,因为用户先点击的就是这个超链接。
<a href="/oa/dept/list">查看部门列表a>
第二:编写web.xml文件
<servlet>
<servlet-name>listservlet-name>
<servlet-class>cw.javaweb.oa.web.DeptListServletservlet-class>
servlet>
<servlet-mapping>
<servlet-name>listservlet-name>
<url-pattern>/dept/listurl-pattern>
servlet-mapping>
第三:编写DeptListServlet类继承HttpServlet类。然后重写doGet方法。
第四:在DeptListServlet类的doGet方法中连接数据库,查询所有的部门,动态的展示部门列表页面.
分析list.html页面中哪部分是固定死的,哪部分是需要动态展示的。
list.html页面中的内容所有的双引号要替换成单引号,因为out.print(“”)这里有一个双引号,容易冲突。
现在写完这个功能之后,你会有一种感觉,感觉开发很繁琐,只使用servlet写代码太繁琐了。
package cw.javaweb.oa.web;
import cw.javaweb.oa.utils.DBUtil;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
public class DeptListServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// 设置响应的内容类型以及字符集,防止中文乱码
response.setContentType("text/html;charset=UTF-8");
// 获取输出流
PrintWriter out = response.getWriter();
out.print("");
out.print("");
out.print("");
out.print(" ");
out.print(" 部门列表页面 ");
out.print("");
out.print("");
out.print("部门列表
");
out.print("
");
out.print("");
out.print(" ");
out.print(" ");
out.print(" 序号 ");
out.print(" 部门编号 ");
out.print(" 部门名称 ");
out.print(" 操作 ");
out.print(" ");
out.print(" ");
out.print(" ");
// 连接数据库,查询所有部门
Connection conn = null;
PreparedStatement ps = null;
ResultSet rs = null;
try {
// 获取链接
conn = DBUtil.getConnection();
String sql = "select deptno, dname, loc from dept";
// 获取预编译的数据库操作对象
ps = conn.prepareStatement(sql);
// 执行sql,获取查询结果
rs = ps.executeQuery();
int i=0;
while (rs.next()) {
String deptno = rs.getString("deptno");
String dname = rs.getString("dname");
String loc = rs.getString("loc");
out.print(" ");
out.print(" "+ (i+1) +" ");
out.print(" "+ deptno +" ");
out.print(" "+ dname +" ");
out.print(" ");
out.print(" 删除");
out.print(" 修改");
out.print(" 详情");
out.print(" ");
out.print(" ");
}
} catch (SQLException e) {
e.printStackTrace();
} finally {
// 释放资源
DBUtil.close(conn, ps, rs);
}
out.print(" ");
out.print("
");
out.print("
");
out.print("新增部门");
out.print("");
out.print("");
}
}
第六步:查看部门详情。
建议:从前端往后端一步一步实现。首先要考虑的是,用户点击的是什么?用户点击的东西在哪里?
一定要先找到用户点的“详情”在哪里。找了半天,终于在后端的java程序中找到了
<a href='写一个路径'>详情a>
详情 是需要连接数据库的,所以这个超链接点击之后也是需要执行一段java代码的。所以要将这个超链接的路径修改一下。
注意:修改路径之后,这个路径是需要加项目名的。“/oa/dept/detail”
技巧:
out.print("详情");
重点:向服务器提交数据的格式:uri?name=value&name=value&name=value&name=value
这里的问号,必须是英文的问号。不能中文的问号。
解决404的问题。写web.xml文件。
<servlet>
<servlet-name>detailservlet-name>
<servlet-class>com.bjpowernode.oa.web.action.DeptDetailServletservlet-class>
servlet>
<servlet-mapping>
<servlet-name>detailservlet-name>
<url-pattern>/dept/detailurl-pattern>
servlet-mapping>
编写一个类:DeptDetailServlet继承HttpServlet,重写doGet方法。
在doGet方法当中:连接数据库,根据部门编号查询该部门的信息。动态展示部门详情页。
package cw.javaweb.oa.web;
import cw.javaweb.oa.utils.DBUtil;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
public class DeptDetailServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// 获取部门编号
// /oa/dept/detail?deptno=30
// 虽然是提交的30,但是服务器获取的是"30"这个字符串。
String deptno = request.getParameter("deptno");
// 设置内容类型以及编码
response.setContentType("text/html;charset=UTF-8");
// 获取输出对象
PrintWriter out = response.getWriter();
out.print("");
out.print("");
out.print("");
out.print(" ");
out.print(" 部门详情 ");
out.print("");
out.print("");
out.print(" 部门详情
");
out.print("
");
Connection conn = null;
PreparedStatement ps = null;
ResultSet rs = null;
try {
conn = DBUtil.getConnection();
String sql = "select dname, loc from dept where deptno = ?";
ps = conn.prepareStatement(sql);
ps.setString(1, deptno);
rs = ps.executeQuery();
if (rs.next()) {
String dname = rs.getString("dname");
String loc = rs.getString("loc");
out.print(" 部门编号:"
+ deptno +"");
out.print(" 部门名称:"
+ dname +"");
out.print(" 部门位置:"
+ loc +"");
}
} catch (SQLException e) {
e.printStackTrace();
} finally {
DBUtil.close(conn, ps, rs);
}
out.print("");
out.print("");
out.print("");
}
}
第七步:删除部门
怎么开始?从哪里开始?从前端页面开始,用户点击删除按钮的时候,应该提示用户是否删除。因为删除这个动作是比较危险的。任何系统在进行删除操作之前,是必须要提示用户的,因为这个删除的动作有可能是用户误操作。(在前端页面上写JS代码,来提示用户是否删除。)
<a href="javascript:void(0)" onclick="del(30)" >删除a>
<script type="text/javascript">
function del(dno){
// confirm 弹出一个确认框,点击确定返回true,否则为false
if(window.confirm("亲,删了不可恢复哦!")){
// 发送请求
// document.location.href = ""
// document.location = ""
// window.location.href = ""
// window.location = ""
document.location.href = "/oa/dept/delete?deptno=" + dno;
}
}
script>
以上的前端程序要写到后端的java代码当中:
解决404的问题:
http://localhost:8080/oa/dept/delete?deptno=30
web.xml文件
<servlet>
<servlet-name>deleteservlet-name>
<servlet-class>com.bjpowernode.oa.web.action.DeptDelServletservlet-class>
servlet>
<servlet-mapping>
<servlet-name>deleteservlet-name>
<url-pattern>/dept/deleteurl-pattern>
servlet-mapping>
编写DeptDelServlet继承HttpServlet,重写doGet方法。
删除成功或者失败的时候的一个处理(这里我们选择了转发,并没有使用重定向机制。)
DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>errortitle>
head>
<body>
<h1>操作失败,<a href="javascript:void(0)" onclick="window.history.back()">返回a>h1>
body>
html>
package cw.javaweb.oa.web;
import cw.javaweb.oa.utils.DBUtil;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
public class DeptDelServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// 获取部门id
String deptno = request.getParameter("deptno");
// 连接数据库删除部门
Connection conn = null;
PreparedStatement ps = null;
ResultSet rs = null;
int count = 0;
try {
conn = DBUtil.getConnection();
// 开启事务,关闭自动提交事务
conn.setAutoCommit(false);
String sql = "delete from dept where id = ?";
ps = conn.prepareStatement(sql);
ps.setString(1, deptno);
count = ps.executeUpdate();
// 事务提交
conn.commit();
} catch (SQLException e) {
// 事务回滚
try {
conn.rollback();
} catch (SQLException ex) {
ex.printStackTrace();
}
e.printStackTrace();
} finally {
DBUtil.close(conn, ps, rs);
}
// 判断成功或失败,进行页面的跳转
if (count >= 1) {
//删除成功
//仍然跳转到部门列表页面
//部门列表页面的显示需要执行另一个Servlet。怎么办?转发。
request.getRequestDispatcher("/dept/list").forward(request, response);
} else {
// 删除失败
// 请求转发可以是servlet也可以是页面
request.getRequestDispatcher("/error.html").forward(request, response);
}
}
}
第八步:新增部门
out.print("新增部门");
<servlet>
<servlet-name>addservlet-name>
<servlet-class>cw.javaweb.oa.web.DeptAddServletservlet-class>
servlet>
<servlet-mapping>
<servlet-name>addservlet-name>
<url-pattern>/dept/addurl-pattern>
servlet-mapping>
DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>新增部门title>
head>
<body>
<h1>新增部门h1>
<hr>
<form action="/oa/dept/add" method="post">
部门编号:<input type="text" name="deptno"><br>
部门名称:<input type="text" name="dname"><br>
部门位置:<input type="text" name="loc"><br>
<input type="submit" value="新增">
form>
body>
html>
package cw.javaweb.oa.web;
import cw.javaweb.oa.utils.DBUtil;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
public class DeptAddServlet extends HttpServlet {
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// 注意乱码问题
request.setCharacterEncoding("UTF-8");
// 获取部门信息
String deptno = request.getParameter("deptno");
String dname = request.getParameter("dname");
String loc = request.getParameter("loc");
// 连接数据库执行插入操作
Connection conn = null;
PreparedStatement ps = null;
ResultSet rs = null;
int count = 0;
try {
conn = DBUtil.getConnection();
String sql = "insert into dept(deptno, dname, loc) values (?, ?, ?)";
ps = conn.prepareStatement(sql);
ps.setString(1, deptno);
ps.setString(2, dname);
ps.setString(3, loc);
count = ps.executeUpdate();
} catch (SQLException e) {
e.printStackTrace();
}
// 成功或失败
if (count >= 1) {
request.getRequestDispatcher("/dept/list").forward(request, response);
} else {
request.getRequestDispatcher("/error.html").forward(request, response);
}
}
}
注意:最后保存成功之后,转发到 /dept/list 的时候,会出现405,为什么?
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req, resp);
}
第九步:跳转到修改部门的页面
out.print("修改");
<servlet>
<servlet-name>editservlet-name>
<servlet-class>cw.javaweb.oa.web.DeptEditServletservlet-class>
servlet>
<servlet-mapping>
<servlet-name>editservlet-name>
<url-pattern>/dept/editurl-pattern>
servlet-mapping>
package cw.javaweb.oa.web;
import cw.javaweb.oa.utils.DBUtil;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
public class DeptEditServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// 获取部门编号
String deptno = request.getParameter("deptno");
// 设置响应内容格式和编码
response.setContentType("text/html;charset=UTF-8");
// 获取输出对象
PrintWriter out = response.getWriter();
out.print("");
out.print("");
out.print(" ");
out.print(" ");
out.print(" 修改部门 ");
out.print(" ");
out.print(" ");
out.print(" 修改部门
");
out.print("
");
out.print(" );
out.print(" ");
// 查询数据库
Connection conn = null;
PreparedStatement ps = null;
ResultSet rs = null;
try {
conn = DBUtil.getConnection();
String sql = "select dname, loc from dept where deptno = ?";
ps = conn.prepareStatement(sql);
ps.setString(1, deptno);
rs = ps.executeQuery();
if (rs.next()) {
String dname = rs.getString("dname");
String loc = rs.getString("loc");
out.print(" 部门编号
");
out.print(" 部门名称
");
out.print(" 部门位置
");
}
} catch (SQLException e) {
e.printStackTrace();
} finally {
DBUtil.close(conn, ps, rs);
}
out.print("
");
out.print(" ");
out.print(" ");
out.print("");
}
}
第十步:修改部门
<servlet>
<servlet-name>modifyservlet-name>
<servlet-class>cw.javaweb.oa.web.DeptModifyServletservlet-class>
servlet>
<servlet-mapping>
<servlet-name>modifyservlet-name>
<url-pattern>/dept/modifyurl-pattern>
servlet-mapping>
package cw.javaweb.oa.web;
import cw.javaweb.oa.utils.DBUtil;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
public class DeptModifyServlet extends HttpServlet {
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// 注意乱码问题
request.setCharacterEncoding("UTF-8");
// 获取部门信息
String deptno = request.getParameter("deptno");
String dname = request.getParameter("dname");
String loc = request.getParameter("loc");
// 连接数据库执行插入操作
Connection conn = null;
PreparedStatement ps = null;
ResultSet rs = null;
int count = 0;
try {
conn = DBUtil.getConnection();
String sql = "update dept set dname = ?, loc = ? where deptno = ?";
ps = conn.prepareStatement(sql);
ps.setString(1, dname);
ps.setString(2, loc);
ps.setString(3, deptno);
count = ps.executeUpdate();
} catch (SQLException e) {
e.printStackTrace();
}
// 成功或失败
if (count >= 1) {
request.getRequestDispatcher("/dept/list").forward(request, response);
} else {
request.getRequestDispatcher("/error.html").forward(request, response);
}
}
}
在一个web应用中通过两种方式,可以完成资源的跳转:
转发和重定向有什么区别?
代码上有什么区别?
转发
// 获取请求转发器对象
// 告诉Tomcat下一次请求要发给哪个Servlet对象
RequestDispatcher dispatcher = request.getRequestDispatcher("/dept/list");
// 调用请求转发器对象的forward方法完成转发
// 传入request, response 保证为同一次请求,保证请求域相同
dispatcher.forward(request, response);
// 合并一行代码
request.getRequestDispatcher("/dept/list").forward(request, response);
// 转发的时候是一次请求,不管你转发了多少次。都是一次请求。
// AServlet转发到BServlet,再转发到CServlet,再转发到DServlet,不管转发了多少次,都在同一个request当中。
// 这是因为调用forward方法的时候,会将当前的request和response对象传递给下一个Servlet。
重定向
// 注意:路径上要加一个项目名。为什么?
// 浏览器发送请求,请求路径上是需要添加项目名的。
// 以下这一行代码会将请求路径“/oa/dept/list”发送给浏览器
// 浏览器会自发的向服务器发送一次全新的请求:/oa/dept/list
response.sendRedirect("/oa/dept/list");
形式上有什么区别?
转发和重定向的本质区别?
使用一个例子去描述这个转发和重定向
转发和重定向应该如何选择?什么时候使用转发,什么时候使用重定向?
跳转的下一个资源有没有要求呢?必须是一个Servlet吗?
转发会存在浏览器的刷新问题。由于请求转发的前端路径不会发送变化,所以前端进行刷新会重新执行一次与之前相同的请求,如果之前执行的为向数据库插入信息的请求,则使用转发每次进行刷新都会重新执行一次插入操作。使用重定向则不会,重定向是发送一次与之前请求无关的一次新请求。
删除之后,重定向
// 判断成功或失败,进行页面的跳转
if (count >= 1) {
//删除成功
//仍然跳转到部门列表页面
//部门列表页面的显示需要执行另一个Servlet。怎么办?转发。
// request.getRequestDispatcher("/dept/list").forward(request, response);
response.sendRedirect(request.getContextPath() + "/dept/list");
} else {
// 删除失败
// request.getRequestDispatcher("/error.html").forward(request, response);
response.sendRedirect(request.getContextPath() + "/error.html");
}
修改之后,重定向
// 成功或失败
if (count >= 1) {
// request.getRequestDispatcher("/dept/list").forward(request, response);
response.sendRedirect(request.getContextPath() + "/dept/list");
} else {
// request.getRequestDispatcher("/error.html").forward(request, response);
response.sendRedirect(request.getContextPath() + "/error.html");
}
保存之后,重定向
// 成功或失败
if (count >= 1) {
// request.getRequestDispatcher("/dept/list").forward(request, response);
response.sendRedirect(request.getContextPath() + "/dept/list");
} else {
// request.getRequestDispatcher("/error.html").forward(request, response);
response.sendRedirect(request.getContextPath() + "/error.html");
}
分析oa项目中的web.xml文件
Servlet3.0版本之后,推出了各种Servlet基于注解式开发。优点是什么?
并不是说注解有了之后,web.xml文件就不需要了:
我们的第一个注解:
jakarta.servlet.annotation.WebServlet
在Servlet类上使用:@WebServlet,WebServlet注解中有哪些属性呢?
//@WebServlet(urlPatterns = {"/welcome1", "/welcome2"})
// 注意:当注解的属性是一个数组,并且数组中只有一个元素,大括号可以省略。
//@WebServlet(urlPatterns = "/welcome")
// 这个value属性和urlPatterns属性一致,都是用来指定Servlet的映射路径的。
//@WebServlet(value = {"/welcome1", "/welcome2"})
// 如果注解的属性名是value的话,属性名也是可以省略的。
//@WebServlet(value = "/welcome1")
//@WebServlet({"/wel", "/abc", "/def"})
@WebServlet("/wel")
public class WelcomeServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
response.setContentType("text/html;charset=UTF-8");
PrintWriter out = response.getWriter();
out.print("欢迎学习Servlet。");
}
}
package com.bjpowernode.javaweb.servlet;
import jakarta.servlet.ServletException;
import jakarta.servlet.annotation.WebInitParam;
import jakarta.servlet.annotation.WebServlet;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Enumeration;
@WebServlet(name = "hello",
urlPatterns = {"/hello1", "/hello2", "/hello3"},
loadOnStartup = 1, // 服务器部署启动时加载该类
// 初始化参数
initParams = {@WebInitParam(name="username", value="root"), @WebInitParam(name="password", value="123")})
public class HelloServlet extends HttpServlet {
// 无参数构造方法
public HelloServlet() {
System.out.println("无参数构造方法执行,HelloServlet加载完成");
}
/*@WebServlet
private String name;*/
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
response.setContentType("text/html;charset=UTF-8");
PrintWriter out = response.getWriter();
// 获取Servlet Name
String servletName = getServletName();
out.print("servlet name = " + servletName + "
");
// 获取servlet path
String servletPath = request.getServletPath();
out.print("servlet path = " + servletPath + "
");
// 获取初始化参数
Enumeration<String> names = getInitParameterNames();
while (names.hasMoreElements()) {
String name = names.nextElement();
String value = getInitParameter(name);
out.print(name + "=" + value + "
");
}
}
}
注解对象的使用格式:
public class ReflectAnnotation {
public static void main(String[] args) throws Exception{
// 使用反射机制将类上面的注解进行解析。
// 获取类Class对象
Class<?> welcomeServletClass = Class.forName("com.bjpowernode.javaweb.servlet.WelcomeServlet");
// 获取这个类上面的注解对象
// 先判断这个类上面有没有这个注解对象,如果有这个注解对象,就获取该注解对象。
//boolean annotationPresent = welcomeServletClass.isAnnotationPresent(WebServlet.class);
//System.out.println(annotationPresent);
if (welcomeServletClass.isAnnotationPresent(WebServlet.class)) {
// 获取这个类上面的注解对象
WebServlet webServletAnnotation = welcomeServletClass.getAnnotation(WebServlet.class);
// 获取注解的value属性值。
String[] value = webServletAnnotation.value();
for (int i = 0; i < value.length; i++) {
System.out.println(value[i]);
}
}
}
}
package com.bjpowernode.oa.web.action;
import com.bjpowernode.oa.utils.DBUtil;
import jakarta.servlet.ServletException;
import jakarta.servlet.annotation.WebServlet;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
// 模板类
@WebServlet({"/dept/list", "/dept/save", "/dept/edit", "/dept/detail", "/dept/delete", "/dept/modify"})
// 模糊匹配
// 只要请求路径是以"/dept"开始的,都走这个Servlet。
//@WebServlet("/dept/*")
public class DeptServlet extends HttpServlet {
// 模板方法
// 重写service方法(并没有重写doGet或者doPost)
@Override
protected void service(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
// 获取servlet path
String servletPath = request.getServletPath();
if("/dept/list".equals(servletPath)){
doList(request, response);
} else if("/dept/save".equals(servletPath)){
doSave(request, response);
} else if("/dept/edit".equals(servletPath)){
doEdit(request, response);
} else if("/dept/detail".equals(servletPath)){
doDetail(request, response);
} else if("/dept/delete".equals(servletPath)){
doDel(request, response);
} else if("/dept/modify".equals(servletPath)){
doModify(request, response);
}
}
private void doList(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
// 获取应用的根路径
String contextPath = request.getContextPath();
// 设置响应的内容类型以及字符集。防止中文乱码问题。
response.setContentType("text/html;charset=UTF-8");
PrintWriter out = response.getWriter();
out.print("");
out.print("");
out.print(" ");
out.print(" ");
out.print(" 部门列表页面 ");
out.print("");
out.print(" ");
out.print(" ");
out.print(" 部门列表
");
out.print("
");
out.print(" ");
out.print(" ");
out.print(" 序号 ");
out.print(" 部门编号 ");
out.print(" 部门名称 ");
out.print(" 操作 ");
out.print(" ");
/*上面一部分是死的*/
// 连接数据库,查询所有的部门
Connection conn = null;
PreparedStatement ps = null;
ResultSet rs = null;
try {
// 获取连接
conn = DBUtil.getConnection();
// 获取预编译的数据库操作对象
String sql = "select deptno as a,dname,loc from dept";
ps = conn.prepareStatement(sql);
// 执行SQL语句
rs = ps.executeQuery();
// 处理结果集
int i = 0;
while(rs.next()){
String deptno = rs.getString("a");
String dname = rs.getString("dname");
String loc = rs.getString("loc");
out.print(" ");
out.print(" "+(++i)+" ");
out.print(" "+deptno+" ");
out.print(" "+dname+" ");
out.print(" ");
out.print(" +deptno+")'>删除");
out.print(" 修改");
out.print(" 详情");
out.print(" ");
out.print(" ");
}
} catch (SQLException e) {
e.printStackTrace();
} finally {
// 释放资源
DBUtil.close(conn, ps, rs);
}
/*下面一部分是死的*/
out.print("
");
out.print("
");
out.print(" 新增部门");
out.print(" ");
out.print("");
}
private void doSave(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
// 获取部门的信息
// 注意乱码问题(Tomcat10不会出现这个问题)
request.setCharacterEncoding("UTF-8");
String deptno = request.getParameter("deptno");
String dname = request.getParameter("dname");
String loc = request.getParameter("loc");
// 连接数据库执行insert语句
Connection conn = null;
PreparedStatement ps = null;
int count = 0;
try {
conn = DBUtil.getConnection();
String sql = "insert into dept(deptno, dname, loc) values(?,?,?)";
ps = conn.prepareStatement(sql);
ps.setString(1, deptno);
ps.setString(2, dname);
ps.setString(3, loc);
count = ps.executeUpdate();
} catch (SQLException e) {
e.printStackTrace();
} finally {
DBUtil.close(conn, ps, null);
}
if (count == 1) {
// 保存成功跳转到列表页面
// 转发是一次请求。
//request.getRequestDispatcher("/dept/list").forward(request, response);
// 这里最好使用重定向(浏览器会发一次全新的请求。)
// 浏览器在地址栏上发送请求,这个请求是get请求。
response.sendRedirect(request.getContextPath() + "/dept/list");
}else{
// 保存失败跳转到错误页面
//request.getRequestDispatcher("/error.html").forward(request, response);
// 这里也建议使用重定向。
response.sendRedirect(request.getContextPath() + "/error.html");
}
}
private void doEdit(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
// 获取应用的根路径。
String contextPath = request.getContextPath();
response.setContentType("text/html;charset=UTF-8");
PrintWriter out = response.getWriter();
out.print("");
out.print("");
out.print(" ");
out.print(" ");
out.print(" 修改部门 ");
out.print(" ");
out.print(" ");
out.print(" 修改部门
");
out.print("
");
out.print(" );
// 获取部门编号
String deptno = request.getParameter("deptno");
// 连接数据库,根据部门编号查询部门的信息。
Connection conn = null;
PreparedStatement ps = null;
ResultSet rs = null;
try {
conn = DBUtil.getConnection();
String sql = "select dname, loc as location from dept where deptno = ?";
ps = conn.prepareStatement(sql);
ps.setString(1, deptno);
rs = ps.executeQuery();
// 这个结果集中只有一条记录。
if(rs.next()){
String dname = rs.getString("dname");
String location = rs.getString("location"); // 参数"location"是sql语句查询结果列的列名。
// 输出动态网页。
out.print(" 部门编号
");
out.print(" 部门名称
");
out.print(" 部门位置
");
}
} catch (SQLException e) {
e.printStackTrace();
} finally {
DBUtil.close(conn, ps, rs);
}
out.print("
");
out.print(" ");
out.print(" ");
out.print("");
}
private void doDetail(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
response.setContentType("text/html;charset=UTF-8");
PrintWriter out = response.getWriter();
out.print("");
out.print("");
out.print(" ");
out.print(" ");
out.print(" 部门详情 ");
out.print(" ");
out.print(" ");
out.print(" 部门详情
");
out.print("
");
// 获取部门编号
// /oa/dept/detail?fdsafdsas=30
// 虽然是提交的30,但是服务器获取的是"30"这个字符串。
String deptno = request.getParameter("fdsafdsas");
// 连接数据库,根据部门编号查询部门信息。
Connection conn = null;
PreparedStatement ps = null;
ResultSet rs = null;
try {
conn = DBUtil.getConnection();
String sql = "select dname,loc from dept where deptno = ?";
ps = conn.prepareStatement(sql);
ps.setString(1, deptno);
rs = ps.executeQuery();
// 这个结果集一定只有一条记录。
if(rs.next()){
String dname = rs.getString("dname");
String loc = rs.getString("loc");
out.print("部门编号:"+deptno+"
");
out.print("部门名称:"+dname+"
");
out.print("部门位置:"+loc+"
");
}
} catch (SQLException e) {
e.printStackTrace();
} finally {
DBUtil.close(conn, ps, rs);
}
out.print(" ");
out.print(" ");
out.print("");
}
private void doDel(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
// 根据部门编号,删除部门。
// 获取部门编号
String deptno = request.getParameter("deptno");
// 连接数据库删除数据
Connection conn = null;
PreparedStatement ps = null;
int count = 0;
try {
conn = DBUtil.getConnection();
// 开启事务(自动提交机制关闭)
conn.setAutoCommit(false);
String sql = "delete from dept where deptno = ?";
ps = conn.prepareStatement(sql);
ps.setString(1, deptno);
// 返回值是:影响了数据库表当中多少条记录。
count = ps.executeUpdate();
// 事务提交
conn.commit();
} catch (SQLException e) {
// 遇到异常要回滚
if (conn != null) {
try {
conn.rollback();
} catch (SQLException ex) {
ex.printStackTrace();
}
}
e.printStackTrace();
} finally {
DBUtil.close(conn, ps, null);
}
// 判断删除成功了还是失败了。
if (count == 1) {
//删除成功
//仍然跳转到部门列表页面
//部门列表页面的显示需要执行另一个Servlet。怎么办?转发。
//request.getRequestDispatcher("/dept/list").forward(request, response);
response.sendRedirect(request.getContextPath() + "/dept/list");
}else{
// 删除失败
//request.getRequestDispatcher("/error.html").forward(request, response);
response.sendRedirect(request.getContextPath() + "/error.html");
}
}
private void doModify(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
// 解决请求体的中文乱码问题。
request.setCharacterEncoding("UTF-8");
// 获取表单中的数据
String deptno = request.getParameter("deptno");
String dname = request.getParameter("dname");
String loc = request.getParameter("loc");
// 连接数据库执行更新语句
Connection conn = null;
PreparedStatement ps = null;
int count = 0;
try {
conn = DBUtil.getConnection();
String sql = "update dept set dname = ?, loc = ? where deptno = ?";
ps = conn.prepareStatement(sql);
ps.setString(1, dname);
ps.setString(2, loc);
ps.setString(3, deptno);
count = ps.executeUpdate();
} catch (SQLException e) {
e.printStackTrace();
} finally {
DBUtil.close(conn, ps, null);
}
if (count == 1) {
// 更新成功
// 跳转到部门列表页面(部门列表页面是通过Java程序动态生成的,所以还需要再次执行另一个Servlet)
//request.getRequestDispatcher("/dept/list").forward(request, response);
response.sendRedirect(request.getContextPath() + "/dept/list");
}else{
// 更新失败
//request.getRequestDispatcher("/error.html").forward(request, response);
response.sendRedirect(request.getContextPath() + "/error.html");
}
}
}
将上面的项目部署之后,启动服务器,打开浏览器,访问以下地址:
JSP实际上就是一个Servlet。
jsp文件第一次访问的时候是比较慢的,为什么?
JSP是什么?
对JSP进行错误调试的时候,还是要直接打开JSP文件对应的java文件,检查java代码。
开发JSP的最高境界:
JSP既然本质上是一个Servlet,那么JSP和Servlet到底有什么区别呢?
在jsp文件中直接编写文字,都会自动被翻译到哪里?
JSP的page指令(这个指令后面再详细说,这里先解决一下中文乱码问题,写在文件内容开始位置),解决响应时的中文乱码问题:
<%
System.out.println("hello JSP");
%>
在JSP中如何编写JSP的专业注释
<%--JSP的专业注释,不会被翻译到java源代码当中。--%>
JSP基础语法总结:
使用Servlet处理业务,收集数据。 使用JSP展示数据。
将之前原型中的html文件,全部修改为jsp,然后在jsp文件头部添加page指令(指定contentType防止中文乱码),将所有的JSP直接拷贝到web目录下。
完成所有页面的正常流转。(页面仍然能够正常的跳转。修改超链接的请求路径。)
Servlet中连接数据库,查询所有的部门,遍历结果集。
在JSP中:
思考一个问题:如果我只用JSP这一个技术,能不能开发web应用?
JSP文件的扩展名必须是xxx.jsp吗?
jsp文件的扩展名是可以配置的。不是固定的。
在CATALINA_HOME/conf/web.xml,在这个文件当中配置jsp文件的扩展名。
<servlet-mapping>
<servlet-name>jspservlet-name>
<url-pattern>*.jspurl-pattern>
<url-pattern>*.jspxurl-pattern>
servlet-mapping>
xxx.jsp文件对于小猫咪来说,只是一个普通的文本文件,web容器会将xxx.jsp文件最终生成java程序,最终调用的是java对象相关的方法,真正执行的时候,和jsp文件就没有关系了。
小窍门:JSP如果看不懂,建议把jsp翻译成java代码,就能看懂了。
同学问:包名bean是什么意思?
完成剩下所有功能的改造。
index.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>欢迎使用OA系统title>
head>
<body>
<a href="<%= request.getContextPath() %>/dept/list">查看部门列表a>
body>
html>
list.jsp
<%@ page import="java.util.ArrayList" %>
<%@ page import="cw.javaweb.bean.Dept" %>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%
Object deptList = request.getAttribute("deptList");
ArrayList<Dept> depts = (ArrayList<Dept>) deptList;
%>
DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>部门列表页面title>
head>
<body>
<h1 align="center">部门列表h1>
<hr>
<table align="center" border="1px solid black" width="50%">
<thead>
<tr>
<th>序号th>
<th>部门编号th>
<th>部门名称th>
<th>操作th>
tr>
thead>
<tbody>
<%
for (int i=0; i<depts.size(); i++) {
Dept dept = depts.get(i);
%>
<tr>
<td><%= (i+1) %>td>
<td><%= dept.getDeptno()%>td>
<td><%= dept.getDname()%>td>
<td>
<a href="javascript:void(0)" onclick="del(<%= dept.getDeptno() %>)" >删除a>
<%-- 由于修改和详情页面只是页面显示不同,数据的查询逻辑类似,所以数据的查询使用相同的逻辑 --%>
<a href="<%= request.getContextPath() %>/dept/detail?f=m&deptno=<%= dept.getDeptno() %>">修改a>
<a href="<%= request.getContextPath() %>/dept/detail?f=d&deptno=<%= dept.getDeptno() %>">详情a>
td>
tr>
<%
}
%>
tbody>
table>
<hr>
<a href="<%= request.getContextPath() %>/add.jsp">新增部门a>
body>
<script type="text/javascript">
function del(dno){
if(window.confirm("亲,删了不可恢复哦!")){
document.location.href = "/oa/dept/delete?deptno=" + dno;
}
}
script>
html>
add.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>新增部门title>
head>
<body>
<h1>新增部门h1>
<hr>
<form action="<%= request.getContextPath() %>/dept/add" method="post">
部门编号:<input type="text" name="deptno"><br>
部门名称:<input type="text" name="dname"><br>
部门位置:<input type="text" name="loc"><br>
<input type="submit" value="新增">
form>
body>
html>
detail.jsp
<%@ page import="cw.javaweb.bean.Dept" %>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%
Dept dept = (Dept) request.getAttribute("dept");
%>
DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>部门详情title>
head>
<body>
<h1>部门详情h1>
<hr>
<p>部门编号:<%= dept.getDeptno() %>p>
<p>部门名称:<%= dept.getDname() %>p>
<p>部门位置:<%= dept.getLoc() %>p>
<input type="button" value="后退" onclick="window.history.back()">
body>
html>
edit.jsp
<%@ page import="cw.javaweb.bean.Dept" %>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%
Dept dept = (Dept) request.getAttribute("dept");
%>
DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>修改部门title>
head>
<body>
<h1>修改部门h1>
<hr >
<form action="<%= request.getContextPath() %>/dept/modify" method="get">
部门编号<input type="text" name="deptno" value="<%= dept.getDeptno() %>" readonly /><br>
部门名称<input type="text" name="dname" value="<%= dept.getDname() %>"/><br>
部门位置<input type="text" name="loc" value="<%= dept.getLoc() %>"/><br>
<input type="submit" value="修改"/><br>
form>
body>
html>
Dept.java
package cw.javaweb.bean;
public class Dept {
private String deptno;
private String dname;
private String loc;
public Dept() {
}
public Dept(String deptno, String dname, String loc) {
this.deptno = deptno;
this.dname = dname;
this.loc = loc;
}
public String getDeptno() {
return deptno;
}
public void setDeptno(String deptno) {
this.deptno = deptno;
}
public String getDname() {
return dname;
}
public void setDname(String dname) {
this.dname = dname;
}
public String getLoc() {
return loc;
}
public void setLoc(String loc) {
this.loc = loc;
}
@Override
public String toString() {
return "Dept{" + "deptno='" + deptno + '\'' + ", dname='" + dname + '\'' + ", loc='" + loc + '\'' + '}';
}
}
DeptServlet.java
package cw.javaweb.oa.action;
import cw.javaweb.bean.Dept;
import cw.javaweb.utils.DBUtil;
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;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
@WebServlet({"/dept/list", "/dept/detail", "/dept/modify", "/dept/delete", "/dept/add"})
public class DeptServlet extends HttpServlet {
@Override
protected void service(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
// 获取servlet路径
String servletPath = request.getServletPath();
if ("/dept/list".equals(servletPath)) {
doList(request, response);
} else if ("/dept/detail".equals(servletPath)) {
doDetail(request, response);
} else if ("/dept/modify".equals(servletPath)) {
doModify(request, response);
} else if ("/dept/delete".equals(servletPath)) {
doDel(request, response);
} else if ("/dept/add".equals(servletPath)) {
doAdd(request, response);
}
}
private void doAdd(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
// 获取部门的信息
// 注意乱码问题(Tomcat10不会出现这个问题)
request.setCharacterEncoding("UTF-8");
String deptno = request.getParameter("deptno");
String dname = request.getParameter("dname");
String loc = request.getParameter("loc");
// 连接数据库执行insert语句
Connection conn = null;
PreparedStatement ps = null;
int count = 0;
try {
conn = DBUtil.getConnection();
String sql = "insert into dept(deptno, dname, loc) values(?,?,?)";
ps = conn.prepareStatement(sql);
ps.setString(1, deptno);
ps.setString(2, dname);
ps.setString(3, loc);
count = ps.executeUpdate();
} catch (SQLException e) {
e.printStackTrace();
} finally {
DBUtil.close(conn, ps, null);
}
if (count == 1) {
// 保存成功跳转到列表页面
// 转发是一次请求。
//request.getRequestDispatcher("/dept/list").forward(request, response);
// 这里最好使用重定向(浏览器会发一次全新的请求。)
// 浏览器在地址栏上发送请求,这个请求是get请求。
response.sendRedirect(request.getContextPath() + "/dept/list");
}else{
// 保存失败跳转到错误页面
//request.getRequestDispatcher("/error.html").forward(request, response);
// 这里也建议使用重定向。
response.sendRedirect(request.getContextPath() + "/error.html");
}
}
private void doDel(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
// 根据部门编号,删除部门。
// 获取部门编号
String deptno = request.getParameter("deptno");
// 连接数据库删除数据
Connection conn = null;
PreparedStatement ps = null;
int count = 0;
try {
conn = DBUtil.getConnection();
// 开启事务(自动提交机制关闭)
conn.setAutoCommit(false);
String sql = "delete from dept where deptno = ?";
ps = conn.prepareStatement(sql);
ps.setString(1, deptno);
// 返回值是:影响了数据库表当中多少条记录。
count = ps.executeUpdate();
// 事务提交
conn.commit();
} catch (SQLException e) {
// 遇到异常要回滚
if (conn != null) {
try {
conn.rollback();
} catch (SQLException ex) {
ex.printStackTrace();
}
}
e.printStackTrace();
} finally {
DBUtil.close(conn, ps, null);
}
// 判断删除成功了还是失败了。
if (count == 1) {
//删除成功
//仍然跳转到部门列表页面
//部门列表页面的显示需要执行另一个Servlet。怎么办?转发。
//request.getRequestDispatcher("/dept/list").forward(request, response);
response.sendRedirect(request.getContextPath() + "/dept/list");
}else{
// 删除失败
//request.getRequestDispatcher("/error.html").forward(request, response);
response.sendRedirect(request.getContextPath() + "/error.html");
}
}
private void doModify(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
// 解决请求体的中文乱码问题。
request.setCharacterEncoding("UTF-8");
// 获取表单中的数据
String deptno = request.getParameter("deptno");
String dname = request.getParameter("dname");
String loc = request.getParameter("loc");
// 连接数据库执行更新语句
Connection conn = null;
PreparedStatement ps = null;
int count = 0;
try {
conn = DBUtil.getConnection();
String sql = "update dept set dname = ?, loc = ? where deptno = ?";
ps = conn.prepareStatement(sql);
ps.setString(1, dname);
ps.setString(2, loc);
ps.setString(3, deptno);
count = ps.executeUpdate();
} catch (SQLException e) {
e.printStackTrace();
} finally {
DBUtil.close(conn, ps, null);
}
if (count == 1) {
// 更新成功
// 跳转到部门列表页面(部门列表页面是通过Java程序动态生成的,所以还需要再次执行另一个Servlet)
//request.getRequestDispatcher("/dept/list").forward(request, response);
response.sendRedirect(request.getContextPath() + "/dept/list");
}else{
// 更新失败
//request.getRequestDispatcher("/error.html").forward(request, response);
response.sendRedirect(request.getContextPath() + "/error.html");
}
}
private void doDetail(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException{
// 获取部门编号
String deptno = request.getParameter("deptno");
// 连接数据库,根据部门编号查询部门信息。
Connection conn = null;
PreparedStatement ps = null;
ResultSet rs = null;
try {
conn = DBUtil.getConnection();
String sql = "select dname,loc from dept where deptno = ?";
ps = conn.prepareStatement(sql);
ps.setString(1, deptno);
rs = ps.executeQuery();
// 这个结果集一定只有一条记录。
if(rs.next()){
String dname = rs.getString("dname");
String loc = rs.getString("loc");
Dept dept = new Dept(deptno, dname, loc);
request.setAttribute("dept", dept);
}
} catch (SQLException e) {
e.printStackTrace();
} finally {
DBUtil.close(conn, ps, rs);
}
// 获取是修改还是查看详情
String f = request.getParameter("f");
if ("m".equals(f)) {
request.getRequestDispatcher("/edit.jsp").forward(request, response);
} else {
request.getRequestDispatcher("/detail.jsp").forward(request, response);
}
}
private void doList(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
// 连接数据库,查询所有的部门
Connection conn = null;
PreparedStatement ps = null;
ResultSet rs = null;
ArrayList<Dept> depts = new ArrayList<Dept>();
try {
// 获取连接
conn = DBUtil.getConnection();
// 获取预编译的数据库操作对象
String sql = "select deptno as a,dname,loc from dept";
ps = conn.prepareStatement(sql);
// 执行SQL语句
rs = ps.executeQuery();
// 处理结果集
int i = 0;
while(rs.next()){
String deptno = rs.getString("a");
String dname = rs.getString("dname");
String loc = rs.getString("loc");
Dept dept = new Dept(deptno, dname, loc);
depts.add(dept);
}
} catch (SQLException e) {
e.printStackTrace();
} finally {
// 释放资源
DBUtil.close(conn, ps, rs);
}
request.setAttribute("deptList", depts);
request.getRequestDispatcher("/list.jsp").forward(request, response);
}
}
当前的oa应用存在的问题:
USE dbtest;
DROP TABLE IF EXISTS t_user;
CREATE TABLE t_user (
username VARCHAR(255),
password VARCHAR(255)
);
INSERT INTO t_user(username, password) VALUES ('admin', 'admin');
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>欢迎使用OA系统title>
head>
<body>
<%--<a href="<%= request.getContextPath() %>/dept/list">查看部门列表a>--%>
<h1>登录h1>
<hr>
<form action="<%= request.getContextPath() %>/user/login" method="post">
用户名:<input type="text" name="username"><br>
密码:<input type="password" name="password"><br>
<input type="submit" value="登录">
form>
body>
html>
@WebServlet({"/user/login"})
public class UserServlet extends HttpServlet {
@Override
protected void service(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
// 获取servlet路径
String servletPath = request.getServletPath();
if ("/user/login".equals(servletPath)) {
doLogin(request, response);
}
}
private void doLogin(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
// 设置编码
request.setCharacterEncoding("UTF-8");
// 获取用户名密码
String username = request.getParameter("username");
String password = request.getParameter("password");
// 连接数据库查询比对
Connection conn = null;
PreparedStatement ps = null;
ResultSet rs = null;
// 标记是否成功登录
boolean isSuccess = false;
try {
conn = DBUtil.getConnection();
String sql = "select * from t_user where username = ? and password = ?";
ps = conn.prepareStatement(sql);
ps.setString(1, username);
ps.setString(2, password);
rs = ps.executeQuery();
if (rs.next()) {
isSuccess = true;
}
} catch (SQLException e) {
e.printStackTrace();
} finally {
DBUtil.close(conn, ps, rs);
}
if (isSuccess) {
response.sendRedirect(request.getContextPath() + "/dept/list");
} else {
response.sendRedirect(request.getContextPath() + "/error.jsp");
}
}
}
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>登录失败title>
head>
<body>
<h1>登录失败<a href="/user/login">返回登录页面a>h1>
body>
html>
登录功能实现了,目前存在的最大的问题:
什么是会话?
在java的servlet规范当中,session对应的类名:HttpSession(jarkata.servlet.http.HttpSession)
session机制属于B/S结构的一部分。如果使用php语言开发WEB项目,同样也是有session这种机制的。session机制实际上是一个规范。然后不同的语言对这种会话机制都有实现。
session对象最主要的作用是:保存会话状态。(用户登录成功了,这是一种登录成功的状态,你怎么把登录成功的状态一直保存下来呢?使用session对象可以保留会话状态。)
为什么需要session对象来保存会话状态呢?
张三打开一个浏览器A,李四打开一个浏览器B,访问服务器之后,在服务器端会生成:
为什么不使用request对象保存会话状态?为什么不使用ServletContext对象保存会话状态?
思考一下:session对象的实现原理。
服务器的响应中有JSESSIONID=xxxxxx 这个是以Cookie的形式保存在浏览器的内存中的。浏览器只要关闭。这个cookie就没有了。
session列表是一个Map,map的key是sessionid,map的value是session对象。
用户第一次请求,服务器生成session对象,同时生成id,将id发送给浏览器。
用户第二次请求,自动将浏览器内存中的id发送给服务器,服务器根据id查找session对象。
关闭浏览器,内存消失,cookie消失,sessionid消失,会话等同于结束。
设置session的超时时间
<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">
<session-config>
<session-timeout>30session-timeout>
session-config>
web-app>
Cookie禁用了,session还能找到吗?
总结一下到目前位置我们所了解的域对象:
session掌握之后,我们怎么解决oa项目中的登录问题,怎么能让登录起作用。
JSP中的九大内置对象有session,所以一访问JSP页面就会创建一个相应的会话session对象
<%-- 访问JSP页面时不生成session对象,同时在该JSP页面中也不能使用session内置对象 --%>
<%@page session = "false" %>
销毁session对象:
session.invalidate();
@WebServlet({"/user/login", "/user/logout"})
public class UserServlet extends HttpServlet {
@Override
protected void service(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
// 获取servlet路径
String servletPath = request.getServletPath();
if ("/user/login".equals(servletPath)) {
doLogin(request, response);
} else if ("/user/logout".equals(servletPath)) {
doLogout(request, response);
}
}
private void doLogout(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
// 获取session
HttpSession session = request.getSession();
if (session != null) {
// 从session域中删除数据
session.removeAttribute("username");
// 手动销毁session对象。
session.invalidate();
// 跳转到首页
response.sendRedirect(request.getContextPath());
}
}
private void doLogin(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
// 设置编码
request.setCharacterEncoding("UTF-8");
// 获取用户名密码
String username = request.getParameter("username");
String password = request.getParameter("password");
// 连接数据库查询比对
Connection conn = null;
PreparedStatement ps = null;
ResultSet rs = null;
// 标记是否成功登录
boolean isSuccess = false;
try {
conn = DBUtil.getConnection();
String sql = "select * from t_user where username = ? and password = ?";
ps = conn.prepareStatement(sql);
ps.setString(1, username);
ps.setString(2, password);
rs = ps.executeQuery();
if (rs.next()) {
isSuccess = true;
}
} catch (SQLException e) {
e.printStackTrace();
} finally {
DBUtil.close(conn, ps, rs);
}
if (isSuccess) {
// 获取session对象,如果没有session对象就创建一个
HttpSession session = request.getSession();
// 向session对象存放用户数据作为登录证明
session.setAttribute("username", username);
response.sendRedirect(request.getContextPath() + "/dept/list");
} else {
response.sendRedirect(request.getContextPath() + "/error.jsp");
}
}
}
@Override
protected void service(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
// 进行登录验证
// 获取session对象,这里只是进行验证,如果没有session对象不需要创建
HttpSession session = request.getSession();
// 获取用户登录证明
Object username = session.getAttribute("username");
if (session != null && username != null) {
// 获取servlet路径
String servletPath = request.getServletPath();
if ("/dept/list".equals(servletPath)) {
doList(request, response);
} else if ("/dept/detail".equals(servletPath)) {
doDetail(request, response);
} else if ("/dept/modify".equals(servletPath)) {
doModify(request, response);
} else if ("/dept/delete".equals(servletPath)) {
doDel(request, response);
} else if ("/dept/add".equals(servletPath)) {
doAdd(request, response);
}
} else {
// 跳转到index页面
response.sendRedirect(request.getContextPath());
}
}
<h2>欢迎 <%= request.getSession().getAttribute("username") %>h2>
<a href="<%=request.getContextPath()%>/user/logout">退出登录a>
<hr>
<h1 align="center">部门列表h1>
session的实现原理中,每一个session对象都会关联一个sessionid,例如:
cookie怎么生成?cookie保存在什么地方?cookie有啥用?浏览器什么时候会发送cookie,发送哪些cookie给服务器???????
cookie最终是保存在浏览器客户端上的。
cookie有啥用呢?
cookie的经典案例
cookie机制和session机制其实都不属于java中的机制,实际上cookie机制和session机制都是HTTP协议的一部分。php开发中也有cookie和session机制,只要是你是做web开发,不管是什么编程语言,cookie和session机制都是需要的。
HTTP协议中规定:任何一个cookie都是由name和value组成的。name和value都是字符串类型的。
在java的servlet中,对cookie提供了哪些支持呢?
在HTTP协议中是这样规定的:当浏览器发送请求的时候,会自动携带该path下的cookie数据给服务器。(URL。)
关于cookie的有效时间
关于cookie的path,cookie关联的路径:
浏览器发送cookie给服务器了,服务器中的java程序怎么接收?
Cookie[] cookies = request.getCookies(); // 这个方法可能返回null,没有提交cookie的时候
if(cookies != null){
for(Cookie cookie : cookies){
// 获取cookie的name
String name = cookie.getName();
// 获取cookie的value
String value = cookie.getValue();
}
}
使用cookie实现一下十天内免登录功能。
cookie用于保存登录状态,session用于保存每次会话的会话状态,每次访问根据cookie判断是否需要登录,如果不需要登录则通过cookie保存的登录状态生成本次会话的session。
配置站点的欢迎路径
<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">
<welcome-file-list>
<welcome-file>welcomewelcome-file>
welcome-file-list>
web-app>
登录后生成cookie保存登录状态
private void doLogin(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
// 设置编码
request.setCharacterEncoding("UTF-8");
// 获取用户名密码
String username = request.getParameter("username");
String password = request.getParameter("password");
// 连接数据库查询比对
Connection conn = null;
PreparedStatement ps = null;
ResultSet rs = null;
// 标记是否成功登录
boolean isSuccess = false;
try {
conn = DBUtil.getConnection();
String sql = "select * from t_user where username = ? and password = ?";
ps = conn.prepareStatement(sql);
ps.setString(1, username);
ps.setString(2, password);
rs = ps.executeQuery();
if (rs.next()) {
isSuccess = true;
}
} catch (SQLException e) {
e.printStackTrace();
} finally {
DBUtil.close(conn, ps, rs);
}
if (isSuccess) {
// 获取session对象,如果没有session对象就创建一个
HttpSession session = request.getSession();
// 向session对象存放用户数据作为登录证明
session.setAttribute("username", username);
// 获取参数判断是否需要免登录
String f = request.getParameter("f");
if ("1".equals(f)) {
// 创建cookie
Cookie username1 = new Cookie("username", username);
Cookie password1 = new Cookie("password", password);
// 设置失效时间
username1.setMaxAge(60*60*24*10);
password1.setMaxAge(60*60*24*10);
// 设置cookie可以作用的path路径
// 在本web项目中所有路径下有效
username1.setPath(request.getContextPath());
password1.setPath(request.getContextPath());
// 将cookie放入响应对象中
response.addCookie(username1);
response.addCookie(password1);
}
response.sendRedirect(request.getContextPath() + "/dept/list");
} else {
response.sendRedirect(request.getContextPath() + "/error.jsp");
}
}
@WebServlet({"/welcome"})
public class WelcomeServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
// 浏览器请求默认为get
// 获取cookie
Cookie[] cookies = request.getCookies();
String username = null;
String password = null;
if (cookies != null) {
// cookie不为空,遍历cookie获取用户名与用户密码
for (Cookie cookie : cookies) {
String cookieName = cookie.getName();
if ("username".equals(cookieName)) {
username = cookie.getValue();
} else if ("password".equals(cookieName)) {
password = cookie.getValue();
}
}
}
if (username != null && password != null) {
// 用户名与密码不为空判断是否可以免登录
// 连接数据库查询
Connection conn = null;
PreparedStatement ps = null;
ResultSet rs = null;
// 标记是否成功登录
boolean isSuccess = false;
try {
conn = DBUtil.getConnection();
String sql = "select * from t_user where username = ? and password = ?";
ps = conn.prepareStatement(sql);
ps.setString(1, username);
ps.setString(2, password);
rs = ps.executeQuery();
if (rs.next()) {
isSuccess = true;
}
} catch (SQLException e) {
e.printStackTrace();
} finally {
DBUtil.close(conn, ps, rs);
}
if (isSuccess) {
// 可以免登录
// 生成本次会话的session
// 获取session对象,如果没有session对象就创建一个
HttpSession session = request.getSession();
// 向session对象存放用户数据作为登录证明
session.setAttribute("username", username);
response.sendRedirect(request.getContextPath() + "/dept/list");
} else {
response.sendRedirect(request.getContextPath() + "/error.jsp");
}
} else {
// 需要进行登录
response.sendRedirect(request.getContextPath()+"/index.jsp");
}
}
}
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>欢迎使用OA系统title>
head>
<body>
<%--<a href="<%= request.getContextPath() %>/dept/list">查看部门列表a>--%>
<h1>登录h1>
<hr>
<form action="<%= request.getContextPath() %>/user/login" method="post">
用户名:<input type="text" name="username"><br>
密码:<input type="password" name="password"><br>
<input type="checkbox" name="f" value="1">十天内免登录<br>
<input type="submit" value="登录">
form>
body>
html>
指令的作用:指导JSP的翻译引擎如何工作(指导当前的JSP翻译引擎如何翻译JSP文件。)
指令包括哪些呢?
指令的使用语法是什么?
关于page指令当中都有哪些常用的属性呢?
<%@page session="true|false" %>
true表示启用JSP的内置对象session,表示一定启动session对象。没有session对象会创建。
如果没有设置,默认值就是session="true"
session="false" 表示不启动内置对象session。当前JSP页面中无法使用内置对象session。
<%@page contentType="text/json" %>
contentType属性用来设置响应的内容类型
但同时也可以设置字符集。
默认 text/html
<%@page contentType="text/json;charset=UTF-8" %>
<%@page pageEncoding="UTF-8" %>
pageEncoding="UTF-8" 表示设置响应时采用的字符集。
设置了<%@page contentType="text/json;charset=UTF-8" %>,又使用pageEncoding,以contentType="text/json;charset=UTF-8"为准
<%@page import="java.util.List, java.util.Date, java.util.ArrayList" %>
<%@page import="java.util.*" %>
import语句,导包。
<%@page errorPage="/error.jsp" %>
当前页面出现异常之后,跳转到error.jsp页面。
errorPage属性用来指定出错之后的跳转位置。
<%@page isErrorPage="true" %>
表示启用JSP九大内置对象之一:exception
默认值是false。
配置的页面可以使用异常对象exception
jakarta.servlet.jsp.PageContext pageContext 页面作用域
jakarta.servlet.http.HttpServletRequest request 请求作用域,一次请求可以跨多页页面
jakarta.servlet.http.HttpSession session 会话作用域
jakarta.servlet.ServletContext application 应用作用域
java.lang.Throwable exception
jakarta.servlet.ServletConfig config
java.lang.Object page (其实是this,当前的servlet对象)
jakarta.servlet.jsp.JspWriter out (负责输出)
jakarta.servlet.http.HttpServletResponse response (负责响应)
EL表达式是干什么用的?
EL表达式出现在JSP中主要是:
EL表达式用于取数据,在页面进行数据的展示
EL表达式很好用,基本的语法格式:
<%@page contentType="text/html;charset=UTF-8" %>
<%
// 向request作用域当中存储username为zhangsan
request.setAttribute("username", "zhangsan");
request.setAttribute("obj", new Object());
%>
<%--将request域当中的数据取出来,并且还要输出到浏览器,使用java代码怎么办?--%>
<%=request.getAttribute("username")%>
<br>
<%=request.getAttribute("obj")%>
<br>
<hr>
<%--使用EL表达式呢?--%>
${username}
<br>
${obj}
<%
// 创建User对象
User user = new User();
user.setUsername("jackson");
user.setPassword("1234");
user.setAge(50);
// 将User对象存储到某个域当中。一定要存,因为EL表达式只能从某个范围中取数据。
// 数据是必须存储到四大范围之一的。
request.setAttribute("userObj", user);
%>
<%--使用EL表达式取--%>
${这个位置写什么????这里写的一定是存储到域对象当中时的name}
要这样写:
${userObj}
等同于java代码:<%=request.getAttribute("userObj")%>
你不要这样写:${"userObj"}
面试题:
${abc} 和 ${“abc”}的区别是什么?
${abc}表示从某个域中取出数据,并且被取的这个数据的name是"abc",之前一定有这样的代码: 域.setAttribute(“abc”, 对象);
${“abc”} 表示直接将"abc"当做普通字符串输出到浏览器。不会从某个域中取数据了。
${userObj} 底层是怎么做的?从域中取数据,取出user对象,然后调用user对象的toString方法,转换成字符串,输出到浏览器。
<%--如果想输出对象的属性值,怎么办?--%>
${userObj.username} 使用这个语法的前提是:User对象有getUsername()方法。
${userObj.password} 使用这个语法的前提是:User对象有getPassword()方法。
${userObj.age} 使用这个语法的前提是:User对象有getAge()方法。
${userObj.email} 使用这个语法的前提是:User对象有getEmail()方法。
没有对应的属性,有对应的get方法不会报错
EL表达式中的. 这个语法,实际上调用了底层的getXxx()方法。
注意:如果没有对应的get方法,则出现异常。报500错误。
${userObj.addr222.zipcode}
以上EL表达式对应的java代码:
user.getAddr222().getZipcode()
在没有指定范围的情况下,EL表达式优先从小范围中读取数据。
EL表达式中有四个隐含的隐式的范围:
范围.数据名
,如${pageScope.data}
EL表达式对null进行了预处理。如果是null,则向浏览器输出一个空字符串。
<%=request.getAttribute("usernam") == null ? "" : request.getAttribute("usernam")%>
EL表达式取数据的时候有两种形式:
掌握使用EL表达式,怎么从Map集合中取数据:
<%@ page import="java.util.HashMap" %>
<%@ page import="java.util.Map" %>
<%@ page import="com.bjpowernode.javaweb.jsp.bean.User" %>
<%@page contentType="text/html; charset=UTF-8" %>
<%
// 一个Map集合
Map<String,String> map = new HashMap<>();
map.put("username", "zhangsan");
map.put("password", "123");
// 将Map集合存储到request域当中。
request.setAttribute("userMap", map);
Map<String,User> userMap2 = new HashMap<>();
User user = new User();
user.setUsername("lisi");
userMap2.put("user", user);
request.setAttribute("fdsafdsa", userMap2);
%>
<%--使用EL表达式将Map集合中的user对象中的username取出--%>
${fdsafdsa.user.username}
<hr>
<%--使用EL表达式,将map中的数据取出,并输出到浏览器--%>
${userMap.username}
<br>
${userMap.password}
<br>
${userMap["username"]}
<br>
${userMap["password"]}
掌握使用EL表达式,怎么从数组和List集合中取数据:
<%
// 数组对象
String[] usernames = {"zhangsan", "lisi", "wangwu"};
// 向域中存储数组
request.setAttribute("nameArray", usernames);
User u1 = new User();
u1.setUsername("zhangsan");
User u2 = new User();
u2.setUsername("lisi");
User[] users = {u1, u2};
request.setAttribute("userArray", users);
List<String> list = new ArrayList<>();
list.add("abc");
list.add("def");
request.setAttribute("myList", list);
Set<String> set = new HashSet<>();
set.add("a");
set.add("b");
request.setAttribute("set", set);
%>
<%--取出set集合--%>
set集合:${set}
<%--无法获取:PropertyNotFoundException: 类型[java.util.HashSet]上找不到属性[0]--%>
<%-- set无序,没有索引 --%>
<%--${set[0]}--%>
<hr>
<%--取出List集合--%>
<%--list集合也是通过下标的方式取数据的。--%>
${myList}
${myList[0]}
${myList[1]}
<hr>
<%--取出数组中第二个用户的用户名--%>
${userArray[1].username}
<hr>
<%--使用EL表达式取出数组中的元素--%>
${nameArray} <%--将数组对象直接输出--%>
${nameArray[0]} <%--取出数组中的第一个元素--%>
${nameArray[1]}
${nameArray[2]}
<%--取不出数据,在浏览器上直接显示的就是空白。不会出现数组下表越界。--%>
${nameArray[100]}
page指令当中,有一个属性,可以忽略EL表达式
<%@page contentType="text/html;charset=UTF-8" isELIgnored="true" %>
isELIgnored="true" 表示忽略EL表达式,整个EL表达式会被当成一个字符串输出在浏览器
isELIgnored="false" 表示不忽略EL表达式。(这是默认值)
isELIgnored="true" 这个是全局的控制。整个JSP页面的EL表达式
可以使用反斜杠进行局部控制:\${username} 这样也可以忽略EL表达式。
通过EL表达式获取应用的根:
EL表达式中其他的隐式对象:
<%@page contentType="text/html;charset=UTF-8" %>
<%--JSP中EL表达式的隐含对象:
1. pageContext
2. param
3. paramValues
4. initParam
5. 其他(不是重点)
--%>
应用的根路径:${pageContext.request.contextPath}<br>
<%--request是JSP九大内置对象之一。--%>
<%--request.getParameter("username") 获取请求的参数。--%>
<%--用户在浏览器上提交数据:http://localhost:8080/jsp/15.jsp?username=lisi --%>
用户名:<%=request.getParameter("username")%><br>
用户名:${param.username}<br>
<%--假设用户提交的数据:http://localhost:8080/jsp/15.jsp?aihao=smoke&aihao=drink&aihao=tangtou--%>
<%--以上提交的数据显然是采用checkbox进行提交的。同一组的checkbox的name是一样的。--%>
<%--param 获取的是请求参数一维数组当中的第一个元素。--%>
爱好:${param.aihao} <br>
爱好:<%=request.getParameter("aihao")%> <br>
一维数组:${paramValues.aihao}<br>
一维数组:<%=request.getParameterValues("aihao")%><br>
<%--获取数组当中的元素:[下标]--%>
爱好:${paramValues.aihao[0]}、${paramValues.aihao[1]}、${paramValues.aihao[2]} <br>
<%--EL表达式中的隐含对象:initParam--%>
<%--ServletContext是Servlet上下文对象,对应的JSP九大内置对象之一是:application --%>
<%
String a = application.getInitParameter("pageSize");
String b = application.getInitParameter("pageNum");
%>
每页显示的记录条数:<%=a%> <br>
页码:<%=b%> <br>
每页显示的记录条数:<%=application.getInitParameter("pageSize")%> <br>
页码:<%=application.getInitParameter("pageNum")%> <br>
每页显示的记录条数:${initParam.pageSize} <br>
页码:${initParam.pageNum} <br>
<web-app xmlns="https://jakarta.ee/xml/ns/jakartaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="https://jakarta.ee/xml/ns/jakartaee https://jakarta.ee/xml/ns/jakartaee/web-app_5_0.xsd"
version="5.0">
<context-param>
<param-name>pageSizeparam-name>
<param-value>20param-value>
context-param>
<context-param>
<param-name>pageNumparam-name>
<param-value>5param-value>
context-param>
web-app>
EL表达式的运算符
<%@ page import="com.bjpowernode.javaweb.jsp.bean.Student" %>
<%@page contentType="text/html;charset=UTF-8" %>
<%--
关于EL表达式中的运算符
1. 算术运算符
+ - * / %
2. 关系运算符
== != > >= < <= eq
3. 逻辑运算符
! && || not and or (注意:!和not都是取反)
4. 条件运算符
? :
5. 取值运算符:
[] 和 .
6. empty 运算符
--%>
${10 + 20} <br>
<%-- 在EL表达式当中“+”运算符只能做求和运算。不会进行字符串拼接操作。 --%>
<%--"20"会被自动转换成数字20--%>
${10 + "20"} <br>
<%-- java.lang.NumberFormatException: For input string: "abc" --%>
<%-- + 两边不是数字的时候,一定会转成数字。转不成数字就报错:NumberFormatException--%>
\${10 + "abc"} <br>
\${"king" + "soft"} <br>
<%-- 关系运算符 --%>
${"abc" == "abc"} <br>
${"abc"} == ${"abc"} <br>
<%
Object obj = new Object();
request.setAttribute("k1", obj);
request.setAttribute("k2", obj);
%>
${k1 == k2} <br> <%--true--%>
<%
String s1 = new String("hehe");
String s2 = new String("hehe");
request.setAttribute("a", s1);
request.setAttribute("b", s2);
%>
字符串String重写了equals方法
${a == b} <%--true--%> <br>
<%
Object o1 = new Object();
Object o2 = new Object();
request.setAttribute("o1", o1);
request.setAttribute("o2", o2);
%>
${o1 == o2} <br> <%--false--%>
<%
Student stu1 = new Student("110", "警察");
Student stu2 = new Student("110", "警察");
request.setAttribute("stu1", stu1);
request.setAttribute("stu2", stu2);
%>
<%--EL表达式当中的 "==" 调用了equals方法。--%>
\${stu1 == stu2} <br> true
\${stu1 eq stu2} <br>
<hr>
<%-- != 也会调用对象的equals方法。--%>
${stu1 != stu2} <br> false
${200 != 200} <br>
<%--以下语法错误,没有加小括号--%>
\${!stu1 eq stu2} <br>
<%--正确的--%>
${!(stu1 eq stu2)}<br>
${not(stu1 eq stu2)}<br>
<%--条件运算符--%>
${empty param.username ? "对不起,用户名不能为空!" : "欢迎访问!!!!" }
<hr>
<%--empty运算符:运算结果是boolean类型。--%>
<%--判断是否为空,如果为空,结果是true。如果不为空,结果是false--%>
${empty param.username} <br>
${!empty param.username} <br>
${not empty param.username} <br>
<hr>
<%--结果false--%>
<%--前半部分是boolean类型。后面是null--%>
${empty param.password == null} <br>
${(empty param.password) == null} <br>
${param.password == null} <br>
什么是JSTL标签库?
tomcat10 JSTL下载地址: http://archive.apache.org/dist/jakarta/taglibs/standard/binaries/
Tomcat8 jstl下载jstl-1.2.jar standard-1.1.2.jar
第一步:引入JSTL标签库对应的jar包。
第二步:在JSP中引入要使用标签库。(使用taglib指令引入标签库。)
JSTL提供了很多种标签,你要引入哪个标签????重点掌握核心标签库。
<%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
这个就是核心标签库。
prefix="这里随便起一个名字就行了,核心标签库,大家默认的叫做c,你随意。使用时标签库的名字"
<%--格式化标签库,专门负责格式化操作的。--%>
<%--<%@taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt"%>--%>
<%--sql标签库--%>
<%--<%@taglib prefix="sql" uri="http://java.sun.com/jsp/jstl/sql" %>--%>
第三步:在需要使用标签的位置使用即可。表面使用的是标签,底层实际上还是java程序。
<%
// 创建List集合
List<Student> stuList = new ArrayList<>();
// 创建Student对象
Student s1 = new Student("110", "经常");
Student s2 = new Student("120", "救护车");
Student s3 = new Student("119", "消防车");
// 添加到List集合中
stuList.add(s1);
stuList.add(s2);
stuList.add(s3);
// 将list集合存储到request域当中
request.setAttribute("stuList", stuList);
%>
<%--需求:将List集合中的元素遍历。输出学生信息到浏览器--%>
<%--使用java代码--%>
<%
// 从域中获取List集合
List<Student> stus = (List<Student>)request.getAttribute("stuList");
// 编写for循环遍历list集合
for(Student stu : stus){
%>
id:<%=stu.getId()%>,name:<%=stu.getName()%><br>
<%
}
%>
<hr>
<%--使用core标签库中forEach标签。对List集合进行遍历--%>
<%--EL表达式只能从域中取数据。--%>
<%--items需要进行迭代的集合,var后面的名字是随意的。var属性代表的是集合中的每一个元素。--%>
<c:forEach items="${stuList}" var="s">
id:${s.id},name:${s.name} <br>
</c:forEach>
<%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
以上uri后面的路径实际上指向了一个xxx.tld文件。
tld文件实际上是一个xml配置文件。
在tld文件中描述了“标签”和“java类”之间的关系。
以上核心标签库对应的tld文件是:c.tld文件。它在哪里。
在jakarta.servlet.jsp.jstl-2.0.0.jar里面META-INF目录下,有一个c.tld文件。
源码解析:配置文件tld解析
<tag>
<description>对该标签的描述description>
<name>catchname> 标签的名字
<tag-class>org.apache.taglibs.standard.tag.common.core.CatchTagtag-class> 标签对应的java类。
<body-content>JSPbody-content> 标签体当中可以出现的内容,如果是JSP,就表示标签体中可以出现符合JSP所有语法的代码。例如EL表达式。
<attribute>
<description>
对这个属性的描述
description>
<name>varname> 属性名
<required>falserequired> false表示该属性不是必须的。true表示该属性是必须的。
<rtexprvalue>falsertexprvalue> 这个描述说明了该属性是否支持EL表达式。false表示不支持。true表示支持EL表达式。
attribute>
tag>
示例:
<c:catch var="">
JSP....
c:catch>
c:if
<%--没有else标签的话,可以搞两个if出来。--%>
<%--if标签还有var属性,不是必须的。--%>
<%--if标签还有scope属性,用来指定var的存储域。也不是必须的。--%>
<%--scope有四个值可选:page(pageContext域)、request(request域)、session(session域)、application(application域)--%>
<%--将var中的v存储到request域。--%>
<%--把test的值以var为键名保存到指定的域中--%>
<c:if test="${not empty param.username}" var="v" scope="request">
<h1>欢迎你${param.username}。</h1>
</c:if>
c:forEach
<%--var用来指定循环中的变量--%>
<%--begin开始--%>
<%--end结束--%>
<%--step步长--%>
<%--底层实际上:会将i存储到pageContext域当中。--%>
<c:forEach var="i" begin="1" end="10" step="1">
<%--所以这里才会使用EL表达式将其取出,一定是从某个域当中取出的。--%>
${i}<br>
</c:forEach>
<%
// 创建List集合
List<Student> stuList = new ArrayList<>();
// 创建Student对象
Student s1 = new Student("110", "经常");
Student s2 = new Student("120", "救护车");
Student s3 = new Student("119", "消防车");
// 添加到List集合中
stuList.add(s1);
stuList.add(s2);
stuList.add(s3);
// 将list集合存储到request域当中
request.setAttribute("stuList", stuList);
%>
<hr>
<%--var="s"这个s代表的是集合中的每个Student对象--%>
<%--varStatus="这个属性表示var的状态对象,这里是一个java对象,这个java对象代表了var的状态"--%>
<%--varStatus="这个名字是随意的"--%>
<%--varStatus这个状态对象有count属性。可以直接使用。--%>
<c:forEach items="${stuList}" var="s" varStatus="stuStatus">
<%--varStatus的count值从1开始,以1递增,主要是用于编号/序号。--%>
编号:${stuStatus.count},id:${s.id},name:${s.name} <br>
</c:forEach>
c:choose c:when c:otherwise
<%--
if(){
}else if(){
}else if(){
}else if(){
}else{
}
--%>
<%--注意:这个嵌套结构不能随便改,只能这样嵌套。--%>
<c:choose>
<c:when test="${param.age < 18}">
青少年
c:when>
<c:when test="${param.age < 35}">
青年
c:when>
<c:when test="${param.age < 55}">
中年
c:when>
<c:otherwise>
老年
c:otherwise>
c:choose>
使用什么技术改造呢?
在前端HTML代码中,有一个标签,叫做base标签,这个标签可以设置整个网页的基础路径。
这是Java的语法,也不是JSP的语法。是HTML中的一个语法。HTML中的一个标签。通常出现在head标签中。
< base href=“http://localhost:8080/oa/”>
在当前页面中,凡是路径没有以“/”开始的,都会自动将base中的路径添加到这些路径之前。
需要注意:在JS代码中的路径,保险起见,最好不要依赖base标签。JS代码中的路径最好写上全路径。
删除cookie时,cookie的路径需要与创建cookie时的一样,否则无法删除,删除的不为原先的cookie
index.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
DOCTYPE html>
<html>
<head>