Java web
一,第一章
1,tomcat的下载及配置部署
学习本章内容需要具备以下知识点
- java基础知识
- 面向对象oop思想
- java进阶
- 数据库
- 前端网页技术(html/css/js/jquery)
1,下载安装tomcat
官网:https://tomcat.apache.org/
解压下载的压缩包
进入解压好的文件,进入bin目录下C:\apache-tomcat-9.0.37-windows-x64\apache-tomcat-9.0.37\bin
显示cmd控制台代表启动完成(一闪而过代表你的电脑缺少java环境或者环境没有配置好)
解决控制台出现的日志中文乱码问题
打开conf文件夹/编辑logging.properties的配置文件将后面编码改为GBK
访问测试()
打开浏览器访问localhost:8080
tomcat访问的端口号修改
编辑conf/server.xml文件
如何关闭tomcat?
不要点击cmd窗口的关闭,再cmd窗口按下ctrl+会自动结束tomcat并关闭cmd窗口或者进入bin目录下带年纪shutdown.bat文件即可关闭
2,认识tomcat文件目录
目录
- bin:二进制文件,存放基本命文件
- conf:tomcat的配置文件夹
- lib: tomcat的执行文件jar包存放位置
- logs:tomcat运行时发生错误的日志存储
- temp:临时文件夹
- webapps:项目的存放位置
- work:编译生成的.class文件就存放在这个目录
3,使用idea创建第一个web项目
我梦到了上帝,他对我说了一句helloword
新建一个空项目/在空项目里面新建一个模块
为它起个动听的名字,点击finish
目前创建了一个什么都没有的空模块,在模块名点击右键为他添加web服务支持
添加web服务后多了一个web文件夹,文件夹里面有个index.jsp
修改index.jsp
配置tomcat启动项
点击diea的run/editconfigrations
按照截图进行配置/找不到tomcat的到设置里下载插件
最后添加项目
添加完了启动项目
访问测试
2,转发(getRequestDispatcher)和重定向(sendRedirect)
1,转发和重定向的区别
- 转发属于request对象的方法,重定向属于response对象的方法
- 转发可以通过forward携带参数,重定向不能,
- 转发url地址栏不会发生改变,重定向会发生改变
- 转发属于服务器行为,重定向属于客户端行为
- 转发发起一次请求,重定向至少发起两次以上的请求
package com.hai.servlet;
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.util.Arrays;
/*转发与重定向的区别,
请求转发,
会携带数据,
url不会改变,
属于服务器行为,
一次内部转发,
重定向,
url发生改变,
不能携带请求参数,
属于客户端行为,
请求时至少发起两次以上的请求
*/
public class LoginServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
req.setCharacterEncoding("utf-8");
//接收用户名
String username = req.getParameter("username");
//接收用户密码
String password = req.getParameter("password");
//接收用户爱好选项
String[] hobbys = req.getParameterValues("hobbys");
System.out.println("======================");
//接收出现乱码
System.out.println(username+":"+password);
System.out.println(Arrays.toString(hobbys));
System.out.println("======================");
//通过请求转发
req.getRequestDispatcher("/success.jsp").forward(req,resp);
//以utf-8的编码格式进行定向转发
resp.setCharacterEncoding("utf-8");
//重定向注意路径,否则出现404
resp.sendRedirect("/r/success.jsp");
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req, resp);
}
}
jsp代码
<%--
Created by IntelliJ IDEA.
User: admin
Date: 2020/8/31
Time: 12:51
To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>用户</title>
</head>
<body>
<div>
<form action="${pageContext.request.contextPath}/login" method="post">
用户名:<label>
<input type="text" name="username">
</label><br/>
密码:<label>
<input type="password" name="password">
</label><br/>
<!--df-->
爱好:
<label>
<input type="checkbox" name="hobbys" value="妹纸">
</label>妹纸
<label>
<input type="checkbox" name="hobbys" value="代码">
</label>代码
<label>
<input type="checkbox" name="hobbys" value="唱歌">
</label>唱歌
<label>
<input type="checkbox" name="hobbys" value="电影">
</label>电影
<br/>
<input type="submit" value="登录">
</form>
</div>
<%out.print(gae);%>
<%String name = "jdo";%>
<%out.print(name);%>
<%!String gae = "20";%>
</body>
</html>
3,请求(Request)和响应(Response)
Request 和 Response 对象起到了服务器与客户机之间的信息传递作用。Request 对象用于接收客户端浏览器提交的数据,而 Response 对象的功能则是将服务器端的数据发送到客户端浏览器。
一、Request对象的五个集合:
QueryString:用以获取客户端附在url地址后的查询字符串中的信息。
例如:stra=Request.QueryString ["strUserld"]
Form:用以获取客户端在FORM表单中所输入的信息。(表单的method属性值需要为POST)
例如:stra=Request.Form["strUserld"]
Cookies:用以获取客户端的Cookie信息。
例如:stra=Request.Cookies["strUserld"]
ServerVariables:用以获取客户端发出的HTTP请求信息中的头信息及服务器端环境变量信息。
例如:stra=Request.ServerVariables["REMOTE_ADDR"],返回客户端IP地址
ClientCertificate:用以获取客户端的身份验证信息
例如:stra=Request.ClientCertificate["VALIDFORM"],对于要求安全验证的网站,返回有效起始日期。
二、Response对象
Response对象用于动态响应客户端请示,控制发送给用户的信息,并将动态生成响应。Response对象提供了一个数据集合cookie,它用于在客户端写入cookie值。若指定的cookie不存在,则创建它。若存在,则将自动进行更新。结果返回给客户端浏览器。
语法格式:Response.Cookies(CookieName)[(key)|.attribute]=value。这里的CookiesName是指定的Cookie的名称,如果指定了Key,则该Cookie就是一个字典,Attribute属性包括Domain,Expires,HasKeys,Path,Secure。
response的方法:
Write:向客户端发送浏览器能够处理的各种数据,包括:html代码,脚本程序等。
Redirect:response.redirect("url")的作用是在服务器端重定向于另一个网页。
End:用来终止脚本程序。
Clear:要说到Clear方法,就必须提到response的Buffer属性,Buffer属性用来设置服务器端是否将页面先输出到缓冲区。语法为:Response.Buffer=True/False
Flush:当Buffer的值为True时,Flush方法用于将缓冲区中的当前页面内容立刻输出到客户端。
4,session
概述:session是一次客户端与服务器的会话,当客户端于服务器建立连接时,session会创建一个唯一的sessionId,session的默认有限时间为30分钟,可以在web.xml文件中的session-config中进行修改,也可以通过session对象的setmaxInactiveInterval方法进行修改,值得注意的是,通过方法修改的参数单位是以秒钟为单位,
session常用的方法,
HttpSession接口源码
package javax.servlet.http;
import java.util.Enumeration;
import javax.servlet.ServletContext;
public interface HttpSession {
long getCreationTime();//获取创建session的时间戳
String getId();//获得session的ID
long getLastAccessedTime();//获取上次session被访问的时间
ServletContext getServletContext();//获取servlet控制器的上下文
void setMaxInactiveInterval(int var1);//设置session的有效时间,以秒为单位
int getMaxInactiveInterval();//获取session的最大有效时间
/** @deprecated */
@Deprecated
HttpSessionContext getSessionContext();//获取session的上下文
Object getAttribute(String var1);//获取session里的属性值
/** @deprecated */
@Deprecated
Object getValue(String var1);//获得值
Enumeration<String> getAttributeNames();//获取所有的session属性
/** @deprecated */
@Deprecated
String[] getValueNames();//获取多个值
void setAttribute(String var1, Object var2);//为session设置属性名和属性值,
/** @deprecated */
@Deprecated
void putValue(String var1, Object var2);
void removeAttribute(String var1);//移除session里面的指定属性
/** @deprecated */
@Deprecated
void removeValue(String var1);
void invalidate();//移除session
boolean isNew();//判断session是创建的
}
5,Cookie
什么是Cookie??
概述:如果想让网页记住你的cookie和session的关系,cookie是依赖这session的,结合了session,首先session是由服务器创建的,服务器把sessionid给到了cookie,cookie数据保存在客户端的,客户端访问服务器的时候,就会把这和JSESSIONID交给cookie,cookie创建后,通过response对象的addCookie()将cookie添加,同获取
Cookie的常用方法
Cookie的源码
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//
package javax.servlet.http;
import java.io.Serializable;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.util.Locale;
public class Cookie implements Cloneable, Serializable {
private static final CookieNameValidator validation;
private static final long serialVersionUID = 1L;
private final String name;
private String value;
private int version = 0;
private String comment;
private String domain;
private int maxAge = -1;
private String path;
private boolean secure;
private boolean httpOnly;
public Cookie(String name, String value) {
validation.validate(name);
this.name = name;
this.value = value;
}
public void setComment(String purpose) {
this.comment = purpose;
}
public String getComment() {
return this.comment;
}
public void setDomain(String pattern) {
this.domain = pattern.toLowerCase(Locale.ENGLISH);
}
public String getDomain() {
return this.domain;
}
public void setMaxAge(int expiry) {
this.maxAge = expiry;
}
public int getMaxAge() {
return this.maxAge;
}
public void setPath(String uri) {
this.path = uri;
}
public String getPath() {
return this.path;
}
public void setSecure(boolean flag) {
this.secure = flag;
}
public boolean getSecure() {
return this.secure;
}
public String getName() {
return this.name;
}
public void setValue(String newValue) {
this.value = newValue;
}
public String getValue() {
return this.value;
}
public int getVersion() {
return this.version;
}
public void setVersion(int v) {
this.version = v;
}
public Object clone() {
try {
return super.clone();
} catch (CloneNotSupportedException var2) {
throw new RuntimeException(var2);
}
}
public void setHttpOnly(boolean httpOnly) {
this.httpOnly = httpOnly;
}
public boolean isHttpOnly() {
return this.httpOnly;
}
static {
boolean strictServletCompliance;
String propStrictNaming;
String propFwdSlashIsSeparator;
if (System.getSecurityManager() == null) {
strictServletCompliance = Boolean.getBoolean("org.apache.catalina.STRICT_SERVLET_COMPLIANCE");
propStrictNaming = System.getProperty("org.apache.tomcat.util.http.ServerCookie.STRICT_NAMING");
propFwdSlashIsSeparator = System.getProperty("org.apache.tomcat.util.http.ServerCookie.FWD_SLASH_IS_SEPARATOR");
} else {
strictServletCompliance = (Boolean)AccessController.doPrivileged(new PrivilegedAction<Boolean>() {
public Boolean run() {
return Boolean.valueOf(System.getProperty("org.apache.catalina.STRICT_SERVLET_COMPLIANCE"));
}
});
propStrictNaming = (String)AccessController.doPrivileged(new PrivilegedAction<String>() {
public String run() {
return System.getProperty("org.apache.tomcat.util.http.ServerCookie.STRICT_NAMING");
}
});
propFwdSlashIsSeparator = (String)AccessController.doPrivileged(new PrivilegedAction<String>() {
public String run() {
return System.getProperty("org.apache.tomcat.util.http.ServerCookie.FWD_SLASH_IS_SEPARATOR");
}
});
}
boolean strictNaming;
if (propStrictNaming == null) {
strictNaming = strictServletCompliance;
} else {
strictNaming = Boolean.parseBoolean(propStrictNaming);
}
boolean allowSlash;
if (propFwdSlashIsSeparator == null) {
allowSlash = !strictServletCompliance;
} else {
allowSlash = !Boolean.parseBoolean(propFwdSlashIsSeparator);
}
if (strictNaming) {
validation = new RFC2109Validator(allowSlash);
} else {
validation = new RFC6265Validator();
}
}
}
6,application
全局作用域,作用域最高,作用于整个web服务器,一般用于统计网页访问量和文章浏览量等。。
二,第二章
1,JDBC(Java DataBase Connectivity)操作数据库
-
认识相关类和接口
-
DriverManager //驱动管理器 Class //通过反射加载对应的数据库驱动 Statement //数据库操作对象 ResultSet //返回对象结果集
-
-
示例代码
package com.hai.dao; import java.sql.*; public class JDBCDemo { public static void main(String[] args) throws ClassNotFoundException, SQLException { //通过反射加载驱动类 Class.forName("com.mysql.cj.jdbc.Driver"); //数据库相关参数 String url="jdbc:mysql://localhost:3306/mybatis?serverTimezone=GMT%2B8"; String user = "root"; String password = "123456"; String sql = "select * from user"; //通过驱动管理器获得连接对象connection Connection connection = DriverManager.getConnection(url,user,password); //获得链接,操作数据库 Statement statement = connection.createStatement();//此方法存在sql注入 //执行sql语句并返回结果集 ResultSet resultSet = statement.executeQuery(sql); //遍历结果集 while (resultSet.next()){ //从数据库里获取第一个字段,字段名和数据库里的字段相对应 resultSet.getInt("id"); resultSet.getString("name"); resultSet.getString("pwd"); } //避免资源浪费,需要关闭资源,遵循先开后关顺序 resultSet.close(); statement.close(); connection.close(); //规范代码:应该本类处理异常, //资源关闭时在必执行代码块关闭 } }
每次使用都要写重复的代码,建议封装为工具类,创建一个数据库连接池
数据 库工具类的源代码
package gz.DBUtil;
import java.sql.*;
public class DBUtil {
//数据库url
private static final String URL = "jdbc:mysql://localhost:3306/qingshukeji?serverTimezone=GMT%2B8";
//用户名
private static final String USERNAME = "root";
//用户密码
private static final String PASSWORD = "123456";
//静态代码块加载数据库驱动
static {
try {
Class.forName("com.mysql.cj.jdbc.Driver");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
//获得数据库连接的方法
public static Connection getConn() {
Connection conn = null;
try {
conn = DriverManager.getConnection(URL, USERNAME, PASSWORD);
} catch (SQLException e) {
e.printStackTrace();
}
return conn;
}
//关闭相应资源的方法,关闭操作对象和恶数据库连接
public static void close(PreparedStatement preparedStatement, Connection connection) {
close(null, preparedStatement, connection);
}
//关闭三个资源
public static void close(ResultSet resultSet, PreparedStatement preparedStatement, Connection connection) {
try{
if (resultSet != null) {
resultSet.close();
}
if (preparedStatement != null) {
preparedStatement.close();
}
if (resultSet != null) {
connection.close();
}
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
}
SQL注入问题
createSteatement存在sql注入问题,应该使用preparedStatement防止sql注入,sql注入带来的影响是巨大的,不可挽回的损失,SQL注入攻击是通过操作输入来修改SQL语句,用以达到执行代码对WEB服务器进行攻击的方法。简单的说就是在post/getweb表单、输入域名或页面请求的查询字符串中插入SQL命令,最终使web服务器执行恶意命令的过程。可以通过一个例子简单说明SQL注入攻击。假设某网站页面显示时URL为http://www.example.com?test=123,此时URL实际向服务器传递了值为123的变量test,这表明当前页面是对数据库进行动态查询的结果。由此,我们可以在URL中插入恶意的SQL语句并进行执行。另外,在网站开发过程中,开发人员使用动态字符串构造SQL语句,用来创建所需的应用,这种情况下SQL语句在程序的执行过程中被动态的构造使用,可以根据不同的条件产生不同的SQL语句,比如需要根据不同的要求来查询数据库中的字段。这样的开发过程其实为SQL注入攻击留下了很多的可乘之机
sql注入模拟登录
public static void main(String[] args) throws SQLException {
String name = "'nicdshnvdjvhuylkvjifblavkfvnfk' or 5782=5782";
boolean execute = DBUtils
.getConn()
.prepareStatement("select * from user
where name='admin' and pwd="+name)
.executeQuery()
.next();
System.out.println(execute?"登录成功":"登陆失败");
}
可以看到在用户输入的密码于数据库密码不匹配也能成功登录
preparedStatement有效的解决了这个问题,使用?作为占位符,
2,数据库的增删改查
- 增(insert)
public static boolean addUser() {
String sql = "insert into user (id,name,pwd) values (?,?,?)";
Connection conn = DBUtils.getConn();
PreparedStatement statement = null;
try {
statement = conn.prepareStatement(sql);
statement.setInt(1, 2);
statement.setString(2, "laoli");
statement.setString(3, "020202");
int statementCount = statement.executeUpdate();
System.out.println(statementCount == 0 ? "添加失败" : "添加成功");
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
} finally {
DBUtils.close(null, statement, conn);
}
}
- 删(delete)
public static boolean deleteUserById(int id){
String sql = "delete from user where id = ?";
Connection conn = DBUtils.getConn();
PreparedStatement statement = null;
try{
statement = conn.prepareStatement(sql);
statement.setInt(1,id);
int statementCount = statement.executeUpdate();
System.out.println(statementCount==0?"删除失败":"删除成功");
return true;
}catch(Exception e){
e.printStackTrace();
return false;
}finally{
DBUtils.close(null,statement,conn);
}
}
- 改(update)
public static boolean updateById(int id,String name,String pwd){
String sql = "update user set name=?,pwd=? WHERE id =?";
Connection conn = DBUtils.getConn();
PreparedStatement statement = null;
try{
statement = conn.prepareStatement(sql);
statement.setInt(3,id);
statement.setString(1,name);
statement.setString(2,pwd);
int statementCount = statement.executeUpdate();
System.out.println(statementCount==0?"修改失败":"修改成功");
return true;
}catch(Exception e){
e.printStackTrace();
return false;
}finally{
DBUtils.close(null,statement,conn);
}
}
- 查(select)
@SuppressWarnings("all")
public static boolean queryUser() {
String sql="select * from user";
Connection conn = DBUtils.getConn();
PreparedStatement statement = null;
ResultSet resultSet = null;
try {
statement = conn.prepareStatement(sql);
resultSet = statement.executeQuery();
while (resultSet.next()){
int id = resultSet.getInt("id");
String name = resultSet.getString("name");
String pwd = resultSet.getString("pwd");
System.out.println("序号:"+id);
System.out.println("用户名:"+name);
System.out.println("用户密码:"+pwd);
System.out.println("===================");
}
return true;
} catch (SQLException e) {
e.printStackTrace();
return false;
} finally {
DBUtils.close(resultSet,statement,conn);
}
}
3,JDBC的封装(DAO)
DAO(Data Access Object)顾名思义是一个为数据库或其他持久化机制提供了抽象接口的对象,在不暴露底层持久化方案实现细节的前提下提供了各种数据访问操作。在实际的开发中,应该将所有对数据源的访问操作进行抽象化后封装在一个公共API中。用程序设计语言来说,就是建立一个接口,接口中定义了此应用程序中将会用到的所有事务方法。在这个应用程序中,当需要和数据源进行交互的时候则使用这个接口,并且编写一个单独的类来实现这个接口,在逻辑上该类对应一个特定的数据存储。DAO模式实际上包含了两个模式,一是Data Accessor(数据访问器),二是Data Object(数据对象),前者要解决如何访问数据的问题,而后者要解决的是如何用对象封装数据。
- 封装工具类BaseDao
package com.hai.dao;
import java.sql.*;
import java.util.Objects;
public class BaseDao {
private static final String DRIVER = "com.mysql.cj.jdbc.Driver";
private static final String URL = "jdbc:mysql://localhost:3306/mybatis?serverTimezone=GMT%2B8";
private static final String USER_NAME = "root";
private static final String PASS_WORD = "123456";
public static Connection getConnection() {
try {
Class.forName(DRIVER);
return DriverManager.getConnection(URL, USER_NAME, PASS_WORD);
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
public static void close(AutoCloseable... autoCloseables) {
if (autoCloseables != null) {
for (AutoCloseable autoCloseable : autoCloseables) {
try {
autoCloseable.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
public static Object executeMethod(String sql,Object... params){
try {
PreparedStatement statement = Objects.requireNonNull(getConnection()).prepareStatement(sql);
if(params.length==0){
return statement.executeQuery();
}
for (int i = 0; i < params.length; i++) {
statement.setObject(i,params[i]);
}
close(statement);
return statement.executeUpdate();
} catch (SQLException e) {
e.printStackTrace();
return 0;
}
}
}
- 封装接口方法
package com.hai.dao;
public interface UserDao {
Object deleteById(int id);
Object add(Object... params);
Object query();
Object update(Object... params);
}
- 创建实现类,
package com.hai.dao.impl;
import com.hai.dao.BaseDao;
import com.hai.dao.UserDao;
import java.sql.ResultSet;
import java.sql.SQLException;
public class UserDaoImpl implements UserDao {
private String sql="";
@Override
public Object deleteById(int id) {
sql="delete from user where id = ?";
return BaseDao.executeMethod(sql,id);
}
@Override
public Object add(Object... params) {
sql="insert into user (id,name,pwd) values (?,?,?)";
return BaseDao.executeMethod(sql,params);
}
@Override
public Object query() {
sql="select * from user";
return BaseDao.executeMethod(sql);
}
@Override
public Object update(Object... params) {
sql="update user set id = ?, name=?,pwd=?";
return BaseDao.executeMethod(sql,params);
}
}
- 编写完成,创建测试类测试效果
package com.hai.dao.test;
import com.hai.dao.UserDao;
import com.hai.dao.impl.UserDaoImpl;
import java.sql.ResultSet;
import java.sql.SQLException;
public class JDBCTest {
public static void main(String[] args) {
UserDao userDao = new UserDaoImpl();
//增加
System.out.println((int)userDao.add(3, "老胡", "020203")>0?"添加成功":"添加失败");
//删除
System.out.println((int)userDao.deleteById(19)>0?"删除成功":"删除失败");
//修改
System.out.println((int)userDao.update("s3006", "沈老师", "2")>0?"修改成功":"修改失败");
//查询
ResultSet query =(ResultSet) userDao.query();
try{
while (query.next()){
System.out.println(query.getInt("id"));
System.out.println(query.getString("name"));
System.out.println(query.getString("pwd"));
}
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
}
- 创建一个和数据库字段对应的实体类,将数据封装为一个对象
package com.hai.dao.pojo;
public class User {
private Integer id;
private String name;
private String pwd;
public User(Integer id, String name, String pwd) {
this.id = id;
this.name = name;
this.pwd = pwd;
}
public User() {
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getPwd() {
return pwd;
}
public void setPwd(String pwd) {
this.pwd = pwd;
}
@Override
public String toString() {
return "User{" +
"id=" + id +
", name='" + name + '\'' +
", pwd='" + pwd + '\'' +
'}';
}
}
- 封装后测试
package com.hai.dao.test;
import com.hai.dao.UserDao;
import com.hai.dao.impl.UserDaoImpl;
import com.hai.dao.pojo.User;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
public class JDBCTest {
public static void main(String[] args) {
UserDao userDao = new UserDaoImpl();
User user1 = new User(11, "老胡", "020203");
User user2 = new User(2,"沈老师", "2");
//增加
System.out.println((int)userDao.add(user1.getId(),user1.getName(),user1.getPwd())>0?"添加成功":"添加失败");
//删除
System.out.println((int)userDao.deleteById(user1.getId())>0?"删除成功":"删除失败");
//修改
System.out.println((int)userDao.update(user2.getName(),user2.getName(),user2.getId())>0?"修改成功":"修改失败");
//查询
ResultSet query =(ResultSet) userDao.query();
ArrayList<User> users = new ArrayList<>();
try{
while (query.next()){
User user = new User();
user.setId(query.getInt("id"));
user.setName(query.getString("name"));
user.setPwd(query.getString("pwd"));
users.add(user);
}
} catch (SQLException throwables) {
throwables.printStackTrace();
}
for (User user : users) {
System.out.println(user.toString());
}
}
}
- 测试结果
添加成功
删除成功
修改成功
User{id=1, name='admin', pwd='123456'}
User{id=2, name='沈老师', pwd='沈老师'}
User{id=15, name='simisi', pwd='hcuisdgcudfu63'}
User{id=16, name='simisis', pwd='121212'}
User{id=20, name='18184278740', pwd='123456'}
User{id=21, name='18184278740', pwd='123456'}
User{id=22, name='18184278740', pwd='123456'}
Process finished with exit code 0
4,properties配置文件的读取
- 配置文件
driver=com.mysql.cj.jdbc.Driver
url=jdbc:mysql://localhost:3306/mybatisuserSSL=true&useUnicode=true&serverTimezone=UTC&characterEncoding=utf-8
username=root
password=123456
- 读取配置文件的工具类(配置文件应该放在src根目录下)
package com.hai.dao.utils;
import java.io.IOException;
import java.io.InputStream;
import java.util.Properties;
public class GetProperties {
private static final Properties properties;
public static GetProperties getProperties;
static {
InputStream stream = GetProperties.class.getResourceAsStream("/database.properties");
properties = new Properties();
try {
properties.load(stream);
stream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
private GetProperties(){}
public static GetProperties getInstance(){
if(getProperties==null){
getProperties = new GetProperties();
}
return getProperties;
}
public String getParams(String key){
return properties.getProperty(key);
}
}
- 读取配置文件的方法
package com.hai.dao.utils;
public class InstanceTest {
public static void main(String[] args) {
System.out.println(GetProperties.getInstance().getParams("driver"));
System.out.println(GetProperties.getInstance().getParams("url"));
System.out.println(GetProperties.getInstance().getParams("username"));
System.out.println(GetProperties.getInstance().getParams("password"));
}
}
5,单例模式
1,饿汉式单例(立即加载)
示例代码:
package com.hai.single;
public class HungryManSingle {
//饿汉式单例
private static final HungryManSingle hungryManSingle = new HungryManSingle();
//构造方法私有化,保证当前对象使用时全局唯一
private HungryManSingle(){}
public static HungryManSingle newInstance(){
return hungryManSingle;
}
public void test(){
System.out.println("饿汉式单例");
}
}
饿汉式单例new多个对象,虽然提高了效率但是浪费了空间,在类加载的时候对象已经完成初始化,通过调用方法返回该对象,属于空间换时间
2,懒汉式单例(延迟加载)
示例代码:
package com.hai.single;
public class LazyManSingle {
//懒汉式单例
private volatile static LazyManSingle lazyManSingle = null;
//双重检测锁模式
public static LazyManSingle neInstance(){
if(lazyManSingle==null){
synchronized (LazyManSingle.class){
if(lazyManSingle==null){
lazyManSingle = new LazyManSingle();
}
}
}
return lazyManSingle;
}
private LazyManSingle(){}
public void test(){
System.out.println("懒汉式单例");
}
}
解决线程安全,但是多线程情况下,需要线程进行排队,大大较低了执行效率,加锁的同时对实例进行了两次非空判断,并使用volatile关键字对当前对象添加了内存屏障,避免计算机cpu发出指令时发生指令重排。在类加载时不创建实例,在调用获取实例方法的时候才会创建实例,加载较慢,属于时间换空间
三,第三章
1,数据库连接池
-
什么是数据库连接池??
:数据库连接池(Connection pooling)是程序启动时建立足够的数据库连接,并将这些连接组成一个连接池,由程序动态地对池中的连接进行申请,使用,释放。
-
为什么需要数据库连接池??
:创建数据库连接是一个很耗时的操作,也容易对数据库造成安全隐患。所以,在程序初始化的时候,集中创建多个数据库连接,并把他们集中管理,供程序使用,可以保证较快的数据库读写速度,还更加安全可靠。
-
数据库连接池的必要参数有哪些??
九个基本参数 按需所取 username 用户名 password 密码 url 数据库地址 driver 连接驱动类 initialPoolSize 初始化连接数 maxPoolSize 最大连接数 minPoolSize 最小连接数 maxStatements 最长等待时间 maxIdleTime 最大空闲时间 c3p0数据源示例代码:
package com.zww.server; import java.beans.PropertyVetoException; import java.sql.Connection; import java.sql.SQLException; import com.mchange.v2.c3p0.ComboPooledDataSource; public final class ConnectionManager { //使用单利模式创建数据库连接池 private static ConnectionManager instance; private static ComboPooledDataSource dataSource; private ConnectionManager() throws SQLException, PropertyVetoException { dataSource = new ComboPooledDataSource(); dataSource.setUser("root"); //用户名 dataSource.setPassword("123456"); //密码 dataSource.setJdbcUrl("jdbc:mysql://127.0.0.1:3306/zww");//数据库地址 dataSource.setDriverClass("com.mysql.jdbc.Driver"); dataSource.setInitialPoolSize(5); //初始化连接数 dataSource.setMinPoolSize(1);//最小连接数 dataSource.setMaxPoolSize(10);//最大连接数 dataSource.setMaxStatements(50);//最长等待时间 dataSource.setMaxIdleTime(60);//最大空闲时间,单位毫秒 } public static final ConnectionManager getInstance() { if (instance == null) { try { instance = new ConnectionManager(); } catch (Exception e) { e.printStackTrace(); } } return instance; } public synchronized final Connection getConnection() { Connection conn = null; try { conn = dataSource.getConnection(); } catch (SQLException e) { e.printStackTrace(); } return conn; } }
2,分层开发
- 为什么需要分层开发?
在项目实际开发过程中,会将整个项目从上到下划分为页面层、业务逻辑层、数据层。
三层开发是项目开发实践中典型的开发模式。
目的:实现高内聚、低耦合。
分层的组成及作用
-
表示层:与用户交互、展示数据
-
业务逻辑层:控制业务流程及事务
-
数据访问层:实现数据库操作
分层优点
-
职责清晰,分工明确
-
利于维护扩展
-
利于代码重用
3,jsp常用标签
<jsp:useBean
id="service"
class="com.hai.service.impl.NewsDetailsServiceImpl"
scope="application"/>
//id是标签名,然后class是类路径,scope是作用域
<jsp:include page="newsDetailList.jsp"/>
//引入公共jsp页面,在运行时将多个页面合成为同一个页面,page为目标页面
<%@include file="adminBottom.jsp"%>
//引入公共jsp页面,与jsp:include不同的是,@include是编译时合成页面,贰jsp:include是运行时合成一个页面
<jsp:forward page="adminBottom.jsp"/>
//实现页面跳转,page为目标页面,可通过parmas携带参数
4,简单文件上传
1,下载相应依赖jar包
代码过于繁琐,简过·
5,分页查询(limit)
分页查询的优点
- 数据直观清晰
- 不受数据量限制
- 页面不再冗长
分页查询的操作步骤
- 计算显示数据的总数量
- 确定每页显示的数据量
- 计算显示的页数
- 页数=总数量/每页显示的数据量[+1]
- 编写分页查询SQL语句
- 实现分页查询
分页查询关键点
-
计算显示数据的总数量需要借助JDBC内容
-
计算页数时,声明一个工具类将功能独立出来,便于复用
-
总记录数/每页显示的记录数
-
整除:总页数=总记录数/每页显示记录数
-
不能整除:总页数=总记录数/每页显示记录数+1
- 编写分页查询SQL语句(limit)
- select * from tableName where 查询条件
- limit (当前页码-1)*页面容量, 页面容量
-
实现代码
DAO层
@Override
public List<NewsDetails> getNewsDetailsListLimit(int pageNumber, int pageSize) {
ArrayList<NewsDetails> newsDetails = new ArrayList<>();
String sql = "select * from `news_detail` limit "+(pageNumber - 1) * pageSize+","+pageSize;
ResultSet resultSet = (ResultSet) BaseDao.executeMethod(sql);
return getNewsDetails(newsDetails, resultSet);
}
private List<NewsDetails> getNewsDetails(ArrayList<NewsDetails> newsDetails, ResultSet resultSet) {
try {
while (resultSet.next()) {
NewsDetails details = new NewsDetails();
details.setId(resultSet.getInt("id"));
details.setAuthor(resultSet.getString("author"));
details.setCategoryId(resultSet.getInt("categoryId"));
details.setContent(resultSet.getString("content"));
details.setCreateDate(resultSet.getDate("createDate"));
details.setModifyDate(resultSet.getDate("modifyDate"));
details.setPicPath(resultSet.getString("picPath"));
details.setTitle(resultSet.getString("title"));
details.setSummary(resultSet.getString("summary"));
newsDetails.add(details);
}
} catch (SQLException e) {
e.printStackTrace();
}
return newsDetails;
}
Service层
@Override
public List<NewsDetails> getNewsDetailsListLimit(int pageNumber, int pageSize) {
return newsDetailsDao.getNewsDetailsListLimit(pageNumber,pageSize);
}
测试
package com.hai.test;
import com.hai.entity.NewsDetails;
import com.hai.service.impl.NewsDetailsServiceImpl;
public class AllTest {
public static void main(String[] args) {
for (NewsDetails newsDetails : new NewsDetailsServiceImpl().getNewsDetailsListLimit(1, 2)) {
System.out.println(newsDetails.toString());
}
}
}
测试结果
三,第三章
1,EL(Expression Language)表达式
-
- EL表达式时一种JSP技术,能够替代JSP中原本要用java语言来进行显示的数据,使得代码更容易维护与编写,最基本的语法是${express}
- 原理:EL表达式在获取某个对象的属性值时,先将某个属性值首字母变成大写,然后加上get前缀,拼接成getter方法,通过反射将该对象构建出来,然后再对该对象执行getter方法,这与私有属性并没有关系,所以要注意,JavaBean的属性名要小写,且要有getter方法,不然会报错。
- EL表达式时一种JSP技术,能够替代JSP中原本要用java语言来进行显示的数据,使得代码更容易维护与编写,最基本的语法是${express}
-
四个域的寻找顺序分别为page, request,session,application。用EL表达式还有一个好处,如果找不到键值为name的属性值,不会显示null,会显示空的字符串,若确定键值是在request域中,则可以用如下EL表达式代码
${requestScope.name}
-
执行运算符
- 语法:${运算表达式}
- 常见运算符:==(eq) !=(ne) <(lt) >(gt) <=(le) >=(ge) &&(and) ||(or) !(not)
- 判断是否为空:${empty name}
- 三元运算符${name == null ? “error”:name}
-
获取常用对象
-
语法:${隐式对象名称}
-
隐式对象表
对象名 用法 等价于JSP代码或作用 param ${param.name} request.getParameter(name) paramValues ${paramValues.name} request.getParameterValues(name) header ${header.name} request.getHeader(name) headerValues ${headerValues.name} request.getHeaderValues(name) cookie ${cookie.name.value} request.getCookie() initParam ${initParam.name} ServletContext.getInitparameter(name) pageContext ${pageContext.request.contextPath} 获取web项目名 pageContext ${pageContext.request.sessionid} 获取sessionid pageContext ${pageContext.request.remoteAddr} 获取主机名
-
-
显示方式
-
对象信息展示
<% NewsDetails newsDetails = new NewsDetails(); newsDetails.setTitle("军事"); newsDetails.setCreateDate(new Date()); newsDetails.setSummary("台湾回归"); newsDetails.setPicPath("www.qliyunbeijing"); newsDetails.setContent("祖国71周岁生日"); request.setAttribute("newsDetails",newsDetails); %> 获取对象信息:${newsDetails.title} 获取对象信息:${newsDetails.createDate} 获取对象信息:${newsDetails.summary} 获取对象信息:${newsDetails.picPath} 获取对象信息:${newsDetails.content} 也可以用${newsDetails['title']}
-
集合信息展示
<% ArrayList<String> list = new ArrayList<>(); list.add("1"); list.add("2"); list.add("3"); list.add("4"); request.setAttribute("arrayList",list); %> 获取集合信息:${arrayList[0]} 获取集合信息:${arrayList[1]} 获取集合信息:${arrayList[2]} 获取集合信息:${arrayList[3]}
-
Map集合通过key获取vaule
<% HashMap<Object, Object> map = new HashMap<>(); map.put("name","老李"); map.put("age","21"); map.put("address","广东深圳"); map.put("weight","60kg"); request.setAttribute("mapInfo",map); %> 获取map集合信息:${mapInfo.name} 获取map集合信息:${mapInfo.age} 获取map集合信息:${mapInfo.weight} 获取map集合信息:${mapInfo.address}
网页展示效果
-
2,JSTL
-
JSTL需要和EL表达式联合使用,先两个下载jar包
-
引入
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
-
使用
踩坑:引入后项目启动报错org.apache.jasper.JasperException: java.lang.ClassNotFoundException: org.apache.jsp.index_jsp
解决方案:jstl和standard两个jar包都引入即可,
- 核心标签库(core)
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> <%@ page contentType="text/html;charset=UTF-8" language="java" %> <html> <head> <title>$Title$</title> </head> <body> <h1>JSTL</h1> <hr/> c:out //输出语句<br/> c:set //设置变量<br/> c:remove //移除变量<br/> <c:out value="${massage}" default="not hava value"/><br/>//default代表要输出的值为null时输出default的值 <c:set var="massage" value="hello JSTL"/><br/>//设置一个变量名为massage的变量并赋值为hello JSTL <c:out value="${massage}"/><br/> <c:remove var="massage"/><br/>//移除变量 <c:out value="${massage}" default="massage is a not defined"/><br/> <c:foreach var="newProperty" items="${list}" varStatus="staIndex">//循环遍历输出集合对象 <tr>//var为新变量名也就是遍历之后获得的属性值会赋值给var //items为被遍历的集合或数组、 //varStatus为遍历计数索引 //varStatus.first判断是否为第一次遍历 //varStatus.last判断是否是最后一次遍历 //varStatus.count累计循环次数 //varStatus.index获得当前遍历的索引号 <td>${newProperty.name}</td> <td>${newProperty.age}</td> <td>${newProperty.weight}</td> </tr> </c:foreach> <c:if test="${1==1}">//if为判断条件是否成立 //条件成立执行体 </c:if> <%--外部url引入 参数标签,与超链接一起被携带--%>
JSTL标签测试
<%–导入资源标签–%>
<c:import url=“index.jsp”/>
</body>
</html>
```
- 格式化标签库
- 导入
```jsp
<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %>
```
- 使用
```jsp
<fmt:ofrmatDate value="${new date()}" pattern="yyyy-MM-dd HH:mm:ss"/>
```
- 详情见帮助文档
常用标签总结
```jsp
EL表达式
. [ ] 算术、关系、逻辑等运算符
访问作用域:pageScope、 requestScope、sessionScope、 applicationScope
JSTL标签
<c:out />、//输出
<c:set/>、//设置
<c:remove/> //移除
<c:if/>、//判断
<c:forEach/> //循环
<c:url/>、//超链接
<c:param/>、//参数携带
<c:import/> //引入资源
<fmt:formatDate/>、//日期格式化
<fmt:formatNumber/> //数字格式化
```
3,Servlet
-
什么是servlet?
- Server+Applet,是一种服务器端的Java应用程序
- 只有当一个服务器端的程序使用了Servlet API的时候,这个服务端的程序才能称之为Servlet
-
为什么需要servlet?
- 没有servlet之前,jsp页面代码过于繁琐且降低了代码可读性与维护性,使得前端代码和后台代码过于紧密
- jsp作为页面层应专注于显示数据,servlet可以使得控制层成为一个完整的javaBean
- servlet使得代码条理清晰,提高可读性和维护性,层次分工明确
-
servlet做了什么?
- 本身不做任何业务处理
- 只是接收请求并决定调用哪个JavaBean去处理请求
- 确定用哪个页面来显示处理返回的数据
-
理解Servlet生命周期
- servlet在检测到客户端请求的时候servlet容器会将其创建,第一就先调用了init方法
- 在客户端发起请求时servlet会调用service方法处理相应请求
- 在web服务器关停时servlet被销毁,在servlet没有任何引用指向的时候会被垃圾回收器回收
-
创建和部署Servlet
-
使用前先引入jar包(javax.servlet-api-4.0.1.jar)
-
servlet创建的三种方式
- 创建一个类实现servlet接口并实现其方法
package com.hai.servlet; import javax.servlet.*; import java.io.IOException; public class HelloServlet implements Servlet { @Override public void init(ServletConfig servletConfig) throws ServletException { System.out.println("servlet创建时调用的初始化方法"); } @Override public ServletConfig getServletConfig() { return null; } @Override public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException { System.out.println("所有的请求都会经过的方法"); } @Override public String getServletInfo() { return null; } @Override public void destroy() { System.out.println("servlet声明周期结束时调用"); } }
- 编写一个类继承GenericServlet并重写service方法
package com.hai.servlet; import javax.servlet.GenericServlet; import javax.servlet.ServletException; import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; import java.io.IOException; public class HelloGenericServlet extends GenericServlet { @Override public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException { System.out.println("所有请求都经过的方法,用来处理servlet请求"); } }
- 编写一个类继承HttpServlet
package com.hai.servlet; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; public class HelloHttpServlet extends HttpServlet { @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { super.doGet(req, resp); } @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { super.doPost(req, resp); } } //需要什么方法就重写什么方法,继承时并不强制的要求实现方法,
三种创建方式均需配置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-name>HelloServlet</servlet-name> <!-- 类的路径,包名和类名 --> <servlet-class>com.hai.servlet.HelloServlet</servlet-class> </servlet> <servlet-mapping> <!-- 注册映射,需要跟上面起的名称一致--> <servlet-name>HelloServlet</servlet-name> <!-- 映射请求的请求名字--> <url-pattern>/HelloServlet</url-pattern> </servlet-mapping> <servlet> <!-- 为servlet起的名称--> <servlet-name>HelloGenericServlet</servlet-name> <!-- 类的路径,包名和类名 --> <servlet-class>com.hai.servlet.HelloGenericServlet</servlet-class> </servlet> <servlet-mapping> <!-- 注册映射,需要跟上面起的名称一致--> <servlet-name>HelloGenericServlet</servlet-name> <!-- 映射请求的请求名字--> <url-pattern>/HelloGenericServlet</url-pattern> </servlet-mapping> <servlet> <!-- 为servlet起的名称--> <servlet-name>HelloHttpServlet</servlet-name> <!-- 类的路径,包名和类名 --> <servlet-class>com.hai.servlet.HelloHttpServlet</servlet-class> </servlet> <servlet-mapping> <!-- 注册映射,需要跟上面起的名称一致--> <servlet-name>HelloHttpServlet</servlet-name> <!-- 映射请求的请求名字--> <url-pattern>/HelloHttpServlet</url-pattern> </servlet-mapping> </web-app>
-
-
web.xml配置理解
- 请求映射路径理解
<servlet-mapping> <servlet-name>HelloHttpServlet</servlet-name> <!-- 请求为/HelloHttpServlet --> <url-pattern>/HelloHttpServlet</url-pattern> </servlet-mapping> <servlet-mapping> <servlet-name>HelloHttpServlet</servlet-name> <!-- 请求为/dev/HelloHttpServlet --> <url-pattern>/dev/HelloHttpServlet</url-pattern> </servlet-mapping> <servlet-mapping> <servlet-name>HelloHttpServlet</servlet-name> <!-- 请求为/HelloHttpServlet下的所有请求路径 --> <url-pattern>/HelloHttpServlet/*</url-pattern> </servlet-mapping> <servlet-mapping> <servlet-name>HelloHttpServlet</servlet-name> <!-- 请求路径后缀为.jsp的都会经过这个请求 --> <url-pattern>*.jsp</url-pattern> </servlet-mapping>
- 初始化参数配置
<servlet> <servlet-name>HaiServlet</servlet-name> <servlet-class>com.hai.servlet.HaiServlet</servlet-class> <init-param> <param-name>username</param-name> <param-value>admin</param-value> </init-param> <init-param> <param-name>password</param-name> <param-value>123456</param-value> </init-param> </servlet>
- servlet
@Override public void init(ServletConfig config) throws ServletException { String username = config.getInitParameter("username"); String password = config.getInitParameter("password"); System.out.println(username+":"+password); }
- :在请求被发起的时候,立即调用init方法,随后拿到在web.xml中配置的初始化参数,getInitParameter(arg0)这个方法在整个servlet类中任何区域都能调用
4,filter
基本概念
在请求发起的时候,有过滤器拦截到,统一的进行处理,例如在每次servlet请求我们都需要设置请求和响应的编码为utf-8,有没有什么方法能让我们设置一次编码,然后在每个servlet中都有效呢,这个时候就体现了过滤器的优点,
- 过滤器同样存在生命周期,实现Filter接口时实现的三个方法就是filter过滤器的生命周期,与servlet不同的是,servlet的init方法是在有请求发起的时候init方法会执行,而filter的init方法是在服务器启动的时候调用方法完成初始化,即便没有请求也不影响。
-
编码过滤器实现代码(实现Filter接口,不要导错包!):
package com.hai.filter; import javax.servlet.*; import java.io.IOException; public class CharacterEncodingFilter implements Filter { @Override public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { //过滤请求编码 servletRequest.setCharacterEncoding("utf-8"); //过滤响应编码 servletResponse.setCharacterEncoding("utf-8"); //过滤后给予放行 filterChain.doFilter(servletRequest, servletResponse); } @Override public void init(FilterConfig filterConfig) throws ServletException { System.out.println("服务器启动时调用此方法,完成过滤器初始化"); } @Override public void destroy() { System.out.println("服务器关闭时销毁过滤器"); } }
-
配置web.xml
<filter> <filter-name>CharacterEncodingFilter</filter-name> <filter-class>com.hai.filter.CharacterEncodingFilter</filter-class> </filter> <filter-mapping> <filter-name>CharacterEncodingFilter</filter-name> <!-- /*代表拦截所有的请求--> <url-pattern>/*</url-pattern> </filter-mapping>
-
Filter初始化参数(同Servlet):
<filter> <filter-name>CharacterEncodingFilter</filter-name> <filter-class>com.hai.filter.CharacterEncodingFilter</filter-class> <init-param> <param-name>encoding</param-name> <param-value>utf-8</param-value> </init-param> </filter> <filter-mapping> <filter-name>CharacterEncodingFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping>
-
获取参数
package com.hai.filter; import javax.servlet.*; import java.io.IOException; public class CharacterEncodingFilter implements Filter { FilterConfig f;//提升FilterConfig作用域作为全局变量来供给方法获取初始化参数 @Override public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { //过滤请求编码 servletRequest.setCharacterEncoding(f.getInitParameter("encoding")); //过滤响应编码 servletResponse.setCharacterEncoding(f.getInitParameter("encoding")); //过滤后给予放行 filterChain.doFilter(servletRequest, servletResponse); } @Override public void init(FilterConfig filterConfig) throws ServletException { this.f = filterConfig;//赋值提升作用域 System.out.println("服务器启动时调用此方法,完成过滤器初始化"); } @Override public void destroy() { System.out.println("服务器关闭时销毁过滤器"); } }
-
Filter工作流程
-
拦截链路理解(多个Filter过滤同一个请求时形成拦截链路):
类似于jvm的压栈,最开始执行的拦截器最后一个结束拦截
5,Listener
-
什么是监听器?
- 监听器是Web应用程序事件模型的一部分
- Web应用中的某些状态发生改变时会产生相应的事件
- ServletContext、HttpSession、ServletRequest三个域对象引发的事件
- 域对象中的属性引发的事件
- 监听器可以接收这些事件,以便在事件发生时做出相关处理
-
web八大监听器
- HttpSessionListener(在session创建后和失效前得到通知)
- ServletContextListener(在servlet上下文对象初始化或销毁时得到通知)
- ServletRequestListener(在请求对象初始化时或被销毁时得到通知)
- HttpSessionAttributeListener(在session中的属性列表发生改变时得到通知)
- ServletContextAttributeListener(在servlet上下文中的属性列表发生改变时得到通知)
- ServletRequestAttributeListener(在请求对象中的属性列表发生变化时得到通知)
- HttpSessionActivationListener(绑定到session中或session被钝化或者激活时得到通知)
- HttpSessionBindingListener(在绑定session或从session中删除时得到通知)
-
使用HttpSessionBindingListener监听器实现网站在线人数监测案例
-
监听器代码(踩坑:监听器实现需要与添加到session的数据处于同一个类,否则监听器不生效)
package com.hai.entity.vo; import javax.servlet.http.HttpSessionBindingEvent; import javax.servlet.http.HttpSessionBindingListener; public class User implements HttpSessionBindingListener { private int id; private String username; 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; } @Override public void valueBound(HttpSessionBindingEvent event) { System.out.println("session里面保存新对象时该方法会调用,监听保存了的user对象"); ConStatus.ONLINE_USER++; } @Override public void valueUnbound(HttpSessionBindingEvent event) { System.out.println("session里面的user对象被销毁时触发该事件"); ConStatus.ONLINE_USER--; } }
-
常量统计类
package com.hai.entity.vo; public class ConStatus { public static int ONLINE_USER=0; }
-
servlet代码
- OnlineServlet.java
package com.hai.servlet; import com.hai.entity.vo.User; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpSession; import java.io.IOException; public class OnlineServlet extends HttpServlet { @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { String username = req.getParameter("username"); if (username == null && username.equals("")) { resp.sendRedirect("index.jsp"); }else { User user = new User(); user.setUsername(username); req.getSession().setAttribute("user",user); req.getRequestDispatcher("ass.jsp").forward(req,resp); } } @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { doGet(req, resp); } }
- LogoutServlet.java
package com.hai.servlet; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; public class LogoutServlet extends HttpServlet { @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { req.getSession().invalidate(); resp.sendRedirect("index.jsp"); } @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { doGet(req, resp); } }
-
前端代码
- login.jsp
<%-- Created by IntelliJ IDEA. User: admin Date: 2020/11/4 Time: 14:02 To change this template use File | Settings | File Templates. --%> <%@ page contentType="text/html;charset=UTF-8" language="java" %> <html> <head> <title>$Title$</title> </head> <body> <form action="${pageContext.request.contextPath}/OnlineServlet"> <input type="text" name="username"> <input type="submit" value="登录"> </form> </body> </html>
- ass.jsp
<%@ page import="com.hai.entity.vo.User" %> <%@ page import="com.hai.entity.vo.ConStatus" %><%-- Created by IntelliJ IDEA. User: admin Date: 2020/11/4 Time: 14:53 To change this template use File | Settings | File Templates. --%> <%@ page contentType="text/html;charset=UTF-8" language="java" %> <html> <head> <title>Title</title> </head> <body> 用户名:<%User user = (User)session.getAttribute("user");%><%=user.getUsername()%> 在线人数:<%=ConStatus.ONLINE_USER%>人 <p><a href="${pageContext.request.contextPath}/LogoutServlet">离开</a></p> </body> </html>
- web.xml(HttpSessionBindingListener为感知监听器,无需配置)
<?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-name>OnlineServlet</servlet-name> <servlet-class>com.hai.servlet.OnlineServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>OnlineServlet</servlet-name> <url-pattern>/OnlineServlet</url-pattern> </servlet-mapping> <servlet> <servlet-name>LogoutServlet</servlet-name> <servlet-class>com.hai.servlet.LogoutServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>LogoutServlet</servlet-name> <url-pattern>/LogoutServlet</url-pattern> </servlet-mapping> <listener> <listener-class>com.hai.listener.OnlineListener</listener-class> </listener> </web-app>
-
效果展示
-
6,MVC
MVC理解
- 视图(View)- JSP、HTML等:负责与用户交互
- 控制器(Controller)- Servlet:负责流程控制
- 模型(Model)- JavaBean:负责业务逻辑处理、数据库访问
MVC处理流程
MVC优缺点
- 优点
- 多视图共享一个模型,大大提高代码的可重用性
- MVC三个模块相互独立,松耦合架构
- 控制器提高了应用程序的灵活性和可配置性
- 有利于软件工程化管理
- 缺点
- 原理复杂
- 增加了系统结构和实现的复杂性
- 视图对模型数据的低效率访问
处理流程再理解
四,第四章
1,ajax(asynchronous javaScript and xml)
-
什么是ajax?
- ajax是一种异步交互技术,在不刷新网页的情况下,改变页面内容
-
为什么需要ajax?
- 在没有ajax这项技术的情况下,我们所有的请求都需要与网页进行同步,也就是要想发起请求整个网页就会刷新
- 给用户带来的体验也是糟糕的,有时候我们可能需要仅仅是需要更新一下网页数据,但是却需要刷新整个网页,服务器响应整个页面所消耗的资源也是无法避免的,而ajax异步交互技术完美的弥补了这些缺点
-
ajax的优点
- 减轻数据传输压力,由页面响应变为数据响应
- 异步刷新改变网页数据,提升用户体验
- 可异步执行多任务,提升效率
-
ajax的执行流程
-
ajax实现的两种方式
-
使用JavaScript的原生js实现
- index.jsp
<%-- Created by IntelliJ IDEA. User: admin Date: 2020/11/5 Time: 14:41 To change this template use File | Settings | File Templates. --%> <%@ page contentType="text/html;charset=UTF-8" language="java" %> <html> <head> <title>$Title$</title> <script type="text/javascript" src="statics/js/jquery-3.4.1.js"></script> <script> //失去焦点时调用此函数 function isyes() { //创建请求 let httpRequest = new XMLHttpRequest(); //获取输入框中的值 let username = $("#username").val(); //过滤空字符 if (username == null) { alert("用户名不能为空") } else { //响应请求的回调函数 httpRequest.onreadystatechange = callBack; //请求服务器的url let url = "UserServlet?username=" + username; //封装请求,以POST方式请求,封装写好的url,true代表为异步请求/false代表同步请求 httpRequest.open("POST", url, true); //封装完请求之后发送这个请求 httpRequest.send(); } function callBack() { //就绪状态为4且状态码为200代表请求成功 if (httpRequest.readyState === 4) { if (httpRequest.status === 200) { //拿到响应过来的数据 let responseData = httpRequest.responseText; //将数据写到span标签里面 $("#tip").html(responseData); } } } } </script> </head> <body> <form> <label> <input type="text" name="username" id="username" οnblur="isyes();"> </label><span id="tip"></span> <p><input type="submit"></p> </form> </body> </html>
- UserServlet.java
package com.ajax.servlet; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; public class UserServlet extends HttpServlet { @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException { req.setCharacterEncoding("utf-8"); resp.setCharacterEncoding("utf-8"); resp.setContentType("text/html"); String username = req.getParameter("username"); if(username.equals("老李呀")){ resp.getWriter().print("<span style=\"color: red\">Someone already uses this nickname. Please use another nickname<span/>"); }else { resp.getWriter().print("<span style=\"color: green\">This nickname can be used<span/>"); } } @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { doGet(req, resp); } }
- 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-name>UserServlet</servlet-name> <servlet-class>com.ajax.servlet.UserServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>UserServlet</servlet-name> <url-pattern>/UserServlet</url-pattern> </servlet-mapping> </web-app>
- 效果展示
-
使用jQuery封装好的ajax实现
- ajax.jsp
<%-- Created by IntelliJ IDEA. User: admin Date: 2020/11/5 Time: 15:35 To change this template use File | Settings | File Templates. --%> <%@ page contentType="text/html;charset=UTF-8" language="java" %> <html> <head> <title> </title> <script type="text/javascript" src="statics/js/jquery-3.4.1.js"></script> <script> //失去焦点调用函数 function isyes() { //封装好的ajax请求 $.ajax({ //请求url url: "UserServlet", //请求的方式 method: "get", //请求的数据,以json形式传递 data: {"username":$("#username").val()}, //请求的数据类型 dataType: "text", //请求成功调用的函数 success: function (data) { $("#tip").html(data); }, //请求失败调用的函数 error: function (data) { $("#tip").html(data); } }) } </script> </head> <body> <form> <label> <input type="text" name="username" id="username" οnblur="isyes();"> </label><span id="tip"></span> <p><input type="submit" value="提交"></p> </form> </body> </html>
- 服务器后台代码与web.xml配置不变,
- 效果展示
- 很明显,jquery封装的ajax确实减少了代码冗余
-
2,JSON(JavaScript Object Notation)
-
什么是JSON?
- 一种轻量级的数据交换格式
- 采用独立于语言的文本格式
- 通常用于在客户端和服务器之间传递数据
-
JSON的优点有哪些?
- 轻量级交互语言
- 结构简单
- 易于解析
-
JSON实例语法:
{ "username":"missLi", "age":21, "address":"云南省昭通市盐津县兴隆乡", "phone":"18184278740", "weight":"60kg", "sex":"男", "goWhere":[ "云南", "四川", "广东", "湖南", "广西", "贵州", "北京" ] };
-
json数据如何在web页面展示
- 直接使用返回的数据的名字. 点他的属性名,类似于对象的获取‘
- 例如返回的数据格式是这样的
{ "username":"missLi", "age":21, "address":"云南省昭通市盐津县兴隆乡", "phone":"18184278740", "weight":"60kg", "sex":"男", "goWhere":[ "云南", "四川", "广东", "湖南", "广西", "贵州", "北京" ] };
- 请求方式
<script type="text/javascript" src="statics/js/jquery-3.4.1.js"></script> <script> //失去焦点调用函数 function isyes() { //封装好的ajax请求 $.ajax({ //请求url url: "UserServlet", //请求的方式 method: "get", //请求的数据,以json形式传递 data: {"username":$("#username").val()}, //请求的数据格式 dataType: "json", //请求成功调用的函数 success: function (data) { $("#tip").html(data); //提取json数据中的属性 let name = data.name; let age = data.age; }, //请求失败调用的函数 error: function (data) { $("#tip").html(data); } }) } </script>
3,Ajax拓展
- jQuery的Ajax方法
- $.ajax()
- $.get()
- $.post()
- $.getJSON()
- .load()
- ……
结语:javaweb是整个学习开发的过程中最痛苦的一点,以后春天就来了
4,操作Linux系统
linux常用指令
shutdown -h now #关机
ls #查看文件或者文件夹
ls –l #以列表的形式查看文件
ls –a #可以查看隐藏的文件
ls –l –a #以列表的形式查看文件,包括隐藏文件
ll #以列表的形式查看文件
pwd #查看当前目录
cd .. #返回上一级目录
cd / #进到根目录
cd home #进到家目录(查看普通用户)
cd ~ #进到超级管理员的家目录
mkdir #文件夹名称 创建单个文件夹
mkdir -p a/b/c #创建多级文件夹
clear #清屏
rmdir #删除文件夹
rm -rf #删除多级文件夹
touch #创建文件
rm -r #删除文件之前询问一下,回答yes删除,回答no不删除
#移动mv move缩写: 文件名称 目的地
mv 修改名字:
mv 原名称 新名称
拷贝cp copy缩写: cp 第一个文件原目的地 目的地
Linux 文件操作命令
head 文件名 查看前10行内容
head –3:查看前3行
tail 文件名 查看后10行内容
tail –F:动态加载查看文件的内容变化
tail –3:查看文件后3行的内容
vi命令
vi 文件名称 :打开文件 相当于notepad
vim 文件名称:打开文件 相当于notepad++
按下i键 insert缩写 进入编辑模式 在某个文字之前写内容
按下a键 after缩写 进入编辑模式 在某个文字之后写内容
按下 Esc 键 :退出编辑模式
按下 :冒号,进入命令模式
按下:wq 保存并退出
按下:q 直接退出
按下:x 直接退出
按下:q! 退出但不保存:
按下:w 保存
Linux权限操作命令
su 用户名 切换用户
exit 退出上一层用户来操作
whoami 查看当前用户UID
groups 查看当前用户所在的用户组GID
id 查看用户ID和组ID
创建新用户 :useradd 用户名
创建新用户并制定用户ID:useradd -u 1001 用户名
tail /etc/passwd查看用户信息
passwd [用户名]:修改用户密码
userdel 用户名 :删除用户
usermod -l 新用户名 老用户名
usermod -g 新组名称 用户名
groupadd 组名
groupmod -g 新组ID 现有用户名
drwxr-xr-x
d文件夹
rwx 拥有者u用户的权限 ,可读r,可写w,可执行x
r-x 组用户g,可读,不可写,可执行
r-x 其他用户o,可读,不可写,可执行
权限模式
chmod –r U+X,G+W 文件名
数字模式
chmod 753 –r 文件名
认识linux文件夹
/ #linux系统根目录
/bin #系统启动时需要执行二进制文件
/dev #设备文件目录
/etc #操作系统的配置文件目录
/home #用户信息存放的目录,用户的默认工作目录
/user #程序和数据存放目录
/var #包含在正常操作中被改变的文件,假脱机文件,记录文件,临时文件和页格式化文件
使用Xshell连接远程服务器或者本地虚拟机
-
下载Xshell:https://www.netsarang.com/zh/free-for-home-school
-
-
新建会话连接
-
输入服务器或虚拟机的连接信息
-
输入服务器的用户名跟密码
-
连接成功后显示会用户名
-
-
- :出现用户名代表连接成功
使用xftp连接服务器和上传文件至服务器
-
下载xftp:https://www.xshellcn.com/xiazai.html
-
安装后通过xshell直连xftp
-
首先连接xshell
-
点击这个浅绿色的小文件夹图标直接发起连接
-
连接成功
-
-
文件上传:将本地文件拖拽至服务器的目标目录即可