JDBC
一、JDBC环境搭建
Idea
1.左上文件==新建==项目==左侧新建项目==输入JDBC==构建系统选择IDEA==点击创建
2.选中项目名JDBC==右键新建==目录==输入lib==回车
3.复制mysql-connector-java-5.1.37.jar包==对着lib粘贴==点击确定
4.选中lib文件夹==右键添加为库==点击确定
5.对着src==右键新建==软件包==com.tys.JDBC
6.对着com.tys.JDBC==右键新建==Java类==TestJDBC
7.打开SQLyog,并且确认companydb数据库存在
8.复制增删改代码,右键运行即可
二、JDBC开发步骤
package com.tys.JDBC;
import java.sql.*;
public class TestJDBC {
public static void main(String[] args) throws Exception {
//1.注册驱动,加载驱动
Class.forName("com.mysql.jdbc.Driver") ;
//2.获得数据库连接
String url="jdbc:mysql://localhost:3306/companydb?useUnicode=true&characterEncoding=utf-8&useSSL=false"; //连接到哪个数据库
String user="root";
String password="123456";
Connection connection=DriverManager.getConnection(url,user,password);
if(connection!=null){
System.out.println("连接到数据库了");
}else{
System.out.println("连接失败");
}
//3.获得执行SQL语句的对象(使用Statement类)
Statement statement=connection.createStatement();
//4.编写SQL语句 执行SQL语句
String sql="INSERT INTO t_jobs(job_id,JOB_TITLE,MIN_SALARY,MAX_SALARY) VALUES ('H5_Mgr','H5_Manager',4000,10000)";
int result=statement.executeUpdate(sql); //返回受影响的行数
//5.处理数据库的返回结果
if(result==1) { //返回受影响的行数
System.out.println("创建成功");
}else{
System.out.println("创建失败");
}
//6.关闭资源
statement.close();
connection.close();
}
}
三、SQL注入
SQL注入问题:
用户输入的数据中有SQL关键字或语法并且参与了SQL语句的编译,导致SQL语句编译后的条件含义为true,一直得到正确的结果,这种现象称为SQL注入。
PreparedStatement(修复SQL注入):
由于编写的SQL语句是在用户输入数据,整合后再进行编译,所以为了避免SQL注入的问题,我们要使SQL语句在用户输入数据前就已进行编译成完整的SQL语句,再进行填充数据
#创建用户表users
#插入两条测试语句
CREATE TABLE users(
id INT PRIMARY KEY AUTO_INCREMENT,
username VARCHAR(20) UNIQUE NOT NULL,
pwd VARCHAR(20) NOT NULL,
phone VARCHAR(11)
)CHARSET=utf8;
INSERT INTO users(username,pwd,phone) VALUES ('zhangsan','1234','4008100100');
INSERT INTO users(username,pwd,phone) VALUES ('lisi','1234','4008120120');
package com.tys.JDBC;
import java.sql.*;
import java.util.*;
public class TestJDBC {
public static void main(String[] args) throws Exception{
Scanner scanner=new Scanner(System.in);
System.out.println("请输入用户名:");
String username= scanner.nextLine(); //改成nextLine 可以接受空格
System.out.println("请输入密码:");
String password= scanner.nextLine(); //abc' or 1=1;#
//1.注册驱动,加载驱动
Class.forName("com.mysql.jdbc.Driver") ;
//2.获得数据库连接
Connection connection= DriverManager.getConnection("jdbc:mysql://localhost:3306/companydb?useUnicode=true&characterEncoding=utf-8&useSSL=false","root","123456");
//3.获得PreparedStatement的对象
PreparedStatement preparedStatement=connection.prepareStatement("select * from users where username=? and pwd=?");
//4.为?占位符进行赋值
preparedStatement.setString(1,username); //下标从1开始
preparedStatement.setString(2,password);
//5.编写SQL语句 执行SQL语句
ResultSet resultSet=preparedStatement.executeQuery();
//6.处理数据库的返回结果
if(resultSet.next()) { //查询到数据
System.out.println("登陆成功");
}else {
System.out.println("登陆失败");
}
//7.关闭资源
resultSet.close();
preparedStatement.close();
connection.close();
}
}
四、封装工具类与ORM
DBUtils工具类:
在实际JDBC开发中,存在大量重复代码,我们把传统的JDBC代码进行重构,抽取出通用的JDBC工具类,以后连接任何数据库,释放资源都可以使用这个工具类
ORM封装零散数据:
从数据库查询到的结果集(ResultSet)在进行遍历时,逐行遍历。取出的都是零散的数据,在实际应用开发中,我们需要将零散的数据进行封装整理
实体类:是零散数据的载体
- 一行数据中,多个零散的数据进行整理
- 通过entity的规则对表中的数据进行对象的封装
- 表名=类名,列名=属性名,提供各个属性的get set方法
- 提供无参构造方法,视情况添加有参构造
五、DBUtils工具类
package com.tys.utils;
import java.sql.*;
public class DBUtils {
private static ThreadLocal<Connection> threadLocal=new ThreadLocal<>();
static final String JDBC_DRIVER = "com.mysql.jdbc.Driver";
static final String url = "jdbc:mysql://localhost:3306/university_graduatedb?useUnicode=true&characterEncoding=utf-8&useSSL=false";
static final String user = "root";
static final String password = "123456";
//获得连接
public static Connection getConnection() {
Connection connection = threadLocal.get();//将当前线程中绑定的connection对象赋值给connection
try {
Class.forName(JDBC_DRIVER);
if (connection==null){
connection =DriverManager.getConnection(url, user, password);
threadLocal.set(connection);//把连接存在当前线程共享中
}
} catch (Exception e) {
e.printStackTrace();
}
return connection;
}
//开启事务
public static void begin(){
try {
Connection connection=getConnection();
connection.setAutoCommit(false);
}catch (SQLException e){
e.printStackTrace();
}
}
//提交事务
public static void commit(){
Connection connection=null;
try {
connection=getConnection();
connection.commit();
}catch (SQLException e){
e.printStackTrace();
}finally {
closeAll(connection,null,null);
}
}
//回滚事务
public static void rollback(){
Connection connection=null;
try {
connection=getConnection();
connection.rollback();
}catch (SQLException e){
e.printStackTrace();
}finally {
closeAll(connection,null,null);
}
}
//关闭连接,释放资源
public static void closeAll(Connection connection,Statement statement,ResultSet resultSet) {
try{
if (resultSet!=null){
resultSet.close();
}
if (statement!=null){
statement.close();
}
if (connection!=null){
connection.close();
threadLocal.remove();//关闭连接后,移除已关闭connection对象
}
}catch (SQLException e){
e.printStackTrace();
}
}
}
六、DAO层增删改查
//新增 管理员主页,添加学生账户的实现
import java.sql.*;
public int addStudent(Student student) throws SQLException {
Connection conn= DBUtils.getConnection();
String sql="insert into account_student(stuname,password,studentId) values (?,?,?)";
PreparedStatement ps=conn.prepareStatement(sql);
ps.setString(1, student.getStuname());
ps.setString(2, student.getPassword());
ps.setString(3, student.getStudentId());
int rs=ps1.executeUpdate();
if(rs==1) {
DBUtils.closeAll(conn,ps,null);
return 1;
}else {
DBUtils.closeAll(conn,ps,null);
return 0;
}
}
======================================================================================
//修改 教师列表,点击编辑按钮
import java.sql.*;
public int EditTeacher(Teacher teacher) throws SQLException {
String sql="update teacherinfo set tname=?,tdept=?,tel=? where teacherId=?";
Connection conn= DBUtils.getConnection();
PreparedStatement ps=conn.prepareStatement(sql);
ps.setString(1, teacher.getTname());
ps.setString(2, teacher.getTdept());
ps.setString(3, teacher.getTel());
ps.setString(4, teacher.getTeacherId());
int rs=ps.executeUpdate();
if(rs==1) {
DBUtils.closeAll(conn,ps,null);
return 1;
}else {
DBUtils.closeAll(conn,ps,null);
return 0;
}
}
======================================================================================
//删除 教师列表,点击删除按钮
import java.sql.*;
public int deleteTeacher(String teacherId) throws SQLException {
Connection conn= DBUtils.getConnection();
String sql="delete from teacherinfo where teacherId=?";
PreparedStatement ps=conn.prepareStatement(sql);
ps.setString(1, teacherId);
int rs=ps.executeUpdate();
if (rs==1) {
DBUtils.closeAll(conn,ps,null);
return 1;
}else {
DBUtils.closeAll(conn,ps,null);
return 0;
}
}
======================================================================================
//查单个,登录时验证账号密码
import java.sql.*;
public Admin selectAllAdmin(String username, String password) throws SQLException {
String sql="select * from account_admin where username=? and password=?";
Connection conn=DBUtils.getConnection();
PreparedStatement ps=conn.prepareStatement(sql);
ps.setString(1, username);
ps.setString(2, password);
ResultSet rs=ps.executeQuery();
Admin admin=new Admin();
if(rs.next()==true) {
admin.setUsername(rs.getString("username"));
admin.setPassword(rs.getString("password"));
DBUtils.closeAll(conn,ps,rs);
return admin;
}else {
DBUtils.closeAll(conn,ps,rs);
return null;
}
}
======================================================================================
//查所有,取整个mysql数据表格,此为获得教师列表
import java.util.*;
import java.sql.*;
public List<Object> selectTeacherList() throws SQLException {
String sql="select * from teacherinfo";
Connection conn= DBUtils.getConnection();
PreparedStatement ps=conn.prepareStatement(sql);
ResultSet rs=ps.executeQuery();
List<Object> list=new ArrayList<Object>();
while(rs.next()) {
Teacher teacher=new Teacher();
teacher.setTeacherId(rs.getString("teacherId"));
teacher.setTname(rs.getString("tname"));
teacher.setTdept(rs.getString("tdept"));
teacher.setTel(rs.getString("tel"));
list.add(teacher);
}
DBUtils.closeAll(conn,ps,rs);
return list;
}
======================================================================================
//模糊搜索,此为搜索毕设选题
import java.sql.*;
public List<Object> selectTitleListFuzzy(String name) throws SQLException {
String sql="select * from titleinfo where titlename like'%"+name+"%'";
Connection conn= DBUtils.getConnection();
PreparedStatement ps=conn.prepareStatement(sql);
ResultSet rs=ps.executeQuery();
List<Object> list=new ArrayList<Object>();
while(rs.next()) {
Title title=new Title();
title.setTitleId(rs.getInt("titleId"));
title.setTitlename(rs.getString("titlename"));
title.setTname(rs.getString("tname"));
list.add(title);
}
DBUtils.closeAll(conn,ps,rs);
return list;
}
七、Druid连接池
在程序初始化时,预先创建指定数量的数据库连接对象存储在池中。当需要连接数据库时,从连接池中取出现有链接,使用完毕后,也不会进行关闭,而是放回池中,实现复用,节省资源。
//database.properties
//需要引入jdbc和druid两个jar包
//IDEA,对着src,右键==New==Resource Bundle==输入database==点击OK
driver=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/companydb?useUnicode=true&characterEncoding=utf-8&useSSL=false
username=root
password=123456
initialSize = 10
maxActive = 30
minIdle = 5
maxWait = 3000
//新建DBUtils.java
package com.tang.jdbcpool;
import com.alibaba.druid.pool.*;
import java.io.*;
import java.sql.*;
import java.util.*;
public class DBUtils {
//声明一个连接池对象
private static DruidDataSource ds;
static {
Properties properties=new Properties();
InputStream is= DBUtils.class.getResourceAsStream("/database.properties");
try{
properties.load(is);
//创建连接池
ds=(DruidDataSource)DruidDataSourceFactory.createDataSource(properties);
}catch (Exception e){
e.printStackTrace();
}
}
public static Connection getConnection(){
try {
return ds.getConnection(); //通过连接池获得连接对象
} catch (SQLException e) {
e.printStackTrace();
}
return null;
}
}
====================================================================================== //测试连接池,在包下新建TestPool.java
package com.tang.jdbcpool;
import java.sql.Connection;
public class TestPool {
public static void main(String[] args) throws Exception {
for(int i=1;i<=20;i++){
Connection conn= DBUtils.getConnection();
System.out.println(conn);
// conn.close();//放回池里,这句代码不注释,取回的值是一样的,因为循环了20次,从池子里创建connection,然后又放回池里
}
}
}
Servlet
一、创建Servlet项目
(1)创建web项目
Idea
1.新建项目:左上文件==新建==项目==左侧新建项目==输入WebProject==构建系统选择IDEA==点击创建
2.选中项目名WebProject==右键==添加框架支持==左侧Web应用程序(4.0)==右侧勾选创建web.xml==点击确定
3.点开web文件夹==选中WEB-INF==右键新建==目录==输入lib==回车
4.复制servlet-api.jar包==对着lib粘贴==点击确定
5.选中lib文件夹==右键添加为库==点击确定
6.选中src==右键新建==软件包==com.tys.servlet
7.选中com.tys.servlet==右键新建==Java类==MyServlet==回车
(2)编写Servlet,MyServlet.class添加以下代码
package com.tys.servlet;
import javax.servlet.*;
import java.io.IOException;
public class MyServlet implements Servlet {
@Override
public void init(ServletConfig servletConfig) throws ServletException {
}
@Override
public ServletConfig getServletConfig() {
return null;
}
@Override
public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
System.out.println("My First Web Project");
}
@Override
public String getServletInfo() {
return null;
}
@Override
public void destroy() {
}
}
(3)修改web.xml,添加以下代码
<!-- 配置Servlet信息 -->
<servlet>
<!-- 配置Servlet名称,名称必须唯一 -->
<servlet-name>my</servlet-name>
<!-- 配置Servlet完全路径(包名+类名) -->
<servlet-class>com.tys.servlet.MyServlet</servlet-class>
</servlet>
<!-- 配置Servlet映射(访问路径) -->
<servlet-mapping>
<!-- 配置Servlet名称,必须和上面的相同 -->
<servlet-name>my</servlet-name>
<!-- 配置虚拟路径(访问路径) -->
<url-pattern>/myservlet</url-pattern>
</servlet-mapping>
(4) 项目集成Tomcat
1. 右上角添加配置==点击加号==找到Tomcat服务器==点击本地==右侧找到部署==点击加号==工件==点击确定
2. 右上角编辑配置==右侧找到部署==往下拉,把后缀删掉==点击确定
3. 点击Tomcat左边绿色箭头==在360浏览器输入==localhost:8080/WebProject/myservlet
4. 查看IDEA控制台输出(My First Web Project) 表示成功!
二、Servlet详解
2.1 三种创建方式
(1)实现Servlet接口(不用)
五个方法需要重写,实际只有service要重写
package com.tang.servlet;
import javax.servlet.*;
import java.io.IOException;
public class MyServlet implements Servlet {
@Override
public void init(ServletConfig servletConfig) throws ServletException {
}
@Override
public ServletConfig getServletConfig() {
return null;
}
@Override
public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
System.out.println("My First Web Project");
}
@Override
public String getServletInfo() {
return null;
}
@Override
public void destroy() {
}
}
==================================================================================
(2)GenericServlet(不用)
只需重写抽象service方法即可
package com.tang.servlet;
import javax.servlet.*;
import java.io.IOException;
public class GenServlet extends GenericServlet{
@Override
public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
System.out.println("嘿嘿嘿");
}
}
<!-- 配置Servlet信息 -->
<servlet>
<!-- 配置Servlet名称,名称必须唯一 -->
<servlet-name>gs</servlet-name>
<!-- 配置Servlet完全路径(包名+类名) -->
<servlet-class>com.tang.servlet.GenServlet</servlet-class>
</servlet>
<!-- 配置Servlet映射(访问路径) -->
<servlet-mapping>
<!-- 配置Servlet名称,必须和上面的相同 -->
<servlet-name>gs</servlet-name>
<!-- 配置虚拟路径(访问路径) -->
<url-pattern>/gs</url-pattern>
</servlet-mapping>
==================================================================================
(3)HttpServlet(推荐)
继承GenericServlet的基础上进一步扩展,必须重写一个方法。doGet doPost doPut doDelete
package com.tys.servlet;
import javax.servlet.ServletException;
import javax.servlet.http.*;
import java.io.IOException;
public class HttpsServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("这是get请求过来的");
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("这是post请求过来的");
}
}
<!-- 配置Servlet信息 -->
<servlet>
<!-- 配置Servlet名称,名称必须唯一 -->
<servlet-name>hs</servlet-name>
<!-- 配置Servlet完全路径(包名+类名) -->
<servlet-class>com.tys.servlet.HttpsServlet</servlet-class>
</servlet>
<!-- 配置Servlet映射(访问路径) -->
<servlet-mapping>
<!-- 配置Servlet名称,必须和上面的相同 -->
<servlet-name>hs</servlet-name>
<!-- 配置虚拟路径(访问路径) -->
<url-pattern>/hs</url-pattern>
</servlet-mapping>
2.2 两种配置方式
使用web.xml
匹配规则 | 举例:/hs | |
---|---|---|
精确匹配 | /具体的名称 | 只有url路径是具体名称才会触发servlet |
后缀匹配 | *.xxx | 只要是以xxx结尾的就会触发servlet |
通配符匹配 | /* | 匹配所有请求,包含服务器的所有资源 |
通配符匹配 | / | 匹配所有请求,包含服务器的所有资源,不包含jsp |
<!-- load-on-startup 表示加载顺序,从0开始,数值越小越先被加载,如果没有设置,则访问时,被加载 -->
<servlet>
<!-- 配置Servlet名称,名称必须唯一 -->
<servlet-name>hs</servlet-name>
<!-- 配置Servlet完全路径(包名+类名) -->
<servlet-class>com.tys.servlet.HttpsServlet</servlet-class>
<load-on-startup>0</load-on-startup>
<!-- load-on-startup 表示加载顺序,从0开始,数值越小越先被加载,如果没有设置,则访问时,被加载 -->
</servlet>
<!-- 配置Servlet映射(访问路径) -->
<servlet-mapping>
<!-- 配置Servlet名称,必须和上面的相同 -->
<servlet-name>hs</servlet-name>
<!-- 配置虚拟路径(访问路径) -->
<url-pattern>/hs</url-pattern>
</servlet-mapping>
==================================================================================
使用注解
注解名字 | 作用 |
---|---|
name | servlet名字,因为是注解所以可以省略 |
value | 配置url路径,可以配置多个 |
urlPatterns | 配置url路径,和value作用一样,不能同时使用 |
loadOnstartup | 配置servlet创建的时机,数值越小,优先级越高 |
package com.tang.servlet;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
@WebServlet(value ={"/bs","/bss"},loadOnStartup = 0)
public class BasicServlet extends HttpsServlet{
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("这是get");
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("这是post");
}
}
三、Get与Post
3.1 两者区别
get请求 | post请求 |
---|---|
get提交的数据会放在url之后,以?分隔url和传输数据,参数之间以&相连 | post方法是把提交的数据放在http包的body中 |
get方式明文传递,数据量小,不安全 | 密文传递数据,数据量大,安全 |
效率高,浏览器默认请求方式为get请求 | 效率相对没有get高 |
对应的Servlet方法是doGet | 对应的Servlet方法是doPost |
3.2 表单提交的数据
//对着web,右键==新建==HTML文件==输入register
//项目名WebProjet
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>注册页面</title>
</head>
<body>
<form action="/WebProjet/rs" method="post">
用户名:<input type="text" name="username"/> <br/>
密码:<input type="password" name="password"/> <br/>
<input type="submit" value="注册"/>
</form>
</body>
</html>
//src包下,新建class文件,RegisterServlet.class
package com.tys.servlet;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.*;
import java.io.IOException;
import java.io.PrintWriter;
@WebServlet(value = "/rs")
public class RegisterServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doPost(req,resp);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//设置统一的编码
req.setCharacterEncoding("utf-8");
resp.setCharacterEncoding("utf-8");
resp.setContentType("text/html;charset=utf-8");
//获取用户请求发送的数据
String username=req.getParameter("username");
String password=req.getParameter("password");
System.out.println("提交的数据:"+username+"\t"+password);
//响应数据给客户端,客户端注册点击后显示注册成功
PrintWriter printWriter= resp.getWriter();
printWriter.println("注册成功!");
}
}
// http://localhost:8080/WebProjet/register.html 测试
四、转发和重定向
4.1 转发
转发的页面跳转:服务器内部跳转,url地址不改变,属于同一次请求
a跳转到b页面,打印出B 执行了
@WebServlet(value = "/a")
public class AServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
req.getRequestDispatcher("/b").forward(req,resp);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req,resp);
}
}
=============================================================================
@WebServlet(value = "/b")
public class BServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("B 执行了");
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req,resp);
}
}
==================================================================================
转发的数据传递:
可以将数据存入request后,在一次请求过程中的任何位置进行获取
可传递任何数据(基本数据类型,对象,数组,集合等)
a跳转到b页面
@WebServlet(value = "/a")
public class AServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
req.setAttribute("username","gavin");
req.getRequestDispatcher("/b").forward(req,resp);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req,resp);
}
}
===================================================================================
@WebServlet(value = "/b")
public class BServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String s=(String)req.getAttribute("username");
System.out.println(s); //输出gavin
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req,resp);
}
}
===================================================================================
转发的特点:
- 转发是服务器行为
- 转发是浏览器只做了一次访问请求
- 转发浏览器地址不变
- 转发两次跳转之间传输的信息不会丢失,所以可以通过request进行数据的传递
- 转发只能将请求转发给同一个Web应用中的组件
4.2 重定向
重定向作用在客户端,客户端将请求发送给服务器后,服务器响应给客户端一个新的请求地址,客户端重新发送新请求。
======================================================================================
重定向的页面跳转:
跳转时,地址栏改变,说明发送两次请求,response没有作用域,两次request请求中的数据无法共享
@WebServlet(value = "/a")
public class AServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.sendRedirect("/WebProject_war_exploded/b")
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req,resp);
}
}
===================================================================================
@WebServlet(value = "/b")
public class BServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("B 执行了");
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req,resp);
}
}
======================================================================================
重定向的特点:
- 重定向是客户端行为
- 重定向是客户端至少做了两次访问请求
- 重定向浏览器地址改变
- 重定向两次跳转之间传输的信息会丢失
- 重定向可以指向任何的资源
五、状态管理
5.1 Cookie
主要由标识该信息的名称(name)和值(value)组成,键值对
查看cookie:360浏览器,设置-高级设置-网页内容高级设置-所有cookie和网站数据-输入localhost
======================================================================================
设置cookie访问路径和有效期
//1.服务端创建cookie对象
Cookie cookie=new Cookie("username","gavin");
Cookie cookie2=new Cookie("password","123456");
//1.1设置cookie的访问路径
cookie.setPath("WebProject_war_exploded/get"); //代表只有get下面的资源可以访问cookie
cookie.setPath("WebProject_war_exploded");
//1.2设置cookie生命周期,取值有三种,>0,有效期单位秒,=0浏览器关闭,-1浏览器内存临时存储
cookie.setMaxAge(60*60);
cookie2.setMaxAge(60*60); //代表有效期1小时
//2.将cookie响应给客户端
resp.addCookie(cookie);
resp.addCookie(cookie2);
======================================================================================
循环遍历,获取cookie
//1通过request对象获取所有的cookie
Cookie[] cookies=req.getCookies();
//2 通过循环遍历cookie
if (cookies!=null){
for (Cookie cookie:cookies) {
System.out.println(cookie.getName()+":"+cookie.getValue());
}
}
======================================================================================
cookie的修改
只需要保证cookie的名字和路径一致即可修改,原理是替换原cookie
//新建一个cookie类,路径名字与之前一致,会自动替换掉之前的cookie
Cookie cookie=new Cookie("username","aaron");
cookie.setPath("WebProject_war_exploded/get");
cookie.setMaxAge(60*60);
resp.addCookie(cookie);
======================================================================================
cookie中文编码与解码
cookie默认不支持中文,所以需要编码
import java.net.*;
//编码 在创建对象时候,直接这么创建
Cookie cookie=new Cookie(URLEncoder.encode("姓名","UTF-8"),URLEncoder.encode("张三","UTF-8"));
//解码,在循环遍历,获取cookie里面,foreach改成输出这个就行
System.out.println(URLDecoder.decode(cookie.getName(),"UTF-8")+":"+URLDecoder.decode(cookie.getValue(),"UTF-8"));
5.2 Session
Session用于记录用户的状态。Session指的是在一段时间内,单个客户端与Web服务器的一连串相关的交互过程
session由服务端创建,服务器会为每一次会话分配一个session对象,首次使用到Session时,服务器会自动创建Session,并创建Cookie存储Session id发送回客户端
Session基本操作:
//1 通过request对象获取Session对象
HttpSession session= req.getSession();
System.out.println(session.getId());
//2.session保存数据
session.setAttribute("key",value); //以键值对形式存储在session作用域中
//3.session获取数据
session.getAttribute("key"); //通过String类型的key访问Object类型的value
//4.session移除数据
session.removeAttribute("key"); //通过键移除session作用域中的值
//5.生命周期
//5.1浏览器关闭,则结束
//5.2设置session生命周期10秒,session超时则结束
session.setMaxInactiveInterval(10);
//5.3手工销毁,则结束
session.invalidate();
5.3 ServletContext
- 全局对象,也有作用域,对应一个Tomcat中的Web应用
- 只要容器不关闭或应用不卸载,ServletContext就一直存在,因为是全局容器所以应用很少
======================================================================================
获得 ServletContext对象
//1 通过this.getServletContext() (推荐)
ServletContext servletContext1=this.getServletContext();
//2 通过request对象获取 (推荐)
ServletContext servletContext2=req.getServletContext();
//3 通过session对象获取 (不推荐)
HttpSession session= req.getSession();
ServletContext servletContext3=session.getServletContext();
======================================================================================
ServletContext基本使用:
//获取当前项目在服务器发布的真实路径
System.out.println(servletContext1.getRealPath("/"));
//获取项目的上下文路径,两句一样
System.out.println(servletContext2.getContextPath());
System.out.println(req.getContextPath()); //两句一样,输出/MyServlet_war_exploded
//全局容器
servletContext1.setAttribute("name",value); //存储数据
servletContext1.getAttribute("name"); //获取数据
servletContext1.removeAttribute("name"); //移除数据
六、过滤器
- 过滤器执行过程:客户端发送请求时,会先经过Filter,再到达目标Servlet中;响应时,再依次返回
2. API中提供了一个Filter接口,开发人员编写一个Java类实现了这个接口即可,这个类称之为过滤器
- 导入包: import javax.servlet.*;
6.1 过滤器基本实现
//过滤器
package com.tys.servlet;
import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import java.io.IOException;
@WebFilter(value = "/t")
public class MyFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
Filter.super.init(filterConfig);
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
System.out.println("---StartFilter---");
filterChain.doFilter(servletRequest,servletResponse); //让请求继续
System.out.println("---EndFilter---");
}
@Override
public void destroy() {
Filter.super.destroy();
}
}
======================================================================================
//目标Servlet
package com.tang.servlet;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
@WebServlet(value = "/t")
public class TargetServlet extends HttpsServlet{
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("---target---");
}
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doPost(req, resp);
}
}
=====================================================================================
输出的执行结果,可以看出执行顺序,客户端到filter到Servlet,再返回filter,再返回客户端
---StartFilter---
---target---
---EndFilter---
6.2 两种配置方式
注解配置
@WebFilter(value = "/hs")
@WebServlet(value = "/hs") 这两个要一样,才能过滤
=====================================================================================
xml配置
<filter>
<filter-name>sf</filter-name>
<filter-class> com.tang.servlet.FirstFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>sf</filter-name>
<url-pattern>/hs</url-pattern>
</filter-mapping>
6.3 过滤器优先级
在一个Web应用中,可以开发编写多个Filter,这些Filter组合起来称之为一个FIlter链
优先级:
1. 如果为注解的话,是按照字母的排序顺序
2. 如果web.xml,是按照从上往下顺序
3. web.xml配置高于注解方式
4. 如果注解和web.xml同时配置,会创建多个过滤器对象,造成过滤多次
配置拦截路径:
1. 精确拦截匹配:/index.jsp
2. 后缀拦截匹配:*.jpg
3. 通配符拦截匹配:/*
七、监听器
7.1 监听Request作用域
package com.tys.servlet;
import javax.servlet.*;
public class MyListener implements ServletRequestListener,ServletRequestAttributeListener{
//监听request对象的创建
@Override
public void requestInitialized(ServletRequestEvent sre) {
System.out.println("request被创建了");
sre.getServletRequest();//获取监听的request对象
}
//监听request对象的销毁
@Override
public void requestDestroyed(ServletRequestEvent sre) {
System.out.println("request被销毁了");
sre.getServletRequest();//获取监听的request对象
}
//监听request作用域数据添加
@Override
public void attributeAdded(ServletRequestAttributeEvent srae) {
System.out.println("request作用域增加了一条数据:"+srae.getName()+":"+srae.getValue());
}
//监听request作用域数据移除
@Override
public void attributeRemoved(ServletRequestAttributeEvent srae) {
System.out.println("request作用域移除了一条数据:"+srae.getName()+":"+srae.getValue());
}
//监听request作用域数据替代
@Override
public void attributeReplaced(ServletRequestAttributeEvent srae) {
System.out.println("request作用域替换了一条数据:"+srae.getName()+":"+srae.getValue());
}
}
7.2 监听Session作用域
package com.tys.servlet;
import javax.servlet.http.*;
public class MyListener implements HttpSessionListener,HttpSessionAttributeListener{
//监听session对象的创建
@Override
public void sessionCreated(HttpSessionEvent se) {
System.out.println("session被创建了");
se.getSession();//获取监听的session对象
}
//监听session对象的销毁
@Override
public void sessionDestroyed(HttpSessionEvent se) {
System.out.println("session被销毁了");
se.getSession();//获取监听的session对象
}
//监听session作用域数据添加
@Override
public void attributeAdded(HttpSessionBindingEvent se) {
System.out.println("session作用域增加了一条数据:"+se.getName()+":"+se.getValue());
}
//监听session作用域数据移除
@Override
public void attributeRemoved(HttpSessionBindingEvent se) {
System.out.println("session作用域移除了一条数据:"+se.getName()+":"+se.getValue());
}
//监听session作用域数据替代
@Override
public void attributeReplaced(HttpSessionBindingEvent se) {
System.out.println("session作用域替代了一条数据:"+se.getName()+":"+se.getValue());
}
}
7.3 监听ServletContext作用域
package com.tys.servlet;
import javax.servlet.*;
public class MyListener implements ServletContextListener, ServletContextAttributeListener {
//监听ServletContext对象的创建
@Override
public void contextInitialized(ServletContextEvent sce) {
System.out.println("ServletContext被创建了");
sce.getServletContext();//获取监听的ServletContext对象
}
//监听ServletContext对象的销毁
@Override
public void contextDestroyed(ServletContextEvent sce) {
System.out.println("ServletContext被销毁了");
sce.getServletContext();//获取监听的ServletContext对象
}
//监听ServletContext作用域数据添加
@Override
public void attributeAdded(ServletContextAttributeEvent scae) {
System.out.println("ServletContext作用域增加了一条数据:"+scae.getName()+":"+scae.getValue());
}
//监听ServletContext作用域数据移除
@Override
public void attributeRemoved(ServletContextAttributeEvent scae) {
System.out.println("ServletContext作用域移除了一条数据:"+scae.getName()+":"+scae.getValue());
}
//监听ServletContext作用域数据替代
@Override
public void attributeReplaced(ServletContextAttributeEvent scae) {
System.out.println("ServletContext作用域替代了一条数据:"+scae.getName()+":"+scae.getValue());
}
}
7.4 web.xml
//对于Servlet2.3规范,监听器必须位于所有的<servlet>元素之前以及<filter-mapping>之后
//对于Servlet2.4及以后的规范,这些同级元素之间的顺序可以任意。
<listener>
<listener-class>com.tys.listener.MyListener</listener-class>
</listener>
JSP
一、JSP环境搭建
Idea
1.新建项目:左上文件==新建==项目==左侧新建项目==输入Web_JSP==构建系统选择IDEA==点击创建
2.选中项目名Web_JSP==右键==添加框架支持==左侧Web应用程序(4.0)==右侧勾选创建web.xml==点击确定
3.右上角添加配置==点击加号==找到Tomcat服务器==点击本地==右侧找到部署==点击加号==工件==点击确定
4.右上角编辑配置==右侧找到部署==往下拉,把后缀删掉==点击确定
5.选中web==右键==新建==JSP/JSPX==输入first==全部删掉,然后复制以下代码
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
Now:<%=new java.util.Date() %>
</body>
</html>
8.启动Tomcat,360浏览器访问:http://localhost:8080/Web_JSP/first.jsp,看见当前时间代表成功
二、JSP与HTML集成开发
2.1 脚本
普通脚本
语法: <% Java代码 %>
普通脚本可以使用所有java语法,但不能定义函数
脚本与脚本之间不可嵌套,脚本与HTML标签不可嵌套
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>脚本的使用</title>
</head>
<body>
<%
int a=10;
System.out.println(a); //输出在控制台
out.println(a);//输出在页面
%>
</body>
</html>
======================================================================================
声明脚本
语法: <%! 定义变量 函数 %>
声明脚本声明的变量是全局变量
声明脚本的内容必须在普通脚本<% %>中调用
如果声明脚本中的函数具有返回值,使用输出脚本调用<%= %>
<%!
int b=20;
public void play(){
System.out.println("play执行了");
}
%>
<%
out.println(b); //页面打印20
play(); //控制台打印play执行了
%>
======================================================================================
输出脚本
语法: <%= Java表达式 %> 直接输出在页面上,输出脚本本质就是out.println();调用该方法
输出脚本可以输出带有返回值的函数
输出脚本不能加;分号
2.2 JSP指令
page指令
page指令用来提供当前页面的使用说明。就是新建jsp页面的第一行
<%@ page contentType="text/html;charset=UTF-8" language="java" errorPage="error.jsp" %>
指定当前jsp页面发生异常时需要转向的错误处理页面,errorPage="error.jsp" 跳转到error.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" isErrorPage="true" %>
新建error.jsp,指定isErrorPage="true"当前页面就是用户交互更友好的404页面
<%@ page import="java.util.Date" %>
写在jsp页面第二行,写这个可以导包
<%@ page contentType="text/html;charset=UTF-8" language="java" pageEncoding="UTF-8"%>
指定jsp页面的解码格式UTF-8
======================================================================================
include指令
通过这个指令,来包含其他文件,可以是jsp,html,文本文件
可能会有重名的冲突问题,不建议使用,用动作标签include
<%@ include file="header.jsp" %>
======================================================================================
taglib指令
主要用来引入jsp的标准标签库
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
2.3 动作标签
include(推荐)
<jsp:include page="header.jsp" />
前面include指令,是将外部文件的代码直接复制到了当前jsp文件中,再去编译运行,即如果内部文件(String name=“1”)和外部文件(String name=“2”),然后都要打印name,就会有重名,然后报错
而这里的动作标签则是将外部文件的已经执行完的输出结果引入到了当前jsp文件中,就是页面显示打印name结果1,2
======================================================================================
useBean
//先定义一个User的实体类
package com.tys.entity;
public class User {
private String username;
private String password;
public User() {
}
public User(String username, String password) {
this.username = username;
this.password = password;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
@Override
public String toString() {
return "User{" +
"username='" + username + '\'' +
", password='" + password + '\'' +
'}';
}
}
======================================================================================
<jsp:useBean id="user" class="com.tys.entity.User" />
用来加载一个将在JSP页面中使用的实体类,即给com.tang.entity.User创建一个对象名叫user
<jsp:setProperty name="user" property="username" value="tom" />
<jsp:setProperty name="user" property="password" value="123456" />
给刚刚创建的user对象的username赋值tom,user对象的password赋值123456
<jsp:getProperty name="user" property="username"/>
<jsp:getProperty name="user" property="password"/>
获取实体类放入的值,转换成字符串,然后输出到页面.这个就是在页面上输出tom,123456
======================================================================================
forward
<jsp:forward page="B.jsp" /> 页面跳转到B.jsp页面
======================================================================================
param就是参数传递
<jsp:forward page="B.jsp" >
<jsp:param name="name" value="gavin" />
</jsp:forward>>
B.jsp页面写以下内容
<% String name=request.getParameter("name"); %>
<%= name %>
2.4 内置对象
四大作用域对象
JSP有九大内置对象
但常用的就以下四个作用域对象,存储数据和获取数据的方式一样,不同的是取值的范围有差别
- pageContext:当前JSP页面范围有效
- request:一次请求有效
- session:一次会话有效,关闭浏览器失效
- application:整个web应用有效,Tomcat服务器重启或关闭失效,其实就是ServletContext
======================================================================================
pageContext应用
<%
pageContext.getRequest();//返回Request内置对象
pageContext.getResponse();//返回Response内置对象
pageContext.getSession();//返回Session内置对象
pageContext.getServletContext();
pageContext.getOut();
pageContext.getException();
pageContext.getPage();
pageContext.getServletConfig();
%>
<!-- pageContext操作其他内置对象的作用域 -->
<%
pageContext.setAttribute("page","123");//当前jsp有效,范围太小,使用场景极少
pageContext.setAttribute("req","aaa",PageContext.REQUEST_SCOPE);
pageContext.setAttribute("sess","bbb",PageContext.SESSION_SCOPE);
pageContext.setAttribute("app","ccc",PageContext.APPLICATION_SCOPE);
String req=(String) pageContext.getAttribute("req",PageContext.REQUEST_SCOPE);
String sess=(String) pageContext.getAttribute("sess",PageContext.SESSION_SCOPE);
String app=(String) pageContext.getAttribute("app",PageContext.APPLICATION_SCOPE);
String result=(String) pageContext.findAttribute("app");
%>
request:<%=req%>
session:<%=sess%>
application:<%=app%>
find:<%=result%>
2.5 JSP注释
语法 | 描述 |
---|---|
<%-- JSP注释 --%> | JSP注释内容,注释内容不会被发送至浏览器甚至不会被编译 |
HTML注释,通过浏览器查看网页源代码时可以看见注释内容 |
三、EL表达式
3.1 获取作用域对象
<%
request.setAttribute("key1","value1");//当前jsp有效
session.setAttribute("key2","value2");
application.setAttribute("key3","value3");
%>
<h1>通过作用域对象获取:</h1>
<%--通过作用域对象获取数据--%> //EL和脚本的区别,就是EL返回空,脚本返回null
<%=request.getAttribute("key1")%>
<%=session.getAttribute("key2")%>
<%=application.getAttribute("key3")%>
<h1>通过EL表达式获取:</h1>
${requestScope.key1}
${sessionScope.key2}
${applicationScope.key3}
3.2 获取普通对象
之前useBean有创建User的实体类,EL表达式本质还是在调用实体类的get方法
<%
User user=new User("gavin","123456");
request.setAttribute("user",user);
%>
${user.username}
${user.password}
3.3 获取数组与集合对象
<%@ page import="java.util.*" %>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>EL操作数组,集合元素</title>
</head>
<body>
<%
int[] array=new int[]{1,2,3,4,5};
request.setAttribute("array",array);
List<String> nums=new ArrayList<>();
nums.add("A");
nums.add("B");
nums.add("C");
request.setAttribute("nums",nums);
Map<String,String> maps=new HashMap<>();
maps.put("CN","中国");
maps.put("US","美国");
maps.put("IT","意大利");
request.setAttribute("maps",maps);
%>
${array[0]}
${array[1]}
${array[2]}
${nums[0]}
${nums[1]}
${nums.get(2)} 通过get和直接中括号都可以
${maps["CN"]}
${maps.IT} 通过点的方式和直接中括号都可以
</body>
</html>
3.4 获取EL内置对象
${pageContext.request.contextPath} 获得应用上下文
${cookie.username.value}
${cookie.password.value} 如果写自动登录,可以直接获得cookie的username和password
四、JSTL标准标签库
4.1 准备工作
- 把standard.jar和jstl.jar拷贝到/WEB-INF/lib下
- 在JSP页面引入标签库<%@ taglib uri=“http://java.sun.com/jsp/jstl/core” prefix=“c” %>
4.2 if单条件判断
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<%
request.setAttribute("username","tom");
%>
<c:if test="${username eq 'tom'}">
<h1>欢迎你,${username} </h1>
</c:if>
<c:if test="${username ne 'tom'}">
<h1>请你重新登陆 </h1>
</c:if>
</body>
</html>
4.3 choose多条件判断
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<%
request.setAttribute("age",38);
%>
<c:choose>
<c:when test="${age<18}"> 少年 </c:when>
<c:when test="${age>=18&&age<30}"> 青年 </c:when>
<c:when test="${age>=30&&age<50}"> 中年 </c:when>
<c:otherwise>老年</c:otherwise>
</c:choose>
</body>
</html>
4.4 迭代foreach标签
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ page import="java.util.*" %>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<%
List<String> list=new ArrayList<>();
list.add("A");
list.add("B");
list.add("C");
list.add("D");
request.setAttribute("list",list);
%>
<c:forEach var="str" items="${list}" begin="0" end="3" step="1">
${str}
</c:forEach>
<%-- 以下为注释内容
相当于foreach循环遍历, begin="0" end="3" step="1" 起始下标,结束下标,间隔长度,这三个可省略
<%
for (String str : list){
out.println(str);
}
%>
--%>
</body>
</html>
4.5 重写URL
在cookie禁用的情况下,通过重写URL拼接session id来传递id值,便于下一次访问时仍可查找到上一次的Session对象,所有涉及到页面跳转或者重定向跳转,都应该使用URL重写
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<c:url context="${pageContext.request.contextPath}" value="/jsp/jstl.jsp"></c:url>
</body>
</html>