1) 选择工做空间 workspace 选择一个文件夹存放程序(代码) 不要用中文和空格javascript
2) 新建一个java 工程(Project)css
3) 建包 建类html
alt + / : 代码自动补齐,须要配置的java
打开preferences(首选项), 搜keys,打开快捷键配置面板mysql
搜 alt+/ ,取消绑定程序员
搜 content assist ,取消原先的绑定,配置成alt+/web
ctrl+1: 错误自动修复, 注意,放行的红叉是可修复的,圆形的是不可修复的面试
导包的三种方式:ajax
1)将类名敲完整,按下alt+/ 进行补齐,会自动导包算法
2) ctrl+1,修正错误
3) ctrl+shift+o 整理包,导入须要的包,去掉多余的导包语句
ctrl+shift+f : 代码格式化
ctrl+2,L 自动声明变量
shift+enter 光标直接进入下一行
ctrl+alt+方向键(向上向下) 复制行
alt+ 方向键(向上向下) 移动当前行
1) 能够对程序进行调试
在行的开头双击左键打断点,用debug方式来运行程序,程序就会停留在断点位置
F5 跳入(step into) 跳入方法内部 F6 跳过, 让当前行运行 F7跳出 返回上一层程序
resume 直接运行到下一个断点
2) 查看源代码建议使用断点调试的方式,注意当前运行环境须要包含源码
1) 写一个java类, 声明测试方式
修饰符为 public void ,
在方法前面加注解,(@Test)
此方法就能够进行测试了(交给junit运行)
2) 须要在测试方法前作初始化工做
写一个静态方法 public static void init()
在方法前加注解 @BeforeClass
3) 须要在测试后释放资源
写一个静态方法
在方法前加注解 @AfterClass
1) 导入类的静态属性
import static java.lang.System.out;
out.println("haha");
2) 导入类的静态方法
import static java.lang.Math.*; // 导入Math类的全部静态成员
int num = abs(-10);
String [] arr = {"a", "b", "c"}; //数组的静态定义方式,只试用于数组首次定义的时候
// 传统方式
for(int i=0; i<arr.length; i++) {
// i依次表示数组的角标
String s = arr[i];
System.out.println(s);
}
System.out.println("-------------------------------------");
// 在jdk5中咱们可使用加强for循环迭代
// 加强for循环括号里写两个参数,第一个是声明一个变量,变量类型必须是数组元素的类型
// 第二个就是须要迭代的容器
// for循环会循环容器的length次, 每次都将容器的第n-1个元素赋值给声明的变量
for(String s : arr) {
// 循环体, 执行arr.length
// 每次都将arr中的第n-1个元素给s
System.out.println(s); //
}
int --> Integer
byte --> Byte
short --> Short
long --> Long
char --> Character
double --> Double
float --> Float
boolean --> Boolean
1) Integer x = 1; x = x + 1; 经历了什么过程? 装箱à 拆箱 à 装箱
2) 为了优化,虚拟机为包装类提供了缓冲池, Integer池的大小 -128~127 一个字节的大小
3) String池
Java为了优化字符串操做 提供了一个缓冲池
面试题:
String s = “abc” 和 String s = new String(“abc”) 的区别
String s = new String(“abc”) 建立了几个对象
String s = “a” + “b” + “c” + “d” 建立了几个对象
String s1 = “a” String s2 = “b” String s3 = s1 + s2; s3==”ab”?
/*1. String s = "abc", 虚拟机首先会检查String池里有没有"abc"对象(经过equals方法)
// 若是有,直接返回引用,若是没有,会在池里建立一个“abc”对象,并返回引用
String s1 = "abc";
String s2 = "abc";
System.out.println(s1==s2); // result: true
*/
/* 2. String str = new String("abc");
无论缓冲池是否有"abc", 都会在堆内存建立一个"abc"对象,返回引用
// 此时,负责检查并维护缓冲池,其实堆内存的对象是缓冲池中"abc"对象的一个拷贝
String s1 = new String("abc");
String s2 = new String("abc");
System.out.println(s1==s2); // result: false
*/
/* 3. String s = "a" + "b" + "c" + "d"; java编译器有个合并已知量的优化功能
// 在编译阶段就把"a" + "b" + "c" + "d" 合并为 ”abcd“
String s = "a" + "b" + "c" + "d";
// String s = "abcd";
System.out.println(s=="abcd");// result: true
*/
/* 4. String s1 = "a"; String s2 = "b"; String s3 = s1 + s2;
// String是常量,不能相加的,java如何实现的?
StringBuilder sb = new StringBuidler(s1);
sb.append(s2);
s3 = sb.toString();
也就是说实际上s3是方法返回的String对象
凡是方法返回的字符串对象都是在堆内存的
*/
String s1 = "a";
String s2 = "b";
String s3 = s1 + s2; // 堆内存的对象
System.out.println(s3=="ab");// result: false
List list = new ArrayList();
list.add("aaa");
list.add("bbb");
list.add("ccc");
// 传统方式1
/* 1.得到迭代器
Iterator iter = list.iterator();
// 2.循环判断迭代器是否有下一个
while(iter.hasNext()) {
String str = (String) iter.next(); // 将迭代器的指针移向下一个,并将迭代当前指向的元素返回
System.out.println(str);
}
*/
// 传统方式2
for(Iterator iter=list.iterator(); iter.hasNext(); ) {
String s = (String) iter.next();
System.out.println(s);
}
System.out.println("--------------------------------");
// 加强for循环, 没有使用泛型的集合能不能使用加强for循环迭代?能
for(Object obj : list) {
String s = (String) obj;
System.out.println(s);
}
Map map = new HashMap();
map.put("a", "aaa");
map.put("b", "bbb");
map.put("c", "ccc");
// 传统方式迭代1
// 1. 得到全部的key
Set keys = map.keySet();
// 2.迭代keys得到全部的key
Iterator iter = keys.iterator();
while(iter.hasNext()) {
String key = (String) iter.next(); // a b c
// 3.根据key得到对应的value
String value = (String) map.get(key);
System.out.println(key + "=" + value);
}
System.out.println("---------------------------------");
// 传统方式2,必须掌握这种方式
// 1.得到全部的键值对Entry对象
Set entrys = map.entrySet();
// 2.迭代出全部的entry
iter = entrys.iterator();
while(iter.hasNext()) {
Map.Entry entry = (Entry) iter.next();
// 分别得到key和value
String key = (String) entry.getKey();
String value = (String) entry.getValue();
System.out.println(key + "=" + value);
}
System.out.println("-------------------------------------");
System.out.println("加强for循环迭代,");
// 加强for循环迭代,
// 原则上map集合是没法使用加强for循环来迭代的,
// 由于加强for循环只能针对实现了Iterable接口的集合进行迭代
// Iterable是jdk5中新定义的接口,就一个方法iterator方法
// 只有实现了Iterable接口的类,才能保证必定有iterator方法
// java有这样的限定是由于加强for循环内部仍是用迭代器实现的
// 而实际上,咱们能够经过某种方式来使用加强for循环
for(Object obj : map.entrySet()) {
// obj 依次表示Entry
Map.Entry entry = (Entry) obj;
System.out.println(entry.getKey() + "=" + entry.getValue());
}
// 在使用迭代器迭代集合的过程当中,不能对集合进行增删操做
@Test
public void test4() {
List list = new ArrayList();
list.add("wangwu");
list.add("zhangsan");
list.add("lisi");
Iterator iter = list.iterator();
while(iter.hasNext()) {
String name = (String) iter.next();
if("wangwu".equals(name)) {
// 从集合中删掉
//list.remove(name);
// 迭代过程当中删除元素须要调用迭代器的方法
iter.remove(); // 删除我迭代的集合被我迭代的最后一个元素
}
}
// 1 2 4
System.out.println(list.size());
}
@Test
public void test5() {
List list = new ArrayList();
list.add("aa");
list.add("bb");
// 使用ListIterator迭代器
ListIterator listIterator = list.listIterator();
while(listIterator.hasNext()) {
listIterator.next();
// 迭代过程当中增长元素
listIterator.add("cc");
}
System.out.println(list.size());
}
//在使用加强for循环时,不能对元素进行赋值
int[] arr = {1,2,3};
for(int num : arr) {
num = 0;
}
System.out.println(arr[1]);
1) jdk5中方法的形参能够定义为可变参数,传入实参个数可变
// 设计一个方法求n个数的和
public static int getSum(int... arr) {
// 可变参数在方法中仍被看作一个数组
int sum = 0;
for(int num : arr)
sum += num;
return sum;
}
2)Arrays.asList为例演示传入不一样参数的状况
// list长度为3
List list = Arrays.asList("a","b","c");
// list长度为1, 由于考虑1.4语法
String[] arr = {"a","b","c"};
List list = Arrays.asList(arr);
// 同时符合1.4和1.5的语法,此时会优先考虑1.4的语法
// 缘由是有了新功能要保证之前的代码不出错,向后兼容
// 如今就须要将arr做为一个元素存入集合
Object obj = arr;
List list2 = Arrays.asList(obj); // 此时只符合1.5的语法,不符合1.4的语法,没有歧义
List list3 = Arrays.asList(new Object[]{arr}); // 优先考虑1.4,因此数组会拆开
//System.out.println(list3.size());
// 基本数据类型数组只符合1.5的语法
int[] nums = {1,2,3};
list = Arrays.asList(nums);
System.out.println(list.size());
问题:对象的某个属性的值不能是任意的,必须为固定的一组取值其中的某一个
解决办法:
1) 在setGrade方法中作判断,不符合格式要求就抛出异常
2) 直接限定用户的选择,经过自定义类模拟枚举的方式来限定用户的输入
写一个Grade类,私有构造函数,对外提供5个静态的常量表示类的实例
3) jdk5中新定义了枚举类型,专门用于解决此类问题
4) 枚举就是一个特殊的java类,能够定义属性、方法、构造函数、实现接口、继承类
//枚举类就是一个java类,也能够声明属性,方法,构造函数
public enum Grade4 {
A("90-100"),B("80-89"),C("70-79"),D("60-69"),E("0-59");
private String value;
private Grade4(String value) {
this.value = value;
}
public String getValue() {
return value;
}
}
//枚举类就是一个java类, 也能够继承抽象和实现接口
public enum Grade5 {
// 抽象类不能建立实例对象
A("90-100"){
// new了一个Grade5的子类实例
public String toLocaleString() {
return "优";
}
}
,B("80-89"){
// new了一个Grade5的子类实例
public String toLocaleString() {
return "良";
}
}
,C("70-79"){
// new了一个Grade5的子类实例
public String toLocaleString() {
return "中";
}
}
,D("60-69"){
// new了一个Grade5的子类实例
public String toLocaleString() {
return "差";
}
}
,E("0-59"){
// new了一个Grade5的子类实例
public String toLocaleString() {
return "不及格";
}
};
private String value;
private Grade5(String value) {
this.value = value;
}
public String getValue() {
return value;
}
// 对外提供一个方法,返回枚举的本地信息
// 一个方法不知道如何实现,能够定义为抽象的
public abstract String toLocaleString();
}
l 练习:请编写一个关于星期几的枚举WeekDay,要求:
Class对象封装了一个java类中定义的成员变量、成员方法、构造方法、类名、包名等
得到class对象的三种方式和区别:
// 1. 根据给定的类名来得到 用于类加载
String classname = "cn.itcast.reflect.Person"; // 来自配置文件
Class clazz = Class.forName(classname); // 此对象表明Person.class
// 2. 若是拿到了对象,不知道是什么类型 用于得到对象的类型
Object obj = new Person();
Class clazz1 = obj.getClass(); // 得到对象具体的类型
// 3. 若是是明确地得到某个类的Class对象 主要用于传参
Class clazz2 = Person.class;
// 在java中全部的类型都会对应一个Class对象 int Integer
Class intClazz = int.class;
Class intarrClazz = int[].class;
Class voidClazz = void.class;
反射就是得到一个java类的各个组成部分
// 反射类的成员方法
Class clazz = Person.class;
Method method = clazz.getMethod(methodName, new Class[]{paramClazz1, paramClazz2});
method.invoke();
// 反射类的构造函数
Constructor con = clazz.getConstructor(new Class[]{paramClazz1, paramClazz2,...})
con.newInstance(params...)
// 反射类的属性
Field field = clazz.getField(fieldName);
field.setAccessible(true);
field.setObject(value);
到底框架是什么? 框架就是将开发中大量重复的代码集中起来写个通用的程序
框架就是用反射来实现的, 框架须要如今的类调用未来写的类
框架是未来的程序员调用的,框架不能实现完整的功能,框架只是一些一些通用的代码;
框架要依赖未来写的类来运行.
如今写的类要调用未来写的类,咱们先针对接口进行调用,未来的类须要实现接口,那么方法就固定了
可是未来写的类的类名咱们没法获知,这时就须要调用者经过配置文件告诉框架具体的类名
1) 泛型是一种可变化的类型, 类型不肯定,须要调用者来指定
2) 用途:
一个类的多个成员方法用到的参数类型或返回值类型都是未知的类型,但又须要是同一个类型,就可将方法的
参数类型定义为泛型,此泛型必须在类上先予以声明才能在方法中使用
一个方法的多个参数和返回值须要是同一个类型,也能够用泛型来解决,在方法返回值前面声明泛型
泛型的细节:
1) 泛型到底表明什么类型取决于调用者传入的类型,若是没传,默认是Object类型
2) 使用带泛型的类建立对象时, 等式两边指定的泛型必须一致
缘由: 编译器检查对象调用方法时只看变量,然而程序运行期间调用方法时就要考虑对象具体类型了
3) 等式两边能够在任意一边使用泛型 在另外一边不使用 (考虑向后兼容)
3. 泛型的基本概念
以List<E>为例:<>念着typeof 例, List<String> 就是 List typeof String
List<E>中的E称为类型参数变量 方法定义参数形式参数
List<Integer>中的Integer称为实际类型参数
整个List<E>称为泛型类型 GenericType
整个List<Integer>称为参数化的泛型类型
4. 泛型的使用
1)使用带泛型的类时,在建立对象时能够为泛型指定实际类型参数,指定的具体类型至关于给泛型传参
2)子类在继承父类的时候,能够为父类定义的泛型指定实际类型参数
class B<T>
class A extends B<String>
经过子类A得到的父类类型就是一个参数化的类型
3)调用方法时传入参数的具体类型将做为方法中泛型的实际类型
Lesson 2 Servlet(超级重要)
用 java 语言开发动态的web资源,接下来就是介绍如何开发动态的web资源
对于java程序员而言,所谓动态web资源就是à能够运行在服务器上的java程序
开发人员写好一个java类,到底有哪些方法tomcat服务器是不可能知道的
tomcat服务器欲执行咱们编写的java类, 就须要知道咱们的java类有哪些方法,而后在适当的时间调用这些方法, 因此咱们在写的java程序要想运行在服务器上,就必需要实现一个特殊的接口 Servlet.java
interface Servlet { ... }
Servlet接口中就定义了能够被tomcat服务器调用的java方法
一般来说,咱们将实现了Servlet接口的java类称之为 Servlet
编写好的Servlet须要在web.xml文件中作配置, 才能供外界访问
3.1 写一个java类实现Servlet接口
package cn.itcast.servlet;
import java.io.*;
import javax.servlet.*;
public class HelloWorldServlet extends GenericServlet
{
// 实现 service 方法
public void service(ServletRequest request,ServletResponse response)
throws ServletException,java.io.IOException {
// 向浏览器输出一句话
PrintWriter out = response.getWriter();
out.write("hello world!!!");
}
public void init()throws ServletException {
// 初始化 servlet 时被调用
System.out.println("init()");
}
public void destroy() {
// 摧毁 servlet 时被调用
System.out.println("destroy()");
}
}
3.2. 导入 servlet jar包
set classpath=%classpath%;D:\apache-tomcat-6.0.20\lib\servlet-api.jar
3.3. 编译带包的类
javac -d . HelloWorldServlet.java
3.4. 将包拷贝至 day05/WEB-INF/classes 目录下 --> 发布 web 工程
3.5. 在 web.xml 文件中作映射
<!-- 作servlet映射 -->
<!-- servlet元素用于给一个类起别名 -->
<servlet>
<servlet-name>HelloWorldServlet</servlet-name>
<servlet-class>cn.itcast.servlet.HelloWorldServlet</servlet-class>
</servlet>
<!-- servlet-mapping元素用于将一个Servlet映射到url -->
<!—url必须以/开头,/ 表示当前web应用即上下文路径 -->
<servlet-mapping>
<servlet-name>HelloWorldServlet</servlet-name>
<url-pattern>/HelloWorldServlet</url-pattern>
</servlet-mapping>
注意: servlet 对象一旦建立就会驻留在内存中,为全部的请求服务,
servlet 对象何时销毁? 直到服务器关闭时或web应用被移除才销毁
3.6. Servlet 执行流程图
4.1. 建一个 web project
4.2. 在src下建包,建立一个java类实现Servlet接口
4.3 在 Webroot\WEB-INF\web.xml 作 servlet 映射
4.4 配置 tomcat 服务器
window--> preferences --> tomcat6.x
4.5 将web工程发布至tomcat 服务器
发布的web应用名称能够配置: web工程右键 选properties-->myeclipse-->web
默认状况使用工程名做为发布后的web应用名
4.6 启动tomcat服务器运行程序
通常来说咱们开发一个Servlet会去继承 HttpServlet
在 eclipse 下开发Servlet 能够直接新建一个Servlet, 覆写 HttpServlet 的 doGet和doPost方法
继承 HttpServlet 的缘由是: HttpServlet实现了service方法,将ServletRequst和ServletResponse
强转为子类 HttpServletRequest和HttpServletResponse,让咱们用起来更加方便,同时,在service方法中,它判断了请求方式,根据请求方式来调用 doGet 和 doPost
1. * 号统配符
一个Servlet能够映射为多个路径
在映射 Servlet 路径时可使用‘/*’ 或 ‘*.扩展名’ 的形式
注意: 二者不能同时使用
/* 具备较高的优先级
2. load-on-startup 元素
<servlet>元素下能够配置< load-on-startup>子元素,
配置方式以下:
<load-on-startup>1</load-on-startup>
若是一个Servlet配置了该项,web容器会在web应用被加载时就初始化该Servlet,数字越小则越先初始化
3. tomcat\conf\web.xml
服务器下全部web 应用中的web.xml 都会自动继承该文件中全部的配置
http://localhost:8080/day05/a.html a.html是资源名
上面的url访问的url在web.xml文件中并无配置
此时会去访问缺省的Servlet,在tomcat\conf\web.xml文件中就配置了一个缺省的DefaultServlet
DefaultServlet帮咱们去web应用下读取 a.html 文件,并打给浏览器,若是没有发送 404 页面
也就说,咱们经过ie访问服务器访问的都是 Servlet
4. Servlet线程安全问题
解决Servlet线程安全问题: 加上同步的锁(lock)
实现SingleThreadModel接口的Servlet
服务器会作判断,当有请求过来,若是Servlet对象忙着呢,服务器会再建立一个Servlet对象为用户
提供服务,若是Servlet闲置,就直接提供服务
这样的方式其实是回避了线程安全问题, 单线程访问Servlet, 这样的方式不可取
Lesson 3
1、 WEB 服务器
WEB在英语中即表示网页的意思,它用于表示Internet主机上供外界访问的资源以及超连接所组成的链表
放在internet网上供外界访问的文件或程序被称为web资源
web资源被分为:
静态web资源: html、css、jpg
动态web资源:Servlet、Jsp
思考问题: 从一台计算机的 IE 浏览器如何去访问另外一台计算机中的文件
3.1 两台计算机是如何实现通信的?
IP地址(计算机的惟一标识)
IPV4 4个字节的整数,每一个字节以 点号 隔开 192.168.1.100 每一个字节的取值 0~255
在计算机中程序会绑定在某一个端口 0~65535 尽可能用 1024 以上的
链接一台计算机就须要输入 ip 地址和端口号
做为接收方, 应该绑定ip,监听指定的端口
3.2 在本地写程序添加一个服务,供别人来访问, 假设监听 8888 端口
3.3 编码实现了一个本地服务器程序
做用: 管理本地的资源,只要将html页面放到指定的目录下,外界就能够访问了
3.4 安装服务器的目的: 开发好的web资源能够发布到服务器上,这样外界就能够经过浏览器访问了
源程序: MyServer.java
// ServerSocket 对象能够监听端口
ServerSocket serversocket = new ServerSocket(6666);
while(true) {
Socket socket = serversocket.accept(); // 等待客户端的链接请求,一旦有请求过来,就结束阻塞,返回客户端对象
// 一旦有客户来访问, 就另开一个新线程去提供服务, main线程继续等待下一个客户的链接
new Thread(new MyService(socket)).start();
}
MyService.java
// 提供服务
InputStream in = socket.getInputStream();
Thread.sleep(200);
int len = in.available(); // 估计此流不受阻塞能读取的字节数
byte[] buffer = new byte[len];
in.read(buffer);
String request = new String(buffer);
// 截取第一行
String firstLine = request.substring(0, request.indexOf("\n"));
String uriName = firstLine.split(" ")[1];
OutputStream out = socket.getOutputStream();
// 根据须要访问的资源建立 File 对象
File file = new File("src" + uriName);
if(!file.exists()) {
out.write("对不起!您访问的资源不存在!别瞎搞!!".getBytes());
out.close();
return ;
}
// 从文件读, 往浏览器写
FileInputStream fis = new FileInputStream(file);
buffer = new byte[1024];
while ((len = fis.read(buffer)) > 0) {
out.write(buffer, 0, len);
}
socket.close();
1. 使用 tomcat6.0.20.rar 文件解压即完成安装
2. tomcat 就是一个java程序,必定会用到 jre
因此须要配置环境变量 java_home 配置成jdk的安装目录 c:\jdk1.6
tomcat的启动是经过 startup.bat 文件, 实际上 startup.bat 中是去调用
catalina.bat 文件, 并且是经过 %catalina_home%\bin\catalina.bat 去找
因此为了保证服务器启动正常, 须要配置 catalina_home 环境变量为 tomcat的安装目录
3. tomcat 的目录结构
bin : 存放一些执行文件
conf : 存放服务器的配置文件
lib : 存放tomcat 所依赖的 jar 文件
logs: 存放日志文件
temp: 存放临时文件
webapps: web applications 存放全部的web应用程序(web资源)
work: tomcat 的工做目录, jsp翻译成的Servlet就在这个目录下
4. web应用
多个web资源存放在一个目录下即为一个web应用(web应用程序、web工程)
web 应用的目录结构
静态web资源直接放在目录下
java 类放在classes目录下
web.xml 文件负责管理web应用下全部的web资源
全部jar包放在lib目录下
一个web应用(服务器上一个目录) 须要供外界访问的路径,须要映射虚拟目录
在 tomcat6 中,放在webapps下的web应用,服务器会自动作映射(将文件夹名称做为虚拟路径)
对于 webapps 目录外的web应用须要手动映射虚拟路径
1.1. 在 server.xml 文件能够配置
<host>
<Context path=”/itcast” docBase=”f:\itcast” />
</host>
1.2. 在 %tomcat目录%\conf\catalina\localhost 下写一个 xml文件
文件名就是 虚拟目录
<Context docBase=”f:\itcast” />
多级目录配置 aaa#bbb.xml 对应 /aaa/bbb
若是文件名 是 ROOT.xml 那就是配置了缺省的web应用, 访问时不须要输入 虚拟目录
Web.xml的做用: 管理 web 应用下全部的web资源
通俗地讲,一个web应用下全部的web资源如何被外界访问都须要在此文件下作映射
包括我们后面学的Servlet jsp 都须要在这个文件中作映射
实验: 配置web应用的 首页
在 web 应用下新建目录 WEB-INF ,在此目录下 新建 web.xml 文件
<?xml version="1.0" encoding="ISO-8859-1"?>
<web-app xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
version="2.5">
<welcome-file-list>
<welcome-file>itcast.html</welcome-file>
</welcome-file-list>
</web-app>
一个完整 url
http:\\www.sina.com:80\itcast\index.jsp
协议名 主机名 端口号 资源名 (uri)
IE 访问服务器的原理,
在IE中输入的地址中包含域名,域名就须要被翻译成服务器的IP,才能访问到服务器
新建一个 web 应用
配置成缺省的web应用
配置首页
配置tomcat监听端口为80
在 windows 中注册主机名
服务器容许在一个IP上配置多个主机,即虚拟主机
http://www.sina.com:80/index.html
中的 www.sina.com 起了两个做用:
找DNS服务器,做为域名被解析为IP
经过Host头告诉服务器访问的主机名
配置方式: 在 server.xml 文件中配置Host元素,须要指定name(主机名)、appBase(默认web应用存放目录)
appBase目录下全部的web应用,tomcat会自动映射虚拟路径
<Host name="www.sohu.com" appBase="F:\sohu\webapps" />
作实验: 给新配置的虚拟主机配置缺省的web应用
<Host name="www.sohu.com" appBase="F:\sohu\webapps" >
<Context path="" docBase="F:\sohu\webapps\abc" />
</Host>
http://www.sohu.com/abc/a.html
访问一个 a.html 静态web资源, IE 作了什么事
1) 将 www.sohu.com 做为域名发送给DNS , 解析成 IP 地址, 访问一台服务器
2) 发送 Host 头(www.sohu.com),告诉服务器我要访问的虚拟主机 ,服务器拿着Host头找匹配的Host元素
3) 将abc做为虚拟目录,告诉服务器我要访问的web应用 ,服务器拿着 abc 找匹配的web应用
4) 将 a.html 做为资源名, 告诉服务器我要访问的 web 资源, 服务器拿着 a.html 去web.xml文件中找映射
虚拟目录对应是一个web应用的目录,因此虚拟目录也被咱们称做web应用路径(web应用的上下文contextpath)
1. tcp/ip 协议: 网络通讯协议(链接)
ip 协议 : 127.0.0.1 ip地址对应一台计算机 (互联网层)
tcp 高级协议: 三次握手, 发送请求、返回响应、传输数据 (传输层)
2. http 协议是创建在 tcp协议的基础之上 (应用层)
3. Http协议的版本 (w3c)
Http1.0 : 创建链接,发送一次请求就断开
Http1.1 :创建链接,能够无限次发送请求
http请求消息内容: 包括一个请求行、若干消息头、以及实体内容,其中的一些消息头和实体内容都是可选的,消息头和实体内容之间要用空行隔开。
请求行
POST /itcast/ HTTP/1.1
消息头
Accept: image/gif, image/x-xbitmap, */*
Referer: http://localhost:8080/itcast/
Accept-Language: zh-CN,en-GB;q=0.8,ar-YE;q=0.7,ja-JP;q=0.5,de-CH;q=0.3,en-US;q=0.2
Content-Type: application/x-www-form-urlencoded
UA-CPU: x86
Accept-Encoding: gzip, deflate
User-Agent: Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1; GTB6.5; CIBA)
Host: localhost:8080
Content-Length: 33
Connection: Keep-Alive
Cache-Control: no-cache
Cookie: JSESSIONID=B0B3FB4FFB0315B3D3C620548DD4E1EB
空一行
消息体
username=zhangsan&password=123456
1. 请求行 GET /itcast/a.html HTTP/1.1
GET 为请求方式 : get方式的请求参数直接跟在url后面,例如:/itcast/a.html?username=aaa&password=111
POST方式: post方式请求参数放在消息体中传输,相对安全,get大小限制1K,post 无数据量限制
2. 请求消息头 : IE浏览器用于向服务器说明状况的(浏览器使用环境)
Accept: text/html,image/* 说明浏览器接受的数据类型
Accept-Charset: ISO-8859-1 说明浏览器使用的字符编码
Accept-Encoding: gzip,compress 说明浏览器支持的压缩格式
Accept-Language: en-us,zh-cn 说明浏览器的语言环境
Host: www.it315.org:80 说明浏览器要访问的主机名
If-Modified-Since: Tue, 11 Jul 2000 18:23:51 GMT 文件的修改事件,用于作缓存
Referer: http://www.it315.org/index.jsp 说明请求来自哪里,防盗链 (作实验)
User-Agent: Mozilla/4.0 (compatible; MSIE 5.5; Windows NT 5.0) 说明浏览器内核
Cookie 向服务器发送Cookie
Connection: close/Keep-Alive 说明链接状态
Date: Tue, 11 Jul 2000 18:23:51 GMT 客户端计算机时间
3. 实体内容(消息体)
浏览器向服务器发送的数据,例如上传的文件、提交的表单等
http响应消息的内容包括: 一个状态行(类比 http请求信息的”请求行”)、若干消息头、以及实体内容 ,其中的一些消息头和实体内容都是可选的,消息头和实体内容之间要用空行隔开。
HTTP/1.1 200 OK
Server: Apache-Coyote/1.1
Content-Type: text/html;charset=GB18030
Content-Length: 766
Date: Thu, 07 Jul 2011 15:40:02 GMT
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<title>My JSP 'index.jsp' starting page</title>
</head>
<body>
hello
</body>
</html>
1. 状态行 HTTP/1.1 404 Not Found
协议版本: 目前广泛采用的都是http1.1
响应状态码: 说明服务器的响应状态
经常使用状态码
状态码 |
含义 |
100~199 |
表示成功接收请求,要求客户端继续提交下一次请求才能完成整个处理过程 |
200~299 |
表示成功接收请求并已完成整个处理过程,经常使用200 |
300~399 |
为完成请求,客户需进一步细化请求。例如,请求的资源已经移动一个新地址,经常使用30二、307和304 |
400~499 |
客户端的请求有错误,经常使用404 |
500~599 |
服务器端出现错误,经常使用 500 |
200: 一切OK
302\307 请求重定向,你访问我,我通知你访问另外一个资源
304 通知浏览器去读缓存
404 找不到资源 ,客户端的请求有错误
500 服务器程序出错(服务器端的程序抛异常了)
2. 响应消息头 服务器向浏览器说明状况(处理结果)
Location: http://www.it315.org/index.jsp 通知浏览器去访问另外一个资源
Server:apache tomcat 说明服务器
Content-Encoding: gzip 通知浏览器数据的压缩格式
Content-Length: 80 通知浏览器发送数据的长度
Content-Language: zh-cn 通知浏览器语言环境
Content-Type: text/html; charset=GB2312 通知浏览器文件的格式和编码
Last-Modified: Tue, 11 Jul 2000 18:23:51 GMT 告诉浏览器文件的修改时间
Refresh: 1;url=http://www.it315.org 通知浏览器自动刷新
Content-Disposition: attachment; filename=aaa.zip 通知浏览器如下载的方式打开资源
Set-Cookie:SS=Q0=5Lb_nQ; path=/search 发cookie
Expires: -1//3种禁止缓存的头字段
Cache-Control: no-cache
Pragma: no-cache
Connection: close/Keep-Alive 链接状态
Date: Tue, 11 Jul 2000 18:23:51 GMT 系统时间
3. 实体内容(响应消息体)
通常为服务器发送给ie浏览器的页面数据
1. https 是一种加密协议 能保证数据的安全
2. 不对称加密 对称加密
3. https
1) 制做数字证书
keytool -genkey -alias tomcat -keyalg RSA
2) 将证书拷贝至 tomcat\conf
3) 修改server.xml 文件 配置https 链接器
<Connector port="8443" protocol="HTTP/1.1" SSLEnabled="true"
maxThreads="150" scheme="https" secure="true"
clientAuth="false" sslProtocol="TLS"
keystoreFile="conf\.keystore" keystorePass="123456" />
4) 重启服务器 打开ie访问
https://localhost:8443
5) ie 中安装证书
继续浏览
查看证书
安装证书
删除证书:
Lesson 5
做用 : 封装 Servlet 初始化参数
1. 能够在 web.xml 文件中 Servlet 元素下 为Servlet配置初始化参数
<init-param>
<param-name>name</param-name>
<param-value>aaaa</param-value>
</init-param>
2. web 容器在初始化Servlet时,会将初始化参数封装到一个 ServletConfig 对象中,传给init方法
3. 咱们在Servlet 中覆写 init方法,就能够得到ServletConfig
4. 父类 GenericServlet 中定义了一个成员变量用于记住此对象,并提供了 getServletConfig 方法
咱们能够直接调用此方法 得到 config对象
5. 再调用 getInitParameter(name) 方法得到想要配置项
// 指定编码
// 得到ServletConfig 对象
ServletConfig config = getServletConfig();
String encoding = config.getInitParameter("encoding");
System.out.println("encoding=" + encoding);
1. ServletContext对象表明整个web应用
2. ServletContext对象是一个“域”对象(即:能够存储数据的对象)
ServletContext对象的内部维护了一个map集合, key是String类型 value是Object类型
class ServletContext {
private Map<String, Object> map ;
}
3. ServletContext 做为域对象, 多个Servlet 能够共享数据
Servlet6
// 1. 得到ServletContext 对象
ServletContext context = getServletContext();
// 2. 存入域
context.setAttribute(“name”, “zhangsan”);
Servlet7
// 得到 context 域, getAttribute
String name = (String) getServletContext().getAttribute("name");
4.获取web应用的初始化参数
getContext().getInitParameter(“name”);
5. 统计一个web应用的访问量
在 context 域中维护一个count变量
访问Servlet时,取出变量 加1
6. 实现请求转发
实现请求转发须要用到 转发对象 RequestDispatcher,RequestDispatcher有一个 forward 方法能转发请求
7.1. 读取web工程下的资源文件
// 得到绝对路径
String realPath = ServletContext.getRealPath(相对web应用的路径);
注意URL url = ServletContext.getResource(); web的url
// 得到与文件关联的流
InputStream in=
ServletContext.getResourceAsStream(“WEB-INF/classes/config.properties”;
7.2 读取java工程下的文件
// 不能相对虚拟机目录 不能用绝对路径
// 只能类加载的方式读
// 得到 流
ClassLoader classLoader = Demo.class.getClassLoader();
InputStream in = classLoader.getResourceAsStream("a.txt");
// 得到绝对路径
URL url = Demo.class.getClassLoader().getResource("a.txt");
String path = url.getPath();
类加载方式 缺点
1) 不能读取类路径之外文件
2) 因为须要加载到内存,不能读大文件
3) web工程中若是用类加载的方式读
类加载实际上读取的是内存中加载的文件,此时将读不到硬盘上资源文件的修改
解决办法: 经过绝对路径去读硬盘上的文件 避开内存的文件
例如: Demo.Class.getClassLoader().getResource("a.txt").getPath()
HttpServlet 的 Service()方法中的代码
// 调用方法
long lastModified = getLastModified(req);
// 若是为 -1 ,就直接放行,给最新的
if (lastModified == -1) {
doGet(req, resp);
}
// 方法返回不是-1
else {
// 读取IE发送的头If-Modified-Since
long ifModifiedSince = req.getDateHeader(HEADER_IFMODSINCE);
// 拿着客户端的时间头和方法的返回值比较
if (ifModifiedSince < (lastModified / 1000 * 1000)) {
// If the servlet mod time is later, call doGet()
// Round down to the nearest second for a proper compare
// A ifModifiedSince of -1 will always be less
// 方法的返回值大于ie发送过来的时间头
// 从新向浏览器发送了一个时间头
maybeSetLastModified(resp, lastModified);
// 放行, 发送页面
doGet(req, resp);
} else {
// 方法的返回值没有大于ie发送过来的时间头
// 发送 304 状态码,让用户去读缓存
resp.setStatus(HttpServletResponse.SC_NOT_MODIFIED);
}
}
Lesson 6
1) 在计算机中数据以二进制的形式进行存储的, 数据的传输也是通二进制的形式
2)须要存字符,会出现字符与字节之间的转换 (输入字符 读到的字符)
3) 字符和字节之间如何实现转换? 都是经过查码表
4) 字符到字节是编码,字节到字符是解码, 编码和解码用到了不一样码表就会出现乱码问题
1) 拿到乱码基本上都是因为解码错误致使的, 解决办法,从新编码再解码
2) 之后将文件交给解析器,出现乱码,首先想到是否通知别人文件的编码格式
Servlet对象 一旦建立就驻留在内存,
Request和response对象,web容器会针对每次用户的请求建立一个request和response
请求结束,响应发给ie。 Request和response对象就会当即被销毁
1. response getWriter方法得到字符流,用于向浏览器输出字符数据
文件名中文乱码问题
因为文件名是跟着 content-disposition 头发送给浏览器的
凡是http头的数据都会通过 url 编码, 编成全世界通用的符号,这样传输过程当中才不会乱码
发送 302 状态码和 location 头
ie向浏览器发送了两次请求 第一次直接请求资源,第二次重定向的页面
地址栏是有变化
4. getWriter 和 getOutputStream 不能同时使用,有冲突
response的流咱们不用关闭,交给web容器去管理
1. request 帮助咱们取获取 ie 浏览器发送给 服务器的请求消息 (请求行 消息头 消息体)
2. request 得到请求参数: getParameter() 和 getParameterValues()
Request 中文参数乱码的解决方法:
1.表单提交post方式下 : request.setCharacterEncoding(“utf-8”); //指定处理消息体的解码方式
2.表单提交get方式下 :
1) 手动解决乱码问题
byte[] bytes = username.getBytes("iso-8859-1");
String str = new String(bytes, "utf-8");
2) 在 %tomcat%\conf\server.xml 中找到当前链接器元素 Connector
配置URIEncoding 属性 它用于指定web容器的url解码方式
3) 配置useBodyEncodingForURI 属性 指定为 true
用于让tomcat按照处理消息体的编码方式处理消息头
超连接提交参数:也是使用 get 方式提交,(和表单的区别是,表单提交,ie会自动进行url编码)
而超连接提交,ie不进行url编码,此时咱们须要将超连接中的中文参数手动进行url编码.
同理: 在服务器端的处理方式和 表单 get 方式的处理同样
Servlet负责处理用户的请求,处理完的结果交给jsp来作显示(在Servlet中不输出任何数据,全部数据的显示都交给jsp)
得到RequestDispatcher对象作请求转发,
1.请求转发时, 须要将处理结果存入request域 带给 jsp
2.请求转发时, web容器(如:tomcat)会清空response中的数据
3.转发后, 没法向response输入数据
面试题: 请求转发forward和包含include的区别
Forward的时候,web容器会清空response的数据,而且转发以后,当前Servlet就没法再写入数据了
Include的时候,web容器会让两个Servlet的数据都写入response
1) 用户发送请求(提交表单,超连接)
2) Servlet 作处理, 将数据(响应结果)封装到 javabean对象
3) 将 javabean 对象存入request域
4) 请求转发给 jsp
5) jsp 页面中 从request域取出数据 作显示
Web资源访问的流程
1. 客户端(IE)发送请求给服务器
2. 用一个 Servlet 来响应
客户的需求基本上分为两种:
1) 查看数据
请求转发给jsp作显示
2) 用户登录\购买商品 完成某一个特定的功能 结果都是成功或失败
请求重定向到一个目标资源
面试题: 请求转发和请求重定向的区别?
1) 请求重定向地址栏有变化 请求转发地址栏无变化
2) 请求重定向客户端向服务器发送两次请求 请求转发发送一次请求
3) 应用场景:
一件事情作完了,应该作第二件事情了,就请求重定向 (用户登录、购物)
Servlet处理完了,让jsp作显示,用请求转发(mvc设计模式)
读取web中的文件 用 context 读取,严禁用绝对路径,统统以”/”开头
原则: 就看路径是给浏览器用的仍是给服务器用的,
给浏览器用的,要加上web应用的名称
给服务器用的,斜线”/”即表示:当前web应用(/表示web应用,不用加web应用的名称)
各类场景:
1) 请求转发: 地址是给服务器用的: /表示web应用,不用加web应用的名称 /a.jsp
web.xml文件中用到了路径: 地址是给服务器用的, 不须要加web应用 /b.jsp /servlet/Servlet1
2) 请求重定向: 路径给浏览器用的,因此须要加上web应用名称 /day06/a.jsp
3) 超连接: 路径给浏览器用的,因此须要加上web应用名称 /day06/a.jsp
4) 表单的action提交: 路径给浏览器用的,/day06/a.jsp
5) img标签的src属性: 路径给浏览器用的,/day06/a.jpg
6) script src: 路径给浏览器用的 /day06/js/a.js
记住:路径 以“/”开头,请求转发不加web应用名,其余的所有都要加
Lesson 7
Cookie cookie = new Cookie(key, value);
Response.addCookie(cookie);
// 得到用户发送的全部Cookie
Cookie[] cookies = request.getCookies(); // 若是没发Cookie过来,为null
// 遍历 找到lastAccessTime
String lastAccessTime = null;
for(int i=0; cookies!=null&&i<cookies.length; i++) {
String name = cookies[i].getName(); // 一个Cookie的name
if("lastAccessTime".equals(name)) {
// 找到了, 几下value
lastAccessTime = cookies[i].getValue();
}
}
cookie默认状况下在当前浏览器进程有效,若是想让cookie长期驻留客户机的硬盘,就须要指定时间
cookie.setMaxAge(time) time以秒为单位
setMaxAge(0) 通知浏览器删除该Cookie
两个页面
显示全部的商品(从数据库找出商品显示),显示最近的记录(读取客户发送的Cookie中存的id,找出书显示)
CookieDemo2
显示商品的详细信息,并向客户端发送Cookie,难点若是产生新的Cookie
将客户端带过来的Cookie分割后存入集合,根据不一样的状况将新的id加进去,迭代集合用分隔符串起来
CookieDemo3
request对象有个方法是getSession()
首先看浏览器是否发送了Cookie (JSESSIONID) ,若是发了,拿着id去内存中找对应的session对象返回;
若是没有发Cookie或者没找到对应的Session对象,建立一个新的Session对象
getSession( boolean create)
create 为true的时候,若是内存有session就返回,没有就建立新的
create为false的时候,意味着只查找不建立,有就返回,没有不建立,返回null;
若是想让多个浏览器共享一个session,咱们就须要人工发送Cookie,并设置Cookie的有效时间
3.若是浏览器禁用Cookie,那么Session也玩不起来了
要想让session好用,就须要在页面跳转时发送sessionid
有一个技术url重写:
重写后的地址会在原有的url地址的基础上加上JSESSIONID
超连接或表单提交的地址,
重写方式以下:String newUrl = response.encodeURL(url);
请求重定向的地址,
重写方式以下:String newUrl = response.encodeRedirectURL(url);
简单购物车的实现 ListServlet BuyServlet ListCartServlet
用户登陆 Login.jsp LoginServlet indes.jsp LogoutServlet
防止表单重复提交(2种方法)
1.能够用js来作,可是只能增长用户的体验,不能彻底防止坏人
2.用session作防表单重复提交
FormServlet HandleFormServlet TokenProcessor
html: 静态web资源,DefaultServlet读取html文件,经过response输出给IE浏览器
Servlet:动态web资源,web容器(Servlet引擎)解析web.xml文件,找到url对应的java类
经过反射建立Servlet对象,调用service方法
Class.forName(“cn.itcast.servlet.Servlet1”).newInstance();
jsp: 动态web资源
jsp页面的执行过程: (重要)
1.jsp页面在第一次被访问的时候,web容器(jsp引擎)会将jsp翻译成一个Servlet,而后调用servlet的service方法
2.jsp翻译后的Servlet会被放到 %tomcat安装目录%\work\Catalina\localhost\webcontext
3.当jsp页面被再次访问的时候,web容器会去直接调用Servlet的service()方法,因此一般来说jsp只是在第一次被访问的时候比较慢
4.若是jsp页面作了修改,此时web容器会从新翻译jsp
实际上jsp就是Servlet,只是提供了一种比较直观的书写方式,由于写jsp就像在写Html
jsp中能够写 java 代码, 有两种jsp中写java代码的方法:
1) jsp 脚本表达式
内容会被放到 out.print()里面,输出给浏览器
格式: <%=new Date() %>
2) jsp脚本片断
内容会原封不动地被翻译到 Servlet 的service方法中
<%
// java 代码
%>
1) Servlet 适合写java代码,由于Servlet就是一个java类
在开发中使用Servlet对用户发送的请求进行处理并作出响应
2) jsp 适合作数据美化,做为 数据显示模板
由于jsp页面直接书写HTML标签
3) 项目中的web层一般使用mvc设计模式 Servlet+jsp+javabean
其中, Servlet作控制器,处理用户请求
jsp做为显示模板
javabean 做为封装数据的实体
4) 如何养成一种良好的编码风格
在Servlet中应避免作任何的数据输出
在 jsp 中应避免去直接书写java代码, 而实际上要作到这点很难, 因此须要用到 el 和 jstl
在 jsp 页面中全部的 html 标签部分被称做模板元素,用于对整个网页进行布局
jsp脚本有三种形式
1) 脚本表达式: 被翻译到 out.print() 方法中
<%=new Date() %>
2) 脚本片断: 被翻译到 service()方法中
<%
for(int i=0; i<10; i++) {
System.out.println(i);
}
%>
3) jsp 声明 : 被翻译到 service()方法外, 用于:写成员变量\成员方法\静态代码块
<%!
private String name;
static {
.......
}
%>
Include指令: 用于包含一个页面
taglib指令: 用于引入标签库文件
page指令: 用于向jsp引擎说明jsp的页面状况
page指令: 通常都放在页面的开头,可是无论放在哪都对整个页面起做用
page指令: 经常使用的主要有一下几个:
1) import 导包, 导入多个包时须要以“,”隔开 也能够分做多条指令书写
2) session 说明是否使用session
默认值为true,被翻译的Serlvet中会自动得到Session
若是将该值指定为false 被翻译的Servlet的service方法中将不获取session
3) errorPage 指定错误跳转的页面
在 web.xml 文件中一样能够配置错误页面
可根据异常类型或错误编码进行配置error-page
4) pageEncoding
1.通知jsp引擎在翻译jsp的过程当中以什么编码方式来解码jsp文件
2.通知 Servlet引擎 response编码方式,至关于 response.setContentType()
说白了-à 只要给jsp指定了 pageEncoding=”utf-8”, jsp引擎在翻译jsp时, 就会自动加上一句
response.setContentType(“text/html;charset=utf-8”)
扩展 : jsp 乱码问题(only in tomcat5)
在 tomcat6 之后jsp就没有乱码问题了, 若是是使用tomcat5 才会出现乱码问题
jsp 乱码解决 à告诉jsp引擎jsp页面是什么码,这样翻译才不会错;告诉response用什么码编码再发给浏览器
对象变量名 ( 可直接在jsp中使用 ) |
对象类型 |
config |
ServletConfig |
application |
ServletContext |
response |
HttpServletResponse |
request |
HttpServletRequest |
session |
HttpSession |
out |
JspWriter |
page |
this |
exception |
Throwable(不是每一个jsp都有) |
pageContext |
PageContext |
JspWriter类型, 带缓冲的字符流 (包装流) BufferedWriter
对response.getWriter() 进行了包装,提供了缓冲区,默认大小8KB
写入该流的数据最终会被刷新到 response , 调用response.getWriter().write() 方法
Question: 什么状况下 JspWriter 会将数据刷新?
1) 缓冲区写满(默认大小为8kb, 能够在page指令中经过 buffer属性设置缓冲区大小)
2) jsp 页面结束(此流会被关闭,数据刷新到底层的流)
注意:
1) jsp中输出数据尽可能使用 out, 不要使用response直接得到流输出
缘由在于: 写入 out 的数据会先进缓冲区,再刷新到response; 若是两个都用,会致使后写的数据显示在前面
2) jsp 页面结束时, 会自动调用 response.getWriter() 将数据刷新
因此在jsp中不要调用 getOutputStream(), 固然也不方便作文件下载
结论: 在jsp中用out输出数据
主要功能: 用于得到其余8大隐式对象
这样作的意义:
欲移除jsp中的java代码,就须要将java代码写到一个java类的成员方法中,而后想办法在jsp
页面中调用该方法,以达到代码复用的目的.
因为jsp中的java代码不免会访问8个隐式对象,由于这些对象都是和web开发相关的对象,因此要移除这部分java代码就须要”将8个对象传递给java类”的方法,
为了方便,咱们一般的作法是:只传递一个pageContext对象过去,这样在方法中就能够经过该对象很轻松地得到其余8个对象了
pageContext 也是一个域对象,但只在当前jsp页面有效
重点:
1) 默写9个对象, (具体描述9个对象怎么用)
2) 理解pageContext对象的意义 (得到其余8个对象)
Btw: pageContext 有个特殊的方法 findAttribute()
范围由小到大: page(jsp有效) request(一次请求) session(一次会话) application(当前web应用)
page : PageContext对象
request : request对象
session : session对象
application : ServletContext对象
生命周期: 就是指对象的建立到销毁的期间
page: jsp 页面被执行,生命周期开始,jsp 页面执行完毕 ,生命周期结束
request : 用户发送一个请求,开始,服务器返回响应,请求结束,生命周期结束
session : 用户打开浏览器访问,(getSession方法被调用来)建立session ---(开始)
当session超时或被声明失效,该对象生命周期结束 ---(结束)
application: web应用加载的时候建立(开始), web应用被移除或服务器关闭,对象销毁(结束)
2.1. 什么是域? 为何把这4个对象叫作域对象呢?
域: 即范围的意思
web中的域对象:能够存储对象,在做用范围内均可以取到,其内部是Map集合的实现 Map<String, Object>
class PageContext {
private Map map = new HashMap();
private HttpSession session;
……
}
四种域对象的做用范围 ( 重要 ):
page: 只在当前jsp页面有效
request: 只在当前请求有效, 每次请求分别对应不一样的request域对象
session : 只在一次会话中有效,会话结束就没法取到数据了 (特殊状况,发送Cookie)
// session: 默认状况下,同一个浏览器来访问有效(发送同一个sessionid)
application : 在一个web应用中有效 (只要服务器不关,web应用不移除就能够取数据)
四个域对象的范围由小到大排列依次为: page < request < session < application
原则: 四个域对象在选择的时候,能用范围小的毫不用范围大的
page: 数据只是暂时存放在集合中,若是要在jsp页面的其余地方使用,须要用page(页面中自定义的map)
ps: 何时须要用map了,就用page
request:数据只是作显示的,看完了就没用了,就存request域
ps: 请求转发, Servlet 产生的处理结果(数据) 交给jsp显示,
session: 数据给用户看完了,一会还要用,会话结束了就没用了
ps :用户登录,用户信息发给客户端看,看完了,一会访问别的页面还要看用户信息
ps: 购物车,购物成功了,给用户看购物车,待会随时能够查看购物车
ps: 请求重定向,由于是两次请求,第一次请求的数据,第二次请求还要看
application : 数据给一个用户用完了,别人还要用
ps: 聊天室,聊天记录,须要给全部的用户看
ps: 统计网站在线人数,全部人看到的应该是一个数
总结: 须要定义Map不如用page,
请求转发Servlet带给jsp的数据存request
请求重定向带过去的数据存Session,
全局的数据存application
5、 jsp 细节
只有当jsp页面指定的page指令isErrorPage为true时,才有exception隐式对象
Jsp注释:
jsp 出错:
1) 被翻译的Servlet不能编译,语法错,这时会报告是由于jsp中的哪行致使不能编译
2) 翻译的Servlet 在运行期间出现异常, 报告是jsp的哪行致使的异常
此时会进一步报告致使异常的缘由,在Servlet中的哪行出现异常
2. jsp 映射 也是经过servlet 元素
6、内省( introspect )
1)必须有无参构造函数
2)属性必须私有,咱们称为字段
3)必须提供标准的get和set方法
例: name 字段 的getter: String getName() settter: void setName(String name)
Jdk中的api : PropertyDescriptor类操做Bean的属性
核心类 BeanUtils
setProperty(bean, name, value)
copyProperties(target, source);
BeanUtils的做用:
1.能够支持String到8种基本数据类型转换
2.其余引用数据类型都须要注册转换器 ConvertUtils.register(Converter, Class)
为了移除jsp页面的java代码,sun公司提供了一些内置的标签à咱们称为jsp标签,或jsp动做元素
1. <jsp:include> 至关于 RequestDispatcher 对象的页面引入
dispatcher.include 用于实现 Servlet包含
dispatcher.forward 用于实现servlet转发
forward 在转发时, web容器会清空response中的数据,转发以后就没法向response写入数据
动态引入方式: 在程序运行期间引入,jsp被翻译成两个servlet (check?)
静态引入方式: include指令也能实现页面的引入,将两个jsp翻译成一个Servlet
包含和被包含的jsp页面指令不能发生冲突(其中,import和pageEncoding能够冲突)
2. <jsp:forward> 实现请求转发
结合 <jsp:param> 标签传参,自动进行url编码,编码的方式参照request编码
3. <jsp:useBean id class scope> 内省
反射建立javabean,以id做为key存入指定的域
其实在建立以前,会先去域中找,找到了则不建立
4. <jsp:setProperty> 设置属性值
<jsp:setProperty name=”user” property=”username” value=”zs” />
<jsp:setProperty name=”user” property=”username” param=”username” />
<jsp:setProperty name=”user” property=”*” /> 批量
5. <jsp:getProperty> 得到属性值
8、web开发模式
Sun公司针对web开发提供了两种模式
Model1: jsp+javabean 只适合小型应用
Model2: servlet+jsp+javabean MVC (重要)
Lesson 13: EL表达式 和 JSTL标签
1. el 全名为Expression Language。它是一种数据访问语言(不能写if语句,for循环等逻辑语句),简称: el 表达式
2. EL 能实现以下功能:
1) 使用变量访问web域中存储的对象 ${user}
2) 访问javabean的属性 ${user.address.city}
3) 执行基本的逻辑运算
4) 直接使用隐式对象
5) 调用 el 函数
3. el 表达式用在哪里
1) 在 jsp 页面直接输出数据
2) 在标签中使用el直接为属性赋值
4. el 表达式获取数据
1.在jsp页面使用el表达式能够轻松地得到web域中的对象
2.并对 javabean 、 数组、 list 、 map 进行取值
5. el表达式不能写if,for等逻辑语句,因此须要对web域中的list和map集合进行迭代就须要结合 jstl 迭代标签
JSTL是sun公司开发的一套标签库
1. JSTL标签的做用: 使用JSTL能够在页面中实现一些简单的逻辑,从而替换页面中的脚本代码
2. 如何在页面中使用JSTL标签? 在页面中使用JSTL标签需完成如下2个步骤:
1) 导入jstl.jar和standerd.jar这两个JSTL的jar文件。
2) 在JSP页面中使用 <%@ taglib=”” uri=“” prefix=“” %> 元素导入标签库。
3. 最经常使用的 jstl 标签为 forEach 和 if 标签
<c:foreach var=”” items=””>
<c:if test=””>
6. el表达式虽然不能进行逻辑判断和写for循环,可是el表达式中能够进行逻辑运算
7. el表达式中的保留关键字
隐含对象名称 |
描 述 |
pageContext |
对应于JSP页面中的pageContext对象 (用于获取JSP的8个隐式对象.最主要用途:${pageContext.request.contextPath} |
pageScope |
表明page域中用于保存属性的Map对象 |
requestScope |
表明request域中用于保存属性的Map对象 |
sessionScope |
表明session域中用于保存属性的Map对象 |
applicationScope |
表明application域中用于保存属性的Map对象 |
param |
表示一个保存了全部请求参数的Map对象 (应用:表单回显) |
paramValues |
表示一个保存了全部请求参数的Map对象,它对于某个请求参数,返回的是一个string[] |
header |
表示一个保存了全部http请求头字段的Map对象 |
headerValues |
同上,返回string[]数组。注意:若是头里面有“-” , 例Accept-Encoding,则要headerValues[“Accept-Encoding”] |
cookie |
表示一个保存了全部cookie的Map对象 |
initParam |
表示一个保存了全部web应用初始化参数的map对象 |
el表达式的11个隐式对象的具体用途 ( 除了pageContext,其他10个都是map类型 )
1. pageContext 做用: 得到servlet上下文路径 (web应用名称)
最经常使用代码: ${pageContext.request.contextPath }
2. pageScope、requestScope、sessionScope、applicationScope
做用: 分别对应每一个域所对应的那个map,能够准确地得到四个域中的对象,用于取值
3. param、paramValues 做用: 得到请求参数,通常用于作表单的回显
4. header、headerValues 做用: 得到请求消息头
5. cookie 做用: 得到浏览器发送的cookie
Cookie也是map集合,key是cookie的name value是对应的cookie对象
6. initParam 做用: 得到web 初始化参数
1. 咱们能够开发自定义函数,用于调用java类的方法
案例: 对页面输出的内容进行 html 转义
实现步骤:
1) 编写一个java类, 定义一个静态方法
去tomcat的例子中拷贝一个现成的
%tomcat安装目录%\webapps\examples\WEB-INF\classes
2) 编写标签库描述符(tld)文件,在tld文件中描述自定义函数
找个现成的修改一下
%tomcat安装目录%\webapps\examples\WEB-INF\jsp2
3) 在jsp页面中导入标签库 便可使用函数
<%@ taglib prefix="myfn" uri="/WEB-INF/myfn.tld" %>
<%@ taglib prefix="myfn" uri="http://www.itcast.cn/myfn" %>
帖子的内容 : ${myfn:transfer(requestScope.data) }
2. sun 公司针对经常使用的字符串处理在jstl技术中提供了el函数中
Lesson 17
1、自定义标签入门
jsp页面做为显示的模板,应尽可能使用页面标签来实现,避免写java代码
若是在jsp页面写java代码,首先会令jsp页面难以阅读,不利于页面排版,其次,做为页面美化人员有可能会看不懂java代码,若是将java代码替换成标签,那么只要是懂html标签的人都能看得懂
移除jsp页面中的java代码其实很简单,只须要写一个java类实现Tag接口,在java类中实现jsp页面的代码,在tld标签描述文件中针对标签和java类进行映射。
案例: 实现标签显示来访者的ip
实现步骤:
1) 写一个类 IpTag 实现 Tag 接口
2) 在 WEB-INF 目录下 新建tld文件, 对标签进行描述
3)在 jsp 页面进行调用
2、传统标签 (已过期,可是框架中仍是会有不少传统标签)
执行流程:
1)建立对象 初始化
2)调用setPageContext方法传入表示jsp页面的pageContext
3)若是有父标签调用 setParent 方法传入父标签
4)调用 setter 方法为属性赋值
5)调用 doStartTag 方法
6)执行标签体 可选
7)调用 doEndTag 方法
8)调用release方法释放资源
Tag.EVAL_BODY_INCLUDE 执行标签体
Tag.SKIP_BODY 跳过标签体
Tag.EVAL_PAGE 执行剩余页面
Tag.SKIP_PAGE 跳过剩余页面
1) IterationTag 接口
增长doAfterBody()方法,在方法体执行完毕时被调用
增长返回值EVAL_BODY_AGAIN 用于执行再次执行标签体
实现此接口能够实现循环执行标签体
通常经过继承默认实现类BodyTagSupport来实现该标签
2) BodyTag 接口
增长setBodyContent(BodyContent b)方法传入标签体
咱们能够经过该方法得到标签体 从而对标签体进行操做
tld 是 taglib descriptor的缩写, tld文件为标签库描述文件
经过 tag元素描述一个标签
<tag>
<description>Outputs Hello, World</description>
<name>helloWorld</name>
<tag-class>cn.itcast.tag.HelloWorldTag</tag-class>
<body-content>empty</body-content>
</tag>
其中 name用于指定标签的名称(name怎么写,在jsp页面怎么写)
tag-class用于指定标签对应的java类
body-content用于指定标签对之间能够输入的内容类型
empty: 表示空标签,即不能有标签体 (经常使用)
JSP: 标签体不为空,能够写jsp脚本
Scriptless: 不能写jsp脚本,但能够写表达式和el (经常使用)
Tagdependent: 将原始内容提交 忽略el表达式等运算
须要在页面标签中使用属性需完成如下步骤:
1) 在标签类中定义一个属性<attribute>
2) 为属性添加 setter 方法
3) 在tld文件中对属性进行描述
实现:
<tag>元素的<attribute>子元素用于描述自定义
标签的一个属性,自定义标签所具备的每一个属性
都要对应一个<attribute>元素 。
<attribute>
<description>description</description>
<name>aaaa</name>
<required>true</required>
<rtexprvalue>true</rtexprvalue>
<type>ObjectType</type>
</attribute>
attribute的子元素
元素名 |
是否必须指定 |
描 述 |
description |
否 |
用于指定属性的描述信息。 |
name |
是 |
用于指定属性的名称。属性名称是大小写敏感的,而且不能以jsp、_jsp、java和sun开头。 |
required |
否 |
用于指定在JSP页面中调用自定义标签时是否必须设置这个属性。其取值包括true和false,默认值为false,true表示必须设置,不然能够设置也能够不设置该属性。 |
rtexprvalue |
否 |
rtexprvalue是runtime expression value(运行时表达式)的英文简写,用于指定属性值是一个静态值或动态值。其取值包括true和false,默认值为false,false表示只能为该属性指定静态文本值,例如"123";true表示能够为该属性指定一个JSP动态元素,动态元素的结果做为属性值,例如JSP表达式<%=value %>。 |
type |
否 |
用于指定属性值的Java类型。 |
实现将标签体循环执行指定次数的标签 Loop
因为传统标签使用三个标签接口来完成不一样的功能,显得过于繁琐,不利于标签技术的推广, SUN公司为下降标签技术的学习难度,在JSP 2.0中定义了一个更为简单、便于编写和调用的SimpleTag接口来实现标签的功能
咱们能够经过继承 SimpleTagSupport 类来实现SimpleTag接口
方法详解:
setJspContext方法
用于把JSP页面的pageContext对象传递给标签处理器对象
setParent方法
用于把父标签处理器对象传递给当前标签处理器对象
getParent方法
用于得到当前标签的父标签处理器对象
setJspBody方法
用于把表明标签体的JspFragment对象传递给标签处理器对象
doTag方法
用于完成全部的标签逻辑,包括输出、迭代、修改标签体内容等。
在doTag方法中能够抛出javax.servlet.jsp.SkipPageException异常,用于通知WEB容器再也不执行JSP页面中位 于结束标记后面的内容,这等效于在传统标签的doEndTag方法中返回Tag.SKIP_PAGE常量的状况。
Question: 方法的调用顺序?
开发防盗链标签
开发<c:if>标签
开发<c:if><c:else>标签
choose when otherwise
开发迭代标签
Foreach
开发html转义标签
打包标签库
Lesson 9
jdbc : Java Database Connectivity
sun公司为了统一对数据库的操做,定义了一套api,称之为jdbc
这套api彻底由接口组成,咱们在编写程序的时候针对接口进行调用
这些接口交给数据库厂家去实现, 不一样的数据库厂商会提供不一样的实现类,这些实现类被咱们称做数据库的驱动
步骤:
建 user 表 user.sql
create database day12 character set utf8 collate utf8_general_ci;
use day12;
create table users(
id int primary key auto_increment,
name varchar(40),
password varchar(40),
email varchar(60),
birthday date
)character set utf8 collate utf8_general_ci;
insert into users(name,password,email,birthday)
values('zs','123456','zs@sina.com','1980-12-04');
insert into users(name,password,email,birthday)
values('lisi','123456','lisi@sina.com','1981-12-04');
insert into users(name,password,email,birthday)
values('wangwu','123456','wangwu@sina.com','1979-12-04');
mysql-connector-java-5.0.8-bin.jar
// 1. 注册数据库的驱动
DriverManager.registerDriver(new com.mysql.jdbc.Driver());
// 2. 创建与mysql数据库的链接 用到 jdbc api
String url = "jdbc:mysql://localhost:3306/day11";
String user = "root";
String password = "root";
Connection conn = DriverManager.getConnection(url, user, password);
// 3. 建立用于发送sql语句的 Statement 对象
Statement stmt = conn.createStatement();
// 4. 编写一句 sql
String sql = "select * from users";
// 5. 发送sql, 得到结果集
ResultSet rs = stmt.executeQuery(sql);
// 6. 处理结果集
System.out.println("id | name | password | email | birthday");
while(rs.next()) {
// 有第一行
int id = rs.getInt("id"); // 经过列名取值比较直观
String name = rs.getString("name");
String psw = rs.getString("password");
String email = rs.getString("email");
Date birthday = rs.getDate("birthday");
System.out.println(id + " | " + name + " | " + psw + " | " + email + " | " + birthday);
}
// 7. 关闭链接 释放资源
rs.close();
stmt.close();
conn.close();
DriverManager.registerDriver(new com.mysql.jdbc.Driver());
上面的语句会致使注册两次驱动
缘由在于,查看Driver类的源码会发如今静态代码块中完成了注册驱动的工做,
也就是说注册驱动其实很简单,只须要加载驱动类便可
Class.forName(“com.mysql.jdbc.Driver”);
Connection conn = DriverManager.getConnection(url, user, password);
其中:url, 至关于数据库的访问地址,程序员经过url指定须要访问的数据库
jdbc:mysql:[]//localhost:3306/test?参数名:参数值
其中jdbc为主协议,mysql为子协议,localhost为主机名,3306为端口号,test为数据库名
url的后面能够跟参数,经常使用的参数有:user=root&password=root&characterEncoding=UTF-8
若是url地址后面跟了user和password,建立Connection对象时将没必要再次传入值
Connection conn = DriverManager.getConnection(url);
补充: 若是访问的localhost:3306,url 可省写为jdbc:mysql:///test
经常使用方法有:
createStatement():建立向数据库发送sql的statement对象。
prepareStatement(sql) :建立向数据库发送预编译sql的PrepareSatement对象。
prepareCall(sql):建立执行存储过程的callableStatement对象。
setAutoCommit(boolean autoCommit):设置事务是否自动提交。
commit() :在连接上提交事务。
rollback() :在此连接上回滚事务。
execute(String sql):用于向数据库发送任意sql语句
executeQuery(String sql) :只能向数据发送查询语句。
executeUpdate(String sql):只能向数据库发送insert、update或delete语句
addBatch(String sql) :把多条sql语句放到一个批处理中。
executeBatch():向数据库发送一批sql语句执行。
存储的形式就是一种表格的形式,一样是列+行,说白了就和咱们在 dos 命令行窗口查询的结果同样
遍历方式:
一开始游标指向结果集第一行, 也就是表头
经过 next 将游标移向下一行, 若是没有下一行,该方法会返回false
得到当前行的数据须要调用get方法:
get(int index)得到第几列 列数从1开始
get(String columnName) 根据列名得到值 (经常使用)
数据库的数据类型与java中数据类型的对应关系
ResultSet对象的经常使用方法
next():移动到下一行
previous():移动到前一行
absolute(int row):移动到指定行
beforeFirst():移动resultSet的最前面。
afterLast() :移动到resultSet的最后面。
因为数据库的资源很是宝贵,因此用完了必定要记得释放资源
特别是Connection对象,由于数据容许的并发访问链接数量每每都比较有限
在java程序中,咱们应该将最终必需要执行的代码放到finally当中
释放资源的代码
if(rs!=null) {
try {
rs.close();
} catch (SQLException e) {
e.printStackTrace();
}
rs = null;
}
if(stmt!=null) {
try {
stmt.close();
} catch (SQLException e) {
e.printStackTrace();
}
stmt = null;
}
if(conn!=null) {
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
conn = null;
}
1. 编写程序对User表进行增删改查操做
2.防止 sql 注入
在 service 层进行逻辑判断
使用PreparedStatement对象
3. 编写工具类对 jdbc 程序进行优化
将得到链接和释放资源的代码写到通用的工具类中
实现一个简单的员工信息管理系统,练习对员工表的crud
字段名 |
说明 |
类型 |
id |
编号 |
varchar(40) |
name |
员工姓名 |
varchar(20) |
gender |
性别 |
varchar(4) |
birthday |
出生日期 |
date |
idcard |
身份证号 |
varchar(20) |
degree |
学历 |
varchar(20) |
entrydate |
入职日期 |
date |
position |
部门 |
varchar(40) |
department |
职位 |
varchar(20) |
web开发的两种模式
model1: jsp+javabean 只适合小型应用
model2: servlet+jsp+javabean mvc
jsp 的做用只是显示的模板,因此在mvc中jsp应该禁止外界直接方法,藏在 web-inf下面
web应用的分层框架
案例实现步骤:
1. 搭建开发环境
1) 建工程 建包
2) 导入须要jar包
BeanUtils 相关jar包
jstl 标签
mysql 的驱动
3) 建立数据库和表
create database day12_employee;
use day12_employee;
create table employee
(
id varchar(40) primary key,
name varchar(20),
gender varchar(6),
birthday date,
idcard varchar(20),
degree varchar(20),
entrydate date,
department varchar(20),
position varchar(40)
);
2. 设计bean
Employee.java
3. 实现dao(不是很理解..须要弄清楚)
4. 实现service
简单service 原封不动调用dao的方法
5. 实现web层
首页
在数据量较大的状况下,咱们会数据分做多页显示,让用户浏览起来更加的方便,能够根据页码去翻阅每一页的数据
说到分页,通常都会马上想到先将数据存入集合,再将数据分做多页显示,这样的作法当然能够,但是一旦数据量较大的话就会形成内存的溢出,再者说,大部分用户的浏览习惯都是只看前三页,若是数据总共有100页,那就彻底不必将数据所有从数据库中查出来了,因此一个广泛的实现方式都是根据用户须要浏览的页码,从数据库中查询一页的数据供用户浏览
分页的目的就是为了更加合理地作页面显示,因此首先要解决的就是页面须要显示哪些数据
一般来说,页面上会显示当前页的信息、当前第几页、总共多少页、页码、上一页、下一页等信息
咱们可使用一个 Page 对象来封装页面须要实现的数据
在service中计算出 Page 对象所需的数据
Service中须要的一些来自于数据库的数据就找 Dao 索取
1)根据需求设计Page对象
2)dao的实现
两个方法:
int getTotalRecord(); // 得到总记录数
List getPageData(int start, int len); // 得到分页数据
3)service 实现
Page getPage(int pageNum); // 计算分页数据
4)Servlet 得到页面提交的 pageNum
找service计算出分页数据 Page
转发给jsp 作分页显示
所谓事务,就是针对数据库的一组操做(多条sql)
位于同一个事务的操做具有同步的特色,也就是要么都成功,要么都失败
在实际中,咱们的不少操做都是须要由多条sql来共同完成的,例如,A帐户给B帐户转帐就会对应两条sql
update account set money=money-100 where name=‘a’;
update account set money=money+100 where name=‘b’;
假设第一条sql成功了,而第二条sql失败了,这样就会致使a帐户损失了100元,而b帐户并未获得100元
若是将两条sql放在一个sql中,当第二条语句失败时,第一条sql语句也一样不会生效,这样a帐户就不会有任何的损失----------------à这就是事务
默认状况下,咱们向数据库发送的sql语句是会被自动提交的,开启事务就是至关于关闭自动提交功能,改成手动提交,咱们只须要将提交事务的操做放在最后一个操做,这样一来,若是在提交事务以前出现异常,因为没有执行提交操做,事务中未提交的操做就会被回滚掉
account.sql
create table account(
id int primary key auto_increment,
name varchar(40),
money float
)character set utf8 collate utf8_general_ci;
insert into account(name,money) values('aaa',1000);
insert into account(name,money) values('bbb',1000);
insert into account(name,money) values('ccc',1000);
aaa 给 bbb 转帐 100元
update account set money=money-100 where name='aaa';
// 异常退出
update account set money=money+100 where name='bbb';
// 查询结果
select * from accont;
若是开启事务就能够避免刚才的错误发生
// 开启事务
start transaction;
// 若是操做执行完毕,咱们须要将事务提交
commit;
// 还能够手动回顾事务中全部未提交的事务
rollback;
命令行作实验得出结论:
若是开了一个事务, 又敲了一次 start transaction 再次开启事务, 前一个事务会自动提交
在使用 jdbc 操做数据库时,须要使用 Connection 对象对事务进行管理
// 开启事务
Connection.setAutoCommit(false); //设置自动提交为false
// 回滚事务
Connection.rollback();
//提交事务
Connection.commit();
在 jdbc 程序中咱们还能够设置回滚点, 让事务回顾到指定的回滚点,而不是自动回滚全部未提交的操做
须要将程序中的异常捕获,在catch语句块中回滚事务,在finally中提交事务
注意 : 将 Commit 操做放在 finally 中是为了保证提交未回滚的事务操做
事务有四大特性,通常来说,判断一个数据库是否支持事务,就看数据库是否支持这四个特性
l 原子性(Atomicity)
原子性是指事务是一个不可分割的工做单位,事务中的操做要么都发生,要么都不发生。
l 一致性(Consistency)
事务必须使数据库从一个一致性状态变换到另一个一致性状态。
l 隔离性(Isolation)
事务的隔离性是多个用户并发访问数据库时,数据库为每个用户开启的事务,不能被其余事务的操做数据所干扰,多个并发事务之间要相互隔离。
l 持久性(Durability)
持久性是指一个事务一旦被提交,它对数据库中数据的改变就是永久性的,接下来即便数据库发生故障也不该该对其有任何影响。
因为数据库是多线程并发访问的,因此很容易出现多个线程同时开启事务的状况
多线程开事务容易引发 赃读、不可重复读、幻读 的状况发生
赃读:dirty read
是指一个事务读取了另外一个事务未提交的数据,这是至关危险的。
设想一下,A要给B转帐100元购买商品, 若是A开启了一个事务作了转帐的工做
update account set money=money+100 while name=‘b’;
update account set money=money -100 while name=‘a’;
A先不提交事务,通知B来查询
这时B来查询帐户,因为会读到A开启的事务中未提交的数据,就会发现A确实给本身转了100元,
天然就会给A发货,A等B发货后就将事务回滚,不提交,此时,B就会受到损失
不可重复读:non-repeatable read
是指事务中两次查询的结果不一致,缘由是在查询的过程当中其余事务作了更新的操做 update
例如,银行作报表,第一次查询A帐户有100元,第二次查询A帐户有200元,缘由是期间A存了100元
这样就会致使一行屡次统计的报表不一致
不可重复读和脏读的区别是:
脏读是读取前一事务未提交的脏数据,不可重复读是在事务内重复读取了别的线程已提交的数据。
有的时候你们会认为这样的结果是正确的,没问题
咱们能够考虑这样一种状况,好比银行程序须要将查询结果分别输出到电脑屏幕和写到文件中,结果在一个事务中针对输出的目的地,进行的两次查询不一致,致使文件和屏幕中的结果不一致,银行工做人员就不知道以哪一个为准了。
幻读:phantom read 又名虚读
是指在一个事务内两次查询中数据笔数不一致
幻读和不可重复读有些相似,是指两次查询过程当中,其余事务作了插入记录的操做,致使记录数有所增长
insert
例如: 银行作报表统计account表中全部用户的总额时,此时总共五个帐户,总金额为500元,
这时有一个新的帐户产生了,而且存了100元,这时银行再统计会发现账户总金额为600元了,
形成虚读一样会使银行遇到一样的困惑
实验发现不会出现虚读
来自网络的解释:幻读只是在理论上会发生的一种状况,而现实操做中并非必定会发生
为了不多线程开事务引起的问题,咱们须要将事务进行隔离
事务有四种隔离级别,不一样的隔离级别能够防止不一样的错误发生
serializable:可串行化,能避免脏读、不可重复读、幻读状况的发生
repeatable read:可重读,能避免脏读、不可重复读状况的发生
read committed:读取提交的内容,可避免脏读状况发生
read uncommitted:读取未提交的内容最低级别,避免不了任何状况
操做:
//设置事务隔离级别 set transaction isolation level
//查询当前事务隔离级别 select @@tx_isolation
查询看到的都是快照
位于事务中的屡次查询,若是隔离级别设置为repeatable read,
那么屡次查询读的就是一个快照说白了就是不更新快照
传统的开发模式下,Servlet处理用户的请求,找Dao查询数据,dao会建立与数据库之间的连接,完成数据查询后会关闭数据库的连接。
这样的方式会致使用户每次请求都要向数据库创建连接而数据库建立链接一般须要消耗相对较大的资源,建立时间也较长。假设网站一天10万访问量,数据库服务器就须要建立10万次链接,极大的浪费数据库的资源,而且极易形成数据库服务器内存溢出、宕机。
解决方案: 就是数据库链接池
链接池就是数据库链接对象的一个缓冲池
咱们能够先建立10个数据库链接缓存在链接池中,当用户有请求过来的时候,dao没必要建立数据库链接,而是从数据库链接池中获取一个,用完了也没必要关闭链接,而是将链接换回池子当中,继续缓存
使用数据库链接池能够极大地提升系统的性能
jdbc针对数据库链接池也定义的接口java.sql.DataSource,全部的数据库链接池实现都要实现该接口
该接口中定义了两个重载的方法
Connection getConnection()
Connection getConnection(String username, String password)
数据库链接池实现思路
1) 定义一个类实现java.sql.DataSource接口
2) 定义一个集合用于保存Connection对象,因为频繁地增删操做,用LinkedList比较好
3) 实现getConnection方法,在方法中取出LinkedList集合中的一个链接对象返回
注意:
返回的Connection对象不是从集合中得到,而是删除
用户用完Connection,会调用close方法释放资源,此时要保证链接换回链接池,而不是关闭链接
重写close方法是难点,解决方案: 装饰设计模式、动态代理
一般咱们把DataSource的实现,按其英文含义称之为数据源,数据源中都包含了数据库链接池的实现。
一些开源组织提供了数据源的独立实现,经常使用的有:
DBCP 数据库链接池
C3P0 数据库链接池
DBCP 是 Apache 软件基金组织下的开源链接池实现
tomcat服务器就是使用DBCP做为数据库链接池
使用DBCP数据源,须要导入两个jar包
Commons-dbcp.jar:链接池的实现
Commons-pool.jar:链接池实现的依赖库
DBCP核心 API
BasicDataSource
数据源实现
BasicDataSourceFactory
用于建立数据源的工厂类
方法1: 直接建立对象,设置参数
BasicDataSource bds = new BasicDataSource();
// 设置链接数据库须要的配置信息
bds.setDriverClassName("com.mysql.jdbc.Driver");
bds.setUrl("jdbc:mysql://localhost:3306/jdbc3");
bds.setUsername("root");
bds.setPassword("root");
// 设置链接池的参数
bds.setInitialSize(5);
bds.setMaxActive(10);
ds = bds
方法2: 经过工厂类建立对象,读取配置文件
try {
Properties prop = new Properties();
// 读配置文件
InputStream in =
JdbcUtils.class.getClassLoader().getResourceAsStream("dbcpconfig.properties");
prop.load(in);
ds = BasicDataSourceFactory.createDataSource(prop);
}catch (Exception e) {
throw new ExceptionInInitializerError(e);
}
#链接设置
driverClassName=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/jdbc3
username=root
password=root
#<!-- 初始化链接 -->
initialSize=5
#最大链接数量
maxActive=10
#<!-- 最大空闲链接 -->
maxIdle=10
#<!-- 超时等待时间以毫秒为单位 6000毫秒/1000等于60秒 -->
maxWait=60000
#JDBC驱动创建链接时附带的链接属性属性的格式必须为这样:[属性名=property;]
#注意:"user" 与 "password" 两个属性会被明确地传递,所以这里不须要包含他们。
connectionProperties=useUnicode=true;characterEncoding=gbk
#指定由链接池所建立的链接的自动提交(auto-commit)状态。
defaultAutoCommit=true
#driver default 指定由链接池所建立的链接的只读(read-only)状态。
#若是没有设置该值,则“setReadOnly”方法将不被调用。(某些驱动并不支持只读模式,如:Informix)
defaultReadOnly=
#driver default 指定由链接池所建立的链接的事务级别(TransactionIsolation)。
#可用值为下列之一:(详情可见javadoc。)NONE,READ_UNCOMMITTED, READ_COMMITTED, REPEATABLE_READ, SERIALIZABLE
defaultTransactionIsolation=READ_UNCOMMITTED
c3p0是一个开源的jdbc链接池,咱们熟悉的 Hibernate和 Sprint 框架使用的都是该数据源
方法1:直接建立对象,设置参数
ComboPooledDataSource cpds = new ComboPooledDataSource();
cpds.setDriverClass("com.mysql.jdbc.Driver");
cpds.setJdbcUrl("jdbc:mysql://localhost:3306/jdbc3");
cpds.setUser("root");
cpds.setPassword("root");
cpds.setInitialPoolSize(5);
cpds.setMaxPoolSize(15);
方法2:读取配置文件
ComboPooledDataSource cpds = new ComboPooledDataSource("itcast");
配置文件为c3p0-config.xml 该文件须要放在类路径下
<c3p0-config>
<default-config>
<!—- 默认配置 –->
<property name="initialPoolSize">5</property>
<property name="maxPoolSize">15</property>
<property name="driverClass">com.mysql.jdbc.Driver</property>
<property name="jdbcUrl">jdbc:mysql://localhost:3306/jdbc3</property>
<property name="user">root</property>
<property name="password">root</property>
</default-config>
<named-config name="xwh">
<property name="initialPoolSize">5</property>
<property name="maxPoolSize">15</property>
<property name="driverClass">com.mysql.jdbc.Driver</property>
<property name="jdbcUrl">jdbc:mysql://localhost:3306/jdbc3</property>
<property name="user">root</property>
<property name="password">root</property>
</named-config>
</c3p0-config>
元数据,能够理解为描述数据的数据
jdbc中的元数据是指数据库、表、列的定义信息
ResultSetMetaData对象表示结果集 ResultSet对象的元数据
得到该对象:
ResultSetMetaData metaData = rs.getMetaData();
经常使用方法:
getColumnCount() 返回resultset对象的列数
getColumnName(int column) 得到指定列的名称
getColumnTypeName(int column) 得到指定列的类型
使用jdbc对数据库进行crud操做时,会有不少重复的代码,仔细分析不难发现其实变化的只是其中几行代码
对于 cud(增删改) 操做,代码几乎彻底同样, 惟一的区别就是sql语句不一样,咱们彻底能够把相同的代码抽取出来定义在一个工具方法中,而后定义一个参数来接收sql语句
对于 r(查询) 操做,除SQL语句不一样以外,根据操做的实体不一样,对ResultSet结果集的处理也有所不相同,所以可义一个query方法,除以参数形式接收变化的SQL语句外,可使用策略模式由qurey方法的调用者决定如何把ResultSet中的数据映射到实体对象中
// 通用的增删改方法
public static int update(String sql, Object[] params) throws SQLException {
Connection conn = null;
PreparedStatement pstmt = null;
ResultSet rs = null;
try {
// 得到链接
conn = getConnection();
// 预编译sql
pstmt = conn.prepareStatement(sql);
// 将参数设置进去
for(int i=0; params!=null&&i<params.length; i++) {
pstmt.setObject(i+1, params[i]);
}
// 发送sql
int num = pstmt.executeUpdate();
return num;
} finally {
// 释放资源
release(conn, pstmt, rs);
}
}
// 优化查询
public static Object query(String sql, Object[] params, ResultSetHandler rsh) throws SQLException {
Connection conn = null;
PreparedStatement pstmt = null;
ResultSet rs = null;
try {
// 得到链接
conn = getConnection();
// 预编译sql
pstmt = conn.prepareStatement(sql);
// 将参数设置进去
for(int i=0; params!=null&&i<params.length; i++) {
pstmt.setObject(i+1, params[i]);
}
// 发送sql
rs = pstmt.executeQuery();
// 不知作别人想如何处理结果集
// 干脆想别人所要一个结果集的处理器
// 为了让当前代码继续,定义一个结果集处理器接口
// 策略模式, 规定算法,具体的算法留给未来的调用者实现
Object obj = rsh.handle(rs);
return obj;
} finally {
// 释放资源
release(conn, pstmt, rs);
}
}
public interface ResultSetHandler {
// 处理结果集的方法
public Object handle(ResultSet rs);
}
public class BeanListHandler implements ResultSetHandler {
private Class clazz;
public BeanListHandler(Class clazz) {
this.clazz = clazz;
}
public Object handle(ResultSet rs) {
try {
// 取出结果集全部的记录,封装到bean,存入list返回
List list = new ArrayList();
while (rs.next()) {
Object bean = clazz.newInstance();
// 得到元数据
ResultSetMetaData metaData = rs.getMetaData();
// 得到列的数量
int count = metaData.getColumnCount();
// 遍历列
for(int i=1; i<=count; i++) {
// 取列名
String columnName = metaData.getColumnName(i);
// 取这列的值
Object value = rs.getObject(columnName);
// 反射出属性
Field field = clazz.getDeclaredField(columnName);
// 设置属性
field.setAccessible(true);
field.set(bean, value);
}
// 加入list
list.add(bean);
}
return list;
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
public class BeanHandler implements ResultSetHandler {
private Class clazz;
public BeanHandler(Class clazz) {
this.clazz = clazz;
}
public Object handle(ResultSet rs) {
// 不知道有几列数据,不知道列名,不知道封装到什么样的bean
// 表的列明和javabean的字段名一致
try {
if(rs.next()) {
// 建立bean
Object bean = clazz.newInstance();
// 封装数据
// 得到结果集的元数据
ResultSetMetaData metaData = rs.getMetaData();
int count = metaData.getColumnCount();
// 迭代取每一列的数据
for(int i=1; i<=count; i++) {
// 得到列名 username
String columnName = metaData.getColumnName(i);
// 得到数据 ddd
Object value = rs.getObject(columnName);
// 根据列名反射出映射的属性 username
Field field = clazz.getDeclaredField(columnName);
// 为属性赋值
field.setAccessible(true);
field.set(bean, value);
}
return bean;
}
return null;
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
// 取出第一行的全部记录存入一个Object数组
public class ArrayHandler implements ResultSetHandler {
public Object handle(ResultSet rs) {
try {
if (rs.next()) {
// 指向了第一行的记录
// 得到元数据
ResultSetMetaData metaData = rs.getMetaData();
// 得到列数
int count = metaData.getColumnCount();
// 建立数组
Object[] arr = new Object[count];
// 迭代全部列的值,存入数组
for(int i=1; i<=count; i++) {
Object value = rs.getObject(i); // 得到指定列的值
arr[i-1] = value;
}
return arr;
}
return null;
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
批处理
处理大数据
Clob Character large Object
text
Blob binary large object
Object-Relation Mapping 对象关系映射(对象关系模型)
经常使用的 O-R Mapping 工具备:
Hibernate
session.save(user)
ibatis
sql 语句要本身写
DBUtils
简单的工具
commons-dbutils 是 Apache 组织提供的一个开源 JDBC工具类库,它是对JDBC的简单封装
DBUtils 核心API
org.apache.commons.dbutils.QueryRunner //提供update(cud)和query(r)方法
org.apache.commons.dbutils.ResultSetHandler //结果集处理器,接口类型
org.apache.commons.dbutils.DbUtils //工具类,提供一系列close方法,装载驱动等
API详解
1. QueryRunner
重载的构造函数
public QueryRunner()
调用无参的构造方法,在进行 crud 操做时须要传入 Connection 对象,通常用于事务
public QueryRunner(DataSource ds)
建立对象时传入 数据源 多数状况下采用此构造函数
2. ResultSetHandler
该接口为结果集处理器,因此对结果集进行处理的程序都须要实现该接口
DBUtils框架提供了一系列经常使用的结果集处理器实现类
l ArrayHandler:把结果集中的第一行数据转成对象数组。
l ArrayListHandler:把结果集中的每一行数据都转成一个数组,再存放到List中。
l BeanHandler:将结果集中的第一行数据封装到一个对应的JavaBean实例中。
l BeanListHandler:将结果集中的每一行数据都封装到一个对应的JavaBean实例中,存放到List里。
l ColumnListHandler:将结果集中某一列的数据存放到List中。
l KeyedHandler(name):将结果集中的每一行数据都封装到一个Map里,再把这些map再存到一个map里,其key为指定的key。
l MapHandler:将结果集中的第一行数据封装到一个Map里,key是列名,value就是对应的值。
l MapListHandler:将结果集中的每一行数据都封装到一个Map里,而后再存放到List
3、 JDBC 操做多表
多表的关系三种:
1. many2one
典型应用 部门和员工
2. mamy2many
典型应用 老师和学生
3. one2one
典型应用 用户和住址
用户上传头像、上传图片、邮件上传附件等
文件上传表单和普通表单有两个区别
1) 须要文件上传字段 <input type=”file” />
2) form 表单的 enctype 属性须要指定为 multipart/form-data
3) 文件的提交方式必须为POST
在 Servlet 中经过 request.getInputStream 得到表单上传数据,会发现数据是分段发送的
因为本身写程序解析有难度,咱们可使用Apache 开发的开源组件Commons-fileupload
须要导入 jar 包Commons-fileupload 和Commons-io
// 1. 建立工厂类
DiskFileItemFactory factory = new DiskFileItemFactory();
// 2. 建立FileUpload对象
ServletFileUpload upload = new ServletFileUpload(factory);
// 3. 判断是不是上传表单
boolean b = upload.isMultipartContent(request);
if(!b) {
// 不是文件上传
request.setAttribute("message", "对不起,不是文件上传表单!");
request.getRequestDispatcher("/message.jsp").forward(request, response);
return;
}
// 是文件上传表单
// 4. 解析request,得到FileItem项
List<FileItem> fileitems = upload.parseRequest(request);
// 5. 遍历集合
for(FileItem item : fileitems) {
// 判断是否是普通字段
if(item.isFormField()) {
String name = item.getFieldName();
String value = item.getString();
// 手工的转换了
value = new String(value.getBytes("iso-8859-1"),"utf-8");
System.out.println(name + "=" + value);
}
else {
// 文件上传字段
// 得到文件名
String filename = item.getName();
System.out.println(filename);
filename = filename.substring(filename.lastIndexOf("\\")+1);
System.out.println(filename);
// 建立文件
ServletContext context = getServletContext();
String dir = context.getRealPath("WEN-INF/upload");
File file = new File(dir, filename);
file.createNewFile();
// 得到流,读取数据写入文件
InputStream in = item.getInputStream();
FileOutputStream fos = new FileOutputStream(file);
int len;
byte[] buffer = new byte[1024];
while((len=in.read(buffer))>0)
fos.write(buffer,0,len);
fos.close();
in.close();
item.delete(); // 删除临时文件
}
1) 文件名中文乱码问题,解决办法: 告诉文件上传组件以什么编码方式来解码文件名
ServletUpload.setCharacterEncoding(“utf-8”);
request. setCharacterEncoding(“utf-8”);
2) 普通字段中文乱码问题
fileitem.getString(“utf-8”);
对于大文件不能缓存在内存,须要缓存到硬盘,为了方便管理,咱们须要设置临时文件存放目录
// 设置临时文件的存放位置
factory.setRepository(new File("d:/temp"));
文件上传完毕须要删除临时文件,不然会致使服务器存在两份上传文件
// 注意,须要先将流进行关闭,不然会致使临时文件没法删除
out.close();
in.close();
// 删除临时文件
fileitem.delete();
1) 目录须要隐藏,禁止外界直接访问
2) 文件名须要保证不重复
3) 文件应该分目录存放
须要实现对文件上传进度的监听,须要给FileUpload 对象添加 ProgressListener
在upload方法中对与进度相关的数据进行处理
upload.setProgressListener(new ProgressListener() {
long num = 0;
public void update(long bytesRead, long contentLength, int items) {
long progress = bytesRead*100/contentLength;
if(progress==num)
return;
num = progress;
System.out.println("上传进度:" + progress + "%");
// request.getSession().setAttribute("progress", progress);
}
});
实验:
1) 使用 iframe 发送请求,请求一个Servlet, 在Servlet 中返回响应,发送自增的num
此时会发现 iframe 会不停地向Servlet发送请求
2) 点击文件上传按钮后,iframe马上中止刷新,直至上传完毕页面跳转至新页面
3)为了观察实验结果,将form 的 target 指定为 iframe, UploadServlet回送上传完毕的结果
4) 出现上述问题的缘由,浏览器不支持多线程同时访问服务器只能同时发送一个请求,
这样的访问方式为同步访问
5) 要在文件上传的同时在iframe中实现进度访问,就须要ie浏览器与服务器进行异步交互
此时就须要 XMLHttpRequest 对象(ajax)
在javascript中能够直接使用XMLHttpRequest 对象与服务器进行异步通讯
Step 1.得到XmlHttpRequest 对象的方式有两种
1. ie7以上版本
var xhr = null;
if(window.XMLHttpRequest)
xhr = new XMLHttpRequest();
2. ie7如下版本
if(window.ActiveXObject)
xhr = new ActiveXObject(“Microsoft.XMLHTTP”);
Step 2.得到对象后须要调用open()方法输入请求地址
注意请求方式, 地址的输入, 而且须要设置为true 指定异步访问该地址
xhr.open(“get”,”/upload/servlet/UploadServlet”, false)
// 调用send 方法发送请求,post方式须要发送消息体,get方式则不用直接传入null值
xhr.send(null);
// 访问 responseText 属性得到 Servlet 回送的数据
document.write(xhr.responseText);
//设置缓冲区大小,字节为单位,默认为10K,通常不用修改
factory.setSizeThreshold(1000);
//设置临时文件存放目录
factory.setRepository(file);
//判断是否为文件上传表单
boolean b = upload.isMultipartContent(request);
//解析request对象
List<FileItem> list = upload.parseRequest(request);
//设置上传文件的最大值
setFileSizeMax(long fileSizeMax)
//设置上传文件总量的最大值
setSizeMax(long sizeMax)
//设置编码格式
setHeaderEncoding(java.lang.String encoding)
//注册进度监听器
setProgressListener(ProgressListener pListener)
//得到表单字段的属性名
item.getFieldName();
//得到普通字段的值
item.getString(charsetName)
//得到文件上传字段的文件名
item.getName()
//得到文件上传的流
item.getInputStream()
Filter 过滤器,又称拦截器
实现 Filter 接口的类咱们称之为 Filter (过滤器或拦截器)
Filter能对用户访问的资源进行拦截
在Filter里面能够用 request得到请求消息 用response写入响应消息
chain.doFilter(request, response) 方法放行 目标Servlet使用的是同一个请求和响应
doFilter 方法后面的代码会执行,在目标Servlet 返回响应后执行, 也可使用同一个请求和响应
1) 写一个类实现 Filter 接口 , 在doFilter 方法中写功能代码
public class Filter1 implements Filter {
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
System.out.println("before");
chain.doFilter(request, response);
System.out.println("after");
}
2) 在web.xml中配置Filter拦截的资源路径
<filter>
<filter-name>filter1</filter-name>
<filter-class>cn.itcast.filter.Filter1</filter-class>
</filter>
<filter-mapping>
<filter-name>filter1</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
能够针对某一个url配置多个Filter, 这些Filter就会组成一个Filter链, 用FilterChain对象表示
FilterChain对象的doFilter方法做用就是让Filter链上的当前拦截器放行,请求进入下一个Filter
response 的中文编码问题, 只能在response.getWriter() 第一次被调用以前指定编码才有效
一旦指定了编码,当前Filter链和目标Servlet使用的response都是同一个编码,由于用的原本就是一个
Response,后面再指定编码将被视为无效
Filter 就像一个特殊的Servlet
Filter 在web容器启动是就初始化
Filter 能够实现拦截功能,由于有个 FilterChain 对象,有个 doFilter方法能够实现对访问资源的放行
Filter能够替代Servlet全部的功能,还多一个放行功能
实现Filter的init和destroy方法就能够观察Filter的声明周期
web容器启动时,会读web.xml文件,将全部的Filter都初始化
Filter对象建立后会驻留在内存,当web应用移除或服务器中止时才销毁
Filter链中全部的Filter的拦截顺序 按照 在 web.xml 文件中的配置的前后顺序来进行拦截
在 web.xml 文件中为Filter 配置初始化参数
<init-param>
<param-name>name</param-name>
<param-value>xxxx</param-value>
</init-param>
在 init 方法中读取配置文件
public void init(FilterConfig filterConfig) throws ServletException {
String name = filterConfig.getInitParameter("name");
}
1) 文件缓存
因为html页面的url是没有在 web.xml 文件中配置的 服务器会调用DefaultServlet
在DefaultServlet 中会检查文件的修改时间, 若是没有修改则发送 304头
这样就会致使过滤器也被缓存
能够经过发送 200 状态码,可是 html 页面的数据仍然得不到读取
2) html 页面乱码
在 Filter 和 Html 中指定编码为 utf-8 , 这样会致使 html 页面中文乱码
缘由是 html 页面数据会经过 DefaultServlet 发送
查看 web.xml 文件 发现DefaultServlet默认使用 gbk编码
修改配置 加初始化参数
<init-param>
<param-name>fileEncoding</param-name>
<param-value>utf-8</param-value>
</init-param>
Filter的dispatcher元素有4种取值, 分别表明四种拦截方式
REQUEST 拦截直接的请求方式
INCLUDE 拦截页面包含的访问方式
FORWARD 拦截请求转发访问方式
ERROR 拦截出错页面的访问方式
拦截的url地址可使用 /* *.扩展名
<filter-mapping> 元素中能够配置多个地址 用于拦截多个url或servlet
对于多个条件都符合的url, filter会进行屡次拦截
1. 缓存
禁止浏览器缓存全部动态页面
response.setDateHeader("Expires",-1);
response.setHeader("Cache-Control","no-cache");
response.setHeader("Pragma","no-cache");
强制浏览器缓存全部的静态页面 html jpg css
String uri = request.getRequestURI();
String time = null;
if(uri.endsWith(".html"))
time = config.getInitParameter("html");
else if(uri.endsWith(".jpg"))
time = config.getInitParameter("jpg");
long l = Long.parseLong(time);
response.setDateHeader("Expires",System.currentTimeMillis() + l);
2. 实现用户自动登录
1)在用户登录成功后,发送一个名称为user的cookie给客户端,cookie的值为用户名和md5加密后的密码
2)编写过滤器检查用户是否带名为user的cookie来,若是有,检查用户名和密码作自动登录
核心思路:
用户登录后找LoginServlet , LoginServlet中作登录,若是登录成功, 得到用户选择的自动登录时间
建立一个新的cookie 将用户名和密码用 “_”链接做为value,autoLogin做为name
设置cookie 的有效路径 request.getContextPath() 做用于整个web应用
设置cookie的有效时间为 autologintime
发送 cookie
写一个过滤器,对全站的资源进行拦截, 检查用户发送的cookie有没有一个名为autologin的
若是有 取出用户名和密码 再次作登录处理 若是登录成功, 将 user 存入session ,放行
出于安全性考虑, cookie 中的密码应该进行 md5 加密
3. 统一全站字符编码
response和request的post的方式好办
// 解决全站的乱码问题 request response
response.setContentType("text/html;charset=utf-8");
request.setCharacterEncoding("utf-8"); // 只对 post 方式起做用, 对get方式不起做用
对于request的get方式须要手工转换,此时就须要用到 包装设计模式decorator包装 getParameter方法
4. 为全站添加页眉和页脚 -> 添加用户模块
5. 发送压缩后的响应数据
给 IE 浏览器会送的数据 须要进行gzip 压缩 访问速度快 省点浏览
在最后将数据打给浏览器的时候, 将 response 中的数据所有压缩
在过滤器放行的时候传入一个 假的 response 提供缓冲区, 这样后面的资源都会写入个人缓冲区
缓冲区满了 或者 请求快结束的时候 将缓冲区的数据压缩后写入 真的 response
j2se 提供了一个流GZIPOutputStream 用于 gzip压缩
j2se 提供了一个流 ZIPOutputStream 用于 zip 压缩
6. 跟踪用户上次访问时间: Cookie lastAccessTime
7. 统计站内各个页面的访问次数
8. 实现某些资源只能被某些ip访问: ->拦截 -> 检查ip -> 决定是否放行
9. 实现防盗链功能: 针对全部的下载页面
10. 实现html标签转义、过滤敏感词汇
思路:
包装request对象
包装 getParameter 方法
将被包装对象的 getParameter 方法返回的数据进
Lesson 20
1、 事件监听
1. 在程序中常常会用到事件监听机制
2. 关键字:
事件: 用户的一个操做, 能够是点击一个按钮、调用一个方法、建立一个对象
事件源: 发生事件的对象
事件监听器: 负责监听发生在事件源上的事件
事件处理器: 监听器的成员方法,当事件发生的时候会触发对应的处理器(成员方法)
3. 事件处理机制
1) 将监听器绑定到事件源 (注册监听器)
2) 事件发生触发监听器的成员方法,即事件处理器,传递事件对象
3) 事件处理器经过事件得到事件源,进行处理
4. 作事件监听通常都须要作两件事情
1) 写一个类实现监听器接口
2) 将监听器注册到事件源上
2、 servlet事件监听器
1. 在Servlet 技术中主要有三类事件监听器:
1) 监听三个域对象的建立和销毁 application、session、request
ServletContextListener、HttpSessionListener、ServletRequestListener
三个监听器都是接口类型
应用: ServletContextListener(监听application) 用户作初始化工做和资源释放
HttpSessionListener(监听Session) 能够用于统计在线人数
ServletRequestListener(监听servlet) 可用于统计访问次数
public interface ServletContextListener
{
// 事件处理器
Init()
Destroy()
}
web应用启动时, web 容器会将全部的监听器都实例化,并绑定到对应的事件源上
2) 监听三个域对象中属性的变化(增长、删除和替换)
ServletContextAttributeListener,
HttpSessionAttributeListener
HttpServletRequestAttributeListener
处理器
attributeAdded
attributeReplaced
attributeRemoved
3) 感知对象被绑定到session域
HttpSessionBindingListener
该接口由javabean对象来实现
该监听器不须要注册
案例: 统计来访者的ip ServletRequestListener
统计在线人数 HttpSessionListener
统计在线用户
1. HttpSessionAttributeListener
当有属性增长了 getName 判断是否是user属性
若是是 说明有人登录了 加1
当有属性移除了 getName 判断是否是user属性
若是是 说明有人注销了 减1
2. HttpSessionBindingListener
session定时扫描器 HttpSessionListener
Lesson 22
大部分的web应用都须要集成邮件发送功能
在 internet 网上发送和接收邮件都必须经过一个专门服务器,处理邮件的服务器咱们称为邮件服务器
如今不少门户网站都拥有本身的邮件服务器,例如:sina、sohu、163等
电子邮箱是指用户在邮件服务器上申请的帐户,
邮件服务器会为每一个帐户分配必定的空间用于存储发送和接收的邮件
咱们发送一封电子邮件就须要将邮件发送给对方电子邮箱所在的服务器,对方能够等待服务器将信件送到邮箱或直接去服务器上收取邮件
在互联网上任何数据的传输都须要遵照协议,好比ie与服务器的数据交互遵循的是http协议
邮件在发送过程当中也须要遵照必定的协议
1)用户发送一封电子邮件须要遵循 SMTP 协议
ehlo 主机名 ehlo
auth login //通过base64编码后的用户名和密码
mail from:<aaa@itcast.cn>
rcpt to:<bbb@itcast.cn>
Data .号表明邮件内容的结束
quit
2)用户接收一封电子邮件须要遵循 POP3 协议
user<SP>username<CRLF>
pass<SP>password<CRLF>
stat<CRLF> 返回邮箱的统计信息
list<SP>[msg#]<CRLF>返回某一封邮件的统计信息
retr<SP>msg#<CRLF> 最重要的一个命令 接收指定编号的邮件
quit<CRLF>
3)邮件服务器也会根据功能的不一样分为接收邮件的服务器和发送邮件的服务器
发送邮件的服务器咱们习惯称为SMTP 服务器 默认监听25端口
接收邮件的服务器咱们习惯称为 POP3 服务器 默认监听110端口
4) 新浪的一个电子邮箱给搜狐的一个电子邮箱发送邮件的过程以下图所示
资料
新浪服务器 POP3服务器: pop3.sina.com,SMTP服务器: smtp.sina.com
搜狐服务器 pop3.sohu.com smtp.sohu.com
1) 安装一台易邮服务器,新建两个邮件帐号
注册的帐号是 zhangsan
电子邮箱地址: zhangsan@itcast.cn
2) 在dos命令行手动输入命令完成发送邮件(smtp)和接收邮件(pop3)
提示: 用户名和密码须要使用base64编码
String username = "aaa";
String password = "123456";
BASE64Encoder encoder = new BASE64Encoder();
System.out.println(encoder.encode(userName.getBytes()));
System.out.println(encoder.encode(password.getBytes()));
3) 经过RFC822文档实现发送一封简单邮件(不安全.已淘汰)
该文档规定了如何写一封简单邮件
文档中规定邮件分为邮件头和邮件体两部分,两部分须要使用一个空行来分隔,邮件以一个’.’结束
邮件头
from 指定发件人
to 指定收件人
subject 指定主题
cc\bcc 指定抄送和密抄
邮件体
xxxxx
例:
ehlo
auth login
YWFh
MTIzNDU2
mail from:aaa@itcast.cn
rcpt to:bbb@itcast.cn
Data
from<aaa@itcast.cn>
to<bbb@itcast.cn>
subject<a mail>
xxxxxxxxxxxx
.
RFC822文档有漏洞,能够冒名发送邮件
3) 配置outlook软件 完成邮件的发送和接收
1. Mime 协议
2. javamail
Session 与邮件服务器的会话
Message(抽象类)-à MimeMessage(Message的子类) ---à 用于封装Mime消息
Multipart(抽象类)-à MimeMultipart(Multipart的子类) ---à 用于封装Mime消息体
Bodypart(抽象类)-à MimeBodyPart(Bodypart的子类) ---à 用于封装Mime消息体个部分数据
Transport 用于发送邮件