1. web开发概述
web开发,将开发的程序部署在服务器中,通过浏览器访问(http请求),在java后端接收、处理、响应。
服务器:
狭义上:指一种软件,里面可以存放文件,别人可以访问,服务器可以做出回应。
广义上:硬件(云服务机房) + 软件
总之:服务器是一个容器(部署项目),是连接用户与程序之间的中间件。
2. web开发环境搭建
第一步:获取Tomcat安装程序包
第二步:(1)设置JAVA_HOME环境变量(D:\Program Files\Java\jdk1.8.0_261)
(2) 设置PATH环境变量(配置JDK)
第三步:解压tomcat压缩文件
第四步:双击 bin 目录下的 startup.bat 文件
输入 http://localhost:端口号 或 http:127.0.0.1:端口号 或 局域网ip:端口号
3. 创建发布web项目
1. 创建java项目,添加web功能
2. 发布web应用到服务器
3. 在WEB-INF中创建 lib 文件,并将要用的jar包放在lib文件中。
注:自动将web应用发布到web服务器下,需要在开发工具中集成tomcat进
4. Servlet概述
Servlet是Server Applet的简称,意思为用Java编写的服务器端的程 序.
它运行在web服务器中,web服务器负责Servlet和客户的通信以及调 用Servlet方法
server == 用java写的服务器端程序。
Servlet的作用:
- 接收客户端的请求
- 调用其他程序处理请求(dao,JDBC等)
- 根据处理的结果向客户端做出相应。
5. Servlet创建和概述
web.xml 是 web 项目中的配置文件。在服务器启动时,有服务器加载启动。
1、创建一个类继承javax.servlet.http包下的HttpServlet
继承HttpServlet ,我们的类,就具备 servlet 功能(接收、处理、响应)
(1)无参的构造方法:服务器在创建Servlet 对象,调用构造方法,只调用了一次。
public DemoServlet() {
System.out.println("无参的构造方法");
}
(2)init()方法(初始化):初始化Servlet,ServletConfig config: 用来获取Servlet中的一些参数信息。如果没有什么可以初始化的内容,此方法可以不用·重写,但是服务器会调用,只会调用父类中的Servlet的init()只调用一次;
@Override
public void init(ServletConfig config) throws ServletException {
System.out.println("init");
}
(3)service() 方法:提供服务。HttpServletRequest : 表示请求,客户端请求的数据,都分装在此对象中。 HttpServletResponse:表示响应,用来为客户端响应数据。(只多次调用)
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("service");
}
(4)destroy()方法:在servlet对象销毁前,会调用destroy(),一般是在关闭服务器时,在此方法中,可以执行一些最终想要的操作,例如保存日志...。如果没有,也可以不用重写,但是服务器还会调用,可以调用父类中的。(只调用一次)
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("service");
}
总体代码:
- 构造方法 默认存在
- init() 父类有
- service() 父类有, 判断请求方式,调用doGet或doPost
- destroy() 父类有
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
// 继承 HttpServlet ,我们的类就具有了 servlet 功能(接收、处理、响应)
public class DemoServlet extends HttpServlet {
/*
服务器在创建Servlet对象时,调用构造方法。
在第一次访问 Servlet 时创建 Servlet 对象,调用构造方法,只调用了一次
*/
public DemoServlet() {
System.out.println("无参的构造方法");
}
/*
初始化 Servlet ,ServletConfig : 用来获得Servlet中的一些参数信息
如果没有什么可以初始化的内容
*/
@Override
public void init(ServletConfig config) throws ServletException {
System.out.println("init");
}
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("service");
}
@Override
public void destroy() {
System.out.println("destroy");
}
}
2、在web.xml文件中配置Servlet
3、运行Servlet程序 通过浏览器访问http:localhost:8080/ 项目名/配置的url
4、客户端是通过URL地址访问web服务器中的资源,所以Servlet程序若想被 外界访问,必须把servlet程序映射到一个URL地址上,这个工作在web.xml文 件中使用元素和元素完成。
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
version="4.0">
<!-- 配置注册servlet-->
<servlet>
<!--生成的对象名,只有一个,只创建一个Servlet 对象-->
<servlet-name>demo</servlet-name>
<!--配置类的地址,由服务器创建Sevlet 对象,通过反射机制创建-->
<servlet-class>demoserver.DemoServlet</servlet-class>
<!-- 配置一个参数-->
<init-param>
<param-name>count</param-name>
<param-value>10</param-value>
</init-param>
<!-- 配置服务器启动时,就创建Servlet对象-->
<load-on-startup>0</load-on-startup>
</servlet>
<!-- 为servlet提供一个前端访问地址-->
<servlet-mapping>
<servlet-name>demo</servlet-name>
<!-- http://127.0.0.1:8080/backServer/demo -->
<url-pattern>/demo</url-pattern>
</servlet-mapping>
</web-app>
6. Servlet生命周期
- 实例化。构造方法:只调用一次,只有第一次访问Servelt时或服务器启动时 创建Servlet实例
- 初始化。Init方法:只被调用一次,在Servlet实例创建好实例后立即执行, 用于执行一些初始化Servlet的操作
- 服务。service方法:多次调用,每次请求都调用service方法,根据请求方式 调用doGet()或doPost()方法。
- 消亡。destroy:只被调用一次,在服务器关闭时调用,执行一些操作。
7. http协议,http请求
http协议
在网页中点一个http击超链接,提交表单都是向服务器端发送http请求。一个http 请求,包含三部分:
- 请求行
- 请求头:包括请求地址、方式、客户端信息。
- 请求体:百世post请求中的数据都是在请求体中。
http请求(get和post的区别)
get:主要用于从服务器端获取数据,传递少量的数据,获取大量数据,在地址中拼接携带的,一个是不安全的(相对于post),传递数据量小。
在servlet 中使用doget(),处理get请求。
post:主要用于向服务器发送数据,注册、登录、提交表单等用post,post数据在请求体中存储,相对安全,且传输的数据量大,上传文件都是post。
在servlet 中使用doPost(),处理post请求。
HttpServletRequest(接口) :对象表示请求,所用请求数据都包装在此对象中。是由某个子类实现的,org.apache.catalina.connector.RequestFacade@255a0e9b
model层
package model;
public class User {
int id;
String account;
String password;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getAccount() {
return account;
}
public void setAccount(String account) {
this.account = account;
}
public void setPassword(String password) {
this.password = password;
}
public String getPassword() {
return password;
}
@Override
public String toString() {
return "User{" +
"id=" + id +
", account='" + account + '\'' +
", password='" + password + '\'' +
'}';
}
}
dao层(连接数据库 查询验证账号密码是否正确)
package dao;
import model.User;
import java.sql.*;
public class LoginDao {
public User checkLogin(String account, String password) throws ClassNotFoundException, SQLException {
Connection connection = null;
PreparedStatement ps = null;
ResultSet rs = null;
User user = null;
try {
Class.forName("com.mysql.cj.jdbc.Driver");
String url = "jdbc:mysql://127.0.0.1:3306/school?characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai";
connection = DriverManager.getConnection(url,"root", "123456");
ps = connection.prepareStatement("select id,account from user where account =? and password =? ");
ps.setObject(1, account);
ps.setObject(2, password);
rs = ps.executeQuery();
while(rs.next()){
user = new User();
user.setId(rs.getInt("id"));
user.setAccount(rs.getString("account"));
}
return user;
}finally {
if(ps!=null){
ps.close();
}
if (connection!=null){
connection.close();
}
}
}
}
servic层
import dao.LoginDao;
import model.User;
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;
public class LoginServlet extends HttpServlet {
/*
处理get请求
*/
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("doGet");
}
/*
处理post请求
*/
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//转码 post请求中文接收时解码格式不是utf-8,接收前设置解码格式
req.setCharacterEncoding("utf-8");
//接收表单请求数据
String account = req.getParameter("account");
String password = req.getParameter("password");
//调用dao 连接数据库 查询验证账号密码是否正确
LoginDao loginDao = new LoginDao();
resp.setContentType("text/html;charset=utf-8");
PrintWriter out = null;
try {
out = resp.getWriter();
User user = loginDao.checkLogin(account,password);
if(user!=null){
out.print(user);
}else{
out.print(user);
out.print("账号或密码错误");
}
} catch (Exception e) {
e.printStackTrace();
out.print("系统忙!");
}
}
}
前端
直接通过submit 提胶片表单到后端服务器,这种方式属于同步提交,服务器响应回来的内容会将整个网页覆盖,会影响前端页面中的正常操作。
在现在的前后端分离开发模式中,前端向后端发送请求,都是异步发送。
何为异步发送?
异步发送的本质是使用浏览器中提供的一个js对象,就是XMLHttpRequest,使用此对象异步向服务器发送请求,后端响应的内容会反馈给XMLHTTPRequest对象,再从js中把响应的内容更新到网页的某个标签上,整个过程页面不会被覆盖,不会刷新。
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title></title>
<script type="text/javascript">
function login(){
var httpobj = new XMLHttpRequest();
httpobj.open("POST","http://127.0.0.1:8080/backServer/login",true);
httpobj.send();
httpobj.onreadystatechange = function(){
document.getElementById("accountMsgId").innerHTML = httpobj.responseText;
}
}
</script>
</head>
<body>
<form>
账号<input type="text" name="account" /> <br/>
<span id="accountMsgId"></span> <br/>
密码<input type="password" name="password" /> <br/>
<input type="button" value="登录" onclick="login()"/>
</form>
</body>
</html>
- getMethod()得到客户机请求方式
- getScheme()请求协议
- getRemoteAddr()返回发出请求的客户机的IP地址
- getServerName()服务器名(ip或域名)
- getServerPort()服务器端口 http请求
- getParameter(name) --- String 通过name获得值
- getParameterValues --- String[ ] 通过name获得多值
- 处理中文乱码 post setCharacterEncoding(编码格式)
前端:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title></title>
</head>
<body>
<form action="http://127.0.0.1:8888/backServer/login" method="post">
账号<input type="text" name="account" /> <br/>
密码<input type="password" name="password" /> <br/>
<!-- 只提交选中的-->
性别<input type="radio" name="gender" value="男" />男 <br/>
<input type="radio" name="gender" value="女" />女 <br/>
省份<select name="province">
<option value="101">北京</option>
<option value="102">上海</option>
<option value="103">石家庄</option>
</select> <br/>
爱好<input type="checkbox" name="hobby" value="打游戏"/> 打游戏
<input type="checkbox" name="hobby" value="打球"/> 打球
<input type="checkbox" name="hobby" value="敲代码"/> 敲代码
<input type="submit" value="登录" />
</form>
</body>
</html>
后端(servlet)
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//转码 post请求中文接收时解码格式不是utf-8,接收前设置解码格式
req.setCharacterEncoding("utf-8");
//接收表单请求数据
String account = req.getParameter("account");
String password = req.getParameter("password");
String gender = req.getParameter("gender");
String province = req.getParameter("province");
String[] hobby = req.getParameterValues("hobby");//接收name相同的多个值
System.out.println(account+":"+password+":"+gender+":"+province+":"+ Arrays.toString(hobby));
//接收请求行,请求头中的数据
System.out.println(req.getMethod());
System.out.println(req.getScheme());
System.out.println(req.getProtocol());
System.out.println(req.getRemoteAddr());//获得客户机ip
System.out.println(req.getRemotePort());
System.out.println(req.getServerName());
System.out.println(req.getServerPort());
System.out.println(req.getContentLength());
System.out.println(req.getHeader("User-Agent"));
//调用dao 连接数据库 查询验证账号密码是否正确
LoginDao loginDao = new LoginDao();
//User user = loginDao.checkLogin(account,password);
resp.setContentType("text/html;charset=utf-8");
//根据处理的结果,向前端响应
resp.getWriter().print("<h1>登录成功</h2>");
}
8. http响应
- HttpServletResponse 是 ServletResponse 的子接口。
- Web 服务器收到客户端的http 请求,会针对每一次请求,分别创建一个代表响应的HttpServletResponse 对象。
- HttpServeltResponse 对象代表响应。
一个http响应代表服务器向客户端回送的数据,它包括:响应行,响应头,响应体。
响应行里包含了http协议版本,以及用于描述服务器对请求的处理结果。
状态码:服务器和浏览器用于确定状态的固定数字号码。
例:
- 200 :请求成功
- 302 :请求重定向
- 400 :语义有无,当前请求无法被服务器理解或请求参数有误
- 404 :请求资源不存在,通常是路径写错了或者服务器资源删除了
- 500 :服务内部错误(代码异常)
9. 过滤器
作用:对服务器Web资源进行拦截。
跨服务器访问:
前后端分离开发,前端一个服务器,后端一个服务器;在发送ajax 请求时,浏览器有一个安全限制(默认是遵守同源策略);只要请求协议,域名/ip,端口有一个不同,就是称为跨域访问。
解决方法:前端解决和后端解决。
Servlet API 中,与过滤器有关的API共有三个接口,分别是:
- Filter
- FilterChain
- FilterConfig
Filter接口是过滤器类必须实现的接口,该接口中有三个方法。
- init(FilterConfig filterConfig):该方法是对filter 对象进行初始化的方法,仅在容器初始化filter对象结束后被调用一次。参数FilterConfig 可以获得filter 的初始化的参数。
- doFilter(ServletRequest request,ServletResponse response,FilterChain chain): 该方法是filter进行过滤操作的方法,是最重要的方法。过滤器实现类必须实现该方法。方法体中可以对request和response进行预处理。其中FilterChain 可以将处理后的request =和 response 对象传递到过滤链上的下一个资源。
- destroy():该方法在容器销毁过滤器对象前被调用。
过滤器的作用:
- 过滤器用来实现通用的功能,减少代码冗余,提高可维护性
- 一个过滤器可以配置给多个资源使用
- 一个资源也可以配置多个过滤器,按照配置顺序调用
1. 跨域过滤器
public class CorsFilter implements Filter {
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain)
throws IOException, ServletException {
System.out.println("跨域过滤器");
HttpServletResponse httpResponse = (HttpServletResponse) servletResponse;
HttpServletRequest httpRequest = (HttpServletRequest) servletRequest;
//允许携带Cookie时不能设置为* 否则前端报错
httpResponse.setHeader("Access-Control-Allow-Origin", httpRequest.getHeader("origin"));//允许所有请求跨域
httpResponse.setHeader("Access-Control-Allow-Methods", "*");//允许跨域的请求方法GET, POST, HEAD 等
httpResponse.setHeader("Access-Control-Allow-Headers", "*");//允许跨域的请求头
httpResponse.setHeader("Access-Control-Allow-Credentials", "true");//是否携带cookie
filterChain.doFilter(servletRequest, servletResponse);
}
}
配置文件(web.xml)
<filter>
<filter-name>cors</filter-name>
<filter-class>filter.CorsFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>cors</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
2. 编码过滤器
public class EnCodingFilter implements Filter {
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
System.out.println("编码过滤器");
servletRequest.setCharacterEncoding("utf-8");
servletResponse.setContentType("text/html;charset=utf-8");
filterChain.doFilter(servletRequest,servletResponse);
}
}
略(与跨域过滤器配置文件)
3. 验证用户是否登录是否失效过滤器
public class IsLoginFilter implements Filter {
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
System.out.println("后端验证用户是否登录失效过滤器");
HttpServletRequest request = (HttpServletRequest) servletRequest;
HttpSession session = request.getSession();
User user = (User) session.getAttribute("user");
if(user == null){
servletResponse.getWriter().print(201);
}else{
filterChain.doFilter(servletRequest,servletResponse);
}
}
}
配置文件(web.xml)
<filter>
<filter-name>islogin</filter-name>
<filter-class>filter.IsLoginFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>islogin</filter-name>
<url-pattern>/back/*</url-pattern>
</filter-mapping>
10. web会话跟踪机制
如何将后端更多的信息,传递到前端去?
json 是一种轻量级的数据交换格式,已经成为前后端数据交换的规范。
java对象 ---> json格式(字符串)---> 前端js(转为js 对象)
会话:是打开浏览器,成功连接到后端服务器开始,到关闭浏览器,整个过程称为一次会话。
会话对象(HttpSession):session 对象用于服务器端存储用户信息,会话 id 作为客户端和服务器之间身份识别。打开浏览器,成功连接到后端服务器开始,到浏览器关闭,整个过程称为一次会话。
HttpSession接口: 会话对象是Web容器创建的,在Servlet中使用 HttpServletRequest中的方法获得会话对象。session对象
public HttpSession getSession():获取跟当前请求相关的 session,如果不存在session,就创建一个新的session对象返回。
session实现会话机制的过程:
当一个客户端向服务器第一次发送请求时,服务器会创建一个Httpsession 对象,,此对象会生成一个id 号,就将此id号响应到浏览器,在浏览器中保存。
浏览器:
id 保存在浏览器中;之后的每次请求,都把此id 号放在请求头中国,向后端发送,浏览器中的id也是会话级别的,关闭即消失。
服务器:
接收请求时,在服务器端的众多的Httpsession 中找到对应的会话对象;如果找不到,就会创建一个新的对象。
登录成功后,开始了与后端服务的会话,此时生成HttpSession对象,req.getSession();获得session,第一次请求中,没有会话id,会新建一个会话对象,之后的请求中,如果有会话id,去找对应id 的Session对象。
HTTPSession session = req.getSession();
session.setAttribute("user",user);
在后端的session对象中存储用户信息,服务器会响应会话 id。
- setAttribute(String key,Object value) 以key/value的形式保存对象值
- getAttribute(String key) 通过key获取对象值
- removeAttribute(String key) 通过key删除属性
- getMaxInactiveInterval() 获取session的有效非活动时间,以秒为单位
- setMaxInactiveInterval (int interval ) 设置session的最大非活动时间,以秒为单位, 超时将被销毁。
- getId() 获取session对象的编号
- invalidate() 设置session对象失效
- isNew() 判断一个session是不是一个新创建的会话对象
会话对象的生命周期
开始于成功连接到后端服务,使用req.getSesstion() 获得 session 开始。
- 当容器创建一个新的HttpSession对象后,即生成一个随机数,称为 会话ID,并将ID值封装成一个名字为JSESSIONID的session,返回给 客户端。
- 调用request.getSession()方法获得会话对象时,容器先从request 中获取JSESSIONID值,根据JSESSIONID值查找到对应的会话对象, 返回使用。
- 如果没有获取到JSESSIONID值,认为当前的请求没有相关联的会话 对象,重复步骤1
销毁:
- 服务器关闭
- 到达最大的非活动时间(服务器会销毁,退出时,强制销毁session)
- 使用req.invalidate();