1. web相关概念回顾
2. web服务器软件:Tomcat
3. Servlet入门学习
软件架构
资源分类
3. 网络通信三要素
1. IP:电子设备(例如服务器)在网络中的唯一标识。
2. 端口:/*应用程序在计算机中的唯一标识,比如qq有一个端口,一般1024一下的端口被操作系统占用了*/0~65536
3. 传输协议:规定了数据传输的规则
1. 基础协议:
1. tcp:安全协议,因为经过了三次握手。 速度稍慢
2. udp:不安全协议。 速度快
* 服务器:安装了服务器软件的计算机,公司用的一般硬件较好
* 服务器软件:接收用户的请求,处理请求,做出响应
* web服务器软件(如tomcat):接收用户的请求,处理请求,做出响应。
* 在web服务器软件中,可以部署web项目,让用户通过浏览器来访问这些项目
* 也被称为web容器,动态资源只有在web容器中才能运行,容器之名的来由
* JavaEE:Java语言在企业级开发中使用的技术规范的总和,一共规定了13项大的规范
* Tomcat:web服务器软件
1. 下载:http://tomcat.apache.org/
2. 安装:解压压缩包即可。
* 注意:安装目录建议不要有中文和空格
3. 卸载:删除目录就行了
4. 启动:
* bin/startup.bat ,双击运行该文件即可
* 访问:浏览器输入:http://localhost:8080 回车访问自己,localhost其实是自己电脑的一个ip(127.0.0.1)的映射,
http://别人的ip:8080 访问别人
* 可能遇到的问题:
1. 黑窗口一闪而过:
* 原因: 没有正确配置JAVA_HOME环境变量
* 解决方案:正确配置JAVA_HOME环境变量
2. 启动报错:
1. 暴力:找到占用的端口号,并且找到对应的进程,杀死该进程
* netstat -ano
2. 温柔:修改自身的端口号
* conf/server.xml
*
* 一般会将tomcat的默认端口号修改为80。80端口号是http协议的默认端口号。
* 好处:在访问时,就不用输入端口号
5. 关闭:
1. 正常关闭:
* bin/shutdown.bat
* ctrl+c
2. 强制关闭:
* 点击启动窗口的×
6. 配置:
* 部署项目的方式:
1. 直接将项目放到webapps目录下即可。
* /hello/hello.html:项目的访问路径-->虚拟目录(虚拟目录是什么详情见2.)
* 简化部署:将项目打成一个war包(zip改名),再将war包放置到webapps目录下。
* war包会自动解压缩
2. 配置conf/server.xml文件(很不安全的做法!)
在标签体中配置
* docBase:项目存放的路径
* path:虚拟目录,即将项目的路径映射到浏览器上的路径
3. 在conf\Catalina\localhost创建任意名称的xml文件。在文件中编写(best,因为2.还要重启服务器,这是最好的,取消部署只要改文件名不要改配置文件,又称热部署)
* 虚拟目录:xml文件的名称,刚才自己命名的
* 注意path删掉了
* 静态项目和动态项目:
* 目录结构
* java动态项目的目录结构:
-- 项目的根目录
-- WEB-INF目录:
-- web.xml:web项目的核心配置文件
-- classes目录:放置字节码文件的目录
-- lib目录:放置依赖的jar包
将Tomcat集成到IDEA中,并且创建JavaEE的项目,部署项目。
实操:记得虚拟目录在debug/run configuartion里面的deployment里面
下面是tomcat目录一览
* 概念:运行在服务器端的小程序
* Servlet就是一个接口,定义了Java类被浏览器访问到(tomcat识别和运行)的规则。
* 将来我们自定义一个类,实现Servlet接口,复写方法。
快速入门:
1. 创建JavaEE项目
2. 定义一个类,实现Servlet接口
* public class ServletDemo1 implements Servlet
3. 实现接口中的抽象方法
4. 配置Servlet
在web.xml中配置:
demo1
cn.itcast.web.servlet.ServletDemo1
demo1
/demo1
当服务器接受到客户端浏览器的请求后,会解析请求URL路径,获取访问的Servlet的资源路径,即获得虚拟目录的路径。从设定的虚拟目录的根开始
查找web.xml文件,是否有对应的标签体内容。如果有看在同一个父元素的servlet name是什么。
如果有servlet name,则在找到对应的全类名
tomcat会将字节码文件加载进内存,并且创建其对象(反射机制)
调用其方法,所以控制台输出hello
ps:全类名(即文件在项目中的位置,注意从在idea中从src开始)–>反射机制。
1.**被创建**:执行init方法,只执行一次
Servlet什么时候被创建?
* 默认情况下(可以配置执行Servlet的创建的时机),第一次被访问时,Servlet被创建
* 在标签下配置
1.第一次被访问时,创建
的值为负数
2. 在服务器启动时,创建
的值为0或正整数
Servlet的init方法,只执行一次,说明一个Servlet在内存中只存在一个对象,Servlet是单例的
解决:尽量不要在Servlet中定义成员变量。即使定义了成员变量,也不要对修改值
IDEA会为每一个tomcat部署的项目单独建立一份配置文件,在c盘,部署方法是之前讲过的第二种,指向项目存放的路径。
查看控制台的log:Using CATALINA_BASE:“C:\Users\fqy.IntelliJIdea2018.1\system\tomcat_itcast”
工作空间项目 和 tomcat部署的web项目
1. Servlet
2. HTTP协议
3. Request
GenericServlet:将Servlet接口中其他的方法做了默认空实现,只将service()方法作为抽象
将来定义Servlet类时,可以继承GenericServlet,实现service()方法即可
定义类继承HttpServlet
复写doGet/doPost方法
概念:Hyper Text Transfer Protocol 超文本传输协议
传输协议:定义了客户端和服务器端通信时,发送数据的格式
特点:
请求消息数据格式
请求行
请求方式 请求url 请求协议/版本
GET /login.html HTTP/1.1
请求头:客户端浏览器告诉服务器一些信息
请求头名称: 请求头值(键:值)
可以在服务器端获取该头的信息,解决浏览器的兼容性问题
Referer:http://localhost/login.html
告诉服务器,我(当前请求)从哪里来?(一般在servlet类里面获得它,注意如果直接通过路径访问这个类的referer是null,必须要跳转到某个servlet类中获得跳转前路径)
防盗链:
统计工作:
请求空行
就是空行,用于分割POST请求的请求头,和请求体的。
请求体(正文):
请求消息的字符串格式:
POST /login.html HTTP/1.1(请求行)
Host: localhost
User-Agent: Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:60.0) Gecko/20100101 Firefox/60.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,/;q=0.8
Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2
Accept-Encoding: gzip, deflate
Referer: http://localhost/login.html
Connection: keep-alive
Upgrade-Insecure-Requests: 1(请求头,客户端给客户端的信息)
(请求空行)
username=zhangsan(请求参数)
响应消息数据格式
request对象和response对象的原理
request对象继承体系结构:
ServletRequest – 接口
| 继承
HttpServletRequest – 接口(还记得httpservlet接口吗对应这个)
| 实现
org.apache.catalina.connector.RequestFacade 类(tomcat创建的对象,在tomcat源码中可以找到这个类)
request功能:
获取请求行数据
获取请求方式 :GET
(*****)获取虚拟目录:/day14
获取Servlet路径(区别一下虚拟目录,这个加虚拟目录=URI): /demo1
获取get方式请求参数:name=zhangsan
(*****)获取请求URI:/day14/demo1
String getRequestURI(): /day14/demo1
StringBuffer getRequestURL() :http://localhost/day14/demo1,URL更加长
URL:统一资源定位符 : http://localhost/day14/demo1
URI:统一资源标识符 : /day14/demo1 和URL关系=共和国和中华人民共和国的关系
获取协议及版本:HTTP/1.1
获取客户机的IP地址:
获取请求头数据
获取请求体数据:
请求体:只有POST请求方式,才有请求体,在请求体中封装了POST请求的请求参数。参数被封装成流的格式
步骤:
BufferedReader getReader():获取字符输入流,只能操作字符数据
ServletInputStream getInputStream():获取字节输入流,可以操作所有类型数据
2.再从流对象中拿数据。带高级readline方法的缓冲流用readline
其他功能:
请求转发:一种在服务器内部的资源跳转方式
步骤:
特点:
共享数据:
获取ServletContext:
用户登录案例需求:
1.编写login.html登录页面
username & password 两个输入框
2.使用Druid数据库连接池技术,操作mysql,day14数据库中user表
3.使用JdbcTemplate技术封装JDBC
4.登录成功跳转到SuccessServlet展示:登录成功!用户名,欢迎您
5.登录失败跳转到FailServlet展示:登录失败,用户名或密码错误
分析
开发步骤
创建项目,导入html页面,配置文件,jar包
创建数据库环境
CREATE DATABASE day14;
USE day14;
CREATE TABLE USER(
id INT PRIMARY KEY AUTO_INCREMENT,
username VARCHAR(32) UNIQUE NOT NULL,
PASSWORD VARCHAR(32) NOT NULL
);
创建包cn.itcast.domain,创建类User
package cn.itcast.domain;
/**
用户的实体类
*/
public class User {
private int id;
private String username;
private String password;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
@Override
public String toString() {
return “User{” +
“id=” + id +
“, username=’” + username + ‘’’ +
“, password=’” + password + ‘’’ +
‘}’;
}
}
创建包cn.itcast.util,编写工具类JDBCUtils
package cn.itcast.util;
import com.alibaba.druid.pool.DruidDataSourceFactory;
import javax.sql.DataSource;
import javax.xml.crypto.Data;
import java.io.IOException;
import java.io.InputStream;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.Properties;
/**
JDBC工具类 使用Durid连接池
*/
public class JDBCUtils {
private static DataSource ds ;
static {
try {
//1.加载配置文件
Properties pro = new Properties();
//使用ClassLoader加载配置文件,获取字节输入流
InputStream is = JDBCUtils.class.getClassLoader().getResourceAsStream("druid.properties");
pro.load(is);
//2.初始化连接池对象
ds = DruidDataSourceFactory.createDataSource(pro);
} catch (IOException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
}
/**
/**
创建包cn.itcast.dao,创建类UserDao,提供login方法
package cn.itcast.dao;
import cn.itcast.domain.User;
import cn.itcast.util.JDBCUtils;
import org.springframework.dao.DataAccessException;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.JdbcTemplate;
/**
操作数据库中User表的类
*/
public class UserDao {
//声明JDBCTemplate对象共用
private JdbcTemplate template = new JdbcTemplate(JDBCUtils.getDataSource());
/**
登录方法
@param loginUser 只有用户名和密码
@return user包含用户全部数据,没有查询到,返回null
*/
public User login(User loginUser){
try {
//1.编写sql
String sql = “select * from user where username = ? and password = ?”;
//2.调用query方法
User user = template.queryForObject(sql,
new BeanPropertyRowMapper(User.class),
loginUser.getUsername(), loginUser.getPassword());
return user;
} catch (DataAccessException e) {
e.printStackTrace();//记录日志
return null;
}
}
}
编写cn.itcast.web.servlet.LoginServlet类
package cn.itcast.web.servlet;
import cn.itcast.dao.UserDao;
import cn.itcast.domain.User;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
@WebServlet("/loginServlet")
public class LoginServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//1.设置编码
req.setCharacterEncoding(“utf-8”);
//2.获取请求参数
String username = req.getParameter(“username”);
String password = req.getParameter(“password”);
//3.封装user对象
User loginUser = new User();
loginUser.setUsername(username);
loginUser.setPassword(password);
//4.调用UserDao的login方法
UserDao dao = new UserDao();
User user = dao.login(loginUser);
//5.判断user
if(user == null){
//登录失败
req.getRequestDispatcher("/failServlet").forward(req,resp);
}else{
//登录成功
//存储数据
req.setAttribute(“user”,user);
//转发
req.getRequestDispatcher("/successServlet").forward(req,resp);
}
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
this.doGet(req,resp);
}
}
7. 编写FailServlet和SuccessServlet类
@WebServlet("/successServlet")
public class SuccessServlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//获取request域中共享的user对象
User user = (User) request.getAttribute("user");
if(user != null){
//给页面写一句话
//设置编码
response.setContentType("text/html;charset=utf-8");
//输出
response.getWriter().write("登录成功!"+user.getUsername()+",欢迎您");
}
}
@WebServlet("/failServlet")
public class FailServlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//给页面写一句话
//设置编码
response.setContentType("text/html;charset=utf-8");
//输出
response.getWriter().write("登录失败,用户名或密码错误");
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.doPost(request,response);
}
}
JavaBean:标准的Java类
概念:区分属性和成员变量
成员变量:
属性:是setter和getter方法名字截取后并将首字母小写后的产物
例如:getUsername() --> Username–> username
可见成员变量名不等于属性名,虽然我们总是让两者相等,不相等会有点问题,详情可见login_page代码里面的一个测试代码
方法(三个方法都是操作属性,属性进而影响成员变量的值):
setProperty()为属性赋值
getProperty()得到属性的值
populate(Object obj , Map map):将map集合的键值对信息,封装到对应的JavaBean对象的属性中。由上面红字可知,我们需要保证 成员变量名属性名map的key==页面传来的参数名
ps:今天下午打开别人的代码打不开,但是自建工程后再复制进来就好了,推测是配置问题,所以把自建工程artifacts的配置截个图
1. HTTP协议:响应消息
2. Response对象
3. ServletContext对象
1. 请求消息:客户端发送给服务器端的数据
* 数据格式:
1. 请求行
2. 请求头
3. 请求空行
4. 请求体
2. 响应消息:服务器端发送给客户端的数据,类似相应消息
* 数据格式:
1. 响应行
1. 组成:协议/版本 响应状态码 状态码描述
2. 响应状态码:服务器告诉客户端浏览器本次请求和响应的一个状态。
1. 状态码都是3位数字
2. 分类:
1. 1xx:服务器就收客户端消息,但没有接受完成,等待一段时间后,发送1xx状态码询问客户端还有消息没
2. 2xx:成功。例如:200
3. 3xx:重定向。例如:302(重定向),304(访问缓存)
4. 4xx:客户端错误。
* 例如:
* 404(请求路径没有对应的资源)
* 405:请求方式没有对应的doXxx方法
5. 5xx:服务器端错误。例如:500(服务器内部出现异常)
2. 响应头:
1. 格式:头名称: 值
2. 常见的响应头:
1. Content-Type(html代码里面也有这个属性):服务器告诉客户端本次响应体(例如HTML)数据格式以及编码格式
2. Content-disposition:服务器告诉客户端以什么格式打开响应体数据
* 值:
* in-line:默认值,在当前页面内打开
* attachment;filename=xxx:以文件形式打开响应体。应用于文件下载,此时肯定响应体不是html代码
3. 响应空行
4. 响应体:传输的数据(例如HTML代码但不止于,还有图片等)
* 响应字符串格式
HTTP/1.1 200 OK
Content-Type: text/html;charset=UTF-8
Content-Length: 101
Date: Wed, 06 Jun 2018 07:08:42 GMT
$Title$
hello , response
* 功能:设置响应消息
1. 设置响应行
1. 格式:HTTP/1.1 200 ok
2. 设置状态码:setStatus(int sc)
2. 设置响应头:setHeader(String name, String value)
3. 设置响应体:
* 使用步骤:
1. 获取输出流
* 字符输出流:PrintWriter getWriter()
* 字节输出流:ServletOutputStream getOutputStream()
2. 使用输出流,将数据输出到客户端浏览器
案例:
重定向:资源跳转的方式
代码实现:
//1. 设置状态码为302
response.setStatus(302);
//2.设置响应头location
response.setHeader(“location”,"/day15/responseDemo2");
图示如下
//很明显其实只有路径可以变,所以提供了简单的重定向方法
response.sendRedirect("/day15/responseDemo2");
重定向的特点(和转发完全相反):redirect
地址栏发生变化
重定向可以访问其他站点(服务器)的资源
重定向是两次请求。不能使用request对象来共享数据
转发的特点:forward
路径分类
不以/开头,以.开头路径
* 规则:找到当前资源和目标资源之间的相对位置关系
* ./:当前目录(./是可以省略的)
* ../:后退一级目录
绝对路径:通过绝对路径可以确定唯一资源
以/开头的路径,和相对相反,
* 规则:判断定义的路径是给谁用的?
**给客户端浏览器使用:需要加虚拟目录**(项目的访问路径,很正常,因为这些都可以**跳转到服务器外,当然需要虚拟目录**)
建议虚拟目录**动态获取**(防止改变虚拟目录后的影响):写成如下字符串
request.getContextPath()+“Servlet的路径”
**例如前端的标签(这个路径给客户端来跳转),重定向(这个路径给客户端来去找新的定向)**..
**给服务器使用:不需要加虚拟目录**
**例如转发路径**
服务器输出字符数据到浏览器(案例)
步骤:
注意:
乱码问题的唯一原因:编码集不等于解码集
PrintWriter pw = response.getWriter();获取的流的默认编码是ISO-8859-1,因为不是中国人写的tomcat
怎么解决?灵活的方法:
response.setHeader(“content-type”,“text/html; charset=utf-8”)
即通过设置响应头里面的键值对来告诉浏览器用什么编码来解码
//上面方法的简化,省略第一个不变的参数,设置编码,是在获取流之前设置
response.setContentType(“text/html;charset=utf-8”);
服务器输出字节数据到浏览器(案例)
* 步骤:
1. 获取字节输出流(同上理,记得设置编码)
2. 输出数据
本质:图片
目的:防止恶意表单注册
域对象:共享数据,类似response对象
removeAttribute(String name)
获取文件的真实(服务器)路径(通过idea启动时候的CATALINA_BASE找到配置文件,通过配置文件找到真实部署在tomcat的项目的位置,观察该项目位置得到下面的绝对路径,不要看工作空间的,这点可参考tomcat第一次笔记也有记载)
方法:String getRealPath(String path) (很重要的方法,转工作空间路径为真实路径,context对象由ServletContext对象获得)
String b = context.getRealPath("/b.txt");//web目录下资源访问
String c = context.getRealPath("/WEB-INF/c.txt");//WEB-INF目录下的资源访问
String a = context.getRealPath("/WEB-INF/classes/a.txt");//src目录下的资源访问
* 文件下载需求:
1. 页面显示超链接
2. 点击超链接后弹出下载提示框
3. 完成图片文件下载
分析:
步骤:
1. 会话技术
1. Cookie
2. Session
2. JSP:入门学习
概念:客户端会话技术,将数据保存到客户端。为什么说是保存在客户端,因为换一个浏览器就访问不到了
快速入门:
实现原理
cookie的细节
默认情况下cookie不能共享
cookie.setPath(String path):设置cookie的获取范围。默认情况下,设置当前的虚拟目录,如果要共享,则可以将path设置为"/"
2.不同的tomcat服务器间cookie共享问题?
setDomain(String path):如果设置一级域名相同,那么多个服务器之间cookie可以共享,path:**可以共享的项目的一级域名**
setDomain(".baidu.com"),那么tieba.baidu.com和news.baidu.com中cookie可以共享
Cookie的特点和作用
案例:记住上一次访问时间
需求:
分析:
有:不是第一次访问
没有:是第一次访问
注意c ookie.setValue
3.代码实现:
package cn.itcast.cookie;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.net.URLDecoder;
import java.net.URLEncoder;
import java.text.SimpleDateFormat;
import java.util.Date;
@WebServlet("/cookieTest")
public class CookieTest extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//设置响应的消息体的数据格式以及编码
response.setContentType("text/html;charset=utf-8");
//1.获取所有Cookie
Cookie[] cookies = request.getCookies();
boolean flag = false;//没有cookie为lastTime
//2.遍历cookie数组
if(cookies != null && cookies.length > 0){
for (Cookie cookie : cookies) {
//3.获取cookie的名称
String name = cookie.getName();
//4.判断名称是否是:lastTime
if("lastTime".equals(name)){
//有该Cookie,不是第一次访问
flag = true;//有lastTime的cookie
//设置Cookie的value
//获取当前时间的字符串,重新设置Cookie的值,重新发送cookie
Date date = new Date();
SimpleDateFormat sdf = new SimpleDateFormat("yyyy年MM月dd日 HH:mm:ss");
String str_date = sdf.format(date);
System.out.println("编码前:"+str_date);
//URL编码
str_date = URLEncoder.encode(str_date,"utf-8");
System.out.println("编码后:"+str_date);
cookie.setValue(str_date);
//设置cookie的存活时间
cookie.setMaxAge(60 * 60 * 24 * 30);//一个月
response.addCookie(cookie);
//响应数据
//获取Cookie的value,时间
String value = cookie.getValue();
System.out.println(“解码前:”+value);
//URL解码:
value = URLDecoder.decode(value,“utf-8”);
System.out.println(“解码后:”+value);
response.getWriter().write(“
if(cookies == null || cookies.length == 0 || flag == false){
//没有,第一次访问
//设置Cookie的value
//获取当前时间的字符串,重新设置Cookie的值,重新发送cookie
Date date = new Date();
SimpleDateFormat sdf = new SimpleDateFormat(“yyyy年MM月dd日 HH:mm:ss”);
String str_date = sdf.format(date);
System.out.println(“编码前:”+str_date);
//URL编码
str_date = URLEncoder.encode(str_date,“utf-8”);
System.out.println(“编码后:”+str_date);
Cookie cookie = new Cookie(“lastTime”,str_date);
//设置cookie的存活时间
cookie.setMaxAge(60 * 60 * 24 * 30);//一个月
response.addCookie(cookie);
response.getWriter().write("您好,欢迎您首次访问
");
}
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.doPost(request, response);
}
}
1. 概念:
* Java Server Pages: java服务器端页面
* 可以理解为:一个特殊的页面,其中既可以指定定义html标签,又可以定义java代码
* 用于简化Servlet的response.write方法的书写!!!,不然我想响应一个页面,那个参数岂不是有几百行啊
原理
JSP的脚本:JSP定义Java代码的方式
<% 代码 %>:定义的java代码,在service方法中。service方法中可以写什么,该脚本中就可以写什么。
<%! 代码 %>:定义的java代码,在jsp转换后的Servlet类的成员位置。
<%= 代码 %>:定义的java代码,会输出到页面上。输出语句中可以定义什么,该脚本中就可以定义什么。
JSP的内置对象:
案例:改造Cookie案例
1. 概念:服务器端会话技术,在一次会话的多次请求间共享数据,将数据保存在服务器端的对象中。HttpSession
2. 快速入门:
1. 获取HttpSession对象:
HttpSession session = request.getSession();
2. 使用HttpSession对象:
Object getAttribute(String name)
void setAttribute(String name, Object value)
void removeAttribute(String name)
3. 原理
* Session的实现是依赖于Cookie的。
4. 细节:
1. 当客户端关闭后,服务器不关闭,两次获取session是否为同一个?
* 默认情况下。不是。
* 如果需要相同,则可以创建Cookie,键为JSESSIONID,设置最大存活时间,让cookie持久化保存。
Cookie c = new Cookie(“JSESSIONID”,session.getId());
c.setMaxAge(60*60);
response.addCookie©;
2. 客户端不关闭,服务器关闭后,两次获取的session是同一个吗?
* 不是同一个,但是要确保数据不丢失。tomcat自动完成以下工作
* session的钝化:
* 在服务器正常关闭之前,将session对象系列化到硬盘上
* session的活化:
* 在服务器启动后,将session文件转化为内存中的session对象即可。
3. session什么时候被销毁?
1. 服务器关闭
2. session对象调用invalidate() 。
3. session默认失效时间 30分钟
选择性配置修改
30
5. session的特点
1. session用于存储一次会话的多次请求的数据,存在服务器端
2. session可以存储任意类型,任意大小的数据
* session与Cookie的区别:
1. session存储数据在服务器端,Cookie在客户端
2. session没有数据大小限制,Cookie有
3. session数据安全,Cookie相对于不安全
1. 案例需求:
1. 访问带有验证码的登录页面login.jsp
2. 用户输入用户名,密码以及验证码。
* 如果用户名和密码输入有误,跳转登录页面,提示:用户名或密码错误
* 如果验证码输入有误,跳转登录页面,提示:验证码错误
* 如果全部输入正确,则跳转到主页success.jsp,显示:用户名,欢迎您
2. 分析:
1. JSP:
1. 指令
2. 注释
3. 内置对象
2. MVC开发模式
3. EL表达式
4. JSTL标签
5. 三层架构
指令
page: 配置JSP页面的
include :导入别的页面,比如导航之类的
<%@include file=“top.jsp”%>
3.taglib : 导入外部资源,比例导入标签库,下面例子就是导入jstl标签库
<%@ taglib prefix=“c” uri=“http://java.sun.com/jsp/jstl/core” %>
注释:
内置对象
1. jsp演变历史
1. 早期只有servlet,只能使用response输出标签数据,非常麻烦
2. 后来又jsp,简化了Servlet的开发,如果过度使用jsp,在jsp中即写大量的java代码,有写html表,造成难于维护,难于分工协作
3. 再后来,java的web开发,借鉴mvc开发模式,使得程序的设计更加合理性
2. MVC:
1. M:Model,模型。JavaBean
* 完成具体的业务操作,如:查询数据库,封装对象
2. V:View,视图。JSP
* 展示数据
3. C:Controller,控制器。Servlet
* 获取用户的输入
* 调用模型
* 将数据交给视图进行展示
* 优缺点:
1. 优点:
1. 耦合性低,方便维护,可以利于分工协作
2. 重用性高
2. 缺点:
1. 使得项目架构变得复杂,对开发人员要求高
概念:Expression Language 表达式语言
作用:替换和简化jsp页面中java代码的编写
语法:${表达式}
注意:
使用:
运算:
获取值
${域名称.键名}:从指定域中获取指定键的值
${键名}:表示依次从最小的域中查找是否有该键对应的值,直到找到为止。
获取对象、List集合、Map集合的值
1. 对象:${域名称.键名.属性名}
* 本质上会去调用对象的getter方法,并不是.操作符获得成员噢,只是老是成员名=属性名易产生误解!即操作属性=调用对应get/set方法=对相应的成员进行操作(看具体方法的实现,属性可不=成员名的)的get/set方法
*当某个方法不对应一个成员变量,只是为了在页面上提供一个好看的格式,这种方法我们称为逻辑视图
public String getBirStr(){
if(birthday != null){
//1.格式化日期对象
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
//2.返回字符串即可
return sdf.format(birthday);
}else{
return "";
}
}
2.List集合:**${域名称.键名[索引]}**
3.Map集合:
**${域名称.键名.key名称}**
**${域名称.键名["key名称"]}**
3.隐式对象:
el表达式中有11个隐式对象
pageContext:获取jsp其他八个内置对象
${pageContext.request.contextPath}:动态获取虚拟目录(request是pageContext的属性,contextPath是request的属性,我们看api可见有getRequest方法在pageContext对象中)
作用:用于简化和替换jsp页面上的java代码
使用步骤:
常用的JSTL标签
if:相当于java代码的if语句
属性:
注意:
* c:if标签**没有else情况**,想要else情况,则可以在定义一个c:if标签
choose:相当于java代码的switch语句
使用choose标签声明 相当于switch声明
使用otherwise标签做其他情况的声明 相当于default
foreach:相当于java代码的for语句
```java
1. 完成重复的操作
for(int i = 0; i < 10; i ++){
}
* 属性:
begin:开始值
end:结束值
var:临时变量
step:步长
varStatus:循环状态对象
index:容器中元素的索引,从0开始
count:循环次数,从1开始
${i} ${s.index} ${s.count}
2. 遍历容器
List list;
for(User user : list){
}
* 属性:
items:容器对象
var:容器中元素的临时变量
varStatus:循环状态对象
index:容器中元素的索引,从0开始
count:循环次数,从1开始
<%
List list = new ArrayList();
list.add("aaa");
list.add("bbb");
list.add("ccc");
request.setAttribute("list",list);
%>
${s.index} ${s.count} ${str}
练习:
1. 界面层(表示层):用户看的得界面。用户可以通过界面上的组件和服务器进行交互
2. 业务逻辑层:处理业务逻辑的。
3. 数据访问层:操作数据存储文件。
1.我们之前都没写什么service层,为什么我们要加上这一层呢?看图可知是为了复用dao层的基本CRUD操作,对于这些基础操作组合起来的复杂的业务逻辑,我们在service层完成
2.既然都讲了service层,就顺带讲一下DAO层,全名 data access object。
通常我们会遇到很多要和数据库打交道的场景,如果为每一个场景都去写一些SQL语句,会让我们代码变得很奇怪,我们希望我们的代码比较干净整洁,那么一个很容易想到的方案就是把数据库封装一下,让我们和数据库的交道看起来比较像和一个对象打交道。这个对象通常就是DAO,当我们操作这个对象的时候,这个对象会自动的产生SQL语句来和数据库打交道,而我们只需要和DAO打交道就可以了。
简单讲,DAO就是把数据持久化包装成一个对象的访问(读写)
3.三层架构命名:web包:表示层,service包:业务逻辑层,dao包:数据访问层。方便看别人代码
1. 需求:用户信息的增删改查操作
2. 设计:
1. 技术选型:Servlet+JSP+MySQL+JDBCTempleat+Duird+BeanUtilS+tomcat
2. 数据库设计:
create database day17; -- 创建数据库
use day17; -- 使用数据库
create table user( -- 创建表
id int primary key auto_increment,
name varchar(20) not null,
gender varchar(5),
age int,
address varchar(32),
qq varchar(20),
email varchar(50)
);
3. 开发:
1. 环境搭建
1. 创建数据库环境
2. 创建项目,导入需要的jar包
2. 编码
4. 测试
5. 部署运维
分析:
重定向/转发适用场景:
1.由于重定向和原请求是两个独立的过程,所以重定向后就不能获得原页面的值或者是跳转之前要对请求进行的一些预处理了(session在多次请求间共享数据共享数据),而转发就没有这个问题,因为request和response都顺带带过去了。这里适用转发没啥问题
2.个人认为这个过程直觉上也应该被认为是一次请求,恰符合转发特点,而重定向不同。
ps:附上idea上连接数据库,好像有这个只要创建好了表和数据,关了数据库管理工具也没事!
同时注意配置文件和jdbc工具类可以每次复用,其中配置文件要改一下database名噢!
1. 综合练习
1. 简单功能
1. 列表查询
2. 登录
3. 添加
4. 删除
5. 修改
2. 复杂功能
1. 删除选中
2. 分页查询
* 好处:
1. 减轻服务器内存的开销
2. 提升用户体验
3. 复杂条件查询
1. 调整页面,加入验证码功能
2. 代码实现
这种复杂功能该怎么构造思路?
!从输入和输出入手!,输出即想一下服务器要给客户端输出什么东西才能让客户端展现出需求所要求的样子,这里就是想DAO有那些成员变量。输入即客户端要给服务器什么参数才能让服务器能输出东西给客户端展示,再想一下这些参数怎么传递.
1.复杂条件查询也用分页查询的servlet,
想一想,不就是多几个参数(条件)的问题嘛,
其他差不多,so后续其实是在原来分页查询的方法为基础改一下即可,即不带条件的查询只是正常查询的一种特殊情况,不当成两个情况增加代码复用性。
2.StringBuilder类
可看作可变的字符串。
还记得正常String的实体是不可改变的嘛,方法都是生成了新的字符串,这个缓冲类则是可以改变的。
常用方法:append,toString,构造方法传入string
3.有没有值时我们总是判断两种情况,
因为不同方法返回各不相同有的是null,有的是空字符串
就像我们判断list一样总是判断 null或者长度为0一样,懒得去看方法返回值了
currentPage == null || “”.equals(currentPage)
1. Filter:过滤器
2. Listener:监听器
web三大组件:servlet Filter Listener
概念:
生活中的过滤器:净水器,空气净化器,土匪、
web中的过滤器:当访问服务器的资源时,过滤器可以将请求拦截下来,完成一些特殊的功能。与资源息息相关
过滤器的作用:
一般用于完成通用的操作(即对于一些资源都要做的操作)。如:登录验证、统一编码处理、敏感字符过滤…
快速入门:
步骤:
代码:
@WebFilter("/*")//访问所有资源之前,都会执行该过滤器
public class FilterDemo1 implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
System.out.println("filterDemo1被执行了....");
//放行
filterChain.doFilter(servletRequest,servletResponse);
}
@Override
public void destroy() {
}
}
过滤器细节:
web.xml配置 (和servlet很类似)
demo1
cn.itcast.web.filter.FilterDemo1
demo1
/*
过滤器执行流程(即过滤器对于请求和响应都可以做数据增强)
过滤器生命周期方法
过滤器配置详解
过滤器链(配置多个过滤器)
执行顺序(看土匪原理图就知道了):如果有两个过滤器:过滤器1和过滤器2
过滤器先后顺序问题(即为啥不是先过滤器2再过滤器1的问题?):
案例:
案例2_敏感词汇过滤
需求:
分析:
怎么增强对象的功能?:
设计模式:一些通用的解决固定问题的方式,下面两种设计模式都能增强对象的功能
* 概念:web的三大组件之一。
* 事件监听机制
* 事件 :一件事情
* 事件源 :事件发生的地方
* 监听器 :一个对象
* 注册监听:将事件、事件源、监听器绑定在一起。 当事件源上发生某个事件后,执行监听器代码
ServletContextListener:监听ServletContext对象的创建和销毁,回忆一下之前讲过ServletContext对象,其是一个范围最大的域对象,范围为整个服务器,生命周期自然也=服务器的生命周期,且tomcat自动创建。
作用之一:获取供整个服务器使用的资源
指定监听器类的全类名如下
cn.itcast.web.listener.ContextLoaderListener
指定servletContext的初始化参数如下
2.注解:
@WebListener即可标注这是一个监听器类