Mysql
目录结构
- 安装目录
D:/Mysql
- 数据目录
C:/ProgramData/Mysql/Mysql Server 5.5/data
点击mysql文件夹,进入里面看看
MySQL登录
- 登录本机
mysql -uroot -p
- 登录其他机器
mysql -h127.0.0.1 -uroot -p
MySQL表的设计
# 旅游路线种类表
CREATE TABLE tab_category(
cid INT PRIMARY KEY AUTO_INCREMENT,
cname VARCHAR(100) NOT NULL UNIQUE
)
#一对多
CREATE TABLE tab_route(
rid INT PRIMARY KEY AUTO_INCREMENT,
rname VARCHAR(100) NOT NULL UNIQUE,
price DOUBLE,
rdate DATE,
cid INT,
FOREIGN KEY cid REFERENCES tab_categroy(cid)
)
#多对多
CREATE TABLE tab_user(
uid PRIMARY KEY AUTO_INCREMENT,
username VARCHAR(100) UNIQUE NOT NULL,
password VARCHAR(100) NOT NULL,
birthday DATE,
sex CHAR(1) DEFAULT '男',
telephone VARCHAR(11),
email VARCHAR(100)
)
CREATE TABLE tab_favorite(
rid INT,
uid INT,
date DATETIME,
PRIMARY KEY(rid,uid),
FOREIGN KEY(rid) REFERENCES tab_route(cid),
FOREIGN KEY(uid) REFERENCES tab_user(uid)
)
数据库范式
设计关系型数据库,需要遵循不同的范式要求,设计出合理的数据库,一般达到第三范式数据库设计就很合理了,越高范式数据库冗余越小
- 第一范式:每一列都是不可分割的原子项
但是只满足第一范式数据表仍然存在问题:
- 第二范式:在满足第一范式基础上,非码属性必须完全依赖于候选码
在这里,第二范式所说的要在满足第一范式的基础上消除部分依赖,其实就是这里姓名、系名和系主任依赖学号就好了,没必要依赖(学号,课程名称)
因此做下面的优化
得到最终表
但是,也只是解决了第一个问题(存在严重的数据冗余,系名和系主任重复),第二个和第三个问题没有解决 - 第三范式:在满足第二范式基础上,任何非主属性不依赖其他非主属性(消除传递函数依赖)
改进如下:
看看问题解决了吗?
数据库的备份和还原
1.命令行
备份:
<!--mysqldump -uroot -proot 数据库名 > 保存路径-->
mysqldump -uroot -proot db1 > /home/hadoop/apps/mysql/mydatabase
还原:
<!--假设事先已经删除数据库-->
create database db1
use db1
source /home/hadoop/apps/mysql/mydatabase
子查询
① 子查询的结果是单行单列的,使用=、>、<来判断
<!--SELECT AVG(salary) FROM emp)这个子查询的值为5000-->
SELECT * FROM emp WHERE emp.salary =(SELECT AVG(salary) FROM emp))
② 子查询的结果是多行单列,使用in运算符来判断
<!--SELECT id FROM emp WHERE name='财务部' or name='销售部'这个子查询得到的值是2,3,4-->
SELECT * FROM emp WHERE emp.dept_id in (SELECT id FROM emp WHERE name='财务部' or name='销售部' )
③ 子查询的结果是多行多列
<!--当然也可以说还有普通的内连接-->
select * from dept t1,(select * from emp where emp.join_date>'2011-1-1') t2 where t1.dept_id=t2.dept_id)
事务
-
概念:如果一个包含多个步骤的业务操作,被事务管理,那么这些操作作为一个整体要么都执行成功,提交事务,要么中途失败回滚到原点
-
操作:
<!--开启事务--> start transaction <!--张三账户增加500,李四账户减少500,这里有中文字符,所以肯定会出错--> update account set balance=balance+500 where name='张三' 出错了... update account set balance=balance-500 where name='李四' <!--如果失败,回滚事务--> rollback <!--如果没有问题,提交事务;如果没有提交事务,那么当前窗口可以查看到数据被修改了,但是如果关闭窗口,数据会被回滚--> <!--增删改语句默认都是自动提交事务,也就是说执行后永久保存--> commit
-
事务提交两种方式:
① 自动提交
② 手动提交<!--查看事务提交方式 0--手动提交 1--手动提交 --> select @@autocommit; <!--修改事务提交方式--> set @@autocommit=0; <!--设置为手动提交进行增删改后需要commit提交-->
MySQL默认是自动提交,oracle默认是手动提交
-
事务四大特性
① 原子性:事务是最小单位,要么一起成功,要么一起失败
② 持久性:当事务提交或回滚,数据库持久保持数据
③ 隔离性:各个事务之间互相独立,互不影响(其实这个很难做到,所以才有事务的隔离级别)
④ 一致性:事务操作前和操作后,数据总量不变 -
事务的隔离级别
当多个事务操作同一批数据,不可避免会相互影响,这时就需要不同的隔离级别
① read uncommited(读未提交):脏读、虚读、幻读这个事务安全级别最低,如果先执行A事务,然后切到B事务,那么允许A事务读取B事务还没有提交的数据,那么会造成脏读、虚读、幻读
② read commited(读已提交):虚读、幻读 (Oracle)
如果先执行A事务,然后切换到B事务,那么可以保证A不会读到B事务中未提交的数据,但是仍然会有虚读、幻读
③ repeatable read(可重复读):幻读 (MySQL)
如果执行A事务,那么A事务先对读出的数据加锁,只要等到A事务执行完毕提交了,才能切换到B事务,避免了虚读,但是仍然会有幻读,因为幻读涉及到修改数据
④ serializable(可串行化)
A事务执行提交完毕才能执行B事务,解决所有问题
<!--查询隔离级别--> select @@tx_isolation <!--设置隔离级别:set global transaction isolation level 隔离级别字符串--> set global transaction isolation level repeatable read
用户管理
-
用户管理
<!--在安装MySQL初会创建mysql库,里面有user表记录用户信息--> use mysql; select * from user;
创建用户
create user ‘用户名’@‘主机名’ identity by ‘密码’
删除用户
drop user ‘用户名’@‘主机名’
修改用户密码
update user set password=password(‘新密码’) where user=‘用户名’
如果忘记了MySQL的root密码怎么办?
cmd (右键以管理员的身份运行)
关闭MySQL服务net stop mysql
使用无验证方式启动MySQL
mysqld --skip-grant-tables
重新打开一个cmd窗口,直接输入mysql回车
修改密码use mysql; update user set password=password('123') where user='root';
右键打开任务管理器,结束任务进程
启动mysql服务net start mysql
使用新的mysql账户密码登录
-
授权
查询权限--%表示可以登录任意主机 show grants for 'lisi'@'%'
查看root用户的权限show grants for 'root'@'%'
授予权限--grant 权限名 on 数据库名.表名 to '用户名'@'主机名' grant select on db3.account to 'lisi'@'%';
撤销权限-- reovke 权限名 on 数据库名.表名 from '用户名'@'主机名' revoke select on db3.account from 'lisi'@'%';
JDBC
jdbc一些小知识
-
DriverManager 驱动管理对象
它的作用是注册驱动,获取数据库连接
DriverManager有个方法:static void registerManager(Driver driver)
通过查看源码可以发现static{ try{ //它会将导入Class.forName("com.mysql.jdbc.Driver")加载二进制生成对象 java.sql.DriverManager.registerManager(new Driver); }catch(SQLException e){ throw new RuntimeException("Can't register driver"); } }
但是在mysql5后可以不用写Class.forName(“com.mysql.jdbc.Driver”)
因为mysql-conncetor-java.jar驱动文件会读取java.sql.Driver文件中的内容
数据库连接池(C3P0)
- 数据库连接池的配置文件中的maxPoolSize问题
DataSource ds=new ComboPooledDataSource(); for(int i=0;i<11;i++){ Connection conn=ds.getConnection(); System.out.println(i+":"+conn); }
连接设置最大10个,11个超出了DataSource ds=new ComboPooledDataSource(); for(int i=0;i<11;i++){ Connection conn=ds.getConnection(); System.out.println(i+":"+conn); //第五个归还连接 if(i==5){ conn.close(); } }
结果没有报错
- 数据库连接池的配置文件中的maxPoolSize问题
JDBCTemplate
-
JDBCTmeplate是spring框架为了进一步简化jdbc的开发而设计的
-
使用
① 导入jar包(lib目录下)
② 创建JDBCTemplate对象JdbcTemplate template=new JdbcTemplate(DataSource ds);
③ 查询单个数据
JdbcTemplate template=new JdbcTemplate(DataSource);
String sql="select * from emp where id=?";
Map<String,Object> map=template.query(sql,1);
查询一组数据封装到map中
JdbcTemplate template=new JdbcTemplate(DataSource);
String sql="select * from emp";
List<Map<String,Object>> list=template.queryForList(sql);
查询一组数据封装到list集合,里面对象就是emp类型
JdbcTemplate template=new JdbcTemplate(DataSource);
String sql="select * from emp";
List<Emp> list=template.query(sql,new BeanPropertyRowMapper<Emp>(Emp.class));
查询一组记录的个数
JdbcTemplate template=new JdbcTemplate(DataSource);
String sql="select count(id) from emp";
Long count=template.queryForObject(sql,Long.class);
Tomcat
-
Tomcat的安装
Tomcat的安装非常简单,直接解压即可,删除直接把软件目录删除就可以了。
解压完成后可以看到:
-
Tomcat启动
在bin目录下直接双击startup.bat
在浏览器上输入http://127.0.0.1:8080即可访问
启动过程中可能存在的问题:
① 双击startup.bat一闪而过
java的环境变量没有配置好
② 启动报错
错误信息需要在logs目录下查看
解决方法一:直接删除占用8080端口的进程
Windows下输入命令netstat ano
打开任务管理器,选择“进程”|“选择列”
勾选进程标识符
找到5488这个进程,然后杀死即可
方法二:修改自身端口号,不要使用8080端口,使用别的
打开conf目录
修改为任意端口
这里两个redirectPort是一样的端口号一般将Tomcat端口号修改为80,80端口号是http默认端口号,使用它就可以不用输入端口号就可以访问,要改的话,是上面的第二张图的Connector
- 关闭Tomcat
① 正常关闭:双击bin目录下的shutdown.bat或者在命令行窗口Ctrl+C
② 强制关闭:关闭窗口
- 关闭Tomcat
Tomcat部署项目
-
直接将项目文件放入webapps目录下即可
-
将项目打成war包
自动解压缩
自动生成相应目录
如果要删除项目,直接删除war包即可 -
不拷贝文件完成部署
打开conf/server.xml,找到标签,在标签下写上<Context docBase="D:/hello" path="/hehe/" />
重启Tomcat,访问http://localhost/hehe/hello.html
-
在conf/Catalina/localhost目录下
在该目录下创建bbb.xml,在里面编辑<!--这里不需要写Path--> <Context docBase="D:/hello">
保存后重启Tomcat,访问http://bbb/hello.html
使用这种方式还有一个好处是热部署,当不需要该项目资源文件,直接将bbb.xml修改为bbb.xml.bak即可,重新刷新页面会发现404
而server.xml修改后要重启服务器才能生效,而且server.xml是全局,修改后可能会影响其他项目
Tomcat项目目录结构
-
在webapps目录下,存放多个项目,每个项目的目录里存放静态资源和动态资源
具体目录结构如下:webapps |--LogWeb (LogWeb项目) |-- 静态资源文件(HTML、css、js等) |-- WEB-INFO |-- web.xml (web项目的核心配置文件) |-- classes目录 (放置编译好的字节码文件) |-- lib 放置依赖的jar包
将Tomcat集成到IDE中
创建完项目后发现都自动生成对应的javaee项目目录
启动项目,Tomcat 8.3.31已经帮你添加好了
之后启动的话,点击Tomcat 8.3.31的启动按钮即可
如果在这里新增加了一个hello.html
没有重启Tomcat,直接报404错误
这是需要重启Tomcat才能访问,可以在刚刚的Run|Edit Configuration…里配置
配置后在WEB-INFO中添加hehe.html
这个就是热部署,非常方便
Servlet
Servlet入门
- 当浏览器请求,为了响应请求需要执行java类,但是这些java类不是自动运行的,它没有main方法,需要依赖Tomcat执行。但是并不是随便一个类都能够被Tomcat识别并执行,它需要遵守一定的规则(其实在java里就是实现某个接口),而这里的规则(接口)就是Servlet
- 具体实现
① 创建Servlet类
② 配置WEB-INFO目录下的web.xml
在浏览器上输入http://loaclhost/demo1
页面上没有任何输出,但是在控制台上输出了“Hello Servlet”,说明service()方法被调用了
另外,虽然直接访问http://loaclhost/demo1不加项目名比较方便,但是后期项目多就不合适了,因此需要配置虚拟目录
这样访问就需要http://localhost/day13_tomcat/demo1
Servlet执行原理
Servlet的生命周期
servlet生命周期详解:
① init() 创建Servlet
Servlet是什么时候被创建的?
另外,由于Servlet的init()只执行一次,因此他是单例对象,那么就可能存在多线程访问的安全问题
解决方法:
在Servlet中尽量少定义成员变量,改为定义局部变量,万不得已要定义成员变量,就不要修改它,只是读取
② 提供服务:service()
每次访问都会执行一次
③ 销毁: destroy()
它的执行是在servlet被销毁之前,可以把他理解为是遗言,释放资源
Servlet注解配置
- 之前每次创建一个servlet就在web.xml中配置一次,很麻烦,如果后期servlet很多就很混乱了,因此,Servlet3.0支持注解配置
- 具体步骤
① 使用Servlet3.0版本
② 创建Servlet类
③ 为Servlet类添加注解
因此可以简写为
@WebServlet里面也可以配置多个路径
@WebServlet里其他配置
Tomcat与IDE的配置
- IDEA会为每个Tomcat部署的项目单独创建一份配置文件
找到这个目录
进入conf
进入Catalina,它是规定项目day13_serlvet怎么部署和虚拟目录等信息
这个就是之前介绍的热部署方式
这里面记录了项目打成war包的存放路径,打开看看
- 项目存储位置(实际上是同一个项目存在两个地方)
① 工作空间项目
② Tomcat部署项目
它位于我们刚刚打开的项目位置下
点开day13_servlet看看(工作空间)
点开out看看,里面存放项目被打成war包
在这里,如果我们在IntelIJ中给项目添加上aaa.html,会发现他自动部署到Tomcat部署目录下
所以说,Tomcat是去查找Tomcat部署项目目录,Tomcat部署项目目录又对于这工作空间目录下的web目录(里面的WEB-INFO里的classes目录是对于工作空间下src目录编译后的java文件) - WEB-INFO工作目录中的文件不能被浏览器直接访问
- Tomcat中如何断点调试
Servlet的体系结构
在使用Servlet接口,必须实现给定的五个方法(init等),但是我们实际上只需要使用里面的Service()方法,那么可以使用它的子类
Servlet 接口
|
GenericServlet 抽象类
|
HttpServlet 抽象类
使用GenericServlet发现代码清爽了很多
如果发现需要init()方法也可以复写
但是,我们一般使用HttpServlet,它对我们的http协议进行封装
这里继承HttpServlet不需要重写service( )方法,查看源码可以发现
这样就创建HttpServlet类
HTTP
HTTP
- 超文本传输协议
特点:
① 基于TCP/IP的高级协议
② 默认端口80
③ 基于请求/响应模型:一次请求一次响应
④ 无状态:每次请求相互独立,不能相互通信
HTTP版本:
① 1.0:请求一次,响应后服务器和浏览器就断开连接
② 1.1:请求,响应后等一会,如果再有数据传输来,就复用连接 - 请求消息数据格式
① 请求行
② 请求头
③ 空行
④ 请求体
- 响应消息数据格式
① 响应行
② 响应头
③ 响应空行
④ 响应体
Request
-
Request和Response原理
-
Request继承体系
-
Requset功能
① 获取请求消息数据
获取请求行信息
GET /day14/demo1?name=zhangsan HTTP/1.1//获取请求方式 String getMethod() //获取虚拟目录:/day14 String getContextPath() //获取Servlet路径:/demo1 String getServletPath() //获取GET请求数据 name=zhangsan String getQueryString() //获取请求URI /day14/demo1 String getRequestURI() //返回/day14/demo1 StringBuffer getRequestURL() //返回http://localhost/day14/demo1 //获取版本号 HTTP/1.1 String getProtocol() //获取客户机的IP String getRemoteAddr()
获取请求头
//获取请求头 String getHeader(String name) //获取所有请求头名称 Enumeration<String> getHeaderNames()
获取请求体
//获取字符流 BufferReader getReader() //获取字节流 ServletInputStream getInputStream()
② 其他功能
获取请求参数(通用方式)//根据参数获取参数值,比如name=zhangsan String getParameter(String name) //根据参数获取参数值数组,比如hobby=run&hobby=sing String[] getParameterValues(String name) //返回参数名称 Enumeration<String> getParameterNames() //返回所有参数的键值对集合 Map<String,String[]> getParameterMap()
请求转发:在服务器里做资源跳转
两种方式:
第一种://请求demo1,但是内部却将请求转发到demo2,这里使用同一个连接,可以看到forward()里原封不动地传递request和response request.getRequestDispatcher("/demo2").forword(request,response)
特点:
浏览器地址栏不变
只能转发属于Tomcat里的资源
使用一次请求共享数据:
一次请求,从AServlet转发到BServlet,是在一个request域中,因此可以创建一个域来让AServlet存入数据,BServlet取出数据void setAttribute(String name) String getAttribute() void removeAttribute(String name)
因为转发是一次请求,所以共享数据一般用于转发
获取ServletContext对象ServletContext getServletContext()
中文乱码问题
- Tomcat 8已经解决了Get方式的中文乱码问题
- POST方式就需要解决
Response
-
响应消息数据格式
- 响应行
HTTP/2.0 200 OK
响应状态码
服务端告诉浏览器本次请求响应的状态
1xx:服务端接收客户端信息发现客户端发到一半不发了,发来询问客户端怎么回事?
2xx:成功
3xx:重定向 302(重定向) 304(访问缓存)
4xx:客户端错误 404(请求资源路径不在) 405(请求方式没有对于的doXXX方法)
5xx:服务器内部错误 - 响应头
常见响应头
① Content-Type:响应体什么格式,什么编码
text/html;charset=utf-8
② Content-disposition:服务器告诉客户端以什么方式打开响应体
in-line:在当前页面打开(默认)
attachment;filename=xx:以附件的形式打开响应体,常用于文件下载 - 响应空行
- 响应体
- 响应行
-
Response对象
- 设置响应行
setStatus(int status) - 设置响应头
setHeader(String name,String value) - 设置响应体
getWriter( )
getOutputStream( )
- 设置响应行
-
重定向
response.sendRedirect("/day15/demo1"); //它的实现核心代码是: //response.setStatus(302) //response.setHeader("location","/day15/demo1")
特点:
① 地址栏改变
② 可以访问服务器以外的资源
③ 两次请求,request不能共享数据 -
相对路径和绝对路径
① 相对路径:通过路径不能唯一确定资源
以 . 开头的路径,比如:
当前路径:./demo1
上一级路径: …/demo1
② 绝对路径:通过路径能唯一确定资源
以 / 开头的路径
/day15/demo1如果是给浏览器使用个,那么路径需要加虚拟目录(推荐使用动态获取虚拟目录的方式);如果是给服务器内部跳转用,不用加虚拟目录
-
Response中文乱码问题
-
生成图片验证码
//创建一张内存图片 BufferedImage image=new BufferedImage(width,height,TYPE_INT_RGB); //创建画笔 Graphics g=image.getGraphics(); //画笔一系列操作 ImageIO.write(image,"jpg",response.getOutputStream())
ServletContext对象
-
它代表整个web应用程序,通过它可以和外部容器(Tomcat)通信(交互数据)
-
获取
//方式一:通过request来获取 request.getServletContext() //方式二:通过HttpServlet获取 this.getServletContext()
-
作用
-
获取MIME类型
//可以根据传入的文件名(如a.jpg),获取文件类型,之所以可以这样是因为在Tomcat中 //的conf/web.xml中定义了所以MIME类型 String getMineType(String filename)
-
共享数据
//因为ServletContext存在的生命周期最长(从服务器启动到关闭),所以这个的使用要很谨慎 void setAttribute(String key,String name) String getAttribute(String key) void removeAttribute(String key)
-
获取资源路径
-
-
文件下载
protected void doPost(HttpServletRequest request,HttpServletResponse response){ String filename=response.getParameter("filename"); ServletContext context=this.getServletContext(); FileInputStream fis=new FileInputStream(context.getRealPath("/img"+filename)); response.setHeader("Content-Type",context.getMinaType(filename)) response.setHeader("Content-Disposition","attachment;filename="+filename) OutputStream os=response.getOutputStream(); byte[] bytes=new byte[1024]; int len=-1; while((len=fis.read(bytes))>0){ os.write(bytes,0,len); } //os由response打开,会自动关闭 fis.close(); }
如果filename是包含中文,那么可能会有乱码,通过以下代码解决:
//agent:response.getHeader("user-agent")取得 public String getFileName(String agent,String filename) throw UnsupportedEncodingException{ if(agent.contains("MSIE")){ //IE浏览器 filename=URLEncoder.encode(filename,"utf-8"); filename=filename.replace("+",""); }else if(agent.contains("Firefox")){ //火狐浏览器 BASE64Encoder base64Encoder=new BASE64Encoder(); filename="?utf-8?B"+base64Encoder.encode(filename.getBytes("utf-8"))+"?="; }else{ //其他浏览器 filename=URLEncoder.encode(filename,"utf-8"); } return filename; }
Cookie
-
快速入门
//创建cookie Cookie cookie=new Cookie(String name,String value) //服务端发送cookie response.addCookie(Cookie cookie) //服务端获取浏览器携带的cookie Cookie[] cookies=request.getCookies()
-
实现原理
-
cookie细节
-
cookie存储多长时间
//存储10秒 cookie.setMaxAge(10) //关闭浏览器清除cookie(默认,cookie存储在浏览器内存中) cookie.setMaxAge(-1) //服务器告知浏览器删除cookie cookie.setMaxAge(0)
-
cookie能不能存中文
Tomcat 8之前不支持中文,要将中文用URL编码;Tomcat 8后支持中文 -
cookie共享问题
默认情况下,在Tomcat中不同web项目是不能cookie共享
如果要共享,可以设置//默认是设置为cookie.setPath("/项目名"),只能被该项目下共享 cookie.setPath("/")
此外,不同Tomcat中的web项目也可以设置共享
//它需要一级域名相同,news.baidu.com中news是二级域名,.baidu,com是一级域名 cookie.setDomain(".baidu.com")
-
cookie的特点和作用
特点:
① cookie存储在浏览器
② 不同浏览器对cookie大小有限制
作用:
① cookie一般用于少量不敏感信息
② cookie可以用于在不登录情况下做些设置
-
JSP
JSP原理
那么如何查看jsp就是一个Servlet类呢?
查看index_jsp.java
JSP脚本
- <% 代码 %>
在service()里能写的java代码,都可以写在里面,通过查看index_jsp.java可以发现
这个代码是写在这个里面的
- <%! 代码 %>
一般是定义成员变量、静态代码块,它生成的代码位于index_jsp.java的成员变量位置,这种代码使用的比较少,因为在servlet中最好不要定义成员变量
- <%= 代码 %>
定义的代码会输出到页面上
JSP内置对象
- 在jsp中可以直接获取无需创建的对象
Session
-
Session入门
//获取Session HttpSession session=request.getSession(); //使用Session对象 Object getAttribute(String name) setAttribute(String name,Object object) removeAttribute(String name)
-
Session 细节
-
客户端关闭,服务端不关闭后再次访问服务端,两次Session是否一样?
默认是不一样的,但是可以设置
结果
-
客户端没关闭,服务端关闭,再次重启服务器两次Session一样吗?
不是同一个,但是要保证两个Session数据一致
Session钝化:将Session对象序列号到磁盘上
Session活化:将Session对象反序列化恢复IntelIJ完成不了本地session的钝化,要借助Tomcat完成
打开项目的Tomcat部署项目,找到项目war包里的内容,复制一份放到桌面上
将这三个文件打成war包
然后将这个war包丢到Tomcat的webapps目录下
停掉IDE里的Tomcat,然后启动本地的Tomcat,就是双击startup.bat文件
访问http://loaclhost/day16/sessiondemo1在session中存入数据,访问http://localhost/day16/sessiondemo2取出session中的数据并打印
说明本地Tomcat启动正常,并且运行良好
现在我们把Tomcat正常关闭,看看session是不是会被正常序列号到硬盘上
双击shutdown.bat,正常关闭Tomcat
查看Tomcat的work目录,里面存放运行的临时文件
然后启动Tomcat,观察该目录下的SESSIONS.ser
再次访问http://localhost/day16/sessiondemo2,打印“Hello Session”,session的活化成功在IDE中只能完成session钝化,不能活化session
打开IDE的目录
里面有一个work目录
访问http://loaclhost/day16/sessiondemo1
再次访问http://loaclhost/day16/sessiondemo2,打印“null”因为后期都是在Tomcat里部署项目,所以完全不用担心IDE无法活化session
-
session什么时候被销毁?
① 服务器关闭
② session.invalidate( )
③ 默认失效时间是30分钟
在Tomcat的web.xml中可以配置
-
-
Session特点
- Session用于存储一次会话的多次请求的数据,存储在服务端
- Session可以存储任意类型,任意大小的数据,只要内存放的下
JSP指令
-
JSP指令是用于配置页面和导入资源文件
-
快速入门
格式:<%@ 指令名称 属性名1=属性值1 属性名2=属性值2 %>
分类:
① page :配置JSP页面//等同于response.setContentType():设置响应体的MIMA类型和字符集 contentType="text/html;charsetset=UTF-8" //比较鸡肋的属性,写上吧 language="java" //jsp被编译后是通过out输出到页面上的,out是有缓冲区的,可以设置,默认8KB buffer="16kb" //jsp页面上会写一些java代码,用于导包 import="java.util.List" //errorPage是加在当前页面上的,页面可能会出错,出错就跳转到404.jpg页面 //isErrorPage是加在404.jsp页面,默认是false //如果为true,那么可以使用内置对象exception //String error =exception.getMessage(); //一般是将错误信息输出到日志上 errorPage="404.jsp" isErrorPage="true"
② include:包含页面和导入资源文件
③ taglib:导入资源文件库
一般是导入标签库,比如JSTL标签库
创建WEB-INF/lib,为lib目录添加Library
引入标签库
ELT表达式
- ELT表达式是为了简化和替换JSP页面上的java代码
- 快速入门:
语法:${ 表达式 } <!--忽略ELT表达式--> \${ 表达式 }
- 使用
-
获取值
el表达式只能从域对象中获取值
${域名称.键名}//如果取不到值,在页面上没有任何显示 ${pageScope.name} ${requestScope.name} ${sessionScope.name} ${applicationScope.name} //从最小的域开始查起,直到找到为止 ${name}
-
获取对象
//定义User对象 public class User{ private String name; private Date birthday; public void setName(String name){ this.name=name; } public String getName(){ return name; } public String getbirStr(){ SimpleDateFormat sdf=new SimpleDateFormat("yyyy-MM-dd"); return sdf.format(birthday); } } //在jsp页面上可以通过这种方式取得,它的本质是通过getXXX来取值的 ${user.name} //既然可以通过getXXX来这么取,那么只要在User中符合getXXX就可以了,比如:getbirStr() ${user.birStr} //获取list集合,但是如果角标越界了,它不会报错,在页面上没有显示 ${list[0]} //获取map值,name是key ${map["name"]} //字符串、数组、集合判断是否为空或null ${empty list} //如果不为空 ${not empty list}
-
JSP内置对象
- pageContext
- request
- session
- application
- page
- out
- config
- response
- exception
JSTL
-
JSTL是用来简化java代码
-
快速入门
导入包
//test:如果为true,显示标签的内容;为false,不显示标签内容 //<c:if>标签没有else的情况,只能再写一份 <c:if test="${not empty list}"> 遍历集合 </c:if> //相当于java中的switch <c:choose> <c:when test="${season==1}">春天</c:when> <c:when test="${season==2}">夏天</c:when> <c:when test="${season==3}">秋天</c:when> <c:when test="${season==4}">冬天</c:when> <c:otherwise>数字输入有误</c:otherwise> </c:choose> /* 相当于java中的for循环 for(int i=0;i<10;i++){ } begin:初始值 end:结束值 var:临时变量(i) step:步长 varStatus:循环状态对象 index:容器中的索引从0开始 count:循环次数从1开始 */ <c:forEach begin="1" end="10" var="i" step="2" varStatus="s"> ${i} ${s.index} ${s.count} </c:forEach> /* 相当于java代码中的 foreach(String item:list){ } items:容器对象 var:临时变量 varStatus:循环状态对象 index:容器中的索引从0开始 count:循环次数从1开始 */ <c:forEach items="${list}" var="str" varStatus="s"> ${str} ${s.index} ${s.count} </c:forEach>
过滤器
-
快速入门
具体使用:
-
过滤器细节
- 可以使用web.xml配置
- 过滤器请求流程
- 过滤器生命周期
public class FilterDemo1 implements Filter{ //服务器启动后Tomcat创建,只会执行一次,用于加载资源 public void init(FilterConfig filterConfig) throw ServletException{ } //每次请求执行一次,可以执行多次 public void doFilter(ServletRequest servletRequest,ServletResponse servletResponse,FilterChain filterChain){ System.out.println("拦截器被执行了..."); filterChain.doFilter(servletRequest,servletResponse); } //服务器正常关闭执行,用于释放资源 public void destroy(){ } }
- 过滤器拦截配置
-
拦截路径配置
① 具体资源路径:/index.jsp
② 拦截目录:/user/*
③ 后缀名拦截:*.jsp
④ 拦截所有资源:/*
实例:
过滤器FilterDemo4
-
拦截方式配置
① dispatcherTpyes.REQUEST:浏览器直接请求资源被拦下(默认)
②dispatcherTpyes.FORWARDS:只有Servlet内部互相转发被拦下
③dispatcherTpyes.INCLUDE:包含访问资源
④dispatcherTpyes.ERROR:jsp页面发生错误跳转到错误页面被拦下
⑤dispatcherTpyes.ASYNC:异步发送被拦下
-
- 拦截器链先后顺序
- 注解配置:按照类名字符串比较,值小的先执行
如:AFilter和BFilter,AFilter先执行 - web.xml文件配置,谁先定义谁执行
- 注解配置:按照类名字符串比较,值小的先执行
- 可以使用web.xml配置
-
登录案例
@WebFilter("/*") public class LoginFilter implements Filter{ public void init(FilterConfig filterConfig) throw ServletException{ } public void doFilter(ServletRequest servletRequest,ServletResponse servletResponse,FilterChain filterChain){ //直接使用ServletRequest,它是没有getRequestURI()的方法 HttpServletRequest request=(HttpServletRequest)req; String uri=request.getRequestURI(); //在如果仅仅包含/login.jsp和/loginServlet,那么访问的页面就会乱,没有验证码,因为请求被拦截了,index.jsp页面所需要的资源加载不出来 if(uri.contains("/login.jsp") || uri.contains("/loginServlet") || uri.contains("/css/") ||uri.contains("/js/") ||uri.contains("/fonts/") ||uri.contains("/checkCodeServlet/") ){ //用户就是想登录,放行 chain.doFilter(servletRequest.servletResponse) }else{ //用户可能已经登录过了,或者是非法登录 Object user=request.getSession().getAttribute("user") if(user!=null){ //已经登录过,放行 chain.doFilter(servletRequest.servletResponse) }else{ request.setAttribute("login_msg","你尚未登录") request.getRequestDispatcher("/login.jsp").forward(request,servletResponse) } } } public void destroy(){ } }
-
敏感词过滤
//拦截器,请求http://localhost/TestServlet?name="张三"&msg="你是傻逼"
public class LoginFilter implements Filter{
//从src目录下加载敏感词汇表,并在init()中加载
private List<String> list=new ArrayList<String>();
public void init(FilterConfig config) throw ServletException{
//获取敏感文件的目录,需要用到服务器真实路径
ServletContext servletContext=config.getServletContext()
servletContext.getRealPath("/WEB-INF/classes/敏感词汇.txt")
BufferedReader br=new BufferedReader(new FileReader(readPath))
String line=null;
while((line=br.readLine())!=null ){
list.add(line)
}
br.close()
}
public void doFilter(ServletRequest req,ServletResponse resp,FilterChain chain){
//创建代理对象,增强getParameter方法
ServletRequest proxy_req=(ServletRequest) Proxy.newProxyInstance(req.getClass().getClassLoader,req.getClass().getInterface(),new InvocationHandler(){
//当在TestServlet中实际调用request.getParameter()时,会回调到这边来
public Object invoke(Object proxy,Mehtod method,Object[] args)throws Throwable{
//是getParameter方法,需要增强,用代理对象来执行
if(method.getName().equals("getParameter")){
//代理做一些边边角角的东西,还是要实际的req对象执行
String value=(String) method.invoke(req,args)
if(value!=null){
for(String str:list){
if(value.contains(str)){
value=value.replaceAll(str,"**")
}
}
}
return value;
}
//不是getParameter()方法,不关心,直接用实际的req去执行就好
return method.invoke(req,args)
}
})
chain.doFilter(req.resp)
}
public void destroy(){
}
}
//请求的Servlet
public class LoginFilter implements Filter{
public void init(FilterConfig config) throw ServletException{
}
public void doFilter(ServletRequest req,ServletResponse resp,FilterChain chain){
String name=req.getParameter("name")
String msg=req.getParameter("msg")
//敏感词会被过滤
System.out.println(name+":"+msg)
}
public void destroy(){
}
}
监听器Listener
-
JavaWeb三大组件之一:Servlet、Filter、Listener
-
ServletContextListener:监听ServletContext对象的创建和销毁
-
监听器机制
首先得有事件——ServletContext对象的创建和销毁
然后事件发生在Tomcat(事件源)中
由ContextLoaderListener这个监听器来监听public class ContextLoaderListener implements ServletContextListener{ //监听ContextServlet对象的创建 public void contextInitialized(ServletContextEvent event){ //获取ServletContext对象 ServletContext context=event.getServletContext() //在这里介绍一个Listener的应用,读取web.xml并初始化 String value=context.getInitParameter("contextConfig") /* 在web.xml中是有如下配置 <context-param> <param-name>contextConfig</param-name> <param-value>/WEB-INF/classes/application.xml</param-value> </context-param> 其实在以后并不需要开发监听器,而是使用web.xml配置监听器 */ } //监听ContextServlet对象的销毁 public void contextDestroyed(ServletContextEvent event){ } }
有了这个监听器后,就需要将它注册到Tomcat中,负责监听ServletContext对象的创建和销毁
① 注解配置:@WebListener
② web.xml配置:<listener> <listener-class>cn.itcast.web.listener.ContextLoaderListener </listener-class> <listener>
Redies
Redies安装
- Redies下载后直接解压即可
- 先双击redis-server.exe,启动redies服务端
- 双击redies-cli.exe,启动客户端
通过命令行使用Redies
-
Redies数据存储结构
Redies的使用无非就是数据存取,因此需要了解redies的数据结构
redies存储key-value格式数据,value类型可以很丰富
key:String
value:String、map、list(有序但可重复)、set(无序但不可重复)、SortSet(有序且不可重复) -
Redies命令
//设置key-value SET name zhangsan //取值 GET name //删除键值 DEL name //设置键存活时间为1秒 EXPIRE name 1 //移除设置存活时间,使key永久有效,移除成功返回1;key不存在或者没有设置时间返回0 PERSIST name //查询符合条件(pattern)的key KEYS run* //移动name这条记录到数据库1中,成功返回1,失败返回0(比如数据库1中压根就没有name,name,那么移动失败) MOVE name 1 //返回key对于值的类型 TYPE name
通过java操作Redies
-
快速入门
- 导包:commons-pool.jar和jedis.jar
- jedis的使用和命令行的使用相同
Jedis jedis=new Jedis("localhost",6379); jedis.set("name","张三"); jedis.close();
- jedis连接池
JedisPoolConfig config=new JedisPoolConfig(); config.setMaxTotal(100); JedisPool pool=new JedisPool(config,"localhost"); //获取资源 Jedis jedis = pool.getResource(); jedis.set("name","张三"); jedis.close();
Maven
Maven了解
- Maven能帮你构建工程、管理jar包、编译代码、自动运行单元测试打包生成报表、甚至能帮你部署web项目生成站点
- maven核心功能
① 依赖管理:maven将jar包放到仓库中,编译运行时通过jar坐标查找
② 一键构建:之前我们是通过本地Tomcat完成项目编译到部署一系列操作,现在是用集成在maven的Tomcat插件完成
Maven的安装
- maven直接下载并解压即可(apache-maven-3.5.2.zip)
- 解压后如下
逐一看看每个目录
① bin
② boot
③ conf
- 复制解压后的目录(C:/my_java/apache-maven-3.5.2)
- 配置环境变量:
MAVEN_HOME:C:/my_java/apache-maven-3.5.2
Path:%MAVEN_HOME%/bin - 安装完成
Maven仓库种类
- maven仓库种类和彼此关系
① 打开C:/my_java/apache-maven-3.5.2/conf/setting.xml
图解:
maven目录结构
maven帮我们把这套标准弄好了
Maven常用命令
进入项目所在位置
命令行进入项目所在目录
<!--clean是将我们项目中编译好的信息删除掉,这个是下一个人接手上个人项目时,需要重新编译上一个人
的代码,因为不同人的机器上的环境是不一样的-->
mvn clean
<!--将核心代码(src/main/java)的代码进行编译,输出到traget目录下-->
mvn compile
查看target目录
//将核心代码和测试代码编译后输出到target目录
mvn test
//将项目打包输出到target目录下
mvn package
那为什么会被打成war包输出到target目录呢?
后退一级查看pom.xml文件
//除了做mvn package所做的事情,还将maven-helleworld-0.0.1-SNAPSHOT.war包安装到本地仓库中
mvn install
但是打开本地仓库目录
Maven生命周期
Maven的概念模型图
IDA中集成maven
打开setting
使用Maven创建项目
不使用骨架创建
创建完成后
使用Maven创建项目(不使用骨架)
推荐以后使用尽量不使用骨架
使用骨架创建web工程
创建完成
在main目录下创建java目录
默认maven是不允许java目录创建jsp页面
选择File|Project Structure…
这个了解即可,java目录下一般不要创建jsp
下面在java目录下新建com.itheima.servlet这个包,包下创建MyServlet类
打开pom.xml
也可以通过网上下载jar包
但是常用的jar包还是尽量本地仓库有
然后回头看,没有报错
启动项目
访问浏览器,报错
因为maven自身集成了Tomcat插件,虽然我们不知道他集成的插件在哪里,但是它一定要依赖本地Tomcat的jar包,因此我们直接打开本地Tomcat安装目录
maven提供了一种解决冲突依赖的方法:
Maven集成Tomcat 7插件
Maven本身集成的Tomcat是版本6,现在修改为版本7
打开pom.xml文件
这样就将Tomcat 7修改好了
那原来的Tomcat 6还在吗?
答案是肯定的,那在哪里呢?下面来验证下
而且我们也可以发现,同时运行着Tomcat 6和Tomcat 7
此外,访问http://localhost:8080/maven_web/MyServlet和http://localhost:8888/maven_web/MyServlet都是可以访问的
如果每次都要输入Tomcat 7插件的配置,一大段,很麻烦,可以提炼为动态模板
选择File|Settings…
先添加一个模板组——custom
指定maven的jdk插件
使用maven工程存取mysql
创建一个没有骨架的工程
pom.xml中配置mysql驱动包
但是是不是所有的包都要设置属性呢?
别忘了导入junit包
Linux
Linux上安装JDK
卸载Linux上自带的OpenJDK
在centOS 6.7自带jdk是openjdk
但是这不是我们想要的,卸载前要找到openjdk的位置
// -q是查看,-a是所有
rpm -qa | grep java
//这里说个小细节,对java-1.7.0-openjdk-1.7.0.79-2.5.5.4.e16.i686选中并右键,自动复制到光标处
rpm -e --nodeps java-1.7.0-openjdk-1.7.0.79-2.5.5.4.e16.i686
rpm -e --nodeps java-1.6.0-openjdk-1.6.0.35-1.13.7.1.e26_6.i686
验证是否卸载成功
安装jdk
mkdir /usr/local/java
事先上传jdk到Linux上,使用FileZilla-3.7.3
上传成功
// 不同的Linux版本不同,jdk安装需要,但有可能一些Linux已经安装好了
yum install glibc.i686
tar -xvf jdk-7u71-linux-i586.tat.gz -C /usr/local/java
cd /etc/profile
修改profile文件
JAVA_HOME=/usr/local/java/jdk1.7.0_71
PATH=$JAVA_HOME/bin:$PATH
CLASSPATH=.:$JAVA_HOME/lib.tools.jar
export JAVA_HOME PATH CLASSPATH
修改完后要重新加载下
source /etc/profile
Linux安装mysql
rpm -qa | grep mysql
rpm -e --nodeps mysql-libs-5.1/73-5.e16_6.i686
安装mysql
mkdir /usr/local/mysql
//上传的mysql安装包默认在用户目录下
cd ~
tar -xvf MYSQL-5.6.22-1.e16.i686.rpm-bundle.tar -C /usr/local/mysql
rpm -ivh MYSQL-server-5.6.22-1.e16.i686.rpm
安装依赖
yum -y install libaio.so.1 libgcc_.so.1 libstdc++.so.6
提示需要升级
yum update libstdc++-4.4.7-4.el6x86_64
准备工作做完后,安装mysql
cd /user/local/mysql
rpm -ivh MYSQL-server-5.6.22-1.el6.i686.rpm
rpm -ivh MYSQL-client-5.6.22-1-el6.i686.rpm
安装完成后验证
mysql
报错:
因为mysql服务没有启动
service mysql status
service mysql start
启动成功,可以连接,但是要输入密码
mysql -uroot -p
那默认密码是什么呢?
按照指定密码登录
提示修改密码
set password=password('123456');
退出后重新登录
这里需要设置mysql开机自动启动
chkconfig --add mysql
chkconfig mysql on
chkconfig
但是这里有个问题,就是安装后的mysql不支持远程连接,比如在Windows上通过客户端连接mysql
所以需要开启mysql远程登录
mysql -uroot -p
grant all privileges on *.* to 'root'@'%' identified by '123456';
flush privileges;
设置完成后仍然无法连接,因为Linux默认只开放22端口(ssh)
/sbin/iptables -l INPUT-p tcp --dport 3306 -j ACCEPT
//将设置永久保存到防火墙设置中
/etc/rc.d/init.d/iptables save
/etc/init.d/iptables status
这样就可以连接了
Windows上搭建Nginx+Tomcat集群
通过在本地复制两份Tomcat文件来模拟两个Tomcat,通过修改配置文件来避免端口冲突,将一份web项目的war包文件分别发送到两个Tomcat中
现在通过http://localhost:8080/web和http://localhost:8081/web都可以访问到,但是我们不可能访问同一个页面请求两个不同的地址,这就需要Nginx了
nginx直接解压即可
访问http://localhost:80
现在需要通过NGINX映射两个Tomcat的访问地址
修改nginx下的conf/nginx.conf
修改配置文件,就是新增下面这两段内容
重新加载nginx配置文件
通过访问http://localhost/test(nginx已经绑定80端口),nginx会自动将请求转发到两个Tomcat上,刷新多次可以看到请求不同的Tomcat,但是这也存在一个问题:session的id不同,无法共享session
解决方法:
① 每次请求后只让它访问并在一个Tomcat中操作
反复刷新浏览器,只会访问Tomcat2,除非换个浏览器重新访问
② 使用redies服务器共享session(推荐)
③ 使用Tomcat广播机制共享session(不推荐)
修改两个Tomcat的server.xml文件
在两个Tomcat中的项目web.xml文件中同时加入
重新启动Tomcat
Linux上搭建Nginx+Tomcat集群
分别创建/usr/local/tomcat1和/usr/local/tomcat2,并解压两份Tomcat到这里面来
然后修改其中一个端口号,记得修改防火墙放行
安装nginx
//nginx是由C编写,因此需要依赖gcc编译
yum install gcc-c++
//PCRE是一个Perl库,包括Perl兼容的正则表达式库,nginx的http模块需要正则表达式库来解析,pcre-devel是pcre的二次开发库
yum install -y pcre pcre-devel
//zlib提供多种解压缩方式,nginx需要利用zlib对http包进行gzip
yum install -y zlib zlib-devel
//OpenSSL是一个强大的安全套接字层密码库,包含主要的密码算法、常用密钥和证书封装管理功能以及SSL协议
//nginx不仅支持http协议,也支持https协议,因此需要openssl
yum install -y openssl openssl-devel
解压nginx压缩包到/usr/local/nigix
//configure命令是用来检测你的安装平台的目标特征的。
//它定义了系统的各个方面,包括nginx的被允许使用的连接处理的方法,比如它会检测你是不是有CC或GCC,并不是需要CC或GCC
//它是个shell脚本,执行结束时,它会创建一个Makefile文件。
./configure
//make是执行编译,它从Makefile中读取指令,然后编译,生成目标文件、最终的二进制文件。
make
//make install是用来安装的,它也从Makefile中读取指令,安装到指定的位置。
//其根据configure命令执行时的参数将Nginx部署到指定的安装目录,包括相关目录的建立和二进制文件、配置文件的复制。
make install
安装完毕后会有个sbin目录,进入里面
./nignx
查询是否安装成功
ps aux|grep nginx
修改nginx配置文件,重新加载nginx
修改防火墙规则,放出80端口
在浏览器上访问http://192.168.0.12
关闭nginx
./nginx -s stop
退出nginx
./nginx -s quit
修改了配置文件要重新加载nginx
./nginx -s reload