Java 笔记整理
包含内容
Unix
Java 基础,
数据库(Oracle jdbc Hibernate pl/sql),
web,
JSP,
Struts,
Ajax
Spring,
Ejb,
java和模式
Linux/Unix笔记
inode :存储编号(地址)
ls -k:查看磁盘分区
ls -li:显示当前文件的inode号。
目录的大小跟文件的大小有关,跟目录里的文件(目录)数量无关。
一行多个命令的话,中间用分号分开。如:pwd;cal;date
last | grep pts/13 表示查看pts/13登陆服务器的记录。
find . -mtime -10 -print
-10:表示10天以内的,
+10:表示10天以前的,
.:表示当前路径
-mtime:最后一次修改时间。
-print:表示输出到显示器(可有可没有)。
-user 0:表示UID是0。
size+400:表示大于400*0.5K ,-400表示小于400*0.5K
-atime:表示最后一次访问时间。
grep:在文件里查找符合要求的那一行。通常用在管道(|)后面,表示对 | 前面的输出做查找。
如:cat /etc/passwd | gred liu | sort
sort:表示排序。
进程是作业,作业是进程。
前台就是终端,后台就是服务器。
当杀掉父进程,前台子进程会消失,,后台作业不依赖于任何终端,会继续运行
LINUX常用命令(基础)
1. man 对你熟悉或不熟悉的命令提供帮助解释
eg:man ls 就可以查看ls相关的用法
注:按q键或者ctrl+c退出,在linux下可以使用ctrl+c终止当前程序运行。
2. ls 查看目录或者文件的属*,列举出任一目录下面的文件
eg: ls /usr/man ls -l
a.d表示目录(directory),如果是一个"-"表示是文件,如果是l则表示是一个连接文件(link)
b.表示文件或者目录许可权限.分别用可读(r),可写(w),可运行(x)。
3. cp 拷贝文件
eg: cp filename1 filename2 //把filename1拷贝成filename2
cp 1.c netseek/2.c //将1.c拷到netseek目录下命名为2.c
4. rm 删除文件和目录
eg: rm 1.c //将1.c这个文件删除
5. mv 移走目录或者改文件名
eg: mv filename1 filename2 //将filename1 改名为filename2
mv qib.tgz ../qib.tgz //移到上一级目录
6. cd 改变当前目录 pwd 查看当前所在目录完整路径
eg: pwd //查看当前所在目录路径
cd netseek //进入netseek这个目录
cd //退出当前目录
7. cat,more命令
将某个文件的内容显示出来。两个命令所不同的是:cat把文件内容一直打印出来,而 more则分屏显示
eg; cat>1.c //就可以把代码粘帖到1.c文件里,按ctrl+d 保存代码。
cat 1.c 或more 1.c //都可以查看里面的内容。
gcc -o 1 1.c //将1.c编译成.exe文件,我们可以用此命编译出代码。
8.chmod 命令 权限修改 用法:chmod 一位8进制数 filename。
eg: chmod u+x filenmame //只想给自己运行,别人只能读
//u表示文件主人, g 表示文件文件所在组。 o 表示其他人 ;r 表可读,w 表可写,x 表可以运行
chmod g+x filename //同组的人来执行
9. clear,date命令
clear:清屏,相当与DOS下的cls;date:显示当前时间。
10. mount 加载一个硬件设备
用法:mount [参数] 要加载的设备 载入点
eg: mount /dev/cdrom
cd /mnt/cdrom //进入光盘目录
11. su 在不退出登陆的情况下,切换到另外一个人的身份
用法: su -l 用户名(如果用户名缺省,则切换到root状态)
eg:su -l netseek (切换到netseek这个用户,将提示输入密码)
12.whoami,whereis,which,id
//whoami:确认自己身份
//whereis:查询命令所在目录以及帮助文档所在目录
//which:查询该命令所在目录(类似whereis)
//id:打印出自己的UID以及GID。(UID:用户身份唯一标识。GID:用户组身份唯一标识。每一个用户只能有一个唯一的UID和 GID)
eg: whoami //显示你自已登陆的用户名
whereis bin 显示bin所在的目录,将显示为:/usr/local/bin
which bin
13. grep,find
grep:文本内容搜索;find:文件或者目录名以及权限属主等匹配搜索
eg: grep success * /*查找当前目录下面所有文件里面含有success字符的文件
14. kill 可以杀死某个正在进行或者已经是dest状态的进程
eg; ps ax
15. passwd 可以设置口令
16. history 用户用过的命令
eg: history //可以显示用户过去使用的命令
17. !! 执行最近一次的命令
18. mkdir命令
eg: mkdir netseek //创建netseek这个目录
19. tar 解压命令
eg: tar -zxvf nmap-3.45.tgz //将这个解压到nmap-3.45这个目录里
20. finger 可以让使用者查询一些其他使用者的资料
eg: finger //查看所用用户的使用资料
finger root //查看root的资料
ftp上传下载ftp 192.168.1.100
用户:xiangf Pwd xiangf
Put mput上传多个Get mget下载多个
在linux下Jdk的安装
1. 先从网上下载jdk(jdk-1_5_0_02-linux-i586.rpm)
进入安装目录
#cd /home
#cp jdk-1_5_0_02-linux-i586.rpm /usr/local
#cd /usr/local
给所有用户添加可执行的权限
#chmod +x jdk-1_5_0_02-linux-i586.rpm.bin
#./jdk-1_5_0_02-linux-i586.rpm.bin
此时会生成文件jdk-1_5_0_02-linux-i586.rpm,同样给所有用户添加可执行的权限
#chmod +x jdk-1_5_0_02-linux-i586.rpm
安装程序
#rpm -ivh jdk-1_5_0_02-linux-i586.rpm
出现安装协议等,按接受即可
2.设置环境变量。[U1]
#vi /etc/profile
在最后面加入
#set java environment
JAVA_HOME=/usr/java/jdk-1_5_0_02
CLASSPATH=.:$JAVA_HOME/lib.tools.jar
PATH=$JAVA_HOME/bin:$PATH
export JAVA_HOME CLASSPATH PATH
保存退出。
3.检查JDK是否安装成功。
#java -version
如果看到JVM版本及相关信息,即安装成功!
bash-profile是配置文件,配置java-home,path,classpath等
。空格。bash-profile 从新启动
Vim 。bash-profile编辑
Javac 把.java编译成。Class java运行。Class文件
java –d.按包的名字自动生成相对应的
Core Java笔记
人--->源文件--->编译器--->程序--->CPU
编译器:
1,编译执行:源文件--->可执行代码。如:C/C++语言。
执行效率高。可移植性差。---->开发成本的增加。
2,解释执行:源文件--->体系结构中立的中间代码( .class)--- >解释器 --->机器指令。 如 :java语言
执行效率低。 可移植性好。----> 对硬件要求高。
JAVA语言:
(源文件)--->(编译器javac.exe)--->中间码--->(虚拟机java.exe)--->机器指令--->CPU
(编译) (解释)
.java ----> .class ---> 可执行文件
PATH:指定可执行程序(命令)在哪个目录。不同目录用(:)分开。--->SHELL
JAVA_HOME:指定JDK的安装目录。给其它应用程序看的。
CLASSPATH:指定(jar文件)中间字节码文件在什么地方。由多个目录组成的集合。--->
让虚拟机可以找到中间字节码文件。就是可以找到.class文件
服务器上csh:.cshrc
bsh:.profile
客户端上。bash:.bash_profile
1 # .bash_profile
3 # Get the aliases and functions
4 if [ -f ~/.bashrc ]; then
5 . ~/.bashrc
6 fi //if的结尾。
8 # User specific environment and startup programs //#代表注释。
9 JAVA_HOME=/opt/jdk1.5.0_15 JDK 安装路径--- JDK = JRE {JVM(硬件)+编译器( 软件)} +编译器工具+类库
10 PATH=$JAVA_HOME/bin:$PATH:$HOME/bin //系统定义的$PATH 启动命令
11 CLASSPATH=.:java/jar/servlet-api.jar:/java/jar/jsp-api.jar //类路径
12
13 export PATH CLASSPATH JAVA_HOME //使三个变量变成全局变量。。
Source .bash_profile:只能经过这条命令之后,才会让修改的变量生效。(csh)
. .bash_profile。 只能经过这条命令之后,才会让修改的变量生效。 (bash)
java -version:查看虚拟机的版本号。
2.,编写第一个JAVA程序[U2] 。
1),以.java结尾的文件。
2),所有内容,都要写在一个类中(类名要和文件名想同,包括大小写在内)
pbulic class HelloWorld{......}
3),main函数是程序执行的入口,程序从main函数的第一条语句开始执行,执行到main函数结束为止。
public static void main(String argvs[]){
}
4),输出语句:System.out.println( );
5),每条语句以(;)结尾
先指定到源程序存放的目录,然后进行以下步骤运行。
编译:javac命令。如:javac -d . HelloWorld.java -->生成HelloWorld.class类文件
启动虚拟机(java),运行类文件。如:java com.work.core.HelloWorld
后面的.class省略,无需写成java com.work.core.HelloWorld.class
包(package):
1,package com.kettas.corejava; //包声明。
2,必须保证类文件一定放在相应的目录结构中,HelloWorld.class放在com/kettas/corejava目录中。
3,运行时,一定在顶层包的同级目录下运行java命令,
例如:com(shell界面里运行)java com.kettas.corejava.HelloWorld
(如果顶级包所在的目录已经被配置到CLASSPATH中的话可以在任意目录中启动 java命令)
1,类文件太多,不好管理。
2,解决重名。
javac -d . HelloWorld.java (不仅能够编译类文件,同时还能创建包结构)
运行命令java xxxx 类的名字--- 启动虚拟机
(一)语言:适合于internet
1,跨平台的 (属于解释执行的语言)
2,更容易开发出安全的程序:
1)垃圾回收器,(帮助程序员管理内存)
2)取消了指针,对数组越界进行检查
3)安全检查机制,(对生成的字节码检测,如:包结构检测)
Hash算法,,只要加密的源是一样的。经过Hash运算之后,Hash值都一样。
加密的源如果不一样了,经过Hash运算之后,值都不一样。
(二)变量:
如: 学生,姓名,性别,age
帐户,ID,password,balance,username
内存:没记忆,容量小,成本高,存储变量数据,最小逻辑单位:byte(字节)=8bit(位)
外存(硬盘):有记忆,容量大,成本低,存储文件数据
1,变量声明:给变量起名,是给变量选择一种数据类型。如:int age;
不同的变量,
1)参与的运算是不同的,
2)存储的值是不同的,
3)需要的存储空间的大小也不同,
java数据类型:
简单数据类型(原始数据类型)
数字: 整数:byte(1字节)-short(2字节) -int[U3] (在内存中占4个字节)-long(8个字节)
小数:float(4个字节,单精度浮点型) - double(8个字节,双精度浮点型)
字符:char(一个字符=2个字节):只能表示一个字。 如:char c='中'; c存的是‘中‘这个字的编码。
布尔:boolean(true,false),不能用0和非0表示。
String[U4] (字符串)复杂数据类型(类)
String 类提供了数值不可改变的字符串 String s=new String(“abc”);创建了两个对象1,在字符串池中创建一个对象(此对象是不能重复的)2,new出一个对象。Java运行环境有一个字符串池,由String类维护。执行语句String s="abc"时,首先查看字符串池中是否存在字符串"abc",如果存在则直接将"abc"赋给s,如果不存在则先在字符串池中新建一个字符串"abc",然后再将其赋给s。执行语句String s=new String("abc")时,不管字符串池中是否存在字符串"abc",直接新建一个字符串"abc"(注意:新建的字符串"abc"不是在字符串池中),然后将其付给s。
2,初始化 (局部变量而言,必须初始化才能使用) 如:age=10;
3,通过变量的名字对变量进行操作,如:age=other;
赋值时,左=右
1)数据性质是否一样。
2)所占用的内存空间上考虑 (左>右)不用类型转换。(左<右)要类型强制转换,
如:int age=1;
long l=10;
age=(int)l;
符号位:0代表正数,1代表负数。
BigDecimal比double更精确,一般用于处理高精度运算。
&和&&的区别。
&是位运算符,表示按位与运算,&&是逻辑运算符,表示逻辑与(and)
Java中的标识符的要求:
1,只能是字母,数字,_,$。 2,数字不能作为标识符的开头。
3,大小写敏感。 4,没有长度限制。如:ArrayOutOfBoudleException
5,标识符不能是关键字。
一般来说,类的名字以大写字母开头。
方法和变量的名字以小写字母开头。
标识符的名字应当具有含义,如 age , name
表达式:1,由运算符和变量组成。2,都会有返回值。
简单表达式:a/b; 复合表达式:"a/b="+(a/b);
作业,打印润年,
1,能被4整除,但不能被100整除
2,能被400整除。
自增(++),自减(--)运算符。
前++:++a;先对a+1,再返回a+1之后的值。
后++:a++;先返回a的值,然后对a进行+1。
前++,后++共同的特点:a本身的值都会+1;
区别在于表达式的返回值是不一样;用返回值来参与计算。
? : -->(boolean express)? A : B;如:char ch=(5>2)?'Y':'N';
?前面的布尔表达式如果为真就执行A,否则执行B。
(‘:‘左右两侧的返回类型要一致,且与ch的返回类型也一样)
java打包(压缩包 .zip .rar .tar .gz .jar)。
root/
yesq/
|-com/
|-work/
|-core/
|-VarTest.class
|-RunNian.class
. . . . . .
压缩命令: jar -cvf abc.jar dir
解压命令:jar -xvf abc.jar
abc.jar为生成压缩包的名字
dir为被打包的文件目录下的东西
c:是做压缩。v:为压缩过程的信息。f:指定压缩文件的名字。x:是做解压
打包一般从顶层包开始打。如:[java@localhost yesq]$ jar -cvf first.jar com
//当前目录(yesq)下的com文件夹开始打
1,获得别人提供的jar文件
2,将jar文件的名字以及他的绝对路径配置到CLASSPATH中
3,使用import语句,将其他包的内容导入到本文件中,如:引入包com.kettas.common包中的SystemIn类
import com.kettas.common.SystemIn;
------ java中的流程控制 -----
1,顺序
2,条件(代码是否执行,依赖于一个特定的条件)
if(boolean express){
XXXXXX; //如果布尔表达式为真就执行XXXXXX。
XXXXXX;
}else{
XXXXXX; //如果布尔表达式为假就执行XXXXXX。
XXXXXX;
}
3,循环(相同的代码,被重复的执行多次)
a,初始化循环条件。b,确定循环条件。c,确定循环变量在循环体中的变化。
(1) a;
while(boolean express){
XXXXXXXXX; //如果布尔表达式为真就执行XXXXXX
XXXXXXXXX;
c;
}
(2) for(a;b;c){
}
(3) do{
XXXXXXXXXX; //循环体至少执行一次;
XXXXXXXXXX;
}while(boolean express);
while(XXX){
XXXXXX;
XXXXXX;
bread; //或者continue;
XXXXXX;
}
break :用在循环或分支里,用于退出整个循环或分支
continue :用在循环里,用于结束本次循环。接着开始下一次循环
4,分支
switch(var[U5] ){
case 1 : XXXXXX;
break;
case 2 : xxxxxx;
break;
........
default : xxxxxxx; // default后面可以不用break;
}
函数(方法):是由一些相关代码组成的集合,目的是为了解决一个特定的问题,或者实现某个特定的功能。
函数(方法)一定有一个自己的名字,通过函数的名字执行函数中的代码。
2,在java中如何定义函数:
a,声明:public static void printMenu(int a,double b){.....}
b,编写函数体:
如: public static void printMenu(int a,double b){
XXXXXXXXXXX;
XXXXXXXXXX;
}
函数(方法)里面不允许再有其它的函数(方法),函数(方法)的位置必须是并列的。
3,调用函数:
通过函数名,如pirntMenu(a,b);
public static void main(String argv[]){
XXXXXXXXXXXX;
printMenu(a,b);
XXXXXXXX;
}
调用者-原数据->函数
return 作用:1,结束本方法。2,返回结果。
一个函数返回的值只能有一个。
4,值传递。传递的是实参的值,
被调函数使用的数都是实参的拷贝。
是否改变实参,要看被调函数的设计。
数组:一维数组-->:三种声明方式
一,(1)首先要声明。如:int[] array;或int array[];
(2)申请内存空间如:array=new int[2];
array代表数组array[]的首地址(引用:引用就是C++里的指针。);当参数传递时,只要用数组的首地址就可以。
1,数组中的元素类型要一致。
2,数组长度不能缺省,不能改变,但可以删了重建。
3,内存空间连续,有索引下标值(从0开始,最大length-1)
优缺点:查询快,增删慢(链表,查询慢,增删快)
只要有new在,就要为变量分配内存。 array.length //表示数组array的长度。
array存的是array数组首变量的地址。
二,数组的显示初始化:int[] array={1,2,3,4,5,6};
三,int[] array=new int[]{1,2,3,4,5,6,7};
数组的拷贝:
public static int[] expand(int[] a){
int[] b=new int[a.length*2];
/* for(int i=0;i<a.length;i++){
b[i]=a[i];
}
这段代码等于下面那条代码
*/
// 从a数组下标为0的位置开始拷贝,到b数组下标为0 的位置开始粘贴。
// a.length为粘贴的个数。(一般为a数组的长度)。
System.arraycopy(a,0,b,0,a.length);
return b; //返回扩展后的数组b。b是代表数组b的首地址。
}
二维数组(数组的数组)-->:三种声明方式
二维数组的第二个[]在声明时可以留空,如:int[][] array=new int[4][];
//留空说明该二维数组的列是以行的数组下标为数组名,再成为新的数组。
一,声明:int[][] array=new int[3][4]; //声明一个三行四列的整型二维数组。
二,int[][] array={{1,2,3},{2,3,4},{2,5,7},{2,3,6}};
三,int[][] array=new int[][]{{1,2,3},{2,3,4},{2,5,7},{2,3,6}};
1,找对象(客观存在的事物);
2,利用对象的功能。就是让对象干点什么;
3,对象的属性可以是其他对象。
4,对象还可以通过调用其他对象的函数产生联系。
5,简单的对象构成复杂的系统。
有什么:属性-------》 实例变量
做什么:方法-------》函数
对象的属性可以是其他对象
对象还可以通过调用其他对象的函数产生联系
简单的对象构成复杂的系统
思想:解决问题的过程,
总纲(优势)
1,符合人的思维过程。
2,编写出来的代码比较合理。如:可扩展性,可维护性,可读性等等。(存在就是合理),(贴近生活)
3,弱耦合性,
4,可扩展性
5,可复用性。 不要重复制造轮子。
6,更尽所能,更施其职。
==========类:==============
面向过程:是代码的容器。
面向对象:对象所共有的功能和属性进行抽像,成为了类。客观事物在人脑中的主观反映。在程序里类是创建对象的模板。
java中的对象[U6] :对现实对象的抽象(获得有用的,舍弃没用的)。
存储空间中的一块数据区域,用于存数据。如:人(nama sex age)
属性:实例变量(定义在类以内,方法之外)
1.默认的初值
2.实例变量的使用范围至少是类以内
3.实例变量调用必须有对象,实例变量和局部变量重名,局部优先。
例:
public class Test{
public static void main(String[] args){
//新建对象stu;stu存的是新建对象stu的地址。stu的专业术语:引用/对象变量/引用变量/实例变量
Student stu=new Student();
stu.name="tom"; //给对象stu的name属性赋值。
}}
class Student{ //用类声明的数据(str)为引用类型,如:Student str;
//实例变量:定义在方法之外,类以内的,没有static修饰的变量
//实例变量会有一个默认的初始值。初始值与变量定义时的类型有关。
//实例变量(成员变量)--->属性。可通过新建对象的引用来访问类的实例变量。如,stu.name;
String name;
int age;
String sex;
}
实例变量和局部变量的区别:
1,位置:局部变量定义在方法里面。实例变量定义在类以内方法之外。
2,使用的范围:局部变量只能在定义他的方法里面使用,直接调用变量名就可以了。
实例变量至少可以在定义他的整个类内使用,使用时必须用对象去调用。只有跟对象一起实例变量才有意义。
3,局部变量使用之前必须初始化。实例变量不需要赋初值,系统会给默认的初值。
4,局部变量在同一方法里不能重名。局部变量和实例变量可以重名,在方法里采用就近原则。
==方法:======================
包括:
方法:
做什么:方法的定义
修饰符 返回类型 方法名(参数列表) 异常
怎么做:方法的实现 {******}
修饰符(如:public) 返回类型(如:int) 方法名/函数名 (参数表--形参)
如:
public void eat(String fish){ //eat(),吃的功能。
//怎么做.
}
使用实例方法时也需要用对象去调用。如:stu.eat("fish");
方法重载(overloading):编译时多态。[U7]
在一个类的内部,方法名相同形参数不同的方法,对返回类型不要求,这种现象称之为重载;
编译器会自动选择使用的方法。体现了一个编译时多态。
好处:对使用者屏蔽因为参数不同造成方法间的差异。
找方法时如果没有合适的,采取自动向上扩展原则。
调用时形参之间的区别一定要明确。
1. 形参的个数不同
2. 形参的类型不同
3. 形参的顺序不同
4. 形参的名字相同
方法覆盖(override):运行时多态。
1,发生在父类和子类之间
2,方法名相同,参数相同,返回类型相同
3,子类方法的访问权限不能更严格,只能等于或更加宽松。
构造方法:
1,没有返回类型,方法名必须和类同名。
2,构造方法不能手动调用,它只用在创建对象在时候,只出现在new之后。
只要构造方法被调用运行,就一定有对象产生。
3,在一个对象的生命周期里,构造方法只能被调用一次。
4,类写好后一定有构造方法,
如果程序没有显示的提供构造方法,JVM会提供一个默认的构造方法,public classname(){}
如果程序显示提供,系统不再提供默认的
5,同一个类的多个构造方法一定重载。
6,创建对象的同时构造方法的代码块里还可以写需要运行的代码,还可以给属性(实例变量)赋值,
引用类型的属性赋值就是用new创建对象,然后调用构造方法。如:Student stu=new Student();
用new创建对象时JVM做的三件事:
如:Student stu=new Student();
1,申请空间;(把值放到空间里,再把空间的地址给引用变量。)----创建父类对象
2,初始化实例变量;没显示声明值的话就用默认值。
3,执行构造方法,
因为实例变量在创建对象的过程中被初始化,所以使用实例变量前必须创建对象(要用对象去调用),否则实例变量根本不存在
=====关键字:this=======
1,在普通方法里,指代当前对象引用(哪个对象调用该方法,this就指向该对象)
2,this不能出现在静态方法里。
3,在构造方法里,this用来指代本类的其他构造方法。在本类构造方法之间互相调用。如:this(形参);
使用时放在构造方法里的第一行。
4,不能写成死循环(不能递归调用)
=====关键字:super=======
(和this的用法类似)
1,调用父类的构造方法,在子类调用父类的构造方法必须出现在第一句,构造方法第一句可能出现的三种情况(调用父类的构造方法看子类构造方法的第一句)①super();②super(参数)注意传递的参数一定是实体,有值的。③this(),先在本类的构造方法间调用,再看调用那个构造方法的第一句是什么
2,super访问父类的变量和方法,及super代表父类的对象,super.name;super.setName();
======参数传递========
1,参数相当于局部变量
2,参数传递相当于赋值语句
3,基本类型数据传递的是本身的值,引用类型数据传递的是引用xx(Animal animal)(地址,对象变量本身的值)
面向对象的三大特性:
一,封装(Encapsulation):一个系统以合理的粒度出现。
定义:封装就是将客户端不应看到的信息包裹起来。使内部执行对外部来看不一种不透明的、是一个黑箱,客户端不需要内部资源就能达到他的目的。(封装是把过程和数据包围起来,对数据的访问只能通过已定义的界面。面向对象计算始于这个基本概
念,即现实世界可以被描绘成一系列完全自治、封装的对象,这些对象通过一个受保护的接口访问其他对象。)
1.事物的内部实现细节隐藏起来
2.对外提供一致的公共的接口――间接访问隐藏数据 3.可维护性
访问控制修饰符:public(公开的),protected(受保护的,1,本包内可见;2,其他包的子类可见)
default(默认,本包内可见),private(私有的,类内部可见)
访问控制修饰符 (可以范围) (可修饰)下面的类(指外部类)
private 本类 方法,属性
default 本包 类,方法,属性
protected 本包+子类 方法,属性
public 到处可见 类,方法,属性
1,属性:隐藏所有属性,用private。隐藏后属性只能在类以内访问 。程序可以根据需要提供get和set
2,方法(函数):该公开的公开,该私有的就私有。(视情况而定)
3,公开方法的功能,隐藏方法的实现细节。
二,继承(inheritance):抽象出不变性。
从一般到特殊的关系,可以设计成继承
特点:共性写在父类里面,特性写在子类
所有类的总父类是 Object (Object是类的祖先)
父类里写的是共性,子类里写的是特性。
父类中用private修饰的属性和方法不能被子类继承;
但是父类里的属性子类都有,如果子类有特殊属性的话,就要在子类里定义
且在创建该子类对象的时候初始化属性(包括父类所有属性和该子类所有属性);
什么样的功能和属性可以继承到子类?
对于父类里的属性和方法,子类有权访问的,我们称为可以继承;
用new创建子类对象,JVM执行的过程:Dog d=new Dog(); 为d申请空间。 class Dog extends Animal{}
(1)申请空间;(把值放到空间里,再把空间的地址给引用变量。)
(2)看本类构造方法的第一句
(3)默认的创建父类对象:
执行顺序:子类(2)---> 父类(2-->3-->4-->5)---> 子类(4-->5),
新建一个对象空间只申请一次(该空间存放所有父类和子类)。)
(4)初始化本类的实例变量(该类的所有属性);
(5)执行本类的构造方法,(构造方法只会在创建一个对象的时候被执行一次)
(创建是先执行父类的构造方法,在执行子类的构造方法,访问时先访问子类自己的特殊属性和方法再访问父类的属性和方法)
用super调用父类被子类覆盖的普通方法和遮盖的属性,
指代的是在创建子类对象过程中,由JVM自动创建的那个父类,如:super.方法名()/属性
用super调用父类的构造方法;必须出现在子类构造方法的第一句。如:super(形参);
1,在子类的构造方法里如果没有指明调用哪一个父类的构造方法(就是子类中没有super(形参)语句;),
JVM会默认调用父类的无参构造方法,跟本类构造方法的形参没有关系。
2,显示的写super,JVM会根据参数调用相应的父类构造方法。
3,有this(形参),在本类的构造方法之间调用,看被调用的那个构造方法的第一行。
三,多态[U8] (polymorphism):多态只有方法多态,没有属性多态。
用父类类型屏蔽子类之间的差异
所有的子类对象都可以当父类对象来用,一个父类型的引用可能指向的是一个子类对象,
如:把狗(对象)看作动物(类型)。Animal a=new Dog(); 编译看前面,运行看后面。
(编译时类型) (运行时类型)
1,运行时对象不会改变(对象是客观存在的),如:狗这个对象的属性和方法是不会改变的。
2,对一个引用,只能调用编译时类型里的已知方法。
如:编译器知道动物里已有的属性和方法,但不知道狗的属性和方法。
3,运行时会根据运行时类型自动寻找覆盖过的方法运行。
引用 instanceof 类名 //结果为boolean值,
引用所指向的对象和类名类型是否一致(对象是否属于类名类型)
类型的转换:转换编译时类型
Sub su= (Sub) s;
子类型的引用向父类型转换时,不需要强转
父类型的引用向子类型转换时,需要强转
Animal a=new Cat();
Dog d=(Dog)a; // 类型转换异常
引用 instanceof 类名 -----> boolean
引用所指向的对象和类名所代表的类型是否一致
a instanceof Animal -----> true a instanceof Cat----------> true a instanceof Dog----------->false
Employee e=new Manager();
e instanceof Employee ------>true
e instanceof Manager------> true
属性没有多态,属性只看编译时类型
编写程序的顺序:
class 类名{
private属性(有什么)
无参的构造方法(public类名(){})
有参的构造方法(作用:给属性赋值)
set和get(设置和获得属性)
业务方法(做什么)
}
一,修饰符:static [U9]
static变量:如:static int index=2;
类的所有对象共同拥有的一个属性;可以用类名直接访问,又称为类变量,
类的所有对象共同拥有的一个变量;类第一次被加载时会初始化静态变量
(也就是会先执行static修饰的变量);
跟类创建了多少对象无关;任何对象对静态变量做的修改,其他对象看到的是修改后的值。
可以用作计数器,记录类创建对象的个数 , static变量在类加载的时候只会被初始化一次,
static方法:如:public static void teach(){}
可以用类名直接去调用,不需要对象所以不能直接访问(在没有对象的情况下)实例变量,
在静态方法里不能出现this和super,类的所有对象共同拥有的一个方法;跟类创建了多少对象无关。
在继承里,父类的静态方法只能被子类的静态方法覆盖,且覆盖以后没有多态
(访问的是父类的静态方法);
static初始化块:如:class Teacher(){
static int index=4;
static{ //static初始化块
.........
}
}
静态初始华块:用static修饰类里面的一个独立的代码块,类第一次被JVM加载的时候执行,只被执行一次。
类加载:JVM在第一次使用一个类时,会到classpath所指定的路径去找这个类所对应的字节码文件,
并读进JVM保存起来,这个过程称之为类加载,一个线程一个jvm。
二,final (最后的,最终的)final 用于声明属性,方法和类,分别表示属性不可变,方法不可覆盖,类不可继承
final类:如:final class Animal{}
表示该类不能被继承,意味着不能改变里面的代码;
对虚拟机的正常运行有重要作用的类通常用final修饰,如:String,System,Math ...等类
final方法:如:public final void sleep(){} 该方法不能被覆盖(修改),但能被子类访问。
final变量:如:final (static) int index=4;
该变量是常量能被继承(访问);
final修饰的变量就是常量,通常和static一起连用,来声明常量;
final修饰引用类型数据,指的是引用(地址)不能变,但引用里的数据不受限制。
final修饰的变量,只要在初始化的过程中就可以赋值。
实例变量:声明的同时或构造方法里赋值;
静态变量:声明的同时或在静态代码块里赋值;
三,abstract
abstract类:如:abstract class Animal{}
抽象类,不能创建对象(如一些父类),但是可以声明一个抽象类型的引用
(可以声明父类类型子类对象,编译时利用多态调用抽象方法)。
含有抽象方法的类一定是抽象类,但抽象类并不一定要有抽象方法;
抽象类一般是用来被继承的;子类继承自抽象类,就要实现里面的抽象方法,
如果不想让子类也是抽象类的话,必须实现父类里面所有的抽象方法。
抽象类有构造方法,有父类,也遵循单继承的规律。
abstract方法:如:public abstract void sleep();
抽象方法,只有方法名的定义,没有实现体(只定义了能做什么,没定义怎么做),不能被调用,
用于被子类的方法覆盖或重新实现。只能放在抽象类中。
好处:允许方法的定义和实现分开。
public protected default private static final abstract
可以: public static
private static
public final
public static final
不可以:abstract final void eat();
private abstract void eat();
static abstract void eat();
abstract不能和final,private,static连用。
四,interface:是抽象类的变体,。在接口中,所有方法都是抽象的。如:interface M{
int num=3;
void eat();
}
理解为接口是一个特殊的抽象类,所以接口不能创建对象,且接口没有构造方法,
但可以声明一个接口类型的引用(m是接口类型实现类对象,如:M m=new N();)
接口存在的意义是被子类实现,如果不想让子类也抽象,
就要实现接口里面所有的抽象方法,实现过程中注意访问权限;
用 implements 关键字实现接口,如:class N implements M{
public void eat(){...}
}
接口里面的常量默认都是public static final的;
接口里面的方法默认都是public abstract的。
接口本身支持多继承,继承了父接口里功能的定义,如,interface A extends B,C,D{} //A,B,C,D都是接口;
类可以同时继承一个父类和实现接口(或多个接口)。
如:class AA extends BB implements CC,DD,EE{}//AA,BB 是类,CC,DD,EE是接口;
作用:1,用接口去实现多继承,接口是对类的共性进行再次抽象,抽象出类的次要类型。
如:蜘蛛侠,拥有人和蜘蛛的属性,但主要类型是人,次要类型(接口)是蜘蛛,
因为接口是次要类型,所以在类关系里不占一个节点,不会破坏类层次关系的树状结构,
2,标准(保证弱耦合):一个接口就是一个标准(里面的属性不能改变,只定义了功能,
但没有被实现), 接口将标准的制定者,标准的实现者以及标准的使用者分离开,
降低实现者和使用者的耦合。接口是java里一种重要的降低耦合的工具;
接口可以屏蔽不同实现类的差异,
当底层的实现类更换后,不会对上层的使用者产生影响,体现在参数和返回值。
写程序时,应该先写实现者再写使用者,如:Bank.java是实现者,View.java是使用者,
但是有了接口之后,就可以用接口回调的功能;
接口回调:先定义接口,然后写使用者和实现者的顺序随便(一般是先写使用者,
后写实现者);利用参数把实现者传给使用者(即:实现者是使用者的属性),
使用者利用接口调用实现者相应的功能。
**接口和抽象类的区别 1一个类可以implements多个接口,而只能extends一个抽象类
2,一个抽象类可以实现部分的方法,而接口都是抽象的方法和属性
Object是Java里所有类的直接或间接父类,Object类里面的所有功能是所有java类共有的
1,JVM调用垃圾回收器回收不用的内存(没有引用指向的对象)前运行finalize(),给JVM用的方法。
程序显示的通知JVM回收没用的内存(但不一定马上就回收):System.gc();或 Runtime.getRuntime().gc();
2,toString()返回对象的字符串表现形式,打印对象时,虚拟机会自动调用toString获取对象的字符串表现格式,
如:System.out.println(str.toString()); ==System.out.println(str);
如果本类不提供(覆盖)toString(),那么使用的是Object类里的相应方法,打印的就是地址。
如:public String toString(){
return ".....";
}
3,基本类型时“==“判断变量本身的值是否相等;引用类型时,判断的是地址是否相等。
equals判断的是对象内容是否相等。对于自己创建的类,应该覆盖Object类的equals()方法;
否则使用的是Object类里的equals()方法,比的是地址。
覆盖方法如下:
/*****************************************************
public boolean equals(Object o){
if(o==null) return false;
if(o==this) return true;
if(!(o.getClass()==this.getClass())) return false;
final Student s=(Student)o;
return this.name.equals(s.name) && this.age==s.age ; //比较原则;
}
******************************************************/
覆盖euqals()方法时遵循的原则:
自反性:a.quals(a); //true
对称性:a.equals(b);<==> b.equals(a); //true
传递性:a.equals(b);//true b.equals(c); //true
--->则:a.equals(c); //为true
封装类(Wrapper class)[U10] :
OverLoading时,基本类型时采用向上匹配原则,
如果没有基本类型的话就向包装类转换,如果还没有就让这个基本类型在包装类里也采用向上匹配原则;
基本类型-转换到-->包装类
boolean----->Boolean
int-------->Integer //Integer是引用类型,
int-------->Ddouble //合法, 但Integer------>Double 非法
double------>Double
...... -------> ......
任何类型----->Object
基本数据类型int可以向double自动扩展,但是包装类型之间不能自动的相互转换,
基本类型数据--->包装类型
int i=3;
Integer it=new Integer(i); //手动转换;基本类型向包装类型转换。
int <----> Integer <----> String
转换时String类型必须为全数字字符串。如:"2515" 不能为:"abc265","aec"...等
String str=”123”; int it=Integer,parseInt(str);把字符串转换成数字。String str2=it+“”;把数字转化成字符串
==内部类============
定义在其他代码块(类体或者方法体)里的类称为内部类;
编译后每一个内部类都会有自己的独立的字节码文件,
文件名:Outer$Inner.class-->内部类也可以有父类和实现接口。也可以有抽象方法。
根本位置和修饰符的不同分为四种:
1,member inner class 成员内部类,当实例方法或变量一样理解。
1)定义的位置:类以内,方法之外,没有静态修饰符(static)。
2)本身能定义的属性和方法:只能定义非静态的属性和方法。
3)能直接访问的什么:能访问外部类的所有静态和非静态的属性或方法。
4)怎么创建对象:在外部类内的方法内:Outer.Inner inner=new Outer().new Inner();
在外部类外的类的方法内:Outer.Inner inner=new Outer().new Inner();或
在Outer类里提供一个getInner()方法,返回内部类的对象,这样在外部类外的类的方法内也可以用该成员内部类。
2,static inner class 静态内部类(嵌套内部类),当静态方法或变量一样理解。
static只能修饰内部类,不能修饰外部类。
1)定义的位置:类以内,方法之外,有静态修饰符(static)。一般写在外部类的属性下面。
2)本身能定义的属性和方法:可以定义静态和非静态的属性或方法。
3)能直接访问的什么:只能访问外部类的静态属性和方法。
4)怎么创建对象:在外部类内的方法里: Outer.Inner inner=new Outer.Inner();
在外部类外的类方法里: Outer.Inner inner=new Outer.Inner();
3,local inner class 局部内部类 当局部变量一样理解。
1)定义的位置:方法里面的类,前面不能用public或static修饰。
2)本身能定义的属性和方法:只能定义非静态的属性和方法。
3)能直接访问的什么:能访问方法内用final修饰的局部变量(不能与该类内的变量名相同)。
能访问外部类的所有静态和非静态的属性或方法。
4)怎么创建对象:只能在方法内创建对象,如:Inner inner=new Inner(); 对象的作用范围只在方法内。
4,annonymous inner class 匿名内部类 如: Teacher tc=new Teacher(){
1)没有名字的类,没有构造方法。是一个特殊的局部内部类, public void teach(){...}
可以实现一个接口, 或者一个类, }
生命周期里只能产生一个对象(tc),也就是说只能被一个对象(tc)调用,
2)除了没有名字外,看匿名内部类所在的位置,他的定义和访问将和成员内部类、静态内部类、局部内部类一样。
一般像局部内部类的定义和访问比较多。
3)当试图创建接口或者抽象类对象的时候,用匿名内部类。
表示类体的{...}紧跟在抽象实例(接口)之后,表示实现该抽象实例(接口)。
调用匿名内部类的方法只能用写类时创建的那个对象(tc)。
作用:1,不破坏访问权限的情况下,内部类可以使用外部类的私有成员变量和方法。
2,将接口公开,将实现类(实现公开的接口)作成内部类隐藏起来,强制要求使用者使用接口,强制降低偶合度。
3,Java通过接口和内部类两种机制来实现多继承。在类内部可以建立本类的实例,然后调用本类内的其他方法。
Exception(异常):运行时的概念。
1,Throwable:运行时可能碰到的任何问题的总称;
1)Error:指非常严重的错误,系统不要求程序员处理,也处理不了。如:硬件坏了.....等。
2)Exception:从代码的角度是程序员可以处理的问题;
UncheckedException(RuntimeException 的子类) (未检查异常)如果是RuntimeException(或子类)就是为检查异常,其他就是已检查异常
程序员小心谨慎完全可以避免的异常,系统不要求程序员处理(可以不管,运行会提示错误),
如:3/0 数组下标越界。
CheckedExcepiton (已检查异常)
系统要求必须处理异常。
2,异常处理:异常是相对于方法来说的。
1)声明抛出异常(消极的处理)
throws(抛弃):写在方法名的定义上,后面跟要抛弃的异常类型。
如:public void m1() throws Exception{.}
异常产生时,责任可能并不在当前方法,向外抛弃(把异常抛弃,留给调用者处理)可以让异常找到一个最佳的位置处理
抛弃过程中可以对异常类型进行扩展,但是不能缩小。
throw(抛出):一般出现在方法实现里,用来抛出异常对象(或者是产生异常),
如:throw new FileNotFoundException();
当代码出现异常时,代码不会向下执行,JVM会将异常封装成相应的异常类的对象,
然后向外抛出。之后这个方法里剩下的代码就不会再执行了。
对于一个方法的返回值:
1)正常运行时,要求方法必须返回定义的类型的值。
2)如果运行不正常(出现异常),方法返回的是异常对象
方法覆盖:名相同,参数相同,返回类型相同,访问权限不能更小,子类抛弃的异常不能比父类更多。
2)try....catch(积极的处理):
一个try语句后可以跟多个catch语句;catch时异常子类放上面,异常父类放下面。
如果没有父子关系,先后无所谓;
---方法---( ){
try{
//可能会出现异常的代码
xxxxxxxxxx; (1)
xxxxxxxxxx; (2)
}catch(Exception1 e1){
//当try代码块出现异常时,执行catch代码块。
xxxxxxxxx; (3)
}catch(Exception2 e2){
xxxxxxxxx; (4)
}finally{
//不管有没有异常出现都要执行的代码。
xxxxxxxxx; (5)
}
xxxxxxxxx; (6)
}
1)如果(1),(2)没产生异常,(2)执行后直接执行(5),然后执行(6)。
2)如果(1)产生异常,(2)不会被执行,直接跑出try{..},匹配catch,和catch里定义的类型一致,
执行catch完了后,直接跳到(5)执行,最后再执行(6),如果异常类型都不一致,将导至语法问题。
3)自定义异常类型(业务异常):
如:class MyException extends Exception{
public MyException(String str);
super(str);
}
Exception异常总结:
1、如果程序用了System.exit(0);则不会执行finally里的程序
2、在程序return前执行finally里的程序
3、Java中异常分为两类:
1) checked Exception
处理方式一、继续抛出,消极做法,直到抛出到JVM
处理方式二、用try..catch
2) unchecked Exception (runtime exception)
throws ArithmeticException,IOException应该是出现这两种异常就向上抛吧。
什么情况下一个方法抛出几个异常?一般来说应该是会抛出几种异常,然后在一级调用这个方法时处理一下。
如果没有特殊需要的话要把可能出现的异常都截获并处理。
try{
method();
}catch(exception1 e1){
do something;
}catch(exception2 e2){
do something;
}……
e1的范围小于e2.
课外考题:12、final, finally, finalize 的区别。
final 用于声明属性,方法和类,分别表示属性不可变,方法不可覆盖,类不可继承。
finally 是异常处理语句结构的一部分,表示总是执行。
finalize 是Object 类的一个方法,在垃圾收集器执行的时候会调用被回收对象的此方法,可以覆盖
此方法提供垃圾收集时的其他资源回收,例如关闭文件等。
Java高级部分=============================================
集合,常用的接口,Collection,List Set,SortedSet,Map,SortedMap
用来管理/容纳多个对象的对象(或称为容器);
面向对象可重用性的体现,对数组作了功能封装。
Collection是一个接口:是以对象为单位来管理元素的。
基本操作:add remove size,
遍历:迭代遍历
有两个子接口:List接口和Set接口
1,List接口:元素是有顺序(下标),可以重复。有点像数组。可以用迭代器(Iterator)和数组遍历集合。
List接口里本身有自己的方法,还有从父接口Collection里继承的方法。
remove(int)删除某一位置的对象、add(int,Object)往某一位置插入对象 、get(int)查询某一位置上的元素。
遍历:迭代遍历、for遍历
Collections.sort(list); 引入list集合然后对list集合里的对象(如:Student类)进行排序时,
只要在让对象(类)实现Comparable接口,再覆盖接口里面的方法(compareTo()方法),
在compareTo()方法里写出进行什么样的方式排序,
Public int compareTo(Object o){
Work w=(Work)o;
If(this.salary!=w.salary){return (int)w.salary-(int)this.salary;}
Else If(this.age!=w.age){return w.age-this.age;}else retuen this.name.compareTo(w.name);sss
}
然后在主函数里使用Collections.sort(list); ,就对list里的对象进行排序了,
而不用管Collections.sort()方法是怎么实现的,既不用管Collections.sort()方法的实现体。
排序规则:对象实现Comparable接口,覆盖compareTo()方法,Collections.sort(List);
1)ArrayList[U11] 集合类实现List接口,轻量级,底层是以数组实现的,查询快,增删慢,线程不安全,用get(int)方法多时,
Vector集合也实现List接口,底层也是以数组实现的。但这个类已经放弃了。重量级,查询慢,增删快,线程安全。(Vector和HashTable是线程同步的(synchronized),所以线程安全。性能上,ArrayList和HashMap分别比Vector和Hashtable要好。)
2)LinkedList集合类实现List接口,他底层是以链表实现的,查询慢,增删快,
用remove(int)、add(int,Object)方法多时,
自己实现栈(stack),并对栈里进行增删操作。
class MyStack{
private LinkedList list=new LinkedList();
public void push(Object o){
list.addFirst(o);
}
public void pop(Object o){
list.removeFirst();}}
2,Set接口:无顺序,不可以重复(内容不重复,而非地址不重复)。只能用迭代器(Iterator)遍历集合。
Set接口里本身无方法,方法都是从父接口Collection里继承的,
遍历:迭代遍历
实现类:保证元素内容不重复。
1)HashSet集合类实现Set接口,底层是以数组实现的。HashSet集合里不允许有重复对象
每向HashSet集合类里添加一个对象时,先使用HashSet集合类里add(o)方法,
再调用对象o的hashCode()方法算出哈稀码,保证相同对象返回相同的哈希码。
如果哈稀码一样,再调用对象o里的equals()方法对对象的属性进行比较是否相等,
集合也可以构造集合,如:List<Object> list=new ArrayList<Object>(); Set<Object> set=new HashSet<Object>(list);原来在list集合里的对象是可以重复的,但被set集合构造之后,重复的对象将被去掉
按这三种顺序来理解接口: 1)接口特点;2)接口常见操作(方法);3)接口实现类的特点
Map:对应一个键对象和一个值对象,可以根据key找value,
(Key--value)不可重复且无序--可重复
排序(SortedMap是Map的子接口):TreeMap类实现SortedMap接口;对集合进行排序。
基本操作:put() get()
遍历:键遍历 ketSet()返回键的集合(Set)(说明map的键是用set实现的,不能重复)
值遍历 values() 返回值的集合。
HashMap类实现Map接口: 查询快,线程不安全,允许键和值对象为null
Hashtable类实现Map接口:查询慢,线程安全
TreeMap类实现Map接口:SortedMap的实现类,自动对键进行排序。
Iterator 实例
Static void printValue(Map map){
Collection col=map.values();
Iterator it=col.iterator();
While(it.hasNext()){
Object obj=it.next();…;
}}
Jdk1.5新特性加了forEach循环方便数组和集合的遍历(是在泛型的基础上提出的)
Static void printValue(Map<Integer Product> map){
Set<Integer> s=map.ketSet();
For(Integer i:s){xxxx}
}
注意:如果需要定位,就得用“普通”的 for,在列表遍历期间无法删除集合对象。
课外考题、Collection 和 Collections 的区别。
Collection 是集合类的上级接口,继承与他的接口主要有Set 和List.
Collections 是针对集合类的一个帮助类,他提供一系列静态方法实现对各种集合的搜索、排序、线
程安全化等操作
=======图形界面(GUI)==================================================
1,选择容器:图形界面容器,容器也是一个对象(相当一个图形界面),用来装组件的。
JFrame:窗口,相当于一个容器;如一些按钮,最大化,最小化等一些。默认的布局管理器是BorderLayout
JPanel:面版,透明的。默认布局是FlowLayout, 一般放在窗口里
javax.swing.*;存在于该包中。
2,设置布局管理器
FlowLayout(): 流式布局。组件会随着容器的大小而改变位置,
BorderLayout():东西南北中分布组件
GridLayout():网格布局。一个格只能放一个组件,
CardLayout():卡片布局。如按上一步和下一步。界面是一个卡片式的布局。
GridBagLayout():复杂的网格布局,一个格可以放多个组件。
java.awt.*;存在于该包中。
setLayout():用于设置什么样的布局。
3,添加组件:一个组件就是一个对象,
JTextField :单行文本框
JTextArea :多行文本区
JComboBox:下拉列表
JScrollPane:左右拉或上下拉按钮
4,设置事件监听
AWT事件模型,事件三要素:事件对象,事件源,事件监听器
1,事件对象:事件也是一个对象。事件对象继承:java.util.EventObjct类
2,事件源:发生事件的对象,也是报告事件的对象。(点击b1按钮,那么b1按钮就是事件源)
3,事件监听器:处理事件的对象。 监听器接口必须继承java.util.EventListener接口。
事件源预先就某种事件注册监听器,当事件发生时,事件源就给所有注册的监听器发送一个事件对象,
所有监听器存在一个数组集合里(如ArrayList集合),由监听器做出相应处理。
事件源可以同时是多种事件的事件源,就某种事件单独注册监听器。
事件源就同一种事件,可以注册多个监听器。
一个监听器可以同时注册在多个事件源当中。
事件对象中会封装事件源对象。
事件监听接口中的每一个方法,都应以对应的事件对象作为参数类型。
所谓的事件源给监听器发送事件对象,其实就是事件源以事件对象为参数,调用监听器的方法。
getSource()方法:是事件对象(EventObject)里的一个方法,用事件对象e调用(如:e.getSource(); )
getSource()方法返回的是事件对象里的事件源
=======多线程=======================================================
线程:进程中并发的一个顺序执行流程。
并发原理:CPU分配时间片,多线程交替运行。宏观并行,微观串行。
Tread t=new Thread();表示新建一个线程对象,并不表示线程。
当调用t.start();才起动线程,当得到CPU时,就会执行线程t的方法体。
线程三要素:CPU、Date、Code
多线程间堆空间共享,栈空间独立。堆存的是地址,栈存的是变量(如:局部变量)
创建线程两种方式:继承Thread类或实现Runnable接口。
Thread对象代表一个线程。
多线程共同访问的同一个对象(临界资源),如果破坏了不可分割的操作(原子操作),就会造成数据不一致的情况。
在java中,任何对象都有一个互斥锁标记,用来分配给线程。
synchronized(o){..同步代码块. .}
对o(o是临界资源)加锁的同步代码块,只有拿到o的锁标记的线程,
才能进入对o加锁的同步代码块,退出同步代码块时,会自动释放o的锁标记。
synchronized的同步方法,如:public synchronized void fn(){} 对访问该方法的当前对象(this)加锁;哪个线程能拿到该对象(临界资源)的锁,哪个线程就能调用该对象(临界资源)的同步方法。
一个线程,可以同时拥有多个对象的锁标记
在java中,任何对象都有一个锁池,用来存放等待该对象锁标记的线程,
线程阻塞在对象锁池中时,不会释放其所拥有的其它对象的锁标记。
在java中,任何对象都有一个等待队列,用来存放线程,
线程t1对(让)o调用wait方法,必须放在对o加锁的同步代码块中!
1.t1会释放其所拥有的所有锁标记;
2.t1会进入o的等待队列
t2对(让)o调用notify/notifyAll方法,也必须放在对o加锁的同步代码块中!
会从o的等待队列中释放一个/全部线程,对t2毫无影响,t2继续执行。
当一个现成对象调用yield()方法时会马上交出执行权,回到可运行状态,等待OS 的再次调用
线程的生命周期
下面为线程中的7中非常重要的状态:(有的书上也只有认为前五种状态:而将“锁池”和“等待池”都看成
是“阻塞”状态的特殊情况:这种认识也是正确的,但是将“锁池”和“等待池”单独分离出来有利于对程序的理解)
1,初始状态,线程创建,线程对象调用start()方法。
2,可运行状态,也就是等待Cpu资源,等待运行的状态。
3,运行状态,获得了cpu资源,正在运行状态。
4,阻塞状态,也就是让出cpu资源,进入一种等待状态,而且不是可运行状态,有三种情况会进入阻塞状态。
1)如等待输入(输入设备进行处理,而CPU不处理),则放入阻塞,直到输入完毕,阻塞结束后会进入可运行状态。
2)线程休眠,线程对象调用sleep()方法,阻塞结束后会进入可运行状态。
3)线程对象2调用线程对象1的join()方法,那么线程对象2进入阻塞状态,直到线程对象1中止。
5,中止状态,也就是执行结束。
6,锁池状态
7,等待队列
课外问题:71、简述synchronized 和java.util.concurrent.locks.Lock 的异同 ?
主要相同点:Lock 能完成synchronized 所实现的所有功能
主要不同点:Lock 有比synchronized 更精确的线程语义和更好的性能。synchronized 会自动释放锁,
而Lock 一定要求程序员手工释放,并且必须在finally 从句中释放。
=======I/O==========================================================================
File:代表了磁盘上的文件或者目录
I/O:jvm和外部数据源的数据交换。File,db—in-àjvm---out-àfile,db
流一共有三种分类:
方向分:输入流和输出流;
单位分:字节流和字符流;
字节流:
InputStream/OutputStream 字节流的父接口
(1)FileInputStream/FileOutputStream 文件字节流 ((可以向下转换))
DataInputStream/DataOutputStream 读写8种基本类型和以UTF-8读写String
BufferedInputStream/BufferedOutputStream 带缓冲的输入/出流
PrintStream 融合Data和Buffered, System.out所属的类
Piped 管道 用于线程间交换数据
RandomAccessFile 随机访问文件
字符流:处理字符编码问题
Reader/Writer 字符流的父接口
FileReader/FileWriter 文件字符流,和FileInputStream/FileOutputStream 文件流,
((可以向下转换)) 与上面的(1)是相等,
只不过一个是字节流,下面是字符流,所以两个无法相传
InputStreamReader/OutputStreamWriter 桥转换 将字节流转成字符流 在桥转换的过程中,可以制定编解码方式
BufferedReader/PrintWriter 有缓冲
字符流转换为字节流时,指定编解码方式是在桥转换时指定的。
功能分:节点流和过滤流;
节点流:用于传输数据。
过滤流:帮助节点流更好的传输数据。
piped(管道节点流):用于两个线程间传输数据。一个线程的输出,是另一个线程的输入。
对象序列化:
把对象放在流上传输ObjectInputStream/ObjectOutputStream
只有实现了Serializable接口的对象才能序列化
用transient修饰的属性,为临时属性,不参与序列化,只能修饰对象的属性。
===================网络=====================================================
1 网络通信的本质是进程间通信。
2 Tcp协议和UDP协议
TCP:开销大,用于可靠性要求高的场合。
TCP的过程相当于打电话的过程
UDP:用在对实时性要求比较高的场合。
UDP的过程相当于写信的过程。
注意:socket是套接字,ip和port(端口号 0~65535个端口,一个端口只能有一个进程)
3, TCP通信机制,tcp是面向连接的,实现多线程的方法有三个
① 为每个客户分配一个工作线程。
② 创建一个线程池,由其中的工作线程来为客户服务。
③ 利用JDK的Java类库中现成的线程池,由它的工作线程来为客户服务。
下面以为每个客户分配一个工作线程来实现多线程
在服务端
import java.net.*;
import java.io.*;
public class TCPServer2 {
public static void main(String[] args) throws Exception{
ServerSocket ss=new ServerSocket(9000);//端口号
while(true){
Socket s=ss.accept();//连接监听客户端
System.out.println(s.getInetAddress());
Thread t=new ServerThread(s);//实现多线程连接
t.start();
}
}
}
class ServerThread extends Thread{//分配线程
Socket s;
public ServerThread(Socket s){
this.s=s;
}
public void run(){
try {
OutputStream os=s.getOutputStream();//在网络中获取输出流
PrintWriter out=new PrintWriter(os);
for(int i=1;i<=30;i++){
out.println("Hello "+i);//通过网络发消息给客户端
out.flush();
Thread.sleep(1000);
}
} catch (Exception e) {
e.printStackTrace();
}
finally{
try {
s.close();//注意关闭线程而不关闭流
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
在客户端
import java.net.*;
import java.io.*;
public class TCPClient {
public static void main(String[] args) throws Exception{
Socket s=new Socket("192.168.0.10",9000);//连接服务端的ip和端口
InputStream is=s.getInputStream();//获得输入流,读取服务端发来的信息
InputStreamReader ir=new InputStreamReader(is);
BufferedReader in=new BufferedReader(ir);
for(int i=1;i<=30;i++){
System.out.println(in.readLine());
}
s.close();
}
}
4, Udp 面向无连接,不可靠,效率高,资源占用少 ,先从客户端发起通信,让服务端知道其地址等信息,不同的协议其端口号的含义也不一样
例子:
在客户端
import java.net.*;
public class UDPClient {
public static void main(String[] args) throws Exception{
DatagramSocket ds=new DatagramSocket();注册邮箱
String text1="I am here!";
byte[] bs1=text1.getBytes();//转换成字节用来传输
DatagramPacket letter1=new DatagramPacket(
bs1,0,bs1.length,InetAddress.getLocalHost(),9000);
//写好信(地址和内容)
ds.send(letter1);//发信
DatagramPacket letter2=new DatagramPacket(
new byte[100],0,100);
//准备空信用来接受服务端的信
ds.receive(letter2);//收信
byte[] data=letter2.getData();//得到信的内容
int offset=letter2.getOffset();
int length=letter2.getLength();
String str=new String(data,offset,length);//转换成字符串读取
System.out.println(str);
ds.close();
}
}
在服务端
import java.net.*;
public class UDPServer {
public static void main(String[] args) throws Exception{
DatagramSocket ds=new DatagramSocket(9000);//服务端口
while (true) {
byte[] bs1 = new byte[100];
DatagramPacket letter1 = new DatagramPacket(bs1, 0, bs1.length);//制作空信封用来收信
ds.receive(letter1);
InetAddress address = letter1.getAddress();
int port = letter1.getPort();
String str = "北京2008年举行奥运会";
byte[] bs2 = str.getBytes();
DatagramPacket letter2 = new DatagramPacket(bs2, 0, bs2.length,address, port);//获得客户端的ip和port然后将信息发给客户端
ds.send(letter2);
}
}
DatagramSocket,相当是邮箱有send(发消息)receive(收消息)
DatagramPacket,相当是信,里面有ip端口,信息内容
============jdk1.5新特性===============
1, 可变参数 相当一个数组 m(String …s)一个方法只能有一个可变参数,且只是最后一个参数
2, Foreach循环(for(object o:list))方便遍历数组和集合
3, 枚举
enum Course{
UNIX ("Luxw") {
public void study(){}
},
COREJAVA ("Huxz"){
public void study(){}
};
Public void study();
}
使用:
class test { Course [] cs=Course.values(); for(Course c:cs ){System.out.println(s.ordinal+s+s.getName())}}
注意的问题:1枚举值之间用逗号隔开,最后一个用分号2枚举中可以有抽象,但必须在枚举值中实现;
4, 泛型 泛型解决集合中不安全性,泛型强制集合中都是一个类型
List<Integer> l=new ArrayList<Integer>();Map<Integer,String> m=new HashMap<Integer,String>();
泛型方法:public static <T> void copy(List<T> l,T[] os){ for(T o:os){l.add(o);}}
<T extends Integer> <? Extends Integer>
5,反射 类对象,类加载时把类的信息保存在jvm中就会产生一个相应的对象(记录类信息的对象),只要类对象存在则类信息就在
获得类对象应用的三种方式:
①Class c1=ArrayList.class; ②Object l=new ArrayList();Class c2=l.getClass();
③String className="java.util.ArrayList"; Class c3=Class.forName(className);
Class[] cs=c1.getInterfaces();//获得接口
Class c=Animal.class;
Field[] fs=c.getDeclaredFields();//获得属性的数组
Method[] ms=c.getDeclaredMethods();//获得本类所有公私有的方法,getMethods()获得父类所有公开的方法
Constructor[] cons=c.getDeclaredConstructors();//获得构造方法的数组
Object o2=c.newInstance();//创建类的对象
Method m1=c.getDeclaredMethod("study");//找到方法名是study的方法
m1.setAccessible(true);//设置为可以调用
m1.invoke(o2);//调用m1方法
Method m2=c.getDeclaredMethod("study", String.class);
m2.setAccessible(true);
m2.invoke(o2,"CoreJava");//调用m2的方法,方法的参数是CoreJava
6,注释,===注释是一种类型(总共有四种类型:类,接口,枚举,注释)
定义注释
如:@Override;
标记:@注释名
单值:@注释名(属性名=属性值)
多值:@注释名(属性1=值1,属性2=值2,......)
import java.lang.annotation.*;
@Retention(RetentionPolicy.RUNTIME)
@Target()
public @interface MyType {
String authorName();
String lastModified();
String bugFixes() default "ok";
}
在类中应用注释:
import java.lang.annotation.*;
@MyType(authorName="hadeslee",lastModified="20061207")
public class Test1 {
/** Creates a new instance of Test1 */
public Test1() {
}
@Deprecated
@MyType(authorName="hadeslee",lastModified="20061207",bugFixes="what")
public void doSth(){
}
这里我定义了一个我自己的注释类,声明方式和声明接口差不多,只不过在interface前面多了一个@符号.
注释类也可以用注释类注释,如此下去.
@Retention(RetentionPolicy.RUNTIME) 这句表示它的保存范围是到RUNTIME,也就是运行时,这样在类运行的时候,我们也可以取到有关它的信息.
@Target() 这句表示它的适用对象,它可以用在哪里地方,我这里定义的是它可以用在类的定义和方法的定义上
然后我们看我们是怎么为我们写的类加上注释的
OOAD思想
1继承关系:指的是一个类(称为子类、子接口)继承另外的一个类(称为父类、父接口)的功能,并可以增加它自己的新功能的能力,继承是类与类或者接口与接口之间最常见的关系
2关联关系
① 关联关系是对于两个相对独立的对象,当一个对象的实例与另一个对象的一些特定实例存在固定的对应关系时,这两个对象之间为关联,关系是使用实例变量实现的,比如公司和职员
② 聚合关系是关联关系的一种,他体现的是整体与部分、拥有的关系,此时整体与部分之间是可分离的,他们可以具有各自的生命周期,部分可以属于多个整体对象,也可以为多个整体对象共享;比如计算机与CPU、公司与员工的关系等;表现在代码层面,和关联关系是一致的,只能从语义级别来区分,它也是通过实例变量实现的,在java语法上是看不出来的,只能考察类之间的逻辑关系
③ 组合也是关联关系的一种特例,他体现的是一种contains-a的关系,这种关系比聚合更强,也称为强聚合;他同样体现整体与部分间的关系,但此时整体与部分是不可分的,整体的生命周期结束也就意味着部分的生命周期结束;比如你和你的大脑;表现在代码层面,和关联关系是一致的,只能从语义级别来区分
④ 依赖关系是类与类之间的连接,依赖总是单项的(人和车,人和房子),依赖关系在java中体现在局部变量,方法的参数,以及静态方法的使用。在A类中的方法中使用B类的引用
代码表示
class A{
B b;//聚合
C c=new C();//组合
Public A(B b){this.b=b;}
Public void ect(D d){ d.m(); //依赖}
}
class D{void m(){}}
在UML图形表示[U12]
ORACLE
第一天:====================
1, 关系数据库的三要素: 1实体----》表(存放数据)
2属性---》列
3关系---》外键
2, sqlplus常用命令
help index
可以看到所有的命令,
不会的命令用:
help <命令>;例如:help list
exit 退出SQL*PLUS
desc 表名 显示表的结构
show user 显示当前连接用户
show error 显示错误
show all 显示所有68个系统变量值
edit 打开默认编辑器,Windows系统中默认是notepad.exe,把缓冲区中最后一条SQL语句调入afiedt.buf文件中进行编辑
edit 文件名 把当前目录中指定的.sql文件调入编辑器进行编辑
clear screen 清空当前屏幕显示
SQLPLUS到ORACLE的运行图(见笔记本)
select columnname(列名)
1) select *
2) select id,last_name,dept_id
3) select id,last_name "Name",salary*12 anual
4) select last_name||' '||first_name
//如果comm为null时,nvl(comm,0)等于0;否则等于comm;
5) select last_name,salary*(1+nvl(comm,0)/100)
6) select distinct last_name,first_name
from tablename
预定义: 作用的时间长 所有的叫这个列名的列都生效
sql: 当次生效 当前sql的当前表
order by:
select id,last_name,dept_id
from s_emp
1)order by dept_id;
order by title;
2)order by last_name,dept_id;
先按名字升序排,名字相同再按dept_id升序排 ,中间用“ , ”隔开;
3)order by last_name desc;
order by dept_id,last_name desc;
dept_id升序,last_name降序
4)null
在oracle里是最大,也就是升序的时候放在最后面;
5)order by 2; <=======> order by last_name
select * 里面的第二列
6)select id,last_name name
from s_emp
order by NAME;
============================================== select from where order by
select id,last_name,dept_id,start_date
from s_emp
1)where dept_id=41;
where last_name='Smith'; ---->字符串区分大小写,SQL命令不区分大小写;
where start_date='08-mar-90';
2)where salary>1000;
3)where dept_id between 41 and 45;
包括边界值
where dept_id between 45 and 41; //小的在前,如果反了,查不出记录
//dept_id的值只能是41,43,45三个数中的一个,且三个数都会被计算一次。
4)where dept_id in(41,43,45);
5)where commission_pct=null; 查不出记录
where commission_pct is null;
6)where last_name like 'S%';
unix: * ?
oracle: % _ (下划线)
查出last_name是以'S_'
where last_name like 'S_%'; ----> error
where last_name like 'S\_%' escape '\'; //escape '\'表示“\”为换码字符;
7) 非
!= <>
not between and
not in
not like
is not null
8)and > or
where dept_id=41 and id>5;
where dept_id=41 or id>5;
----->where salary>1000
and dept_id=41
or dept_id=42;
----->where salary>1000
and (dept_id=41
or dept_id=42 );
----->where dept_id=41
or dept_id=42
and salary>1000;
select s_customer.name,s_emp.last_name
from
s_customer,s_emp
where s_emp.id(+)=s_customer.sales_rep_id;
//(+)让员工表补个空值。为的是让所有的客户都有对应的员工
//,因为有些客户没有对应的销售代表,所以设为空
=================================================================================================
第二天:
Single Row Function:
varchar2:
1) nvl('hello','world')-----> 'hello'
nvl(null,'world')--------> 'world'
2) select id,last_name,dept_id
from s_emp
where lower(last_name)='smith';
3) select id,concat(last_name,first_name),dept_id
from s_emp;
concat(last_name,first_name)--->result1;
concat(result1,title);
--> concat(concat(last_name,first_name),title);
System.out.println(stu.toString());
select substr('String',-4,3)
from dual;
dual: dummy table ,为了维护select的完整性
select id,last_name,dept_id
from s_emp
where last_name like 'S%';
where substr(last_name,1,1)='S';
number: round trunc
date:日期类型
1) select sysdate from dual;
标准的日期格式:
年: yy 08
yyyy 2008
rr 08
rrrr 2008
year two thousand and eight
月: mm 06
mon JUN
month june
日: dd 19
ddth 19th
ddsp ninteen
ddspth ninteenth
星期: d 4
dy thu
day thursday
小时: hh24 22
hh 10
am pm
分钟: mi 秒: ss
select id,last_name,dept_id,
to_char(start_date,'yyyy-month-dd,hh24:mi:ss') "sdate"
from s_emp
where dept_id=41;
update s_emp
set start_date=to_date('19-jun-08,11:24:56','dd-mon-yy,hh24:mi:ss')
where id=100;
update s_emp
set start_date=to_date('19-jun-90','dd-mon-yy')
where last_name='S_abc';
select id,last_name,to_char(salary,'$99,999.00')
from s_emp;
select id,last_name,
to_char(start_date,'fmdd-mm-yyyy,fmhh:mi:ss') "sdate"
from s_emp;
fm:在不引起歧义的情况下去掉前置的零和空格
类似于开关变量,第一次写打开功能,再写关闭
Join:数据来源于多张表,叫多表联合查询,多个表之间要做连接
1.等值连接(内连接) 条件: fk-----pk
select s_emp.last_name,s_emp.dept_id,
s_dept.id,s_dept.name
from s_emp,s_dept
where s_emp.dept_id=s_dept.id;
oracle:select e.last_name,e.dept_id,d.id,d.name
from s_emp e,s_dept d
where e.dept_id=d.id;
标准sql:select e.last_name,e.dept_id,d.id,d.name
from s_emp e inner join s_dept d
on e.dept_id=d.id
where e.last_name='Smith';
select e.last_name,e.dept_id,d.id,d.name
from s_emp e join s_dept d
on e.dept_id=d.id
where e.last_name='Smith';
打印出员工的名字,所在部门的名字,以及所在地区的名字
select e.last_name,d.name,r.name
from s_emp e,s_dept d,s_region r
where e.dept_id=d.id
and d.region_id=r.id;
select e.last_name,d.name,r.name
from s_emp e join s_dept d
on e.dept_id=d.id
join s_region r
on d.region_id=r.id;
2.外连接(左外,右外,全外)
打印所有的客户名字,以及所对应的销售代表的名字
1)左外:如果左边对应的右边的值为空时,右边补null
oracle:select c.name,c.sales_rep_id,e.id,e.last_name
from s_customer c,s_emp e
where c.sales_rep_id=e.id(+);
标准sql:select c.name,c.sales_rep_id,e.id,e.last_name
from s_customer c left outer join s_emp e
on c.sales_rep_id=e.id;
2)右外 :如果右边对应的左边的值为空时,左边补null
select c.name,c.sales_rep_id,e.id,e.last_name
from s_customer c,s_emp e
where e.id(+)=c.sales_rep_id;
select c.name,c.sales_rep_id,e.id,e.last_name
from s_emp e right join s_customer c
on c.sales_rep_id=e.id;
3)全外 :都不补空值(null)
select c.name,c.sales_rep_id,e.id,e.last_name
from s_customer c full outer join s_emp e
on c.sales_rep_id=e.id;
3.自连接: 本表的fk指向本表的pk
可以给表起不同的别名,再做连接。
打印出员工的姓名和他的经理的名字
select e.last_name,m.last_name
from s_emp e,s_emp m
where e.manager_id=m.id;
打印出所有员工的姓名和他的经理的名字
select e.last_name,m.last_name
from s_emp e,s_emp m
where e.manager_id=m.id(+);
4.非等值连接(t1和t2没有pk和fk,还想连接t1,t2)
table1: id value
1 A
2 B
3 C
4 D
table2: id value
1 A
2 B
3 C
4 D
请写出一句sql语句,打印输出
AB AC AD BC BD CD
select t1.value,t2.value
from table1 t1,table2 t2
where t1.id<t2.id;
请写出一段sql语句,打印输出
AB AC AD BA BC BD CA CB CD DA DB DC
select t1.value,t2.value
from table1 t1,table2 t2
where t1.id!=t2.id;
5.笛卡儿连接(不给连接条件)
请写出一段sql语句,打印输出
AA AB AC AD BA BB BC BD CA CB CC CD DA DB DC DD
select t1.value,t2.value
from table1 t1,table2 t2;
SubQuery:
1.请打印出和Smith在同一部门的员工的姓名,
start_date,dept_id
1) select dept_id
from s_emp
where last_name='Smith'; -----> result1
select last_name,dept_id,start_date
from s_emp
where dept_id=result1;
2) select last_name,dept_id,start_date
from s_emp
where dept_id=(
select dept_id
from s_emp
where last_name='Smith');
2.主查询和子查询靠值联系
1) 值的类型要相同
select last_name,dept_id,start_date
from s_emp
where dept_id=(
select last_name
from s_emp
where last_name='Smith');
select last_name,dept_id,start_date
from s_emp
where dept_id=(
select salary
from s_emp
where last_name='Smith');
select last_name,dept_id,start_date
from s_emp
where dept_id=(
select id
from s_region
where id=1);
2)值的个数要一致
select last_name,dept_id,start_date
from s_emp
where dept_id=(
select dept_id,last_name
from s_emp
where last_name='Smith');
select last_name,dept_id,start_date
from s_emp
where dept_id=(
select dept_id
from s_emp
where last_name='Smith'
or id=3); ---->error
--->改进
select last_name,dept_id,start_date
from s_emp
where dept_id in (select dept_id
from s_emp
where last_name='Smith'
or id=3);
sql----->dbsv
1)编译
a.权限检测
b.语法检测
c.sql翻译成内部指令
2)运行内部指令
3)哪些地方可以用子查询
需要值的地方就可以用子查询
1)select 不可以
from 可以(需要表的时候在这里写子查询)
where 可以(需要值的时候在这里写子查询)
order by 可以(按select里列的下标值排序 ,如:order by 2)
group by 不可以
having 可以(跟where用法一样)
select *
from (select e.last_name,e.dept_id,d.id,d.name
from s_emp e,s_dept d
where e.dept_id=d.id) e;
jobs:
1.SQL-1.pdf (4)
2.请打印出公司入职最早的五个员工的姓名(last_name),dept_id,start_date
select *
from (
select last_name,dept_id,start_date
from s_emp
order by start_date
) e
where rownum between 1 and 5;
伪列:rownum(代表显示在界面上的行号,数据库本身不提供,查询时会默认的提供,只有在select中显示的写rownum,才会显示行号;)
rowid(数据库里本身就有的,代表在数据库里存的物理地址)
select * from s_dept;
desc s_dept;
分页:子查询和rownum
打印表里面第五到第十条记录:
select last_name dept_id,start_date
from(
select e.last_name,e.dept_id,e.start_date,rownum rownumid
from(
select last_name,dept_id,start_date
from s_emp
order by start_date
) e
where rownum between 1 and 10
) a
where a.rownumid between 6 and 10;
第三天:===================================
Group function(组函数):
1) select avg(salary),max(salary)
from s_emp;
select count(*) from s_emp;
select count(id) from s_emp;
select count(commission_pct) from s_emp;
统计commission_pct列不为空的值的个数;
number: max min sum avg count
date: max min count
char: max min count
2) select dept_id,avg(salary),count(*)
from s_emp
group by dept_id;
select from where group by order by
select dept_id,avg(salary)
from s_emp
where dept_id in (41,50)
group by dept_id;
3) 当select里面出现组函数和单独的列并存时,要求必须
写group by子句,并且单独的列必须出现在group by里面
select dept_id,avg(salary)
from s_emp
group by dept_id;
select salary,avg(salary)
from s_emp
group by salary;
如果group by里出现多列,那么按照列的联合分组,
只有多列的值完全相同才会分到一组
select last_name,first_name,avg(salary)
from s_emp
group by last_name,first_name;
请打印出工资低于公司平均工资的人的姓名,
dept_id,start_date
select last_name,dept_id,start_date
from s_emp
where salary<(select avg(salary) from s_emp );
4) 只要写group by,那么select里单独列必须出现在
group by里
select dept_id,last_name,start_date
from s_emp
group by dept_id,last_name,start_date;
5)where执行时数据处于独立个体状态,里面不能对组函数
进行判断,只能对单独记录判断
having一定出现group by后面,此时数据处于组的状态
所以having只能对组函数或者组的共性进行条件判断,
请打印出部门平均工资大于1500的这些部门的id以及平均工资
select dept_id,avg(salary)
from s_emp
where avg(salary)>1500
group by dept_id; ------>error
====================================
select dept_id,avg(salary)
from s_emp
group by dept_id
having avg(salary)>1500;
select dept_id,avg(salary)
from s_emp
group by dept_id -------> group
having salary>1000; ----->整个组 error
select dept_id,avg(salary)
from s_emp
group by dept_id
having dept_id>40;
select dept_id,avg(salary)
from s_emp
where dept_id>40
group by dept_id ;
6) order by 列名,2,别名;
原来对order by里的列没有任何限制,只要表里有就可以
select from where group by having order by
在order by之前如果出现了group by,那么order by里面
就只能按照组函数或者是组的共性排序
eg: select dept_id,avg(salary)
from s_emp
where dept_id > 40
group by dept_id
having avg(salary)>1000
order by last_name; ------>error
order by max(last_name); ---->correct
order by dept_id; -------->dept_id
作业: 请打印出工资低于本部门平均工资的员工的姓名,
dept_id,start_date
select dept_id,avg(salary) asalary
from s_emp
group by dept_id; ---->a1
select e.last_name,e.dept_id,e.start_date
from s_emp e,a1
where e.dept_id=a1.dept_id
and e.salary<a1.asalay;
--->select e.last_name,e.dept_id,e.start_date
from s_emp e,(
select dept_id,avg(salary) asalary
from s_emp
group by dept_id
) a1
where e.dept_id=a1.dept_id
and e.salary<a1.asalary; //数据库表和表连接的时候,比较的是同一元组;
Define Variable
1. select last_name,dept_id,start_date
from s_emp
where dept_id=&did;
where last_name=&name;
where last_name='&name';
2.预定义
1) define varname=value
define did=41
简单,定义出来的变量都是char类型
不能写进sql文件
查询一个数是否已经预定义用define varname
取消用undefine varname
2) accept varname type prompt ' ' hide //单独一句话,为的是永久性的给varname一个值
accept did number prompt 'please input dept_id value:' hide
accept did prompt 'please input dept_id value:'----> char
比define复杂, 可以指定变量的类型,可以写进 .sql 文件,
在bash下:1)进入csh;
2)setenv EDITOR VI ;
3)sqlplus 进入SQL的用户名和密码;
4)写一个要保存进文件的命令;
5)save sqlfile;
6)ed 进入vi编辑命令,在最上面加上(accept varname type prompt ' ' hide)语句;
7)ZZ(保存并退出vi);
8)start sqlfile.sql就可以运行刚才保存的命令了。
下面是用于实验预定义的
/*select e.last_name
from s_emp e
where e.dept_id='&deptid';
accept deptid number prompt 'please input dept_id value:' hide
*/
3) define varname 查看状态
undefine varname 取消预定义
3.哪些地方可以用变量定义:
select
from
where 全部可以
group by
having
order by
select &col1,&col2,&col3
from &tab1
where &con1;
=======================================================================================
第四天:
Create table:
1.db structure
1)table---> store data 2)view----> select 3)sequence 4)index
2.create table luxw1
( id number(7),
name varchar2(15),
registdate date
);
create table luxw2
(id number(7),
film blob
);
1)列一级约束
create table yesq5
( id number(7) constraint yesq5_id_pk primary key,
name varchar2(15) constraint yesq5_name_nn not null,
age number(3) constraint yesq5_age_ck_age check(age>18 and age<60),
sex varchar2(2) default 'M' constraint yesq5_sex_ck_sex check( sex in('F','M') ),
birthday date default sysdate,
phone varchar2(15) constraint yesq5_phone_uk unique,
personid char(18)
constraint yesq5_personid_nn not null
constraint yesq5_personid_uk unique,
class_id number(7) constraint yesq5_class_id_fk references s_dept(id) on delete cascade
);
2)表一级约束
create table yesq5
( id number(7),
name varchar2(15) constraint yesq5_name_nn not null,
age number(3),
sex varchar2(2) default 'M',
birthday date default sysdate,
phone varchar2(15),
personid char(18) constraint yesq5_personid_nn not null,
class_id number(7),
constraint yesq5_id_pk primary key(id),
constraint yesq5_age_ck check( age>18 and age<60),
constraint yesq5_sex_ck check( sex in('M','F') ),
constraint yesq5_phone_uk unique(phone),
constraint yesq5_personid_uk unique(personid),
constraint yesq5_class_id_pk foreign key(class_id) references s_dept(id) on delete cascade
);
五种约束: 无约束的地方不用写constraint。
1) not null
2) unique
3) primary key
4) foreign key references (on delete cascade)
5) check
两种位置定义约束的 区别:
1) 约束前面是逗号就是表一级,
约束前面是空格就是列一级;
2) not null只能定义在列一级
3) 联合键只能定义在表一级
eg:联合唯一: unique(last_name,first_name)
联合主健: primary key(product_id,order_id)
on delete cascade:该句在子表最后声明时,说明他的数据随外键的消失而消失
删除父表记录
1)写了on delete cascade
sql: delete from fathertable where id=1;
2)没写on delete cascade
sql: delete from sontables where fk=1;
delete from fathertable where id=1;
3 用子查询建表
1) 表的列名,列数,类型靠子查询定
create table luxw04
as
select id,last_name,dept_id,start_date
from s_emp
where dept_id in(41,42);
create table asalary
as
select dept_id,avg(salary)
from s_emp
group by dept_id;
2) 主sql规定了列名,列的个数,不能定义列的类型
在建表过程中只保留了非空(not null)约束,其他约束丢失
create table asalary
(did,avgsal)
as
select dept_id,avg(salary)
from s_emp
group by dept_id;
3) 可以制定约束
create table luxw06
(id primary key,
last_name,
dept_id references s_dept(id),
start_date default sysdate
)
as
select id,last_name,dept_id,start_date
from s_emp
where dept_id in(41,42);
约束和数据的关系:
谁先进入表,谁说了算
Data dictionary(表的集合):
1) 表:存放描述数据库状态的数据
db server在建库时建立
由db server维护,在线动态更新
user只能查询
2)user:由用户创建的 ,属于这个用户的。
all: 用户有权使用
dictionary: 保存了所有数据字典的名字,和描述信息
3)查出yesq5表personid列上的唯一性约束的名字
用到下面这两个表:
user_constraints
user_cons_columns
select cons.constraint_name,cons.constraint_type,cols.column_name
from user_constraints cons,user_cons_columns cols
where cons.constraint_name=cols.constraint_name
and cons.table_name='YESQ5'
and cols.column_name='PERSONID';
DML:
insert:
* 1) 全表插入,值的顺序,类型,个数必须和表里的列一致
insert into luxw8
values(1,'zhangsan','m','12-sep-98');
insert into luxw8
values(2,'lisi','f',null);
insert into luxw8
values(3,'wangwu','f', to_date('23-06-08','dd-mm-rr'));
insert into luxw8
values(4,'lisi','f',&da);
* 2)选择插入,列的个数,类型,顺序必须和指定的相等
选择时非空的列必须出现
insert into luxw8(id,name,sex)
values(5,'huxz','m');
insert into luxw8(name,id)
values('wangwu',6);
insert into luxw8(name,sex)
values('liucy','m'); ----->error 无PK
3)insert into luxw8(id,name)
values('7','liucy')
values('8','luxw'); ---->error 不能同时添加两个数据
insert into luxw8(id,name)
values(&idd,&na);
子查询提供值,值的类型和个数要匹配
insert into luxw8(id,name)
select id,last_name
from s_emp
where dept_id=45;
update:
1) update luxw8
set name='sun'
where id=1;
2)update luxw8
set name='fengwang',sex='m',registdate=sysdate
where id=2;
delete:
delete from luxw8
where id between 2 and 5;
Transaction(事务):必须具有原子性
ACID:
A:原子性,加钱和减钱都要成功或都失败,保持原子性;
C:一致性,数据一致性,如:A向B转帐5000块,那B就应该收到5000块,不能多也不能少;
I:隔离性,两个SQL语句的操作只操作自己的,不能影响到另一个语句,也就是相当于每个SQL都要有自己的回滚段;
D:持久性,数据的改变用commit或用rollback,让数据持久化;
1) 一组不可再分的sql命令组成的集合, 如果分了数据将不统一,如:银行转帐
事务的大小跟业务需求有关
2) 事务结束时结果
success: commit 回滚段内的数据写到FILE,锁释放,其它用户也可以看到结果
fail: rollback 清空回滚段内容,没写到FILE,锁释放,其它用户看不到修改的值,
3)
insert update delete update delete commit;
create--->auto commit;
insert
update
delete
create --->auto commit;
insert
update
rollback;
insert update delete exit
============================================================================
第五天:
DDL:
alter:改表结构
1)增加一列,添加到表的最后一列
alter table luxw8
add(age number(3) default 18 check(age<40));
2)删除一列(在oracle8i)
alter table luxw8
drop (age,registdate);
alter table luxw8
drop column age;
3)更改列名(在oracle9i)
alter table luxw8
rename column oldname to newname;
4)更改列(类型,size,添加约束,添加默认值)
alter table luxw8
modify(name varchar2(20) default user unique);
如果表里没有数据,随便改
如果表里有数据,改的时候必须符合表里的数据
alter table luxw8
modify(sex not null);
非空约束只能用modify子句加
5)添加约束
联合键约束加的时候只能用add子句
alter table luxw8
add unique(personid);
alter table luxw8
add constraint luxw8_personid_uk unique(personid);
当A表的PK是B表的FK且B表的PK是A表的FK时,
只能先让一个表(A表)的FK为空,再用add以表结构向该表(A表)添加FK,如下:
create table product
( id number(7) primary key,
name varchar2(15) not null,
price number,
b_id number(7)
);
create table bid
( id number(7) primary key,
userid number(7) references users(id),
bprice number,
product_id number(7) references product(id)
);
alter table product
add foreign key(b_id) references bid(id);
6)删除约束
alter table luxw8
drop constraint luxw8_personid_nn ;
alter table luxw8
drop primary key cascade;
7)失效约束
alter table luxw8
disable constraint luxw8_personid_uk;
alter table luxw8
disable constraint luxw8_id_pk cascade;
级联失效
8)生效约束
alter table luxw8
enable constraint luxw8_personid_uk;
alter table luxw8
enable constraint luxw8_id_pk;
生效时无法级联,只能手动一个一个做
rename oldtablename to newtablename 改表名
truncate table tablename; 截取表
drop table tablename cascade constraint;
删除父表,并且级联删除子表的外键约束
Sequence:
数据库提供的产生一系列唯一值的对象,用来生成主键值;
它是数据库里独立的对象,不依赖任何表
1)create sequence seq1
increment by 1 //每次递增1
start with 100 //从100开始计数
maxvalue 99999 //最大值为99999
nocycle //无循环
nocache; //没有缓冲
create sequence sql_seq
increment by 1
nocycle;
2) nextval:
如果有cache取出cache下一个可用的值
如果没有cache,取出序列下一个可用的值
insert into sql_users(id,name,passwd,phone,email)
values( sql_seq.nextval,'wangwu','123456abc', 12321,'[email protected]');
select seq2.nextval
from dual;
currval:当前的连接最后一次使用序列的值
select seq1.currval
from dual;
last_number(column):序列下一个可用的值
select last_number
from user_sequences
where sequence_name='SEQ2';
3)改序列
alter sequence seq1
increment by maxval minval cycle cache
序列的起始值不可能改变
4)删除序列
drop sequence seq1;
5) insert insert insert rollback;
只能朝前走
==========================================
view:
起了名字的查询语句
1) create view emp_v1
as select id,last_name,dept_id
from s_emp;
select * from emp_v1;
简化select语句的复杂度
限制数据的显示
节省空间
2) 复杂视图:里面包含了函数,连接,组函数
不能应用DML(增删改),只能查询
简单视图:
create view emp_v3
(dept_id,asalary)
as
select dept_id,avg(salary)
from s_emp
group by dept_id;
update emp_v3
set asalary=1111
where dept_id=41; ---->error
3)create view emp_v4
as
select id,last_name,dept_id
from s_emp
where dept_id=41
with check option constraint emp_v4_ck; //表示该视图(emp_v4)里的数据无法删除或改变,但可以添加数据
创建数据库用户:
create user username identified by password
default tablespace users
temprory tablespace temp;
alter user username identified by newpassword;
grant resource,connect to username;
grant create view to cksd0804;
revoke create view from cksd0804;
至少掌握数据库以下四点:
1)crud(增删改查)
2)create table(建表)
3)transaction(事务)
4)create sequence //sequence:序列
JDBC跟下面有很大的联系:
1 interface
2 try catch finally
3 thread
4 static{}
5 socket (url)
6 encapsulation inheritance poly... javabean
7 collection map
建立表空间
create tablespace si datafile '/u/oradata/en73/si.dat' size 70M online
default storage ( pctincrease 2 );
建立用户
create user zhangwf identified by pwd default tablespace si temporary tablespace si;
给用户授权
grant dba, connect, resource, create table to si;
建立表在用户SI上
create table si.tbname(col1 int,col2 varchar2(10));
修改密码:alter user zhangwf identified by newpwd
考点:行列转换, 重复数据的过滤,索引的建立
JDBC笔记
第一天:
OracleDriver{
public void connectOracle(){
......
} }
DB2Driver{
public void connectDB2(int i){}
}
telnet 192.168.0.200
Unix,Linux: echo $ORACLE_SID 在Unix和Linux下查看连接的哪个ORACLE
windows: net start 在windows下查看连接的哪个ORACLE
username
passwd
DriverManager.getConnection();
class OracleDriver implements Driver{ //OracleDriver类的大体实现
static{
OracleDriver d=new OracleDriver();
DriverManager.addDriver(d);
}
}
class DriverManager{ //DriverManager类的大体实现
private static List<Driver> dri=new ArrayList<Driver>();
public static void addDriver(Driver d){
......
dri.add(d);
}
public static Connection getConnection(){
for(Driver d:dri){
if(d!=null) return d.getconnect();
}
} }
boolean execute(sql) ---> all
boolean 有没有ResultSet返回
true:有 false:没有
rs=stm.getResultSet();
ResultSet executeQuery(sql){}-----> select
int executeUpdate(sql){} -----> insert update delete
运行程序可能出现的下面几种异常:
1 Class not found
-------> ojdbc14.jar
2 No suitable Driver
----->url
3 Socket Exception
ping 200
4 SQLException ----> sql
table: jdbc_users
sequence: jdbc_users_seq
driver=oracle.jdbc.driver.OracleDriver
url=jdbc:oracle:thin:@192.168.0.200:1521:oradb10g
username=cksd0804
password=cksd0804
BufferedReader
1)String str=br.readLine();
2)String[] s=str.split("=");
3)if(s[0].equals("driver"){}
以上三步相当于以下两步:
Properties info=new Properties();
info.load(Inputstream); //表示(1)加载输入流,(2)读取文本内容,(3)解析文本文件,//(4)再存入Properties集合
.properties ------> Map<String,String>
------>Properties extends Hashtable<String,String>
JdbcUtil.class.getResourceAsStream("/com/kettas/jdbc/cfg/config.properties"); //为虚拟路径,第一个/代表classpath
/com/kettas/jdbc/cfg/config.properties
/<====>classpath
/home/java/jdbc/..... //全路径
Student s=new Student();
第一次加载类时,JVM加载类的顺序:
1. search file ---->Student.class 通过classpath寻找类文件
path---->command
classpath--->.class
2. read Student.class 读取类文件
3. Class c----> 获得Student类详细信息(类对象)
Student s2=new Student();
.class----> jdbc.jar--->user--->classpath
1 分散代码集中管理(封装)
2 变化的内容写进文件(配置文件)
配置文件一般格试:config.xml和config.properties
3 .properties (是一种特殊的文本文件)
4 Properties (是Hashtable的子类)
5 ExceptionInInitializerError
6 Class.getResourceAsStream( 虚拟路径 )
1. jdbc 六个步骤
1)注册Driver;
Class.forName("oracle.jdbc.driver.OracleDriver");
2)获得连接
String url="jdbc:oracle:thin:@192.168.0.200:1521:oradb10g";
Connection conn=DriverManager.getConnection(url,"用户","密码");
3)创建Statement,stm=conn.createStatement();
4)执行sql, stm.executeUpdate(sql);
5)select--->处理结果集
ResultSet rs=stm.executeQuery(sql);
while(rs.next()){ System.out.println(rs.getInt(1)+"------"+rs.getString(2));
}
6)释放资源(rs,stm,conn)
if(rs!=null) try{ rs.close();} catch(Exception ee){}
if(stm!=null) try{ stm.close();} catch(Exception ee){}
if(conn!=null) try{ conn.close();} catch(Exception ee){}
2. 注册Driver的三种方式
1)Class.forName("oracle.jdbc.driver.OracleDriver");
2)Driver d=new oracle.jdbc.driver.OracleDriver();
DriverManager.registDriver(d);
3)程序里没有指定
java-Djdbc.drivers=oracle.jdbc.driver.OracleDriver classname
3. ResultSet遍历
1) next()---->boolean
2) get***(int) get***(columnname)
eg: getString("name");
开始时指针指向第一行的上一行,最后指针指向最后一行 的下一行
4. 三种execute方法的区别
1)stm.execute(sql) all boolean(ResultSet)(返回布尔型,判断是否有结果集)
2)stm.executeQuery(String selectsql) --->ResultSet(返回结果集,sql是查询语句)
3)stm.executeUpdate(sql) --->int(db row) (返回int,判断改变的行数,一般执行,update,delete,insert)
---->delete update insert
Statement和PreparedStatement
Statement是逐条发送语句(可以执行多条语句),PreparedStatement是先存储sql再一起发送(在sql需要设值的时候,效率要高,但只能执行一条语句)例子:
String sql=
"insert into jdbc_users(id,name) values(users_seq.nextval,? )";
pstm=conn.prepareStatement(sql);
pstm.setString(1, names[i]);//1代表是提几个问号,后面是设值
pstm.executeUpdate();一起提交
5.JdbcUtil类
1)分散代码集中管理(封装)
2)经常变化的写进文件(配置文件)
config.xml config.properties
3)Properties extends Hashtable<String,String>
专门处理properties文件,提供load(InputStream is)
4)在程序加载时读一次文件,所以读文件的代码写在
静态代码块,里面只能抛一种类型的错误
ExceptionInInitializerError
5)利用jvm的类加载器读字节码文件的功能,可以获得
输入流,
InputStream is=JdbcUtil.class
.getResourceAsStream(虚拟路径);
绝对路径:/是真是的目录里面的根
虚拟路径:/是classpath
例子:
private static Properties info=new Properties();
static{ try {
InputStream is=JdbcUtil.class.getResourceAsStream(
"/com/kettas/jdbc/cfg/config.properties");
info.load(is);
is.close();
} catch (Exception e) {
throw new ExceptionInInitializerError(e);
}
}
线程ThreadLocal 每一个线程对象创建好以后,JVM会为其分配一块内存空间用来存放当前线程对象独占的数据,(一个线程对象和另一个独占的数据(对象)绑定(如:(tl , conn)代表某一线程的独占数据 ))空间以map形式存放独占数据,相当于Map集合里的健对象和值对象
每个线程的独占数据不共享,即:健对象(tl)和值对象(connection)不共享:Map<ThreadLocal,Object>
注意:ThreadLocal无法直接获取Map对象,操作Map只能通过ThreadLocal的set和get方法
private static final ThreadLocal<Connection> tl
=new ThreadLocal<Connection>();
public static Connection getConnection() throws Exception{
Connection conn=tl.get();//用get获取当前线程的连接对象
if(conn==null){
Class.forName(info.getProperty("driver"));
conn=DriverManager.getConnection(info.getProperty("url"), info.getProperty("username"),info.getProperty("password"));
tl.set(conn);//用set把连接对象放在当前线程中
} return conn; }
线程ThreadLocal的大致实现过程
public class ThreadLocal<T>{
public T get(){
Map m=Thread.currentThread().getMap();
return m.get(this);
}
public void set(T t){
Map m=Thread.currentThread().getMap();
m.put(this,t);
}
}
ThreadLocal<Integer> ta=new ThreadLocal<Integer>() //例子
ta.set(new Integer(3));
7. Driver方展的四个阶段
1)Jdbc-odbc (桥连接) 2)db client(客户端api)
3)base net pure java (网络服务器) 4)pure java native(纯客户端)
7. 写程序,向jdbc_users表插入五条记录
String[] names={"mary","tom","anna","jerry","george"};
第二天:=======================================
Statement 和 PreparedStatement
1)建立
stm=conn.createStatement();
pstm=conn.prepareStatement(sql);
2)执行
stm.execute(sql); 包含数据的完整的sql命令
pstm.execute(); 数据
3)stm 可以执行多条sql
pstm 只能执行一条sql
4)使用pstm的环境
a.执行同构sql,用pstm效率高
执行异构sql,用stm效率高
b.构成sql语句时需要外部的变量值,用pstm
dao:
public void update(Account a){
sql=update account set username=?,passwd=?,
personid=?,balance=?;
pstm=conn.....
pstm.setString(1,a.getName());
}
=======================================
Transaction: 一组不可再分的sql命令,根具体业务有关
=============================================
A:原子性 :事务要么一起成功,要么一起失败,如:银行转帐
C:一致性 :如:存钱时,存5000,那么在数据库里的钱就要有5000,不能多也不能少
I:隔离性 :连接db的两个线程操作db时,要操作个自的元组,不能同时操作同一个元组
D:持久性--->commit,rollback ,就是让更改后的数据要么写加db要么不写进db;
手动划分事务的边界,一般写在业务层(biz)的代码块里。
划分事物边界的时候一般在要划分的地方加commit和rollback;
conn.setAutoCommit(false);
新事物开始
conn.commit();
旧事务的结束,新事务的开始
conn.rollback();
旧事务的结束,新事务的开始
没有手动划分,采用的是默认事务提交策略:
一条sql一个事务
----------------------------------------------------
Connection是线程不安全的,不能在线程间共享连接(connection)用ThreadLocal实现安全的连接
threada save withdraw transfer
threadb save withdraw
1.common: 5 个 Connection
2.singleton: 1 个 Connection
3.一个线程内部共享连接: 2 个 Connection
t1.start();
t2.start();
class ThreadA extends Thread{
private Connection conn1=null;
public void run(){
conn1=JdbcUtil.getConnection();
save(10000,conn1);
withdraw();
}
public Connection getConn(){return this.conn1;}
}
class Bank{
public void save(double balance){
conn2=Thread.currentThread().getConn();
sql="update account set balance=?";
pstm=conn.prepareStatement(sql);
//设置上面问号(?),如pstm.setDouble(int parameterIndex, double x);
pstm.set***;
pstm.executeUpdate();
}
public void withdraw(double balance){
conn2=Thread.currentThread().getConn();
sql="update account set balance=?";
pstm=conn.prepareStatement(sql);
pstm.set***;
pstm.executeUpdate();
}
}
save ----> update insert
withdraw----> update insert
transfer ---> update insert
update insert
update() insert()
save---->update() insert();
dao: 1)对业务层屏蔽底层数据库设计的细节 ,只有功能被业务层调用
2)o-r mapping:就是把对像自动的保存到数据库的表中
第三天:=======================================
1 PreparedStatement
Statement
stm=conn.createStatement();
stm.execute(sql);
String sql="insert into table value(?,?,?);
pstm=conn.prepareStatement(sql);
pstm.set***(int,value);
pstm.execute();
pstm同构sql,sql语句需要外界变量值
stm 异构sql
2 Transaction
sqls,跟具体业务有关
ACID
biz:
try{
conn.setAutoCommit(false);
conn.commit();
}catch(Exception e){
conn.rollback();
}
3 ThreadLocal
1)将Connection做成Thread的属性
通过参数的传递,保证一个线程一个连接
2)Thread t1-----> map(线程的独占的数据)
Map: 主键ThreadLocal,无法直接获得Map对象
操作map只能通过ThreadLocal的set get操作
eg:让Thread对象独占一个Integer对象
Integer id=new Integer(3);
ThreadLocal<Integer> tl=new ThreadLocal<Integer>();
tl.set(id);
4 Layer
1) app: showinterface
业务判断
=============================
save
transfer
showResultSet
---------------------------------
view ---> showinterface
biz----> 业务处理
1)业务判断
2)业务处理
=====================
account.balance+=money;
3) Connection conn
stm.execute(sql);
commit();
4)return result --->view
save(): update insert
withdraw(): update insert
transfer(): update insert
update insert
update 4 insert 4
view---->showinterface
biz----> 业务处理
1)业务判断 如:查看密码是否正确,或余额是否不足。
2)业务处理如:密码不正确或正确的处理,余额足时的操作
account.balance+=money;
3) 封装对象 如:用户注册
4)用对象调用dao里的方法存贮数据
5)提交事物
dao:---->data access object
1)完成对某一张的增删改查
update(): update
insert(): insert
delete(): delete
query(): select
2)接收从业务层传来的数据(对象),将其存进db
从db里面查数据(零散),封装对象,返回给业务层
object----relation mapping
save(): update() insert()
withdraw: update() insert()
transfer: update() insert();
update:1 insert: 1
1 javabean--->Student
class Student{
private Integer id;
private String name;
private String sex;
private String address;
private int age;
private String studentId;
public Student(String name,String sex,String address,int age,String studentId){
......
} }
2 create table jdbc_student
( id number(7) primary key,
name varchar2(15) not null,
sex varchar2(3) check(sex in('f','m')),
age number(3),
address varchar2(20),
studentid varchar2(15) not null unique
);
3 class StudentDao{
public void insertStudents(Set<Student> stus) throws Exception{}
public void insert(Student s) throws Exception{
Connection conn=null;
PreparedStatement pstm=null;
try{
conn=JdbcUtil.getConnection();
String sql="insert into jdbc_student"
+" values(jdbc_student_seq.nextval,?,?,?,?...)";
pstm=conn.prepareStatement(sql);
pstm.set***();
pstm.execute();
sql="select jdbc_student_seq.currval from dual";
s.setId(?);
}finally{
JdbcUtil.release(...);
} }
public void update(Student s) throws Exception{
String sql="update jdbc_student set name=?,age=?,address=?,sex=?,studentId=? where id=?";
}
public void delete(Student s) throws Exception{
delete from jdbc_student where studentId=?
}
public Student queryById(Integer id) throws Exception{}
public Student queryByStudentId(String studentId) throws Exception{}
public Set<Student> queryByName(String name) throws Exception{
String sql="select * from jdbc_student where name=?";
pstm=conn.prepareStatement(sql);
pstm.setString(1,name);
rs=pstm.executeQuery();
Set<Student> stu=new HashSet<Student>();
while(rs.next()){
Student s=new Student();
s.setId();
s.setName();
......
stu.add(s);
}
return stu;
}
}
4 class StudentBiz{
public void registStudent(String name,String sex,int age,String address,String studentId) throws Exception{
1. 判断 如:判断密码是否正确
2. 封装对象 如:新建对象
3. dao.insert(s);
4. 事务处理 conn.commit 或conn.rollback;
}
public void dropStudent(String studentId)...
public void queryStudent(String studentId)...
public void updateStudent(String name,String address,String oldstudentId,String newStudentId)....
}
com.kettas.student
biz
|--- StudentBiz
impl
|---StudentBizImpl
entity
dao
|--- StudentDao
impl
|---StudentDaoImpl
config
util
excp
view
test
sql
Repeatable read
select *
from s_emp for update;
Serializable
备份数据库提高效率策略:
1) 手动控制事务的大小,节约了db服务器的内存
减少了回滚带来损失
2) 事务结束时写日志,减少了查找失败事务的时间
3) 删除备份数据库表里的约束,减少数据进入时验证的时间
降低数据库服务器维护索引的资源
4) 降低数据库的隔离级别,设置到read uncommited
5) 插入时选择PreparedStatement,执行同构sql,减少编译
sql命令的时间
6)插入时使用批处理,减少网络传输时间。
第四天:===========================
Statement stm=conn.createStatement();
stm2=conn.createStatement(int value1,int value2);
value1:是否可滚动
ResultSet.TYPE_SCROLL_INSENCITIVE
value2:是否可更新
ResultSet.CONCUR_UPDABLE
rs=stm2.executeQuery();
pstm=conn.prepareStatement(sql,int,int);
数据库表数据的分页
1.db--sql
子查询和Rownum
2.jdbc---->可滚动的结果集
String sql="select * from s_emp order by start_date";
rs=stm.execute(sql);
========================================
int num=1;
rs.absolute(5);
while(rs.next()){
if(num>5) return;
.........
}
可用addBatch();方法添加批处理
int[] a=stm.executeBatch(); //执行批处理
批处理里放的是增删改语句,可以减少网络传输时间;
stm批处理里缓存的是sql命令,如:stm.addBatch(sql);
pstm批处理里缓存的是数据,如:pstm.addBatch();
Object----->db
处理大数据的类型
1,Blob------》二进制的大对象(电影,图片,压缩包)
2,clob-------》字符的大对象(文本小说等)
以Blob为例
第一步:用empty_blob占一个空位置
String sql="insert into largetable(id,name,largefile)"
+" values(3,'file3',empty_blob())";
stm.executeUpdate(sql);
第二步:查出位置
sql="select largefile from largetable where id=3";
rs=stm.executeQuery(sql);
第三步:读文件然后插入数据库
if(rs.next()){
Blob blo=rs.getBlob(1); //获得结果集的列值
//强转成Blob对象这样才有输出流
oracle.sql.BLOB bo=(oracle.sql.BLOB)blo;
OutputStream os=bo.getBinaryOutputStream();
InputStream is=new FileInputStream(filename);//获得文件的输入流
byte[] b=new byte[1024]; 每次读文件的字节数
int len=0,num=0;
while(true){
len=is.read(b); 每次读的自己长度一个数组
if(len<=0) break;
os.write(b,0,len); 向数据库写文件
if(++num%10==0){
System.out.println("10 k ok");
}}
is.close();
os.close();
} conn.commit();
(类)class(entity)-------table
(对象)object(persist object)--------row
(属性)field------column(oid-----pk)
relation---------> pk fk
class Student{
private Integer oid;
String name;
int age;
String sex;
public Student(String name,int age,String sex){
}
}
Student s1=new Student("zhangsan",18,"male");
Student s2=new Student("lisi",19,"male");
Student s3=new Student("zhangsan",20,"male");
各种关系表的建立
one-to-one( Car ---- License ),一对一:互相保留对方的引用,既互相都以对方为属性。
1.java
class Car{
//oid
private Integer cid; //与数据库对应的cid不用写进构造方法
// 业务属性
private String manufactory;
private String productType;
//关系属性:其它对象做为该对象的属性,关系属性不用写进构造方法里
private License license;
// get 和 set
// 有参和无参的构造方法
public Car(String manufactory, String productType, License license) {
super();
this.manufactory = manufactory;
this.productType = productType;
}
public void addLicense(License license){
this.license=license;
license.addCar(this);
}
public void removeLicense(){
this.license.removeCar();
this.license=null;
}
}
class License{
private Integer lid;
private String serialNum;
private String LicenseType;
private Car car;
// get 和 set
// 有参和无参的构造方法
public void addCar(Car car){
this.car=car;
}
public void removeCar(){
this.car=null;
}
}
//一对一的数据库建立
2. db
create table jdbc_car
( cid number(7) primary key,
manufactory varchar2(15) not null,
producttype varchar2(15) not null,
);
create table jdbc_license
( lid number(7) primary key,
serialnum varchar2(15) not null,
licensetype varchar2(15) not null,
car_id number(7) references jdbc_car(cid) unique
);
3. dao( CarDao--->jdbc_car , LicenseDao--->jdbc_license)
//当一个程序需要用到两个数据库时,就要建立两个数据访问层(dao)
class CarDao{
public void insertCar(Car c) throws Exception{
1.insert into cartable
2.c.setCid(?);
3.insert into license
4.l.setLid(?);
}
public void deleteCar1(Car c) throws Exception{
1.delete from license
2.delete from car
3.c.setOid(null);
4.license.setLid(null);
5.c.removeLicense(l);
}
public void deleteCar(Car c) throws Exception{
1.update license set car_id=null
2.delete from car
3.c.setOid(null);
4.license.setLid(null);
5.c.removeLicense(l);
}
public void updateCar(Car c) throws Exception{
1. update car table;
2. update license table set car_id=null;
}
public Car queryByCid(Integer cid) throws Exception{
db-->1.select car table
2.select license table
内存--> 3. car.addLicense(license);
4.return car;
}
}
main(){
Car c=queryByCarid(222);
c.addLicense(license);
dao.update(c);
}
1. 维护数据库
2. 维护内存中的oid (insert delete)
3. 维护关系(主要对象)
one-to-one :
弱耦合: fk+uk fk null
强关联: pk+fk fk not null
one-to-many(Company----Employee)
1. java
class Company{
private Integer cid;
private String cname;
private String address;
private Set<Employee> employees=new HashSet<Employee>();
}
class Employee{
private Integer eid;
private String ename;
private Date birthday; // java.sql.Date
private int age;
private Company company;
}
//一对多的数据库建立
2. db
create table jdbc_company
( cid number(7) primary key,
cname varchar2(15) not null,
address varchar2(15)
);
create table jdbc_employee
( eid number(7) primary key,
ename varchar2(15) not null,
birthday date ,
age number(3),
company_id number(7) references jdbc_company(cid)
);
理性思维,一步一步的往下做就OK了:
3 dao
insert(Object o){
1. insert object
2. object oid
3. relation
}
delete(Object o){
1. relation
2. delete object
3. object oid
}
update(Object o){
1. update object field
2. update relation( option )
}
Object query(value){
1. query object
2. query relation
}
-----------------------------------------------------
many-to-many ( Course <----> Student )
1. java
class Student{
private Integer sid;
private String sname;
private int age;
private Set<Course> courses=new HashSet<Course>();
}
class Course{
private Integer cid;
private String cname;
private int hours;
private Set<Student> students=new HashSet<Student>();
}
2. db //多对多的数据库建立
create table jdbc_student
( sid number(7) primary key,
sname varchar2(15) not null,
age number(3)
);
create table relationtable
( studentid number(7) references jdbc_student(sid),
courseid number(7) references jdbc_course(cid),
primary key(studentid,courseid)
);
create table jdbc_course
( cid number(7) primary key,
cname varchar2(15) not null,
hours number(5)
);
3. dao
StudentDao CourseDao
-----------------------------------------------------
inheritance ---> pojo
1. java
class Account{
private Integer id;
private String name;
private String passwd;
private String cardId;
private double balance;
private String personId;
}
class SavingAccount extends Account{
private String cardType;
public SavingAccount(String name,String passwd,
String cardId,double balance,
String personId,String cardType){
super(name,passwd,cardId,balance,personId);
this.cardType=cardType;
}
}
class CreditAccount extends Account{
private double overdraft;
}
2. db ---> one table //一个超级大表
create table inheritance_account
( id number(7) primary key,
name varchar2(15) not null,
passwd varchar2(15) not null,
cardid varchar2(15) not null unique,
balance number(10,2),
personid char(18) not null unique,
cardtype varchar2(10),
overdraft number(5),
atype varchar2(3)
);
3. dao
class AccountDao{
public void insert(Account a){
sql= insert into inheritance_account(.......);
pstm.setInt(1,....);
pstm.setString(2,a.getName());
pstm.setString(3,a.getPasswd());
pstm.setString(4,a.getCardId());
pstm.setDouble(5,a.getBalance());
pstm.setString(6,a.getPersonId());
if(a instanceof SavingAccount){
SavingAccount sa=(SavingAccount)a;
pstm.setString(7,sa.getCardType());
pstm.setDouble(8,null);
pstm.setString(9,"SA");
}else{
.....
}
a.setId(...);
}
public void delete(Account a){
sql= delete from table where id=?
pstm.setInt(1,a.getId());
pstm.executeUpdate();
}
public void update(Account a){
sql= update table set ........ where id=?
pstm.setString(1,a.getName());
................
pstm.setString(5,a.getPersonId());
if(a instanceof SavingAccount){
SavingAccount sa=(SavingAccount)a;
pstm.setString(6,sa.getCardType());
pstm.setDouble(7,null);
}else{
......
}
pstm.setInt(8,a.getId());
}
public Account queryByCardId(String cardId){
sql=select id,name,passwd,cardId,balance,personId,cardtype,overdraft,atype
from table where cardid=?
pstm.setString(1,cardId);
rs=pstm.executeQuery();
if(rs.next()){
String type=rs.getString(7);
if(type.equals("SA"){
SavingAccount sa=new SavingAccount();
sa.set****;
}else{
ca.set****
}
}
}
public Account queryById(Integer id){
}
}
------------------------------------------------------
jobs:
1. license dao ----> self
2. employee dao ----> self -->update(){self relation}
3. companydao----> queryByCname()
4. cardao------> delete2(),queryByCid2()
5. 改写bam
1) value object
2) dao ---> delete(Account a)
3) data---->db
4) biz,view不改
------------------------------------------------------
1.基本六个步骤
2. Statement 和 PreparedStatement
3. JdbcUtil
1) ThreadLocal 2) 配置文件
3) 虚拟路径( Class.getResourcAsStream())
1) 分散代码集中管理
2) 变化的数据写进文件
4. Transaction
1)API 2) ACID 3)并发问题 4)隔离级别
5. Jdbc2.0
1) Batch
6. ormapping:
1) dao 步骤
2) 各种关系表的建立
7. 1)数据备份时提高效率的方法
2)数据分页
8.Driver的四个发展阶段
Hibernate
第一&二天:
ORM,即Object-Relationl Mapping,它的作用是在关系型数据库和对象之间作一个映射,这样,我们在具体的操作数据库的时候,就不需要再去和复杂的SQL语句打交道,只要像平时操作对象一样操作它就可以了(把关系数据库的字段在内存中映射成对象的属性)
1 hibernate---->ormapping
1) CRUD 2)HQL 3)mapping file 4)性能
2 基于hibernate的应用步骤:
1)javabean(pojo)
2) relation db -----> create table
3)mapping file( object----table )
filename.hbm.xml ----> 跟javabean放在一个路径
<hibernate-mapping package="packagename">
<class name="classname" table="tablename">
<id name="oidname" column="pkname" unsaved-value="null">
<generator class="生成器classname">
<param name="type"> value </param>
</generator>
</id>
<propertyname="javaproperty" column="dbcolumnname"></property>
</class>
<class>.....</class>
</hibernate-mapping>
4) configure file ----> classpath(src)
hibernate.cfg.xml
<hibernate-configuration>
<session-factory>
<property>
<mapping resource="filepath">
5) application ( base hibernate )
a. start hibernate
Configuration cfg=new Configuration().configure(...);
B . create sessionfactory
SessionFactory sf=cfg.buildSessionFactory();
c. open Session
Session sess=sf.openSession();
d. begin Transaction
Transaction ta=sess.beginTransaction();
e. persist object
sess.save(Object o); oid=null ; insert
sess.delete(Object o); oid!=null delete
sess.update(Object o); oid!=null update
Object o=sess.get(classname.class,id);
f. ta.commit()/ta.rollback()
g. release resource
( sess , sf ) ----- sess
sess: 线程不安全
sf: 线程安全, 重量级的组件
3 hilo
1) create table hilotable
( value number(7) );
insert into hilotable
values(1);
2) <generator class="hilo">
<param name="table">hilotable</param>
<param name="column">value</param>
</generato>
name= java name
class= java type
type= hibernate type (lower)
table , column= db name (insensitive)
---------------------one-to-one---------------
1. javabean ( 双向(bi),单向 (ui))
2. create table ( fk+uk , pk+fk )
3. mapping file
4. change configure file
5. java app
property-ref="car"
1)双向的一对一
2) 没有写property-ref, 没有写cascade
sess.update(car);
a. update jdbc_car set ***** where cid=?
b. update jdbc_license set car_id=? where lid=?
没有写property-ref, 有写cascade
sess.update(car)
a. update jdbc_car set **** where cid=?
b. update jdbc_license set car_id=? where lid=?
c. update jdbc_license set sn=?,lt=?,car_id=? where lid=?
有写property-ref, 有写cascade
a.update jdbc_car set **** where cid=?
b.update jdbc_license set **** where lid=?
------------------------------------------------------
class People{
Integer id;
String name;
int age;
Set<Hand> hands=new HashSet<Hand>();
}
class Hand{}
1. one-to-one fk+uk(单向)
2. one-to-one pk+fk(单向)
3. one-to-many (单向)
4. many-to-many (javabean table mappingfile)
第三天:==================================
1. 标签: 类驱动表,真实反应Java里的类,即:以类为主
hibernate-mapping---->package
class ----> name table
id ----> name column unsaved-value
generator----> class
param ----> name
property----> name column unique not-null type
one-to-one----> name property-ref cascade constraint
many-to-one---> name column unique cascade
set----> name table cascade inverse
key----> column
one-to-many---> class
2 id 生成器类的别名:
sequence , hilo , sequencehilo , foreign
assigned , increment , identity(mysql) , native
3 assosiation:
===============================
1) one-to-one
a. fk+uk
<class name="Car"....>
<one-to-one name="license" property-ref="car" cascade="save-update"/>
</class>
<class name="License"....>
.......
<many-to-one name="car" column="car_id" unique="true" cascade="save-update"/>
</class>
b. pk+uk
<class name="Car"....>
<id> ....</id>
.....
<one-to-one name="license" property-ref="car"
cascade="all"/>
</class>
<class name="License"....>
<id name="lid"........>
<generator class="foreign">
<param name="property"> car</param>
</generator>
</id>
...
<one-to-one name="car" constrained="true"
cascade="save-update"/>
</class>
2) many-to-one
<class name="Company"....>
......
<set name="employees" table="jdbc_employee">
<key colum="company_id"/>
<one-to-many class="Employee"/>
</set>
</class>
</class name="Employee"...>
.....
<many-to-one name="company" column="company_id"
cascade="save-update"/>
</class>
当写双向级联关系时,确定只有一方维护关系,(一般多的一方维护关系)
1 one-to-one 用 property-ref
2 one-to-many many-to-many 用 inverse="ture"
一的一方要 设 <set (inverse="false") casecade="save-update" />,多的一方要设<many-to-one inverse=true (casecade="none") />
两边都用true或都用false都是不对的,关系最好让一方维护
--------------------------------------------------------
many-to-many
1. javabean
class Student{
private Integer sid;
private String sname;
private int age;
private String sex;
private Set<Course> courses=new HashSet<Course>();
}
class Course{
private Integer cid;
private String cname;
private int hours;
private Set<Student> students=new HashSet<Student>();
}
2 db table
create table hibernate_student
();
create table hibernate_relation
();
create table hibernate_course
();
3 mapping file
<class name=”Student” table=”hibernate_student”>
<id name=”sid” column=”sid” unsaved-value=”null”>
</id>
<property name=”…”/>
<set name=”courses” table=”hibernate_relation”
cascade=”save-update” inverse=”true”>
<key column=”student_id”/>
<many-to-many class=”Course” column=”course_id”>
</set>
</class>
<class name=”Course” table=”hibernate_course”>
<id name=”cid” column=”cid” unsaved-value=”null”>
</id>
<property name=”…”/>
<set name=”students” table=”hibernate_relation”
cascade=”save-update”>
<key column=”course_id”/>
<many-to-many class=”Student” column=”student_id” />
</class>
1.student and course 没有inverse=true 就会多一次维护关系
student and course 都有cascade
update(student)
1)update student table
2)update relation table
3)update course table
4)update relation table 就会多一次维护关系
2.student and course 有inverse=true 就不会维护关系
student and course 都有cascade
insert(student)
1)insert student table
2)insert course table
3.student有inverse=true 一方维护关系才正确
course 没有 inverse=true
student and course 都有cascade
update(student)
1)update student table
2)update course table
3)update relation table
----------------------------------------------------
sess.beginTransaction();
sess.save(Object);
1. insert student table
2. stm.addBatch(sql1);
3. insert course table
4. stm.addBatch(sql2);
5. insert relation table
6. stm.addBatch(sql3);
7. stm.executeBatch();
ta.commit;
catch(Exception e){ta.rollbacka();}
===========================================
获得线程安全的session
1. 修改配置文件
<property name="current_session_context_class">thread</property>
2.在java程序里获取session时
Session sess=sf.getCurrentSession();
session特点:
1) 线程安全
2) sess在事务结束时会自动关闭,一个事务用一个sess,当数据量过多时,
可以用sess.flush(); 将缓存的sql命令发往数据库执行,执行结果保存在回滚段.
--------------------------------------------------------
persist:
Session:一级Cache (缓存session操作过的所有对象 )
SessionFactory: 二级Cache ,用安全的session时,尽量使sf关闭时,线程也要关闭;
(缓存生成的sql,以及hibernate管理过的部分对象
Hibernate的对象状态,即生命周期
1.Transient(临时,暂时)
Student s1=new Student("zhangsan",18);
1)在数据库里没有对应的记录
2)一般情况下,没有数据库唯一标识符
特殊情况 1)assingned 临时对象,即新建(new)对象
2)sess.delete(o) 对象已被删除
3)不在Session里面
2.persist(持久化状态)
sess.save(s1); saveOrUpdate()
Student s2=sess.get(Student.class,123);
1)在数据库里有对应的记录
2)有数据库唯一标识符
3)在Session里面
3.detached(游离,脱管状态)
sess.close(); sess.clear();sess.evict()
1)在数据库里有对应的记录
2)有数据库唯一标识符
3)不在Session里面
Session保留备份(两个集合)
class SessionImpl implements Session{
private LinkedList entities=new LinkedList(); //实体集合,用于存放临时对象
private LinkedList copy=new LinkedList(); //深度拷贝集合,跟集合的数据一样,可以减少访问DB
public Serializable save(Object o){
1.Serializable pk=.......
2.db insert
3.entities.add(o);
4.copy.add(o.deepcopy);
5.return pk;
}
public void delete(Object o){
1.db delete
2.entities.remove(o);
3.copy.remove(o.deepcopy);
}
public void close(){
entities.removeAll();
copy.removeAll();
}
public void evict(Object o){
entities.remove(o);
copy.remove(o.deepcopy);
}
}
sess保留备份的作用:
优势:
1) 查询同一个对象,不用再访问数据库,
直接返回sess里的数据
2) 脏的检测,当检测到实体集合与深度拷贝集合里的数据不同时,会把改变的对象同步到数据库里。
Student s=new Student("zhangsan",18);
sess.save(s);
s.setName("lisi");
sess.close(); (sess.update(s)) -----> db s name="lisi",在sess关闭前会自动更新脏的数据到db里。
<class name="Student" table="" dynamic-update="true">
s.setName("wangwu");
sess.update(s);
1)sess=HibernateUtil.getSession();
ta=sess.beginTransaction();
Student s4=new Student("zhangsan",18);
sess.save(s4);
s4.setName("lisi");
----> 在sess没有关闭前,脏的检测起作用
ta.commit();
db---------> name="lisi"
2)sess=HibernateUtil.getSession();
ta=sess.beginTransaction();
Student s4=new Student("zhangsan",18);
sess.save(s4);
ta.commit(); ----> sess关闭
s4.setName("lisi"); ----->s4 此时已处在游离状态
db---------> name="zhangsan"
3)sess=HibernateUtil.getSession();
ta=sess.beginTransaction();
Student s4=new Student("zhangsan",18); --->临时
sess.save(s4); -----> 持久
ta.commit(); ---->s4 游离
ta2=HibernateUtil.beginTransaction(); --->新的sess
s4.setName("lisi"); ----->s4 游离
ta2.commit; ---> 关闭新的sess
db----------->name="zhangsan"
4)sess=HibernateUtil.getSession();
ta=sess.beginTransaction();
Student s4=new Student("zhangsan",18); --->transient
sess.save(s4); --->persist
sess.evict(s4); --->detached
s4.setName("lisi");
ta.commit(); --->sess.close
db---------> name="zhangsan"
缺点:不适合做大量数据的插入或者修改
Student s=new Student("zhangsan",18); -->transient
sess.save(s); -->persist
sess.evict(s); -->detached
sess.delete(s);
detached--->persist--->transient
jobs:
1. m2m---> javabean table mappingfile dao biz test
2. 如何获得线程安全的session
3. 三种状态,以及它们之间的转换
4. sess脏的检测
5. 测试get和load
第四天:=======================================
1 many-to-many
1) java
class Student
Set<Course> courses
class Course
Set<Student> students
2)db
hibernate_student relationtable hibernate_course
sid studentid cid cid
3)
<class name="Student" table="hibernate_student">
<id name="sid..>
<property>
<set name="courses" table="relationtable" inverse="true" cascade="save-update" >
<key column="studentid" >
<many-to-many class="Course" column="cid">
</set>
</class>
<class name="Course" table="hibernate_course" >
<id.....>
<property>
<set name="students" table="relationtable"
cascade="save-update">
<key column="cid">
<many-to-many .....>
</set>
</class>
------------------------------------------------------
2. hibernate---->ormapping (object-->db)
hibernate--->dao
connection---> not safe ---> ThreadLocal<Connection>
Session---->not safe---> ThreadLocal(Session)
1) ThreadLocal<Session>
2) current_session_context_class thread
Session sess=sf.getCurrentSession();
线程安全,一个事务一个sess,事务结束会自动关闭sess
ta=sess.beginTransaction();
Student stu=new Student();
sess.save(stu);
ta.commit();
dao: save() update() delete() get()
saveOrUpdate()
3.Persist
1)Transient:
id=null ( assigned , sess.delete() )---->saveOrUpdate()
not in sess
not in db----not row
Persistance
in db ---- row
id!=null
in sess
Detached
in db----row
id!=null
not in sess
2) 一级cache(Session)----> 集合
session有两个集合(entities,deepcopy)
a.缓存操作过的对象,加快查询速度
sess.get(Student.class,14);
get--->一级cache---->db
b.脏的检测(关闭时)
在内存中改变对象属性:
显示调用update():detached
隐含调用update():Persistance(已经持久化的对象,如果被修改,就会隐含调用)
3)状态图
4 Student s=new Student();
持久化s;
1) sess.save(s) sess.saveOrUpdate(s)
2) 利用持久化对象的扩散
sess=sf.getCurrentSession();
Course c=sess.get(Course.class,18);
c.getStudents().add(s);
sess.update(c);
ta.commit();
A--->B--->C--->D
save(A);
cascade:
a. none(default)
b. save-update
c. all ( save-udpate-delete )
d. delete
e. delete-orphan
com1-------> delete
emp1 emp2 emp3 ----> delete-orphan
delete(com1); ---> emp1 emp2 emp3
delete(emp1); --->emp1
5 查找Student s
1) query by id ( get() or load() )
Student s=sess.get(Student.class,10);
Student s=sess.load(Student.class,10);
class SessionImpl implements Session{
public Student get(Class c,Serializable id){
1. sql= " select * from student where id=? ";
2. Student o=data
3. return o;
}
public Object load(Class c,Serializable id){
Student s=c.newInstance();
s.setId(id);
return s;
}
}
get和load
a. get时先查询Session的Cache,如果cache里面没有,
再生成sql语句查询数据库,默认查询所有的值
load先在内存中生成一个只有oid的对象, 且返回
没有查询db,需要其他属性时再去查db
b. get回来的对象,所有的属性均有值
load回来的对象只有oid有值,其他属性为null
c. get---->Student
load----> SubStudent(cglib.jar)
2) 曲线查询:
Student s;
Course c=sess.get(Course.class,167);
for(Student s: c.getStudents()){
System.out.println(s.getSname());
}
3)Query(interface)-----> Hibernate查询语言:hql(Hibernate query language)
String hql="from Student s where s.sid>150";
Query q=sess.createQuery(hql);
List l=q.list();
batch insert/update:9999
1) sess=sf.getCurrentSession();
ta=sess.beginTransaction();
String hql="from Student s";
List stus=sess.createQuery(hql).list();
sess.setFlushMode(FlushMode.COMMIT);
for(int i=1;i<=stus.size();i++){
if(i%50==0){
sess.flush(); // batch 里缓存多少sql命令
将缓存的sql命令发往数据库执行,执行结果保存 在回滚段
sess.clear();
清空一级cache
}
stus.get(i).setName("hello"+i);
sess.update(stus.get(i));
}
ta.commit(); // 回滚段的内容写回数据文件
1) 存放临时对象1000块
2) Session里面留有1000个备份对象
3) hibernate自动使用batch,缓存1000条sql
默认情况下一个事务一个batch,flush()以后,把一个事务
划分成若干个batch
2) sess=sf.getCurrentSession();
ta=sess.beginTransaction();
String hql="update Student s set s.sname=?";
Query q=sess.createQuery(hql);
q.setString(1,"haha");
q.executeUpdate();
ta.commit();
总结: Session( entities deepcopy )
1. 三种状态
2. 持久化一个对象
save saveOrUpdate
persistance(持久保存) object ---->脏的检测
3. 查询对象:
1) get load ----> 加快速度
2) relation object //关系对象
3) hql
4. batch update/insert
1) query----> change----> update
batch_size flush() clear()
2) hql
----------------------------------------------------
interfact Transaction{
public Transaction beginTransaction();
public void commit();
public void rollback();
}
class JdbcTansaction implement Transaction{
public Transaction beginTransaction(){
.......
conn.setAutoCommit(false);
}
public void commit(){
conn.commit();
}
public void rollback(){
conn.rollback();
}
}
事务的并发控制
一、多个事务运行时的并发问题
并发问题归纳为以下几类:
1 第一类丢失更新:撤销一个事务时,把其他事务已经提交的更新数据覆盖。
2 赃读:一个事务读到另一个事务未提交的更新数据。
3 虚读:一个事务读到另一个事务提交的新插入的数据。
4 不可重复读:一个事务读到另一个事务已经提交的更新数据。事务A对统一数据重复读两次却得到不同的结果,有可能在A读取数据的时候事务B对统一数据做了修改
5 第二类丢失更新:这是不可重复读中的特例,一个事务付给另一个事务已提交的更新事务。
二数据库系统提供了四种事务隔离级别供用户选择:
A.Serializable(串行化):一个事务在执行过程中完全看不到其他事务对数据库所做的更新。
B.Repeatable Read(可重复读):一个事务在执行过程中可以看到其他事务已经提交的新插入的记录,但是不能看到其他其他事务对已有记录的更新。
C.Read Commited(读已提交数据):一个事务在执行过程中可以看到其他事务已经提交的新插入的记录,而且能看到其他事务已经提交的对已有记录的更新。
D.Read Uncommitted(读未提交数据):一个事务在执行过程中可以拷打其他事务没有提交的新插入的记录,而且能看到其他事务没有提交的对已有记录的更新。
隔离级别越高,越能保证数据的完整性和一致性,但是对并发性能的影响也越大。对于多数应用程序,可以有优先考虑把数据库系统的隔离级别设为Read Commited,它能够避免脏读,而且具有较好的并发性能。尽管它会导致不可重复读、虚读和第二类丢失更新这些并发问题,在可能出现这类问题的个别场合,可以由应用程序采用悲观锁或乐观锁来控制
在hibernate.cfg.xml中设置隔离级别:
<session-factory>
<!-- 设置隔离层次,并发,缺省时Read Committed: 2 -->
<property name="connection.isolation">2</property>
<!-- 配置事务实现类 -->
<property name="transaction.factory_class">
org.hibernate.transaction.JDBCTransactionFactory </property>
</session-factory>
悲观锁: 在查询时加
1. Student s=sess.get(Student.class,id,LockMode.UPGRADE);
五种模式
LockMode.NONE: 查询时先在cache(缓存)里找,如果没有,再到db里加载 无锁机制。
LockMode.READ: 不管cache有没有,都查询数据库,Hibernate在读取记录的时候会自动获取。
LockMode.UPGRADE: 不管cache有没有,都查询数据库,并且 对查询的数据加锁,如果锁被其他事务拿走, 当前事务会一直等到加上锁为止. 利用数据库的for update子句加锁。
LockMode.UPGRADE_NOWAIT: 不管cache有没有,都查询数据库,并且对查询的数据加锁, 如果锁被其他 事务拿走,当前事务会立刻返回. ,hiberna Oracle的特定实现,利用Oracle的for update nowait子句实现加锁
LockMode.WRITE:。在做insert,update,delete会自动使用模式.内部使用
2 . 以下情况用悲观锁:查询数据时,就给数据加锁
1)数据资源被多个事务并发访问的机会很大
2)修改数据所需时间非常短
乐观锁,大多是基于数据版本 (Version)记录机制实现
1在javabean中加上version的属性提供set和get方法
2,在数据库表上加上vershion列
3在映射文件的<id></id>后加上<version>
<version name=”version” column=”version”/>
实现原理:读取出数据时,将此版本号一同读出,之后更新时,对此版本号加一。此时,将提 交数据的版本数据与数据库表对应记录的当前版本信息进行比对,如果提交的数据 版本号大于数据库表当前版本号,则予以更新,否则认为是过期数据。
第五天:=================================================================================
Inheritance:
1. javabean
Account ---> SavingAccount CreditAccount
2. db
1)one table
2)one class one table
3)subclass---->table
3. mapping file
1) one table
<class name="Account" table="inheritance_table"
discriminator-value="AC" >
<id>
<discriminator column="atype" type="string" length="3"/> 在数据库加一列atype用来分开各种不同的父类和子类
<property>
<subclass name="SavingAccount" discriminator-value="SA">
<property name=""> 子类的特别属性discriminator-value="SA"默认值是SA
</subclass>
<subclass name="CreditAccount" discriminator-value="CA">
<property name="">
</subclass>
</class>
2) one class one table 每个类都有一个表 子类的主键同时是父类的外键 即父表的主键也子表主键一样
<joined-subclass name="SavingAccount" table="hibernate_savingaccount">
<key column="id"></key>
<property name="cardType"></property>
</joined-subclass>
3) one subclass one table每个子类一张表
<class name="Account" abstract="true"> 抽象父类不能new对象,父类没有对应的表
<id name="id" column="id" unsaved-value="null">
<generator class="sequence">
<param name="sequence">account_seq</param>
</generator>
</id>
<property name="name"></property> 父类的属性
<union-subclass name="SavingAccount" table="hibernate_savingaccount">
<property name="cardType"></property>
</union-subclass>
<union-subclass name="CreditAccount" table="hibernate_creditaccount">
<property name="overdraft"></property>
</union-subclass>
</class>
4. change config file
5. dao
public void insertAccount(Account a){
sess.save(a);
}
6. biz
7. view
8. test
-----------------------------------------------------
class Person{
private Integer id; ------> oid
private String name; ------>业务属性
private int age;
private Head head; ---->业务属性
private Set<Hand> hands=new HashSet<Hand>(); ---->业务属性
private Computer computer; ---->关系属性
private Set<Book> books=new HashSet<Book>();---->关系属性
}
class Hand{
private String finger;
}
class Book{
private Integer id;
private String bname;
}
class Head{
private String eye;
}
class Computer{
private Integer id;
private String Screen;
private Person person;
}
------------------------------------------------------
以下标签和属性要结合着映射文件看和理解hibernate
base(基础):
hibernate-mapping:表示映射文件的根标签 --> package:表示要映射的类所在的包。
class:该标签下包含映射的类和DB里的表--> name:要做映射的类 table:对应DB里的表 ;
optimistic-lock:经常与version连用,作用是一个数据被多个事务访问时,
如果其中一个事务修改了DB数据,那么其它事务将不能得到提交数据,只能重新访问DB;
1 none 无乐观锁2 version 通过版本机制实现乐观锁
3 dirty 通过检查发生变动过的属性实现乐观锁4 all 通过检查所有属性实现乐
dynimic-update:默认值为"false",当值为"true"时表示更新被修改的列,而不全表更新;
abstract:表示要映射的类是抽象类,不能new对象,只能用他的实现类,否则hibernate会报错;
lazy:表示延时加载,即用到DB里的数据时才去DB里加载,默认为"true"。
id:---> name:表示在类里的对应的属性名; column:表示在DB表里对应的列名;
unsaved-value:未存进DB前或当id为临时对象时,默认为null;
generator:表示生成器标签 ---> class:是用哪种生成器,在Oracle里是系列(sequence)。
param:参数标签 ---> name:指代用什么配置或初始化生成器的值。
property:该标签用于属性与表里的映射----> name:对应类里的属性名;
column:对应DB表里的列名; unique:表示DB表里的列的值是否唯一;
length:表示DB里那一列的数据存放长度; type:DB里那一列的数据存放类型;
not null:DB里那一列的数据是否可以为空。
version:该标签经常与乐观锁连用,表示其它事务是否修改DB里的值--->
name:在类里对应的属性名; column:DB里对应的列名。
assosiation(关联):在类里是一个类的属性是别一个类的类型,在DB里是多表关联。
one-to-one--> name:表示在类里对应的属性名;constrained:表示该属性必须有值;
property-ref:表示只维护该表里的属性列,而不维护以该表的ID为外健的其它表;
cascade:级联操作,即级联关系属性,当值为"save-update"时表示更新A表时,也更新与之对应的B表,(cascade在A表)。
many-to-one-->name:表示在类里对应的属性名; column:表示在DB表里对应的列名; lazy:表示当用到该类的对象(A)的属性(name)时,
会自动去DB里加载这个属性对应的行,并用这行生成对象保存在该类的对象(A)的属性里。
cascade unique
one-to-many--> class:在类里对应哪个类;
set:该标签表示类里的属性是set集合----> name:表示set集合在该类里的名称;
table:表示set集合里的数据在DB里对应哪个表;
cascade:表示更新cascade所在的表时,级联更新table对应的表(或name属性所在的表);
inverse:表示只维护该表里的属性列,而不维护以该表的ID为外健的其它表;
key:该标签表示外健 ----> column:表示表里的外健名;
many-to-many----> class:在类里对应的类;column:在表里对应的外健。
inheritance(继承):
subclass:用于多个子类做成一张表时 ---> name:表示在类里对应的子类名;
discriminator-value:在DB表里对应的列的值;
discriminator:该标签用于描述discriminator-value ---> column:表示该值在哪一列 ;
type:在DB里是什么样的数据类型; length:在DB里以什么样的长度存储的。
joined-subclass:一个类一个表--->name:表示在类里对应的子类名; table:在数据库里对应哪个表;
union-subclass:一个子类一个表---->name table
valuetype(值类型):没有oid的Java对象(某个对象的业务属性,离开该对象无法存在)
component:该标签表示该类(comA)是另一个类(A)的某一部分(属性)--->name:表示comA类在A类里的名字;
parent:该标签表示属于哪个类---> name:在comA类里的属性名;
set-->.....
element:值元素(如在map标签里表示值对象)---> type:在对象在DB里存的数据类型; column:在DB里对应哪一列;
list ---> name table
list-index:表示list集合的下标(在数据库中自动生成)---> column:对应DB里table值的哪一列
idbag:hibernate里特有的一个集合标签,可重复无序。----> name table
collection-id:表示给一重复数据的区别---> column:对应DB表里的哪一列;
composit-element:该标签表示里面的数据是某一集合的组成元素---> class:表示该集合里的元素是属于哪一个类的;
map----> name:表示map集合在该类里的名称; table:表示map集合里存的数据在DB里对应哪个表;
map-key:map集合里的健对象---> column:表示健对象对应table表里的一列; type:表示健对象的类型;
query
第六天:==================================
1.optimistic-lock
1) old: <class optimistic-lock="all/dirty/version"
dynimic-update="true">
2) new: a. javabean ---->version(int)
b. table----->version(number)
c. mapping file---> <version> (紧跟在<id>)
2. 1)javabean ---> extends
2)db ----> a. one table
( pk, 父类共性 , 各个子类特性 , 类型 )
b. one class one table
父表( pk 共性)
子表( pk-fk 特性)
c. one subclass one table
表( pk 共性 特性 )
表 ( pk 共性 特性 )
3) mapping file
a. one table
<class>
<discriminator column="colname" type="string">
<subclass name="subname" discrinator-value="SA">
</subclass>
</class>
b. one class one table
<joined-subclass name="subname" table="subtablename">
<key columm="">
<property>
</joined-subclass>
c. one subclass one table
1) <class> <class>
2) <class name="fatherclassname">
共性
<union-subclass name="" table="">
特性
</union-subclass>
</class>
3. valuetype 值对象 (没有oid的java对象)
1) one value ---> component (两个类一张表)
db: pk 简单类型 v1property1 v1property2
mapping: <component name="javavaluetype name">
<parent>
<property>
</component>
2) one collection (两个类两张表)
set: java---> Set<String>
db-----> table( id(fk) value pk(id,value))
mapping--->
<set name="setname" table="">
<key column="id">//外键
<element column="value" type="string">
list: java---> List<String>
db-----> table( id(fk) index value pk(id,index))
mapping--->
<list name="listname" table="">
<key column="id">
<list-index column="index"> 自动生成下标
<element column="value" type="string">
idbag: java---> List<String>
db-----> table( id(fk) key(pk) value )
mapping--->
<idbag name="listname" table="">
<collection-id column="key">
<generator>
<key column="id">
<element column="value" type="string">
3) one map
java---> Map<String,String>
db-----> table( id(fk) key value pk(id,key))
mapping--->
<map name="setname" table="">
<key column="id">
<map-key column="key">
<element column="value" type="string">
------------------------------------------------------
1.query object(Employee): Company--Employee
1) Company c=sess.get(Company.class,18);
Set<Employee> employees=c.getEmployees();
2) query oid;
Employee e=(Employee)sess.get(Employee.class,18);
Employee e=(Employee)sess.load(Employee.class,18);
3)hql
String hql="from Employee e where e.name='zhangsan'";
Query q=sess.createQuery(hql);
List<Employee> es=q.list();
mapping file:
<query name="querybyname">
<![CDATA[ from Employee e where e.name='zhangsan']]>
</query>
java: Query q=sess.getNamedQuery("queryname");
4) Criteria
Criteria ct=sess.createCriteria(Employee.class);
Criterion ce1=Expression.eq("name","zhangsan");
Criterion ce2=Expression.like("address","W%");
ct.add(ce1);
ct.add(ce2);
List<Employee> es=ct.list();
5) 本地sql
str="select e.NAME as {emp.name},e.AGE as {emp.age}
from EMPLOYEE e where e.NAME='zhangsan'";
SQLQuery sq=sess.createSQLQuery(str);
sq.addEntity("emp", Employee.class);
str=" select {e.*} from EMPLOYEE e where e.NAME='zhangsan'";
sq.addEntity("e",Employee.class);
2. 支持多态查询
public Account queryById(Integer id){
String hql="from Account a where a.id=?"
Account a=query.list().get(0);
}
3.排序(list()和iterate()):
hql1="from Employee e order by e.eid";
Query q1=sess.createQuery(hql1);
List<Employee> es1=q1.list();
hql2="from Employee e where e.age>30";
Query q2=sess.createQuery(hql2);
List<Employee> es2=q1.list();
// select * from Employee where e.age>30; --->sql
Iterator<Employee> et=q.iterate(); --->效率高
// select e.eid from employee e where e.age>30--->sql
4. 数据分页:
1)sql--->rownum
2)jdbc---> ResultSet
3)hibernate--->
hql="from Employee e order by e.start_date";
Query q=sess.createQuery(hql);
q.setFirstResult(5);//从结果集q中的第五行开始
q.setMaxResult(5); //得到五行
List<Employee> es=q.list();
list.size===5;
q.setFirstResult(5);
q.setMaxResult(1);
Employee e=q.uniqueResult();
根据pk查询
5.根据条件查询(where)
hql=" from Employee e "+"where..."
1)where e.name='zhangsan'
where lower(e.name)='zhangsan'
2)where e.age<30
3)where e.age between 30 and 60
4)where e.age in(30,40,50)
5)where e.name like 'S%'
6)where e.salary>1000 and e.dept_id=41
where e.salary>1000 or e.dept_id=41
7)where e.address is null
8)!= , not between and , not in ,not like ,is not null
6. 参数绑定:
1) hql=" from Employee e where e.name=?";
Query q=sess.createQuery(hql);
q.setString(1,"zhangsan");
2) hql=" from Employee e where e.name=:name";
Query q=sess.createQuery(hql);
q.setString("name","zhangsan");
Company c=(Company)sess.get(Company.class,18);
hql="from Employee e where e.company=:com";
Query q=sess.createQuery(hql);
q.setEntity("com",c);
public List<Student> query(String name,Integer age){
StringBuffer sb=new StringBuffer("from Student s ");
if(name!=null) sb.append(" where s.name=:name");
if(age!=null && name!=null)
sb.append(" and s.age=:age");
if(age!=null && name==null)
sb.append(" where s.age=:age");
Query q=sess.createQuery(sb.toString());
if(name!=null) q.setString("name",name);
if(age!=null) q.setInt("age",age);
return q.list();
}
public List<Student> query2(String name,int age,String address,String sex){
Student s=new Student();
s.setName(name);
s.setAge(age);
s.setAddress(address);
s.setSex(sex);
Criteria c=sess.createCriteria(Student.class);
Example e=Example.create(s);
c.add(e);
return c.list();
}
7. 连接:
sql: inner join ; outer join ; no equal join ; self join
hql: inner join --- inner join fetch(迫切内连接)
left outer join -- left outer join fetch(迫切左外连接)
right outer join
隐式连接
1)left outer join:( left join )
hql="from Company c left join c.employees e";
Query q=sess.createQuery(hql);
List<Object[]> list=q.list();
/* Company对象关系属性没有被初始化,所有每条记录
被封装成两个独立的对象
*/
sql= select c.*,e.*
from jdbc_company c left join jdbc_employee e
on c.cid=e.company_id;
result:
cid cname address eid ename age
1 kettas wdk 1 mary 18
1 kettas wdk 2 tom 19
1 kettas wdk 3 ann 20
2 IBM sd
-------------------------------------------------
hql="select c from Company c left join c.employees e";
Query q=sess.createQuery(hql);
List<Company> list=q.list();
sql= select c.*
from jdbc_company c left join jdbc_employee e
on c.cid=e.company_id;
2)left join fetch:
hql="from Company c left join fetch c.employees e";
Query q=sess.createQuery(hql);
List<Company> list=q.list();
Set<Company> set=new HashSet<Company>(list);
/* list里的每一个Company对象,所有的属性都有值,
包括关系属性也被初始化,并且里面存有查回来的
Employee对象
*/
sql= select c.*,e.*
from jdbc_company c left join jdbc_employee e
on c.cid=e.company_id;
result:
cid cname address eid ename age
1 kettas wdk 1 mary 18
1 kettas wdk 2 tom 19
1 kettas wdk 3 ann 20
2 IBM sd
3)隐式连接: 如:写hql语句时,hibernate会自动的使用sql对数据库进行查找。
hql=" select e from Employee e
where e.company.cname='kettas'";
sql= select e.*
from jdbc_employees e,jdbc_company c
where e.company_id=c.cid and c.cname='kettas'
8. 组函数
sql: avg() sum() max() min() count()
hql: avg() sum() max() min() count()
需求:打印出地区名字,以及该地区公司的个数
hql=" select c.address,count(*)
from Company c
group by c.address
having count(*)>1 ";
hql=" select e.dept_id,avg(salary)
from Employee e
group by e.dept_id";
9 子查询:
1)相关: 请查询员工数量大于一的公司
hql=select c from Company c
where 1<( select count(*) from c.employees e );
2)无关: 查询工资高于平均工资的员工对象
hql=select e1 from Employee e1
where e1.salary>( select avg(e2.salary) from Employee e2);
3)all(查询出的所有记录)
some/any/in(任意一条记录)
打印出公司所有员工年龄大于30的公司
hql="select c from Company c
where 30< all(select e.age from c.employees e)";
打印出公司有员工年龄大于30的公司
hql="select c from Company c
where 30< any(select e.age from c.employees e)";
10.投影查询:
hql="select c.cname,c.address,e.ename,e.age
from Company c left join c.employees e";
Query q=sess.createQuery(hql);
List<Object[]> list=q.list();
--->Object[] o1={"kettas","wdk","tom",18};
result:
cname address ename age
kettas wdk tom 18
kettas wdk mary 19
IBM sd
------------------------------------------------
packate hibernate.test;
class UserData{
private String cname;
private String address;
private String ename;
private int age;
public UserData(String cname,String address,String ename,int age){
this.cname=cname;
this.address=address;
this.ename=ename;
this.age=age;
}
}
hql=select new hibernate.test.UserData(c.cname,c.address
,e.ename,e.age)
from Company c left join c.employees e;
Query q=sess.createQuery(hql);
List<UserData> list=q.list();
UserData在session中没有数据备份,是把零散的数据从数据库中拿回来自己现封装的对象。
11.集合过滤:
Company c=sess.get(Company.class,18);
c.employees没有初始化,默认lazy=true
取回c的所有员工
Set<Employee> es=c.getEmployees();
sql="select * from jdbc_employee where company_id=c.getId()
取回的是c的年龄大于30的员工
1)hql="from Employee e where e.company=:com and e.age>30";
Query q=sess.createQuery(hql);
q.setEntity("com",c);
List<Employee> e=q.list();
2)Query q=sess.createFilter(c.getEmployees(),"where this.age>30");
List<Employee> es=q.list();
12. batch update
1)sess
a. List<Employee> list=sess.createQuery("from Employee").list();
b. for(int i=0;i<list.size();i++){
if(i%100==0){
sess.flush();
sess.clear();
}
list.get(i).setName("hello"+i);
}
ta.commit();
2) hql="update Employee e set e.ename=:name";
Query q=sess.createQuery(hql);
q.setString("name","hello");
q.executeUpdate();
3) hql="delete Employee e where e.eid>20";
13. SQL语句是对数据库的表操作,HQL语句是对类的操作。
以下是要掌握的内容:
oracle: create table , create sequence
select update delete insert
jdbc: 基本步骤
jdbcUtil
dao 的概念
Transaction
association(one-to-one)
hibernate: mapping file
Session
1)标签 2)三种状态 3)Session(集合)
4)association 5)Inheriatance(one table)
6)hql
hibernate.cfg.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-configuration PUBLIC
"-//Hibernate/Hibernate Configuration DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
<!--====> 配置文件 <==== -->
<session-factory>
<!--<< 配置事务实现类 ,下面可以不写, 默认情况下为JDBC>> -->
<property name="transaction.factory_class">
org.hibernate.transaction.JDBCTransactionFactory
</property>
<!--<< 配置JDBC里Batch的大小 >> -->
<property name="jdbc.batch_size">50</property>
<property name="cache.use_second_level_cache">false</property>
<!--<< 获得线程安全的Session >> -->
<property name="current_session_context_class">thread</property>
<!--<< 运行时,是否显示SQL语句 >> -->
<property name="show_sql">true</property>
<property name="format_sql">true</property>
<!--<< 配置数据库方言 >> -->
<property name="dialect">org.hibernate.dialect.Oracle9Dialect</property>
<!--<< 配置数据库连接 >> -->
<property name="connection.driver_class">oracle.jdbc.driver.OracleDriver</property>
<property name="connection.username">cksd0804</property>
<property name="connection.password">cksd0804</property>
<property name="connection.url">jdbc:oracle:thin:@192.168.0.200:1521:ORADB10G</property>
<!--<< c3po 配置连接池 >> -->
<property name="c3p0.max_size">2</property>
<property name="c3p0.min_size">2</property>
<property name="c3p0.timeout">5000</property>
<property name="c3p0.max_statements">100</property>
<property name="c3p0.idle_test_period">3000</property>
<property name="c3p0.acquire_increment">2</property>
<property name="c3p0.validate">false</property>
<!-- << 指定(hibernate管理的)映射文件 >> -->
<!--<mapping resource="day1/Users.hbm.xml"></mapping>-->
<!-- << 当用tomcat服务器配置的连接池时用下面代替上面的连接 >>-->
<!--<property name="hibernate.connection.datasource">java:comp/env/oracle/ds</property> -->
<!--该路径是相对于hibernate.cfg.xml的路径 (相对路径)-->
<!--<mapping resource="orm/o2o/fkuk/bi/Car.hbm.xml"></mapping> -->
<!--<mapping resource="orm/o2o/fkuk/bi/License.hbm.xml"></mapping> -->
<!--<mapping resource="orm/o2o/fkuk/ui/CarLicense.hbm.xml"></mapping>-->
<!--<mapping resource="orm/o2m/bi/CompanyEmployee.hbm.xml"></mapping>-->
<mapping resource="orm/m2m/bi/StudentCourse.hbm.xml"></mapping>
</session-factory>
</hibernate-configuration>
Hibernate 的工作原理-------------------------à
HTML&JavaScript
Html:==================================================================================
Head相关
Meta 描述属性
Style 样式
Script 脚本
字体相关
h1 ... h6 字体的大小 一次变小
p 段落标签 换行且有段间距
pre 预备役标签,保留你的输出
font --> size | color 字体变化,设置大小和颜色
b | strong 加粗
center 居中
em 斜体,着重显示,不同的显示器,着重显示不一样
br 换行符
hr 分割线
图片/超链接
图片 img src(名字) | width height(宽高) | alt (图片找不到的时候显示的东西) | align(控制写在图片之后文字的位置 bootom 下方middle中间) | border (控制图片边框)
超级连接a href(连接目标的url) | name(锚点名) | target (4个备选 _new第一次连接开一个新窗口,第2,3次在同一个窗口里刷新 _self自己窗口里打开 _blank连接打开一个新窗口,点几次开几个窗口 _top(我在最顶端,把frameset框架替换了,要代替真个框架显示出来))href=“#”表示本页面
表格
table (tbody) border | bordercolor | bgcolor | cellsapcing | width height | align
|- tr
|- td colspan | rowspan 和并列| 合并行
表单
form --> method | action
|- input --> type : text | password | checkbox | radio | button | submit | reset | hidden
|- select
|- option
|- textarea --> cols rows
name=value
框架
frameset ---> 不允许出现body ---> rows cols
|- frame ---> name | src
C/S B/S
web 1.0 ---> 发布式体系
web 2.0 ---> 强调用户参与 ---> blog
==================================
internet ---> 铺路
web ---> 车
http --- 底层传输层协议 来自于ARPA
---> 无状态协议
---> 一次性连接
---> 80端口
HTML ---> 标识性语言 --- 被浏览器解释
Active X
W3C制定的
<body xxx="xxx">
xxxx
</body>
---------------------
<html>
<head> ---->
</head>
<body>
</body>
</html>
字体相关标签
<h1 --- h6>
<p> <pre>
<font size color>
<center>
<br><hr>
<b><strong>
图片\超链接
<img>
|- src
|- width height
|- alt
|- align
<a>
|- target ---> _new _self _blank _top 如果不加_表示一个frameset的name
|- href
|- name
表格
<table> --> border | bordercolor | width / heigth | align | cellspacing
|- <tr>
|- <td> ---> colspan | rowspan
表单
<form>
|- method
|- get
参数以URL方式进行提交
参数用来提交给服务器看,需要服务器端主动拿取
缺点: 不安全
URL长度有限制
编码问题
|- post
参数包含在请求体中提交
服务器端直接从请求中获得数据
|- action
file:///C:/Documents%20and%20Settings/user/桌面/html/form.html
? --> 连接符 表示后面提交参数
userName=kettas
& --> 参数连接符用于多个参数分隔
password=123456
&radio1=hibernate
&heshang=JinChuangGuang
&heshang=SunShuai&sel=tianjin
<input>
</form>
框架<frameset>
<html>
<frameset rows="15%, 70%, 15%">
<frame name="title" src="title.html">
<frameset cols="15%,*">
<frame name="leftBar" src="leftBar.html">
<frame name="main" src="main.html">
</frameset>
<frame name="footer" src="footer.html">
</frameset>
</html>
Inputàtype:text文本|password|checkbox多选框||radio单选框|botton|submit|reset
Select下拉列表 option标签
Textarea
==================================================================================================
1) Css (Cascading Style Sheets)什么用
优点: 1 避免传统html标签过于繁琐的步骤
2 比传统html标签更灵活
3 可重用性
<head>
<style type="text/css">
h1.rb, h2.rb{
color : red;
background-color : black ;
}
h1.gy{
color :green ;
background-color : yellow;
}
#p1{
color : red;
}
.mylayout{ //一般用点号“。”后面是css的名字
color:red;
}
</style>
</head>
<body>
<h1 class="rb">Hello Css World!</h1>
<h1 class="gy">Have a nice day!</h1>
<h1 class="rb">Good luck & Have fun!</h1>
<h2 class="rb">this is h2 line</h2>
<h3 class="rb">this is h3 line</h3>
<p id="p1">this is p line</p>
<textarea class=”mylayout”/> 用class调用css
2) selector{
property : value;
}
3) 外部引入css 使用的时候和内部一样
1) <style type="text/css"></style>
2) <link rel="stylesheet" type="text/css" href="…/home.css"/>
3) nested
内部标签使用 <h1 style="color:blue">this is 3 line</h1>
4,常用的css相关标签
文本相关
text-align : left | center | right
text-indent : 10px ;
color : 制定文字颜色
字体相关
font-family : 字体:
“times new Roman" , times , serif ....
font-weight : normal , bold , lighter , bolder
font-size : xxxx
% , smaller , larger .font-size:10pt}
.s1 {font-size:16px}
.s2 {font-size:16pt}
.s3 {font-size:80%}
.s4 {font-size:larger}
.s5 {font-size:xx-large}
letter-spacing : 字符间隔
word-spacing : 单词间隔
.p1 {text-indent: 8mm}
.d1 {width:300px}
.p2 {text-indent:50%}
颜色背景
background-color :
background-image:url( images/xxxx.jpg )---背景图片
background-repeat : 决定显示方式。
repeat : 重复显示
repeat-x : 左上方开始横向重复显示。
repeat-y : 纵向重复显示
*no-repeat : 只显示一次 。
关于div span
边框相关
border : style width color
style : 边框的种类.
none , hidden , dotted , dashed, solid , double, groove , ridge, inset , outset
border-style :
border-top-style :
border-left-style :
border-right-style:
border-bottom-style :
border-width:
border-top-width:
border-left-width:
border-right-width:
border-bottom-width:
border-color :
border-top-color:
border-left-color:
border-right-color:
border-bottom-color:
高度,宽度 :
width : height :
补白:变相的缩进
padding:
padding-top
padding-right
padding-left
padding-bottom
margin:
margin-top
margin-right
....................
JavaScript_1:============
动态网页技术
---> 服务端技术
---> 客户端技术
javascript 基于解释性的语言
动态网页:
服务器端动态
客户端动态
减少服务器压力
功能受浏览器控制 需要看浏览器支持什么
========= 词法特性 =================
采用unicode字符集,但是仅限于注释和字符串变量值, 变量和函数的标识符不能使用
Unicode字符集。
基本特征 :
变量没有数据类型 。
JAVA:
int a = 123;
String b = "123";
Javascript :
var a = 123;
var b = "123";
基本程序控制和java一样。
==============================================
数据类型和值 :
弱数据类型 设计的比较简单 随着功能愈加强大 已经成为了一个缺陷
在程序中,变量的值可以是 :
三种基本数据类型 :
数字: 123, 123.22
文本字符串: "zhongguo" ,"中国", '123'
boolean类型: true | false
非0 和 0 非null| null
除基本数据类型以外, javascript 还支持复合类型 :
Object( 对象 ) , Array( 数组 )
boolean :
boolean 的其他表示方法 :
1 , 0 和 非0 值 。
2, 空 和非空 。
特殊数据类型:null 和 undefine (未定义的).
javascript 是 弱数据类型的语言,其变量没有数据类型。
所有变量声明时都使用 var 类型 。 而且统一变量可分别
存储不同类型的值
var a = 123;
a = "123";
var a = 1;
var b = "2";
var c = a + b ; "12"
将javascript代码引入到Html中
1,代码直接嵌入
<script language="javascript">
......
......
</script>
2, 引入外部文件
<script type="text/javascript" src="js/functions.js"></script>
<link rel="stylesheet" type="text/css" href="*.css">
javascript简单交互手段
alert( "" );
document.write( "" ) ;
只有function里才算局部变量
If、for里都不算
创建并使用对象 。
1,
var obj = new Object();
obj.name = "zhangsan" ;
obj.age =123 ;
obj.email = "[email protected]" ;
属性的两种访问方式:
alert( obj.name ) ;
alert( obj["name"] ) ;
本质:多个属性的集合
缺点:不严谨
var obj = { name : "zhangsan" , age : 24 , email : "[email protected]" } ;
alert( obj.gender ) ;
for循环
第一种
for (i = 0; i <= 5; i++)
{
document.write(i)
document.write("<br>")
}
第二种使用 for...in 循环语句
for...in 循环中的循环计数器是一个字符串,而不是数字。它包含了当前属性的名称或者表示当前数组元素的下标。
<script type="text/javascript">
// 创建一个对象 myObject 以及三个属性 sitename, siteurl, sitecontent。
var myObject = new Object();
myObject.sitename = "布啦布啦";
myObject.siteurl = "blabla.cn";
myObject.sitecontent = "网页教程代码图库的中文站点";
//遍历对象的所有属性
for (prop in myObject) prop是数组元素的下标和java种
{ 的foreach有不同的含义
document.write("属性 '" + prop + "' 为 " + myObject[prop]);
document.write("<br>");
}
创建并使用数组。
1,
var arr = new Array();
var[0] = 1 ;
var[1] = 3 ;
2,
var arr = [ 1,2,3,4,5,6 ] ;
3,
var arr = [ 1,,,,6] ;
4,
var arr = [ 1, 4.4, "sd" , true] 类似一个集合
不需要指定数组长度
使用变量 :
变量需要先声明,后使用 。
未付值的变量初始值是undefine .
重复声明 :
使用 var 重复声明是合法的. 如果重复声明中有初始值的话, 则相当于付值
语句 , 没有初始值的话,变量保留以前的值 .
遗漏声明 :
如果使用了一个未声明的变量 , javascript会对这个变量作隐式声明。
但是所有隐式声明的变量,都会成为全局变量,即使声明是发生在函数体
之内的 。
函数声明和使用 :
function name( a , b , c ) {}
支持内联函数 :
function a(){
function b();
b();
}
内联函数只能在作用域内使用。
变量作用域 :
在javascript中不存在块作用域, 声明在块中的变量,在块的外面一样可以使用
if(){
var a = 10 ;
}
alert( a ) ; //合法 。
作为数据的函数 :
function a( x, y ){ .... }
var b = a ;//用变量存函数;
b( 1 , 2 ) ;//相当于a(1,2);
思考:
var student = new Object();
student.name = "zhangsan";
通过构造函数创建函数 。
var a = new Function( "a" , "b" , "return a + b " );
a , b , 新建对象的参数名称 , 如果有多个可以依次填入 :
new Function( "a" , "b" , "c" , ... " return a + b + ... + n ; " ) ;
调用 : a( 10 , 20 ) ;
通过函数直接量:
var a = function ( x , y ){ return x + y ; }
参数数量验证:arguments.length
变量作用域:
不存在块作用域注意这里所说的块并不是函数块
window.parent
JavaScript_2:==================
javascript中的常见事件 :
一般性事件:
onclick 单击事件
ondblclick 双击事件
onmouseomove 鼠标移动
onmouseover 鼠标移入
onmouseout 鼠标移出
onmousedown 鼠标键按下
onmouseup 鼠标键松开
适用 几乎全部的可显示元素(标签) 。
页面相关事件:
onload : 页面加载时触发。即把页面所有的东西都下载完时触发 <body>
onscroll : 页面滚动时触发。 <body>
onstop : 按下stop按钮时触发。 <body>
onresize : 调整大小时触发 。 <body>
onmove : 窗口移动时触发。 <body>
表单相关事件:
onblur : 当前元素失去焦点时触发。 <input>
onchange : 当前元素失去焦点,并且值发生变化时触发。<input>
onfocus : 当前元素获得焦点时触发,指光标移到当前处,即获得焦点 。 <input>
onsubmit : 表单被提交时触发 <form onsubmit=”return tes()”>
==============================================
DOM : 是W3C提供的一组规范, 可以在独立于平台的前提下修改文档的内容和结构。
DOM 将文档的内容封装到对象及对象的属性和关系中 。
通过操作DOM对象及对象的属性,达到修改文档内容及结构的目的 。
DOM里有各种方法,用于修改文档内容和结构;
可以将DOM理解为文档内容的树状表示法 。
<table>
<tbody>
<tr><td>zhangsan</td><td>20</td></tr>
<tr><td>lisi</td><td>21</td></tr>
</tbody>
</table>
用于遍历XML文档的DOM方法:
document.getElementById( "" ) XMLElement
document.getElementsByTagName( "name" ) array
用于处理XML文档的DOM属性 :
childNodes Array //返回的是一个数组
firstChild XMLElement //第一个子标签
lastChild XMLElement //最后一个子标签
nextSibling XMLElement //同级的下一个标签
previousSibling XMLElement //同级的上一个标签
parentNode XMLElement //直接父标签
通过 "." 访问element属性 。
document对象为DOM的内置对象,代表XML文档的根
在HTML文件中可以理解为body标签 。
document.createElement( "div" ) ;
document.createTextNode( "text" ) ; 创建文本
var txtA = document.createTextNode("hello"); //创建文本内容
var colA = document.createElement("td"); //创建标记
colA.appendChild(txtA); //添加子标记
element.getAttribute( Name ) ;
element.setAttribute( "name" , value ) ;
element.appendChild()
element.insertBefore( newNode , targetNode ) ;
element.removeAttribute( node )
element.removeChild( node ) ;
element.replaceChild( newNode , oldNode ) ; //用newNode替换oldNode
element.hasChildnodes()
浏览器差异。
1) table 和 tbody
2) 设置属性 ff element.setAttribute( "name" , "value" ) ;
ie element.name = value
3
设置css ff element.setAttribute("style","color:blue" ) ;
ie element.style.cssText = "color:blue" ;
ff element.setAttribute("class","xxx" ) ;
ie element.className ;
1 变量没有数据类型 ---> 值有
基本数据类型 :
数字 | 字符串 | boolean
|- 0,非0 | null 和非null
复合类型:
数组(没有固定长度,可以装任何类型的值)
对象(只有属性没有方法)
2 变量可以重复声明
3 变量如果没有经过声明,系统会自动对其做隐式声明(作为全局变量)
4 function 函数名(a, b){}
5 通过构造方法创建函数: var fun = new Function("");
通过函数直接量创建函数: var fun = function(){}
6 作为变量函数
var fun;
function testFun(a, b){
return a+b;
}
fun = testFun;
fun(1,2);
7 事件句柄:onclick……
8 DOM ---> getElementById
getElementsByTagName
9各种表单验证=====================
1) 两次输入密码是否相同
<FORM METHOD=POST ACTION="">
<input type="password" id="input1">
<input type="password" id="input2">
<input type="button" value="test" onclick="check()">
</FORM>
<script>
function check(){
with(document.all){
if(input1.value!=input2.value) {
alert("false")
input1.value = "";
input2.value = "";
}
else document.forms[0].submit();
}}
</script>
2)6. 验证油箱格式
<SCRIPT LANGUAGE=javascript RUNAT=Server>
function isEmail(strEmail) {
if(strEmail.search(/^w+((-w+)|(.w+))*@[A-Za-z0-9]+((.|-)[A-Za-z0-9]+)*.[A-Za-z0-9]+$/) != -1)
return true;
else alert("oh");
}
</SCRIPT>
<input type=text onblur=isEmail(this.value)>
3)表单项不能为空
<script language="javascript">
function CheckForm(){
if (document.form.name.value.length == 0) {
alert("请输入您姓名!");
document.form.name.focus();
return false;
}
return true;
}
</script>
4) 比较两个表单项的值是否相同(密码比较)
<script language="javascript">
function CheckForm()
if (document.form.PWD.value != document.form.PWD_Again.value) {
alert("您两次输入的密码不一样!请重新输入.");
document.ADDUser.PWD.focus();
return false;
}
return true;
}
</script>
servlet
Servlet是位于Web 服务器内部的服务器端的Java应用程序,与传统的从命令行启动的Java应用程序不同,Servlet由Web服务器进行加载,该Web服务器必须包含支持Servlet的Java虚拟机。
* 它们不是独立的应用程序,没有main()方法。* 它们不是由用户或程序员调用,而是由另外一个应用程序(容器)调用。
* 它们都有一个生存周期,包含init()和destroy()方法。
要使用Servlet,必须导入包servlet-api.jar。这里使用的服务器是Tomcat,其主目录结构为 :
|- bin : 用于存放可执行命令( catalina.sh )
|- conf : 用于存放tomcat需要的配置文件.
|- lib : 存放tomcat在运行时要用到的类文件(jsp-api.jar、servlet-api.jar、...).
|- webapps : 最重要的一个目录,其下放置各种Servlet文件、网页文件(JSP HTML ...)、
配置文件以及资源文件.此目录的结构 :
|- 应用目录(比如是一个学生管理网站,就在webapps文件夹下建一个StudentManage目录)
|- WEB-INF目录
|- classes目录, 放置所有的Servlet文件或其他类文件
|- lib目录, 放置本应用所要用到的类文件(jar包)
|- web.xml 配置文件
|- 资源文件(比如图片), 网页文件(JSP HTML ...)
|- logs : 日志文件 .
|- work : 内部临时文件.
|- temp : 临时文件.
安装tomcat 环境变量的配置,和配jdk差不多servlet-api.jar;jsp-api.jar
1 Servlet接口、GenericServlet类、HttpServlet类 :
Servlet是最顶层的接口,其提供的方法有 :
init(ServletConfig config) : void // 初始化
getServletConfig() : ServletConfig // 取得该Servlet配置信息
getServletInfo() : String // 取得相关信息
service(ServletRequest req, ServletResponse res) : void //核心方法
destroy() : void // Servlet生命周期结束时候执行的方法
显然我们最关心的是service方法,其他的几个方法在实现的时候是千篇一律、无关痛痒的。故提
供了GenericServlet类,此类实现了Servlet接口,我们在使用Servlet的时候,只需继承这个类然后
覆盖其中的service方法(抛出ServletException、IOException异常)即可。
由于Servlet基本上是在http协议下使用的,故提供了HttpServlet这个类,此类继承自
GenericServlet类,我们在使用Servlet时,只需继承HttpServlet类然后覆盖以下方法 :
service( HttpServletRequest request ,
HttpServletResponse response )
throws ServletException , IOException : void
注意:HttpServletRequest和HttpServletResponse分别是从ServletRequest和ServletResponse继承
此外,HttpServlet还提供了doPost和doGet方法,参数和返回值与service方法一样。只是service
方法可以针对客户端的任何请求类型(GET和POST),而doPost和doGet方法分别只能对应客户端的POST方式
请求和GET方式的请求。
HttpServlet-----àextends GenericServlet ----à implements Servlet
2 使用GenericServlet实例 :
package com.kettas.servlet;
import javax.servlet.* ;
import java.io.* ;
public class GenDateServlet extends GenericServlet{
@Override
public void service( ServletRequest request , ServletResponse response )
throws ServletException ,IOException
{ response.setContentType( "text/html" ) ; // 设置响应内容类型
PrintWriter out = response.getWriter();// 获得文本写入流
// 给客户端回应的html文本
out.println( "<html>" ) ;
out.println( "<body>" ) ;
out.println( "<h1>Hello Servlet !</h1>" );
out.println( "</body>" ) ;
out.println( "</html>" ) ;
out.flush();// 刷新写入
}}
配置文件web.xml如下 :
<?xml version="1.0" encoding="UTF-8"?>
<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">
// Servlet文件路径
<servlet>
<servlet-name>query</servlet-name>
<servlet-class>com.kettas.servlet.GenDateServlet</servlet-class>
</servlet>
// 指定映射,说明在浏览器中输入".../query"则对应当前Servlet
<servlet-mapping>
<servlet-name>query</servlet-name>
<url-pattern>/query</url-pattern>
</servlet-mapping>
</web-app>
3 使用HttpServlet简单实例 :
package com.kettas.servlet ;
import javax.servlet.* ;
import javax.servlet.http.* ;
import java.io.* ;
public class LoginServlet extends HttpServlet{
@Override
public void service( HttpServletRequest request , HttpServletResponse response )
throws ServletException , IOException
{ response.setContentType("text/html; charset=GB2312"); // 注意设置编码的方式
request.setCharacterEncoding("GB2312");
PrintWriter out = response.getWriter();
// 取得客户端提交的数据
String name = request.getParameter( "userName" ) ;
String pwd = request.getParameter( "password" ) ;
out.println("<html>");
out.println("<body>");
out.println("<h1>");
out.println("Name : " + name + " " + "Password : " + pwd);
out.println("</h1>");
out.println("</body>");
out.println("</html>");
out.flush();
}}
配置文件web.xml片段 :
<servlet>
<servlet-name>login</servlet-name>
<servlet-class>com.kettas.servlet.LoginServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>login</servlet-name>
<url-pattern>/login</url-pattern>
</servlet-mapping>
4 请求转发 :
实现不同servlet之间的数据传递,这样便可实现业务逻辑和显示逻辑的分离
实例 :
(1) 第一个servlet,负责业务
package com.kettas.servlet ;
import javax.servlet.* ;
import javax.servlet.http.*;
import java.io.*;
import java.util.* ;
public class ForwardA extends HttpServlet{
@Override
public void service( HttpServletRequest request , HttpServletResponse response )
throws ServletException , IOException
{ System.out.println( "=== This is forward A ===" ) ;
// 将业务部分的数据存储在request对象中,传递给下一个servlet使用
Date d = new Date();
request.setAttribute( "date" , d ) ;
/* 注意转发的过程
* 首先获得一个知道下一棒地址的"接力棒"对象,然后将这个"接力棒"传给下一个
* servlet,这样便将请求转发了。
*/
RequestDispatcher disp = request.getRequestDispatcher( "/forwardB" ) ;
disp.forward( request , response ) ;
}
}
注意 : 1 这种请求转发的方式是共用一个连接的,不管你中途经过了多少个servlet,正因如此,
这些servlet才能共享request中存储的数据。
2 只有最后一个servlet,才能在客户端浏览器中显示。
(2) 第二个servlet,负责显示
package com.kettas.servlet ;
import javax.servlet.* ;
import javax.servlet.http.*;
import java.io.*;
import java.util.* ;
public class ForwardB extends HttpServlet{
@Override
public void service( HttpServletRequest request , HttpServletResponse response )
throws ServletException , IOException
{ response.setContentType( "text/html" );
PrintWriter out = response.getWriter();
out.println( "<h2>This is forwared B</h2>" ) ;
// 通过getAttribute方法,从request中取得数据
// 由于此方法返回的是Object对象,故要强转
Date d = (Date)request.getAttribute( "date" ) ;
out.println( "<h2>" + d + "</h2>" ) ;
System.out.println( "=== This is forward B ===" ) ;
out.flush();
}
}
(3) web.xml片段 :
<servlet>
<servlet-name>a</servlet-name>
<servlet-class>com.kettas.servlet.ForwardA</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>a</servlet-name>
<url-pattern>/forwardA</url-pattern>
</servlet-mapping>
<servlet>
<servlet-name>b</servlet-name>
<servlet-class>com.kettas.servlet.ForwardB</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>b</servlet-name>
<url-pattern>/forwardB</url-pattern>
</servlet-mapping>
页面跳转的两种方式 1)request.getRequestDispatcher(“/forwardA”).forward(request , response); 这种跳转是在服务器内部是servlet之间跳转,显示的总是最后一个servlet A-àB--à----àD
2)response.sendRedirect(“mayservlet/query”) 它其实是在客户端的url发生改变,相当一次新的请求,故不能传递数据,但能在不同的应用中跳转
5 关于Cookie, 在客户端浏览器保存用户状态的一种机制
servlet中的Cookie含有三个属性: name, value, maxAge
maxAge = 60 表示:此cookie在客户端存在1分钟
两个特殊值:
maxAge = -1 表示 : 此Cookie生命周期由保存它的浏览器决定 ,(浏览器开则生,关则死),默认的
maxAge = 0 表示 : 删去以前的相应cookie存储
Cookie应用实例 :
package com.kettas.servlet ;
import javax.servlet.*;
import javax.servlet.http.*;
import java.io.*;
public class CookieServlet extends HttpServlet{
@Override
public void service( HttpServletRequest request , HttpServletResponse response )
throws ServletException , IOException
{// 创建一个新的Cookie对象, 构造参数分别为Cookie的name和value属性
Cookie c = new Cookie( "test" , "1234567890" );
// 将Cookie对象加入response中,这样才能被带入客户端
response.addCookie( c ) ;
// 从请求中获取客户端Cookie数组
Cookie[] cookies = request.getCookies();
response.setContentType( "text/html" );
PrintWriter out = response.getWriter();
out.println("<html>");
out.println( "<body>" ) ;
out.println( "<h1>Cookie List</h1><hr/><p></p>" ) ;
if( cookies != null ){
for( Cookie cookie : cookies ) {
out.println( "<h2>" + cookie.getName() + "=" + cookie.getValue() + "</h2>" ) ;
}
}else{
out.println( "<h2>No cookie</h2>" ) ;
}
out.println( "</body>" ) ;
out.println("</html>");
out.flush();
}
}
6 关于HttpSession, 在服务器端保存用户状态的一种机制
(1) 获取HttpSession对象的方法 :
// 参数为true,表示若存在对应的HttpSession对象,则返回。若不存在,则创建一个新的。
// 若参数为false,表示若存在对应的HttpSession对象,则返回。若不存在,则返回null。
HttpSession session = request.getSession(true);
(2) 对HttpSession对象, 进行存取数据的操作
// 两个参数,分别为命名属性和对应的数据
session.setAttribute("name", data);
// 一个参数,命名属性,注意返回的为Object对象,要强转
session.getAttribute("name");
(3) 比较Session和request :
request :
创建 : 当用户请求到达服务器的时候
销毁 : 当本次请求的应答回到客户端的时候.
客户端的一次请求应答之间
session :
创建 : 当用户第一次调用request.getSession( true )
销毁 : 超时 ( 两级超时限制 )
1) 内存 ---> 文件 .
2) 从文件系统销毁 .
session的原理 :
给每个浏览器一个cookie,这个cookie的name属性为"jsessionid",value属性为这个session
对应的ID值。
(4) 当浏览器拒绝cookie时可以用URL把session的id提交给服务器
如 : http://localhost:8989/servletapp/forwardB;jsessionid=37D50D093CCD4A37CC1118785E38F438
"url;jessionid="+ session.getId()
response.encodeURL("url") :对url进行编码
7 ServletConfig对象和ServletContext对象
(1)ServletConfig : 用来保存一个Servlet的配置信息的(比如 : name, class, url ... )
这些配置信息没什么大用处,我们还可以在ServletConfig中保存自己在web.xml文件中定义的数据
此时的web.xml文件片段如下 :
<servlet>
<!-- 自己定义的,要保存在ServletConfig对象中的数据 -->
<init-param>
<param-name>jdbc.driver</param-name>
<param-value>oracle.jdbc.driver.OracleDriver</param-value>
</init-param>
<init-param>
<param-name>jdbc.user</param-name>
<param-value>yinkui</param-value>
</init-param>
...
<servlet-name>query</servlet-name>
<servlet-class>com.kettas.servlet.Query</servlet-class>
</servlet>
在Servlet中取得这些数据 :
// getServletConfig方法继承自父类GenericServlet
ServletConfig sc = this.getServletConfig();
// 显然,getInitParameter方法返回的只能是字符串类型数据
String driver = sc.getInitParameter("jdbc.driver");
String user = sc.getInitParameter("jdbc.user");
注意: 1 ServletConfig对象只能从web.xml文件中获取自定义数据(字符串数据),不存在setAttribute
方法去存入自定义数据。
2 在Servlet中,若要覆盖父类的init(ServletConfig config)方法,必须这么做 :
public void init( ServletConfig config ){
// 覆盖之前调用父类的这个方法, 否则ServletConfig对象会丢失
// 此时this.getServletConfig()返回的是null, 那样我们就不能使用它了
super.init( config ) ;
... }
(2)ServletContext : 用来保存数据的全局唯一对象,一个应用中只有一个ServletContext对象
1 : 通过web.xml文件,在ServletContext对象中存入数据
此时的web.xml文件片段如下所示 :
<!-- 在此处写入我们要存入ServletContext对象中的数据 -->
<context-param>
<param-name>jdbc.driver</param-name>
<param-value>oracle.jdbc.driver.OracleDriver</param-value>
</context-param>
<context-param>
<param-name>jdbc.url</param-name>
<param-value>jdbc:oracle:thin:@192.168.0.201:1521:kettas</param-value>
</context-param>
...
<servlet>
<servlet-name>...</servlet-name>
<servlet-class>...</servlet-class>
</servlet>
取得其中的数据 : String driver = servletContext.getInitParameter("jdbc.driver");
2 : 通过setAttribute方法,在ServletContext对象中存入数据
servletContext.setAttribute("name", data); // 两个参数分别为命名属性以及对应的数据
// 取得ServletContext对象中的数据, 参数为命名属性
// 返回的是Object对象, 故要强转
servletContext.getAttribute("name");
3 : 取得ServletContext对象的三种方法(this指代当前Servlet)
(1) ServletContext sc = this.getServletContext();
(2) ServletContext sc = this.getServletConfig().getServletContext();
(3) ServletContext sc = request.getSession(true).getServletContext();
ServletContext对象的一个重要方法 :
InputStream is = sc.getResourceAsStream( "fileName" ) ;
fileName : 使用的是虚拟目录, 不依赖于实际路径/books/ajax.pdf
最左边一个"/" : web 应用的根目录
// 获得实际路径 String path = ctx.getRealPath( "/books/ajax.pdf" )
8 监听事件和过滤器
监听包括三种情况,分别是HttpRequest、Session、ServletContext监听。
常用的是implements servletContextListener(全局变量)两个方法 public void contextInitialized(ServletContextEvent arg0)
arg0.getServletContext()
Session监听事件所示 :
import javax.servlet.http.HttpSession;
import javax.servlet.http.HttpSessionEvent;
import javax.servlet.http.HttpSessionListener;
import com.kettas.upp02.util.Constant;
public class SessionListener implements HttpSessionListener {
public void sessionCreated(HttpSessionEvent ent) {
HttpSession session = ent.getSession();
synchronized (this) {
ServletContext ctx = session.getServletContext();
Integer counter = (Integer) ctx.getAttribute("sessionCount");
ctx.setAttribute("sessionCount", counter.intValue() + 1);
System.out.println(Constant.LOGO + "SessionCount:"
+ (counter.intValue() + 1));
}}
public void sessionDestroyed(HttpSessionEvent ent) {
HttpSession session = ent.getSession();
synchronized (this) {
ServletContext ctx = session.getServletContext();
Integer counter = (Integer) ctx.getAttribute("sessionCount");
ctx.setAttribute("sessionCount", counter.intValue() - 1);
System.out.println(Constant.LOGO + "SessionCount:"
+ (counter.intValue() - 1));
}
}}
在web.xml文件中配置如下 :
<listener>
<listener-class>shop. SessionListener </listener-class>
</listener>
其他两个监听事件的实现同上并无二致。
过滤器 // 实现Filter接口
import java.io.IOException;
import javax.servlet.*;
public class EncodingFilter implements Filter{
//销毁时执行,没必要覆盖
public void destroy() {}
//发送请求时执行
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
//设置发送请求和接收请求时的编码方式,统一才能达到过滤作用
request.setCharacterEncoding("UTF-8");
response.setCharacterEncoding("UTF-8");
try {
chain.doFilter(request, response); 请求转发
} catch (RuntimeException e) {
e.printStackTrace();
}}
//加载时执行,也没必要执行
public void init(FilterConfig arg0) throws ServletException {}
}
web.xml文件中:
//配置当发生什么要的请求时,让那个过滤流执行操作
<filter>
<filter-name>encodingFilter</filter-name>
<filter-class>filter.EncodingFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>encodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
9,解决乱码问题 1)response.setContentType(“text/html;charset=gbk2312”)
2)requset.setCharacterEnconding(“gbk”) ------是post的时候
3)在server.xml中加URLEncoding=“gbk”------是get发送数据的时候
10,servlet的生命周期
1)Servlet在容器中运行,其实例的创建及销毁等都不是由程序员决定的,而是由容器进行控制的。
创建Servlet实例有两个时机:1客户端第一次请求某个Servlet时,系统创建该Servlet的实例:大部分的Servlet都是这种Servlet。
2,Web应用启动时立即创建Servlet实例,即load-on-startup <load-on-startup>1</load-on-startup>
Servlet的生命周期通过javax.servlet.Servlet接口中的init()、service()和destroy()方法来表示。
每个Servlet的运行都遵循如下生命周期。
(1)加载和实例化:找到servlet类的位置通过类加载器加载Servlet类,成功加载后,容器通过Java的反射API来创建Servlet实例,调用的是Servlet的默认构造方法(即不带参数的构造方法),
(2)初始化:容器将调用Servlet的init()方法初始化这个对象。初始化的目的是为了让Servlet对象在处理客户端请求前完成一些初始化的工作,如建立数据库的连接,获取配置信息等。对于每一个Servlet实例,init()方法只被调用一次
(3)请求处理:Servlet容器调用Servlet的service()方法对请求进行处理。要注意的是,在service()方法调用之前,init()方法必须成功执行
(4)服务终止:容器就会调用实例的destroy()方法,以便让该实例可以释放它所使用的资源
考点 2)从始至终只有一个对象,多线程通过线程池访问同一个servlet
Servlet采用多线程来处理多个请求同时访问,Servelet容器维护了一个线程池来服务请求。
线程池实际上是等待执行代码的一组线程叫做工作者线程(WorkerThread),Servlet容器使用一个调度线程来管理工作者线程(DispatcherThread)。
当容器收到一个访问Servlet的请求,调度者线程从线程池中选出一个工作者线程,将请求传递给该线程,然后由该线程来执行Servlet的service方法。
当这个线程正在执行的时候,容器收到另外一个请求,调度者线程将从池中选出另外一个工作者线程来服务新的请求,容器并不关系这个请求是否访问的是同一个Servlet还是另外一个Servlet。
当容器同时收到对同一Servlet的多个请求,那这个Servlet的service方法将在多线程中并发的执行。
3)、如何现实servlet 的单线程模式
<%@ page isThreadSafe=”false”%>
JSP
1 JSP 语法:
jsp中嵌入java代码的方式 :
1) 表达式标签 <%= 1 + 1 %>
a) 计算表达式的返回值 .
b) 能将返回值在网页上显示出来 .
不能出现 ";"
<%= 1+1%>则在网页上显示2
2) 声明标签 : <%! %>
用来声明变量和函数, 在声明标签中声明的变量和函数, 可以在本页面的其他的java代码中使用.
声明的位置是首是尾皆无妨. 建议尽量少声明变量, 因为jsp最终要被解释为servlet, 声明的变
量在servlet中就体现为实例变量, 在多个线程访问这个servlet的时候, 由于实例变量被多个线
程共享使用(实例变量线程不安全, 局部变量线程安全), 有可能出现问题, 并且不好解决.
3) 普通脚本 : <% %> 普通脚本是不能嵌套的
<%
for( int i = 0 ; i < 10 ; i++ ){
<%= 1+ 1 %>
}
%>
2 指令元素 : 用来说明一个jsp文件自身的一些特点.
以便服务器(tomcat)作出正确的处理 .
页面指令 :
<%@page contentType="text/html;charset=utf-8" %>
<%@page import="java.util.*" %>
标签库指令
<%@taglib %>
<%@taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%>
包含指令 :静态包含
用来包含其他jsp的源代码 (静态包含).
所谓静态包含, 就是先将引入的jsp页包含入本页面中, 然后解释为同一个servlet
<%@include file="xxx"%> 静态包含包含的是源代码,考虑变量冲突,并且xxx后面不能传参数
3 动作元素
<jsp:include page="/a.jsp" />
动态包含: 包含的是对方的输出结果, 分别解释为不同的servlet.
动态包含实例 :
(1) header.jsp :
<%
String text = request.getParameter( "text" ) ;
%>
<center>
<h1>
<font color="blue">
<%
if( text != null ){
%>
<h1><%=text%></h1>
<%
}else{
%>
<h1>Welcome to kettas</h1>
<%
}
%>
</font>
</h1>
</center>
(2) body.jsp :
<%@page contentType="text/html" %>
<html>
<body>
<!-- 相当于<jsp:include page="/header.jsp?name=This is param"/> -->
<jsp:include page="/header.jsp">
<jsp:param name="text" value="This is param"/>
</jsp:include>
<h1>This is body</h1>
<%
for(int i = 0 ; i< 3 ; i++ ){
%>
<h1><%= new java.util.Date() %></h2>
<%
}
%>
<%
for( int i =0 ; i < 3 ; i++ ){ out.println( "<h2>" + new java.util.Date() + "</h2>" ) ;
}
%>
</body>
</html>
<jsp:forward page="/b.jsp" />
页面转向 : 相当于servlet中的"接力棒"转向, 是在同一个连接中的页面转向.
<% response.sendRedirect("/a.jsp"); %>
页面转向 : 连接已经换了一个.
4 jsp 中的隐含9对象 . (可以直接拿来使用)
request ----> HttpServletRequest .
response ---> HttpServletResponse .
session ----> HttpSession .
application -> ServletContext .
|-> web.xml
|-> setAttribute, getAttribute.
|-> 全局唯一 .
以下四个用的很少, 知道有这个东西即可
out ---------> response.getWriter();<% out.println()%>
config -------> ServletConfig <在xml中也可以配置servlet,可以配置初始化参数>
exception ---> Exception
page ------> Object
相当重要的隐含对象, 重点说明之
pageContext --> javax.serlvet.jsp.PageContext
关于pageContext :
1, 本身也是一个能存储命名属性的作用域 .
setAttribute("name", data)
getAttribute("name")
pageContext 作用域和声明周期 .
声明周期只局限在本页面 .
在同一页面的不同标签之间传递数据 .(本页面共享数据)
同时保证数据不流传到其他页面上 .
2, 可以管理其他作用域中的命名属性 .
pageContext.getAttribute("name");
pageContext.getAttribute("name",int scope);
scope 值为:
PAGE_SCOPE
REQUEST_SCOPE
SESSION_SCOPE
APPLICATION_SCOPE
为了选择作用域
pageContext.setAttribute( "name" , value );
pageContext.setAttribute( "name" , value , int scope );
pageContext.findAttribute( "name" ) ;
按照从小到大的顺序依次查找作用域中的命名属性 .
pageCOntext --> request ---> session --> application
pageCOntext.findAttribute( "a" ) ;
3, 获得其他所有的隐含对象 .
pageContext.getRequest() ---> request
pageCOntext.getSession()
pageCOntext.getConfig()
pageCOntext.getOut()
注意 : 隐含对象在表达式标签和普通脚本中都可以使用
<%= request.getParameter("name") %>
<% sesison.getAttribute() %>
但是在声明脚本中不能用, 比如 :
<%!
void fn(){
session.getAtrreibute();
}
%>
5 JSP的几个页面指令 :
页面指令 : 向服务器说明页面自身的特征,以便服务器
1 <%@page contentType="text/xml;charset=utf-8" %> 客户端--->>服务端的编码
2 <%@page import="" %> 引入名字空间
3 <%@page pageEncoding="GBK/GB2312/utf-8"%>(网页的静态内容乱码想到pageEncoding,
jsp文件文本编辑器所采用的编码)--->服务器内部使用的用来指定jsp 源文件所采用的编码方式.
4 <%@page isELIgnored="false" %> EL一种表达式语言,默认值不同的服务器不一样,最好明确写出
${ 1+1 } (false:显示结果。true:显示 ${ 1+1 }源代码)
5 <%@page errorPage="/error.jsp"%>
指定错误页面 .
jsp ---> 业务代码 .
6 <%@page isErrorPage="true|false"%>当是TRUE时就会有exception的隐含对象
<%@page isErrorPage="true" errorPage="/other.jsp"%> 不能这样转
A(源页面) -------------------------> B(错误页面)
errorPage="B" isErrorPage="true"
7 <%@page session="true"%>--默认值, 表示本页面是否需要session对象.
在servlet中只有直接调用才能得到,而jsp中默认是有的
<%@page language="java"%>默认的语言
<%@page extends="XXX" %>服务器自己决定
<%@page buffer=""%> 服务器自己决定调节
6 JSP(Servlet)中从连接池获取连接
1) 建立连接 .
2) 执行SQL .
3) 处理结果 .
4) 释放资源 .
Connection pool 连接池
DataSource
LDAP ( Light directory access protocal )轻量级目录访问协议
JNDI ( java naming director interface ) Java 命名目录接口
使用连接池 :
1) 配置连接池
改配置文件 conf/context.xml
<Resource driverClassName="oracle.jdbc.driver.OracleDriver"
注:jar包应该放在外边的lib包中,因为它是给tomcat用的
url="jdbc:oracle:thin:@127.0.0.1:1521:XE"
username="kettas"
password="kettas"
maxActive="2" 最大连接数
type="javax.sql.DataSource" 连接类型
auth="Container" 一般不改
name="oracle/ds"
/>
2) 使用DataSource
用法:%@page import="javax.naming.*"%>
<%
Context ctx = new InitialContext();
DataSource ds = (DataSource)ctx.lookup("java:comp/env/oracle/ds");tomcat特点:必须加java:comp/env/*
Connection conn = ds.getConnection();
out.println( "<h1>get connection!</h1>" );
conn.close();
%>
7 处理javabean的JSP标签
(1) 关于javabean要求:
1 具有无参的构造函数 .
2 针对每一个成员变量,因改提供相应get/set
3 implments Serializable(实现才能对象序列化)
(2) <jsp:useBean />
使用一个保存在某个作用域(pagecontext , request ,session, application)中的javabean .
<jsp:useBean id="user" class="beijing.User" scope="session"/>
实际上执行的代码为 : User u = session.getAttribute("user");
id的含义 : 1, 为要使用的javabean取个名字.
2, javabean在session作用域中的命名属性.
class : java bean 的类型.
scope : 指定一个作用域(page,request,session,application), 若不指定, 则由小到大.
(3) <jsp:setProperty /> 五种设置属性的方法
<jsp:setProperty name="" property="" value=""/>
name: 被操作的javabean 的名字. 即useBean中的id
property: 属性名称 id , name , phone
value: 值
<jsp:setProperty name="" property="" param=""/>
param:表示该属性的值来自于客户端提交的参数. param用来指定这个参数的名字.
<jsp:setProperty name="" property=""/>
客户端提交的参数名称和成员变量的名称正好一致. 省略param
<jsp:setProperty name="" property="*"/>
用一个标签为所有的属性赋值, 属性的值来自于客户端提交的参数,
参数名字和属性名字 一一对应.
(4) <jsp:getProperty />
使用javabean中的属性, 可用于页面显示 .
<jsp:getProperty name="" property=""/>
name: 被操作的javabean 的名字. 即useBean中的id
property: 属性名称 id , name , phone
使用片段如下 :
<html>
<body>
<h2><jsp:getProperty name="user" property="name" /></h2>
<h2><jsp:getProperty name="user" property="id"/></h2>
<h2><jsp:getProperty name="user" property="phone"/></h2>
<h2><jsp:getProperty name="user" property="password"/></h2>
</body>
</html>
8 使用自定义的标签
(1) 构思, 比如写一个对指定名字说hello的标签, 应该是<前缀:hello user="zhagnsna"/>
(2) 写类
要实现的基础接口 : javax.serlvet.jsp.tagext.SimpleTag
其中含有五个方法 :
doTag ---> 实现标签的功能 .
setJspContext ---> pageContext .
setParent ---> 父标签的实现类
setJspBody ---> JspFragement
getParent
要实现五个方法, 显得很繁琐, javax.servlet.jsp.tagext.SimpleTagSupport类实现了这个
基础接口, 我们在写类时只需要继承这个类, 然后覆盖doTag方法即可.
类如下 :
package com.kettas.tag ;
import javax.servlet.http.*;
import javax.servlet.jsp.tagext.* ;
import javax.servlet.jsp.* ;
import java.io.* ;
public class HelloHandler extends SimpleTagSupport{
private String user ;
public void setUser( String user ){
this.user = user ;
}
public String getUser(){
return user ;
}
@Override
public void doTag() throws JspException , IOException{
PageContext ctx = (PageContext)this.getJspContext();
ctx.getOut().println( "hello " + user );
ctx.getOut().flush();
}
}
(3) 写tld配置文件(位置为 : /WEB-INF/***.tld)
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE taglib PUBLIC "-//Sun Microsystems, Inc.//DTD JSP Tag Library 1.1//EN"
"http://java.sun.com/j2ee/dtds/web-jsptaglibrary_1_1.dtd">
<taglib>
<tlibversion>1.0</tlibversion>
<jspversion>2.0</jspversion>
<shortname>mytag</shortname>
<!-- 指定命名空间 引入标签时使用 -->
<uri>[email protected]</uri>
<tag>
<!-- 标签名 -->
<name>hello</name>
<!-- 标签对应的类 -->
<tag-class>com.kettas.tag.HelloHandler</tag-class>
<!-- 标签体之中是否含有内容 -->
<body-content>empty</body-content>
<!-- 标签属性 -->
<attribute>
<name>user</name>
<type>java.lang.String</type>
<required>true</required>
<rtexprvalue>true</rtexprvalue>
</attribute>
</tag>
</taglib>
(4) 在jsp文件中使用
<%@page contentType="text/html"%>
<%-- 注意引入自定义标签的方式, prefix属性指定标签前缀, 前缀解决不同标签库标签重名 --%>
<%@taglib uri="[email protected]" prefix="liucy"%>
<html>
<body>
<h2>
<liucy:hello user='<%= request.getParameter( "name" )%>'/>
</h2>
</body>
</html>
自定义一个循环标签 :
类 : =================================================================
package com.kettas.tag ;
import javax.servlet.http.*;
import javax.servlet.jsp.*;
import javax.servlet.jsp.tagext.*;
import java.io.*;
public class LoopHandler extends SimpleTagSupport{
private int begin ;
private int end ;
public void setBegin( int begin ){
this.begin = begin ;
}
public int getBegin( ){
return begin ;
}
public void setEnd(int end ){
this.end = end ;
}
public int getEnd( ){
return end ;
}
@Override
public void doTag()throws JspException ,IOException{
// JspFragment对象可以获取标签体内部的内容
JspFragment f = this.getJspBody();
PageContext ctx = (PageContext)this.getJspContext();
for( int i = begin ; i < end ; i++){
f.invoke( ctx.getOut() );
}
}
}
配置文件如下 :=================================================================
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE taglib PUBLIC "-//Sun Microsystems, Inc.//DTD JSP Tag Library 1.1//EN"
"http://java.sun.com/j2ee/dtds/web-jsptaglibrary_1_1.dtd">
<taglib>
<tlibversion>1.0</tlibversion>
<jspversion>2.0</jspversion>
<shortname>mytag</shortname>
<uri>[email protected]</uri>
<tag>
<name>hello</name>
<tag-class>com.kettas.tag.HelloHandler</tag-class>
<body-content>empty</body-content>
<attribute>
<name>user</name>
<type>java.lang.String</type>
<required>true</required>
<rtexprvalue>true</rtexprvalue>
</attribute>
</tag>
<tag>
<name>loop</name>
<tag-class>com.kettas.tag.LoopHandler</tag-class>
<!-- 标签体内含有内容, 并且是非脚本的内容 -->
<body-content>scriptless</body-content>
<attribute>
<name>begin</name>
<type>int</type>
<required>true</required> <rtexprvalue>true</rtexprvalue>
</attribute>
<attribute>
<name>end</name>
<type>int</type>
<required>true</required>
<rtexprvalue>true</rtexprvalue>
</attribute>
</tag>
</taglib>
使用如下 :======================================================================
<%@taglib uri="[email protected]" prefix="liucy"%>
<liucy:loop begin="0" end="3">
<h2>Hello !</h2>
</liucy:loop>
如此便可连续在网页上输出三个"Hello !"
9 EL表达式( ${ } )
(1) 完成一些简单运算.
数学运算
+ - * % / ${ a + b }
布尔运算
> gt ( great than )
< lt ( less than )
>= ge ( great equal )
<= le ( less equal )
!= ne ( not equal )
== eq ( equal )
${ a > b } ${ a gt b }
逻辑运算
&& || !
and or not
非空运算 :
a == null
${ not empty a }
|-> a 不存在返回true
|-> a 存在 返回false
(2) 通过EL表达式,快捷的访问作用域中的命名属性
<%= session.getAttribute( "name" )%>
用EL表达式 : ${ name }
(3) 快速访问javabean的属性.
<jsp:getProperty name="user" property="name"/>
用EL表达式 : ${ user.name }
(4) 常用隐含对象 .
${ param }
${ param.age }
${ param.name }
相当于 : <%= request.getParameter( "name" ) %>
用来访问客户端提交的参数.
${ cookie.age }
实际要执行的代码 :
Cookie[] c = request.getCookies();
for( Cookie a : c ){
if(a.getName() == "age"){
a.getValue();
...
}
}
10 为JSP写的一套核心标签, 有了这套标签, 根本不需要自定义标签了
(1) 准备
需要standard.jar, jstl.jar两个jar包, 放入Tomcat 6.0/lib目录中(或者是/WEB-INF/lib)
(2)core<%@tagliburi="http://java.sun.com/jsp/jstl/core"
prefix="c"%>
forEach循环
①一般用法 相当普通的for循环
<c:forEach begin =”1” end=”10” varstatas="st">
${ st.count }</c:forEach>
② 迭代循环 集合
<c:forEach var="u" items="${ users }">
${ u .name}
</c:forEach>
若是map集合${ user.key}得到值的集合
set
(1)<c:set var="a" value="1234"/>
(2)<c:set var="a">
xxxxx
</c:set> 把标签体内所有的输出都当作var的值,不直接显示在网页上
使用需要调用${a}
remove
<c:remove var="a"/>
${a}
url
<c:url var="date" value="/jspapp/date.jsp">
<c:param name="age" value="23"/>
<c:param name="name" value="lisi"/>
<c:param name="passwd" value="ffff"/>
</c:url>
<a href="${date}">test</a>
/jspapp/xxx.jsp?age=23
<a href="/jspapp/xxx.jsp?age=23&name=lsis&passwd=123">
test
</a>
同if swith
<c:if test=”s{4>2}”>xxxxx</c:if>
<c:choose>
<c:when test="a">
cccccccc
</c:when>
<c:when test="b"></c:when>
....
</c:choose>
<c:choose>
<c:when test="">
cddddddd
</c:when>
<c:when test="">
sssss
</c:when>
<c"when test="">
xxxxxx
</c:when>
<c:otherwise>
</c:otherwise>
</c:choose>
第五天:===========================
MVC :
M : model ( 业务逻辑与业务数据 ): javabean
V : view ( 显示逻辑 )
将数据按照用户的 要求显示出来 .
对同一份数据而言,可以 以 多种形式
体现 ( 类表, 屏图,柱图 等等 )
用JSP实现。
C : controller ( 控制器 , 负责程序的流程控制 )
接收用户请求 , 根据业务逻辑的执行情况 返回
相应的结果 .
用Servlet来实现。
===================================================
好处 :
1) 各司其职, 解耦合 .
代码可重用。
================================================
前端控制器(Servlet)的工作指责 :
1) 能够接受所有的用户请求 .
<url-pattern>*</url-pattern>
2) 能跟据请求的不同 调用 不同的处理(javabean) .
a, 请求的不同 ---> url ---> servletpath
http://loxxx:8080/app/login
http://loxxx:8080/app/query
request.getServletPath() --> path
login=A f1 f2 f3
=================================
query=B
delete=C
b, 通过一个配置文件,向servlet说明 被调用
组件 和 serlvetpath 之间的对应关系 .
C, 要求所有的被调用的组件 必须按照某个接口
的规发来进行开发 .这样才能由servlet正确的
调用 .
Action的主要作用是获得参数,让业务(Biz)处理,然后把处理的结果返回给Action再返回给Servlet。
public interface Action{
public String execute( request , response )
}
LoginAction
execute( Request , response ){
String name = request.getParameter( .. ) ;
String pwd = request.getParameter( "" );
UserBiz biz = new UserBiz();
biz.login( name , pwd );
return "ok.jsp|error.jsp" ;
}
UserBiz
login( userName , password )
Servlet ---> Action ---> Biz
1) biz 实现业务逻辑
2) 写Action
a, 准备参数
b, 调用biz
c, 根据biz的运行结果,返回对应URL
3)在配置文件中, 指定servletpath 与 Action之间的对应关系 .
4) 编写下一个JSP
============================================
ProductBiz
|-> Collection<Product> getAllProducts();
return Collection;
QuerProductAction
|-> execute()
|-> biz.getAllProducts(); --> disp.jsp
return disp.jsp;
MVC的大至执行顺序:
1,用户发送请求>2获取用户的servletpath>3根据servletpath在配置文件中查找javabean的名字,>4返回名字>5,execute>6,复杂业务计算>7,返回下一个页面的URL>8根据URL转向下一个页面>9把页面返回给用户。
Struts 1.2
第一天:
MVC : M : 业务逻辑,业务数据 ( 可以重复利用 ) java Bean ( VO BO) EJB 其实struts1没有实现业务层,也无法实现
V : 显示逻辑 ,同一份数据 ,对应多种显示方法. JSP代码实现。
C:流程控制器 , Servlet代码实现。javabean Servlet javabean 其实他有两个部分组成:
1,系统核心控制器 由Struts 1 框架提供,就是 ActionServlet
2,业务逻辑控制器 由Struts 1框架提供就是用户自己写的Action 实例
在struts-config.xml配置文件中的一些属性: >
Struts : 基于mvc的软件框架 . Struts :
1使用struts需要完成的准备工作 :
1) 前置控制器 (ActionServlet) .
2) 定义了配置文件的格式 . /WEB-INF/struts-config.xml
3) 提供了一个父类, Action , 要求所有的javabean 都来继承这个父类 , 覆盖其中的方法 . 使用struts1需要完成的准备工作 : version 2.0 version 1.2.x 下面是1.2的版本
1) 获取struts的jar包 --> 拷贝到WEB-INF/lib
2) 对struts提供的前端控制器( ActionServlet ) 在 自己的web.xml中 进行配置
3) 确保struts用到的配置文件被放置到指定的位置 . struts1.2.7/webapps/struts-blank.war 解
压之后,从中拷贝struts-config.xml到我们 自己的应用里 .
4) 将struts提供的jar包配置到classpath中. struts.jar : 编译时依赖
2常用的业务流程
0) 写jsp , 这个jSP中有用户需要的连接或表单,
1) javabean , 通过它里面的方法,完成业务逻辑 .
2) 写Action , 调用javabean ,并根据javabean的结果,返回不同的页面 .
3) 在配置文件中对Action进行配置 .
4) 写jsp login.do http://localhost:8989/strutsapp/ok.jsp Resource file
1) 编写资源文件. a, properties
2) 在struts-config.xml中对资源文件进行配置 . 通知struts, 要使用的资源文件的名称 .
3) 在jsp中使用资源文件的内容 . 编程时使用key 显示时看到的是value
errors.header //errors会在所有错误的开头自动加上该key所代表的值(value), errors.footer //errors会在所有错误的结尾自动加上该key所代表的值(value), ActionForm : 用来保存客户端提交的数据 .
name password ----> Object |->name |->password
1) 写一个类( ActionForm ) , 用于保存客户端提交的参数.
2) 编辑配置文件, 告知struts,什么样的请求 使用哪个ActionForm 对象 .
3) 在Action的execute方法中, 从ActionForm对象中获取数据 .通过ActionForm做验证 .
1) 在配置文件中说明,哪些请求是需要做验证的 .
2) 在ActionForm里编写验证代码 . @Override validate() ActionMessage ---> Resource
3) 在错误页面上显示错误信息 .
0) 编写biz对象的方法,用于处理用户请求 . |-> 测试功能
1) 编写用户发送请、求的页面
2) 根据表单的参数 编写 ActionForm
3) 编写Action对象.execute方法 |-> 调用biz对象的方法. |->
根据biz方法的返回结果,返回不同的ActionForward
4) 对ActionForm , Action 进行配置 .
5) 编写与 ActionForward 对应的jsp页面 .
第二天:具体实现
<form-beans>
<form-bean name="" type=""/>
</form-beans>
<action-mappings>
<action path="" type="" name="" validate="true" input="">
<forward name="" path="" redirect="true"/>
</action>
</action-mappings>
<html:errors />
3,常用配置文件 最原始的Action接口,验证登陆的实例
(1) web.xml文件 :
<?xml version="1.0" encoding="utf-8"?>
<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">
<!—在web.Xml配置前端控制器 -->
<servlet>
<init-param>
<param-name>config</param-name>
<param-value>/WEB-INF/struts-config.xml</param-value>
</init-param>
<init-param>
<param-name>debug</param-name>
<param-value>2</param-value>
</init-param>
<init-param>
<param-name>detail</param-name>
<param-value>2</param-value>
</init-param>
<servlet-name>actionServlet</servlet-name>
<servlet-class>org.apache.struts.action.ActionServlet</servlet-class>
<load-on-startup>3</load-on-startup>
</servlet>
<!-- 对任意请求,都采用Struts提供的那个ActionServlet进行转发 -->
<servlet-mapping>
<servlet-name>actionServlet</servlet-name>
<url-pattern>*.do</url-pattern>
</servlet-mapping>
</web-app>
(2) struts-config.xml文件 :
ActionServlet<?xml version="1.0" encoding="utf-8" ?>
<!DOCTYPE struts-config PUBLIC
"-//Apache Software Foundation//DTD Struts Configuration 1.2//EN"
"http://jakarta.apache.org/struts/dtds/struts-config_1_2.dtd">
<struts-config>
<form-beans>
<!-- 将表单提交的数据封装为一个对象 name属性对应Action中的name 自己手动写的actionform-->
<form-bean name="loginForm" type="com.kettas.struts.LoginForm"/>
<!—动态actionForm
<form-bean name="regForm" type="org.apache.struts.action.DynaActionForm">
<form-property name="userName" type="String"/>
<form-property name="age" type="java.lang.Integer"/>
</form-bean>
</form-beans>
<!-- 指定映射 指定请求路径对应指定Action
name属性指定与此Action对应的表单对象
validate指定是否采用表单验证 input指定若是表单验证出错 要去的页面 -->
<action-mappings>
<action path="/login" type="com.kettas.struts.LoginAction" name="loginForm"
validate="true" input="/login.jsp">
<forward name="ok" path="/ok.jsp" redirect="true"/>
<forward name="error" path="/error.jsp"/>
</action>
</action-mappings>
<!-- 指定资源文件 注意 : 以WEB-INF/classes为根目录 -->
<message-resources parameter="res.AppResources"/>
</struts-config>
(3) 表单对象 自己手动写actionForm
package com.kettas.struts ;
import org.apache.struts.action.* ;
import javax.servlet.http.* ;
// 要继承ActionForm类
public class LoginForm extends ActionForm{
// 成员变量的名字要和客户端参数一一对应.
private String userName ;
private String password ;
// 添加get、set方法
。。。。。。。。
/***********************************************
* 用于对表单的数据进行验证, 该方法在调用execute之前
* 被前端控制器调用,根据他的返回结果,来判断验证是否
* 成功
************************************************/
@Override
public ActionErrors validate( ActionMapping mapping ,
HttpServletRequest request )
{// errors用于存放错误的集合
ActionErrors errors = new ActionErrors();
if( userName == null || userName.trim().length() == 0 ){
// ActionMessage对象用来装载错误信息 初始化参数为资源文件中的键
ActionMessage m = new ActionMessage( "errors.username.required" );
errors.add( "error" , m );
}
if( password == null || password.trim().length()<3 ){
ActionMessage m = new ActionMessage( "errors.password.required" ) ;
errors.add( "error" , m ) ;
}
// 若返回的对象size为0 则说明验证通过 否则进入Action中input属性指定的页面中
return errors ;
}
}
// 在input指定的页面中要显示ActionErrors中的错误信息 则要用到标签 : <html:errors/>
errors.header : 错误信息的头
errors.footer : 错误信息的主体
引入标签库 : <%@taglib uri="http://struts.apache.org/tags-html" prefix="html"%>
// <bean:message key="login.password"/> : 用于显示资源文件中指定键值对应的内容
由于规范的jsp页面中除了标签是不能含有静态文本内容的,故这个标签会被大量使用
引入标签库 : <%@taglib uri="http://struts.apache.org/tags-bean" prefix="bean"%>
(4) Action : 的实现
package com.kettas.struts ;
import org.apache.struts.action.* ;
import javax.servlet.http.* ;
import com.kettas.biz.*;
public class LoginAction extends Action{
@Override
public ActionForward execute( ActionMapping mapping , ActionForm form ,
HttpServletRequest request , HttpServletResponse response )
{ // 传统的获取表单数据方式
// String name = request.getParameter("userName" ) ;
// String pwd = request.getParameter("password" ) ;
// 得到表单对象 从中获取表单数据
LoginForm lf = (LoginForm)form ;
// 调用biz对象,返回下一个页面 .
try{
UserService biz = new UserService();
biz.login( lf.getUserName() , lf.getPassword() ) ;
ActionForward af = mapping.findForward("ok");
return af ;
}catch( Exception e ){
request.setAttribute( "errorMessage" , e.getMessage() ) ;
return mapping.findForward( "error" );
}
}
}
(5) Biz的实现 :
package com.kettas.biz ;
public class UserService{
public void login( String name , String pwd ){
if( ! name.equals( "liucy" ) ){
throw new RuntimeException( "user " + name + " not found" ) ;
}
if( ! pwd.equals( "12345" ) ){
throw new RuntimeException( "Invalid password" ) ;
}
}
}
4,资源文件 .国际化
1, 默认资源文件 : 当没有专门为每个用户准备资源文件
时 ,使用默认资源文件 .
<message-resources parameter="res.AppResources"/>
2,其他资源文件在命名上有一个规定 .
默认资源文件名_国家缩写 .
AppResources_zh
AppResources_ja
内容上是键值对 login.username=USER NAME: 前面是名称后面是显示的值
在jsp上的使用:<bean:define id="add"> <bean:message key=" login.username "/></bean:define>
3, 所有的资源文件要以utf-8的编码来编写 .如果不是的话要进去以下转换。
a,用中文编写一个临时的资源文件a.properties ( 中国字 GBK )
临时文件中的编码 临时文件的名字 新文件的文件名
b, native2ascii -encoding GBK a.properties AppResources_zh.properties
.
5,struts 中的异常处理 :==================
---> Action ---> Biz ---> Dao
Action中对异常进行捕获 .
try{
biz.fn();
return mapping.findforward( "ok" ) ;
}catch( Exception e){
return mapping.findforward( "error" );
}
系统级异常:
* 由于没个具体的技术操作导致的异常 ,
* 系统级异常通常是定义好的类型( SQLException , IOException )
* 一般发生在DAO层 .
* 在处里上要粒度粗一些 RuntimeException
应用级异常
* 由于用户违反了特定的业务逻辑导致的异常
* 应用级一般是用户自定义的异常 .
* 一般发生在 biz 层 .
* 粒度细一些 .
最好做一个异常的体系结构
实例 :
(1) struts-config.xml文件 :
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE struts-config PUBLIC
"-//Apache Software Foundation//DTD Struts Configuration 1.2//EN"
"http://jakarta.apache.org/struts/dtds/struts-config_1_2.dtd">
<struts-config>
<form-beans>
<form-bean name="loginForm" type="com.kettas.struts.LoginForm"/>
动态actionForm
<form-bean name="regForm" type="org.apache.struts.action.DynaActionForm">
<form-property name="userName" type="String"/>
<form-property name="age" type="java.lang.Integer"/>
</form-bean>
</form-beans>
<!-- 全局变量 可用在一些struts标签中 如<html:link /> -->
<global-forwards>
<forward name="login" path="/login.jsp"/>
</global-forwards>
<action-mappings>
<action path="/login" type="com.kettas.struts.LoginAction" name="loginForm"
validate="true" input="/login.jsp">
<!-- 将Action中抛出的异常捕获 并跳转到对应的错误页面
在错误页面中 还是用标签<html:errors />来显示错误
可见 这里key标签中指定的资源文件内容已经被封装到
ActionErrors对象中去了 -->
<exception type="com.kettas.struts.expt.SystemException"
path="/error.jsp" key="errors.system"/>
<exception type="com.kettas.struts.expt.UserNotFoundException"
path="/error.jsp" key="errors.user"/>
<exception type="com.kettas.struts.expt.PasswordException"
path="/error.jsp" key="errors.pwd"/>
<!-- redirect="true"表示在一个新的连接中打开指定页面
默认在同一个连接中打开指定页面 -->
<forward name="ok" path="/ok.jsp" redirect="true"/>
<forward name="error" path="/error.jsp"/>
</action>
</action-mappings>
//引入资源文件
<message-resources parameter="res.AppResources"/>
</struts-config>
(2) 定义根系统异常 作为其他一切系统异常的父类 这一步是有意义的
package com.kettas.struts.expt ;
// 继承至RuntimeException类而不是Exception 如此无需try、catch捕获可自动抛出异常
public class SystemException extends RuntimeException{
// 注意写这三个构造函数 后面都这么写
public SystemException( ){
super();
}
public SystemException( String msg ){
super( msg ) ;
}
public SystemException( Throwable t ){
super( t );
}}
// 定义根业务异常 作为其他一切业务异常的父类
package com.kettas.struts.expt ;
public class BizException extends RuntimeException{
..
}
// 密码错误异常 继承至根业务异常
package com.kettas.struts.expt ;
public class PasswordException extends BizException{
...
}
// 用户未发现异常 继承至根业务异常
package com.kettas.struts.expt ;
public class UserNotFoundException extends BizException{
...
}
(3) dao的实现 :
import ...
// 由于是采用tomcat的连接池获取连接故这里引入这两个名字空间
import javax.sql.* ;
import javax.naming.* ;
public class UserDao{
public User queryByLoginName( String loginName ){
Connection conn = null ;
PreparedStatement pstm = null ;
ResultSet rs = null ;
String sql = "select id , loginname , password from liucy_users where loginname=?" ;
User u = null ;
try{
// 从连接池中获取连接的三个步骤
Context ctx = new InitialContext();
DataSource ds = (DataSource)ctx.lookup( "java:comp/env/oracle/ds" ) ;
conn = ds.getConnection();
pstm = conn.prepareStatement( sql ) ;
pstm.setString( 1 , loginName ) ;
rs = pstm.executeQuery();
if( rs.next() ){
u = new User();
u.setId( rs.getInt( 1 ) ) ;
u.setLoginName( rs.getString(2 ) ) ;
u.setPassword( rs.getString( 3 ) ) ;
}
return u ;
}catch( Exception e){
e.printStackTrace();
// 抛出前面定义的系统异常 抛给biz 再从biz抛出 抛给Action 然后被系统捕获
throw new SystemException( e );
}finally{
if( rs != null ) try{ rs.close(); }catch( Exception e){}
if( pstm != null ) try{ pstm.close();}catch( Exception e ){}
// 由于是连接池中的连接 故不会真的关闭 只是被连接池回收
if( conn != null ) try{ conn.close();}catch( Exception e ){}
}}}
(4) biz的实现 :
public class UserService{
public void login( String name , String pwd ){
UserDao dao = new UserDao();
User u = dao.queryByLoginName(name);
if( u == null ){
// 抛出用户不存在业务异常
throw new UserNotFoundException( "user " + name + " not found" ) ;
}
if( ! u.getPassword().equals( pwd ) ){
// 抛出密码错误业务异常
throw new PasswordException( "Invalid password" );
}}}
(5) Action的实现 :
public class LoginAction extends Action{
@Override
public ActionForward execute( ActionMapping mapping , ActionForm form ,
HttpServletRequest request , HttpServletResponse response ) { LoginForm lf = (LoginForm)form ;
/*
try{
*/
// 这里会自动抛出biz和dao中的异常 抛给struts处理
UserService biz = new UserService();
biz.login( lf.getUserName() , lf.getPassword() ) ;
ActionForward af = mapping.findForward("ok");
return af ;
/*
// 若自己处理异常 则比较麻烦,可以在struts配置文件配置
}catch( SystemException e ){
request.setAttribute( "errorMessage" , "System is busy" ) ;
return mapping.findForward( "error" );
}catch( UserNotFoundException e){
request.setAttribute( "errorMessage" , e.getMessage() ) ;
return mapping.findForward( "error" ) ;
}catch( PasswordException e ){
request.setAttribute( "errorMessage" , e.getMessage() ) ;
return mapping.findForward( "error" ) ;
}catch( Exception e ){
request.setAttribute( "errorMessage" , "you catch an error" );
return mapping.findForward( "error" ) ;
}
*/
}}
4 ActionMessages对象 :
public class MessageAction extends Action{
@Override
public ActionForward execute( ActionMapping mapping , ActionForm form ,
HttpServletRequest request , HttpServletResponse response )
{ActionMessages messages = new ActionMessages();
// 用资源文件内容初始化
ActionMessage m1 = new ActionMessage( "msg1" );
ActionMessage m2 = new ActionMessage( "msg2" ) ;
messages.add( "message" , m1 ) ;
messages.add( "message" , m2 ) ;
request.setAttribute( "msgs" , messages );
return mapping.findForward( "ok" );
}
}
// 在jsp中显示ActionMessages内容
<html>
<body>
This message jsp
<!-- name指定命名属性 -->
<html:messages id="m" name="msgs">
<h1>${m}</h1>
</html:messages>
</body>
</html>
<bean:message />
<html:errors />
html: 处理页面的显示问题
bean: 定义变量和javabean <c:set />
logic: <c:if > <c:forEach>
<forward name="ok" path="/path.jsp"/>
html:
<html:link /> <===> <a href="">
<html:link forward="xxxx" page="xxx">Test</html:link>
page : 指向一个明确的连接地址 .
forward : 全局的forward 的 逻辑名字 .
* 可以自动加应用名称
* 可以使用逻辑名字 .
* 在不支持cookie的情况下, 自动增加jsessionid
<html:link forward="ok"></html:link>
<html:form action="/login.do" method="">
<input type="text" name="age"/>
<html:text property="age"/>
<input type="password" name=""/>
<html:password />
<select name="">
<option>aaa</option>
</slelect>
<html:select>
<html:option></html:option>
</html:select>
<input type="hidden" name="" value=""/>
<html:hidden />
</html:form>
<html:messages id="" name="">
</html:messages>
name : 被迭代的消息结合在作用域中的名字 .
id : 当前整备迭代的消息对象 .
* 被该标签迭代的集合类型是 ActionMessages类型 .
集合中的每个元素都是 一个 ActionMessage , 他
能够封装资源文件中的数据 .
<html:errors />
定义一个变量:id表示变量名,value:表示变量值
<bean:define id="a" value="1234"/>
<bean:define id="">
XXXXXXXX
</bean:define>
定义命名属性:
<bean:define id="a" name="data"/>
Action ---> a.jsp
request.setAttribute( "a" , "hello")
${ a }
第三天:标签的使用======================================================================================
5 struts标签 :
<html:link forward="login"page="/webdir/login.jsp">
Test</html:link>
page : 指向一个明确的连接地址 .
forward : 全局的forward的逻辑名字, 在配置文件中指定的 :
<global-forwards>
<forward name="login" path="/login.jsp"/>
</global-forwards>
* 可以自动加应用名称
* 可以使用逻辑名字, 在配置文件中说明实际路径
* 在不支持cookie的情况下, 自动增加jsessionid
在html中表单标签用struts标签代替 :
<!-- action中无需写应用名 会自动添加上 -->
<html:form action="/login.do" method="">
<-- <input type="text" name="age"/> -->
<html:text property="age" value=""/>
<!-- <input type="password" name="pwd"/> -->
<html:password property="pwd"/>
<!--<select name="">
<option>aaa</option>
</slelect> -->
<html:select>
<html:option></html:option>
</html:select>
<!-- <input type="hidden" name="" value=""/> -->
<html:hidden />
</html:form>
html :
<html:link forward|page="/" >
<html:form action="" method="">
<html:text property="" value=""/>
<html:password property=""/>
<html:select property=""/>
<html:option/>
<html:radio property="" />
<html:checkbo property=""/>
</html:form>
<html:errors/>
<html:messages id="m" name="">
${m}
</html:messages>
bean :
<!-- 在当前页面范围内 定义一个变量 -->
<bean:define id="" value=""/>
<bean:define id="">
...
</bean:define>
<!-- 将某一个命名属性对应的值赋予变量 -->
<bean:define id="a" name="student"/>
<bean:define id="a" name="student" property="name"/>
相当于 : String a = student.getName();
<!-- 将客户端提交的指定表单数据赋予变量 -->
<bean:parameter id="a" name="age"/>
<!-- 将客户端提交的指定cookie的值赋予变量 -->
<bean:cookie id="c" name="JSESSIONID"/>
<!-- 显示资源文件内容 -->
<bean:message key=""/>
logic :
<logic:present parameter|cookie|name="age">
xxxxxxx ;
xxxxxxx ;
xxxxxxx ;
</logic:present>
<logic:notPresent >
Xxxxx
</logic:notPresent>
<logic:greaterThan parameter="age" value="23">
</logic:greaterThan>
<logic:greateThan cookie="age" value="23">
cxddddd
</logic:greateThan >
<logic:greateThan name="student"property="age"value="23">
</logic:greateThan>
<logic:equal>
<logic:notEqual>
<logic:greaterThan>
<logic:lessThan>
<logic:greaterEqual>
<logic:lessEqual>
<!-- 循环标签 -->
<logic:iterate id="" collection="">
</logic:iterate>
=================================================
request ----> ActionForm ---> Action
简化对ActionForm 的开发 .直接在配置文件配置,不用写单独的类
DynaActionForm ( 动态 ActionForm )
1) 在配置文件中配 .
<form-beans>
<form-bean name="" type=""/>
<form-bean name="regForm" type="org.apache.struts.action.DynaActionForm">
<form-property name="userName" type="String"/>
<form-property name="age" type="java.lang.Integer"/>
</form-bean>
</form-beans>
2) Action中使用.
execute( ActionForm form ){
DynaActionForm af = ( DynaActionForm )form ;
af.get( "age" ) ;
af.get( "userName" ) ;
}
================================================
login , register , query
7 关于MappingDispatchAction, DispatchAction, LookupDispatchAction
(1) MappingDispatchAction : 容许一个action中包含多个方法,分别处理与之对应的请求
实例 :
配置文件 :
<form-bean name="dynaLoginForm" type="org.apache.struts.action.DynaActionForm">
<form-property name="userName" type="java.lang.String"/>
<form-property name="password" type="java.lang.String"/>
</form-bean>
<form-bean name="dynaRegForm" type="org.apache.struts.action.DynaActionForm">
<form-property name="userName" type="java.lang.String"/>
<form-property name="password" type="java.lang.String"/>
<form-property name="email" type="java.lang.String" />
</form-bean>
...
...
<action parameter="login" path="/login" type="com.kettas.struts.
UserSelfServiceAction" name="dynaLoginForm">
<exception type="com.kettas.struts.expt.SystemException" path="/error.jsp"
key="errors.system"/>
<exception type="com.kettas.struts.expt.UserNotFoundException"
path="/error.jsp" key="errors.user"/>
<exception type="com.kettas.struts.expt.PasswordException" path="/error.jsp"
key="errors.pwd"/>
<forward name="ok" path="/ok.jsp" redirect="true"/>
</action>
<action parameter="register" path="/reg" type="com.kettas.struts.
UserSelfServiceAction" name="dynaRegForm">
<forward name="ok" path="/regok.jsp"/>
</action>
...
Action :
public class UserSelfServiceAction extends MappingDispatchAction{
// 用于处理用户登陆请求 .
public ActionForward login( ActionMapping mapping , ActionForm form ,
HttpServletRequest request , HttpServletResponse response )
{
DynaActionForm daf = (DynaActionForm)form ;
String userName = (String)daf.get( "userName" );
String password = (String)daf.get( "password" ) ;
UserService biz = new UserService();
biz.login( userName , password );
return mapping.findForward( "ok" ) ;
}
// 负责处理用户的注册请求
public ActionForward register( ActionMapping mapping , ActionForm form ,
HttpServletRequest request , HttpServletResponse response )
{
DynaActionForm daf = ( DynaActionForm ) form ;
System.out.println( daf.get( "userName" ) ) ;
System.out.println( daf.get( "password" ) ) ;
System.out.println( daf.get( "email" ) ) ;
return mapping.findForward( "ok" ) ;
} }
(2) DispatchAction :
1 将相关的操作合并在同一个Action中处理,这些相关的操作,必须使用同一个ActionForm
2 客户端提交的参数值决定使用Action中的哪个方法
3 不能覆盖execute方法,为每一个请求单独写一个方法
4 采用DispatchAction的方式简化了配置文件
实例 :
配置文件 :
<!-- parameter指定客户端请求中决定action方法的那个参数 -->
<action parameter="method" path="/all" type="com.kettas.struts.
DispatchActionSample" name="allForm">
<exception type="com.kettas.struts.expt.SystemException" path="/error.jsp"
key="errors.system"/>
<forward name="ok" path="/ok.jsp" redirect="true"/>
<forward name="regOk" path="/regok.jsp"/>
</action>
Action :
public class DispatchActionSample extends DispatchAction{
public ActionForward login( ActionMapping mapping , ActionForm form ,
HttpServletRequest request , HttpServletResponse response )
{
DynaActionForm daf = (DynaActionForm)form ;
String userName = (String)daf.get( "userName" ) ;
String password = (String)daf.get( "password" ) ;
UserService service = new UserService();
service.login( userName , password ) ;
return mapping.findForward( "ok" ) ;
}
public ActionForward register( ActionMapping mapping , ActionForm form ,
HttpServletRequest request , HttpServletResponse response )
{
DynaActionForm daf = (DynaActionForm)form ;
System.out.println( daf.get( "userName" ) );
System.out.println( daf.get( "password" ) ) ;
System.out.println( daf.get( "email" ) ) ;
return mapping.findForward( "regOk" ) ;
}}
客户端 :
<a href="/webdir/all.do?method=login">登录</a>
<a href="/webdir/all.do?method=register">注册</a>
Method有不同的参数,对应处理的action方法
(3) LookupDispatchAction :
(1) 不能覆盖父类的execute方法,为每一个按钮单独写一个方法
(2) 覆盖父类getKeyMethodMap方法,在这个方法中指明按钮的值与方法名称的对应关系
(3) 按钮的值,不能直接写死在程序中,要来自于资源文件
(4) 在配置文件中对Action进行配置
<action parameter="指明按钮的名称" path="" type="" name="" ></action>
(5) 适合一个表单多种请求方式的时候
实例 :
配置文件 :
<action parameter="btn" path="/math" type="com.kettas.struts.MathAction"
name="allForm">
<forward name="ok" path="/submit.jsp"/>
</action>
Action :
public class MathAction extends LookupDispatchAction{
// 覆盖父类getKeyMethodMap方法,在这个方法中指明按钮的值与方法名称的对应关系
@Override
public Map getKeyMethodMap(){
// 按钮的值应来自于资源文件 在map中保存是资源文件中的key,values是对应的方法
Map m = new HashMap();
m.put( "btn.add" , "addOperate" );
m.put( "btn.subtract" , "subOperate" );
return m ;
}
// 加法运算
public ActionForward addOperate( ActionMapping mapping, ActionForm form ,
HttpServletRequest request , HttpServletResponse response )
{
DynaActionForm daf = (DynaActionForm)form ;
Integer a = (Integer)daf.get( "a" ) ;
Integer b = (Integer)daf.get( "b" ) ;
int ret = a.intValue() + b.intValue();
request.setAttribute( "ret" , ret ) ;
return mapping.findForward( "ok" ) ;
}
// 减法运算
public ActionForward subOperate( ActionMapping mapping, ActionForm form ,
HttpServletRequest request , HttpServletResponse response )
{
DynaActionForm daf = (DynaActionForm)form ;
Integer a = (Integer)daf.get( "a" ) ;
Integer b = (Integer)daf.get( "b" ) ;
int ret = a.intValue() - b.intValue();
request.setAttribute( "ret" , ret ) ;
return mapping.findForward( "ok" ) ;
}
}
客户端 :
<%@page contentType="text/html"%>
<%@page isELIgnored="false"%>
<%@taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%>
<%@taglib uri="http://struts.apache.org/tags-bean" prefix="bean"%>
<html>
<body>
<!-- 为变量赋值 获得资源文件的值 -->
<bean:define id="add">
<bean:message key="btn.add"/>
</bean:define>
<bean:define id="sub">
<bean:message key="btn.subtract"/>
</bean:define>
<form action="/strutsapp/math.do" method="GET">
Number A :<input type="text" name="a"/><br/>
Number B :<input type="text" name="b"/><br/>
<!-- 按钮的name属性与action配置中的parameter属性是对应的 -->
<input type="submit" name="btn" value="${add}"/>
<input type="submit" name="btn" value="${sub}"/>
</form>
<!-- 显示计算结果 -->
<c:if test="${!empty ret}">
<h2>ret = ${ret}</h2>
</c:if>
</body>
</html>
================================== ====================================================
n m ( m-1 ) * n ---> Begin
(m-1)*n + n ---> end
select id
from
( select id , rownum rn from product )
rn> ? and rn < ?
======================================================
ResultSet
1) 改bug.
2) 增加上一页 , 下一页 功能 .
3) 总页数 通过 ServletContextListener
保存到ServletContext对象中 .
第五天:
Validation --> 表便验证 .
Overide
validate()
1) dynaActionForm
2)
3)
validation :
1,varliator.jar | jakarta-ora.jar .
|-> 在这些类中实现了通用的验证方法.
2, validator-rules.xml .
|-> 相当于类的使用说明. 写给struts看的.
not null --->class ---> function
3, validation.xml :
|-> 相当于程序员编写的需求说明书 .
loginForm
|--> userName
|--> not null .
Validation框架的使用方式 :
1) 程序员负责编写需求说明书 . 说明
哪些表单 , 哪些数据,需要做什么验证 .
2) struts 通过阅读validator-rules.xml,
调用相应方法,实现验证逻辑 .
===============================================
使用validation框架的准备工作 :
1) 保证validation.jar , jakarta-ora.jar
保存在应用的/WEB-INF/lib
2) 保证两个配置文件 validator-rules.xml
validation.xml 要保存到/WEB-INF 中 .
3) 将validation以插件的方式嵌入到struts中.
修改 struts-config.xml ,插件的配置标签
可以从validator-rules.xml的注释中拷贝.
4) 确保需要验证的请求 validate="true"
<action path="login" type="xxxAction"
name="xxxForm" validate="true"
input="xxx.jsp"/>
5) 如果要通过Validation作验证的话 .
ActionForm
- DynaValidatorActionForm 在validation.xml配置文件中的form标签中的属性为path
- DynaValidatorForm 在validation.xml配置文件中的form标签中的属性为name
DynaActionForm
- org.apache.struts.validator.DynaValidatorForm
- DynaValidatorActionForm
6) 将错误提示信息从validator-rules.xml中拷贝到自己的资源文件中 .
编辑需求说明书( validation.xml ) 提出自己的验证需求
formset
|-> form
|-> field
|-> form
tiles .
<script>
function fn(){
xxxxxxxxx ;
xxxxxxxxxx;
return false ;
}
</script>
<form action="" method="" onsubmit="return xxxx();">
</form>
1) 将validation写的javascript代码嵌入到自己的网页中.
<html:javascript formName=""/>
2) 获取javascript验证函数的名字 .
allForm ---> valdiateAllForm
abc -------> validateAbc
login.do
regster.do
AllForm
MappingDisaptcherAction
<action path="/login" type="" name="all" validate="true" >
<action path="/reg" type="" name="all" validate="true">
9 struts中的模板
(1) 在struts-config.xml文件中指定使用模板 : 通过插件件的方式
<action-mappings> ... </action-mappings>
<plug-in className="org.apache.struts.tiles.TilesPlugin" >
<!-- 指定模板配置文件的路径 -->
<set-property property="definitions-config" value="/WEB-INF/tiles-defs.xml" />
<!-- 使模板识别生效 -->
<set-property property="moduleAware" value="true" />
</plug-in>
(2) tiles-defs.xml文件 :
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE tiles-definitions PUBLIC
"-//Apache Software Foundation//DTD Tiles Configuration 1.1//EN"
"http://jakarta.apache.org/struts/dtds/tiles-config_1_1.dtd">
<tiles-definitions>
<!-- template.jsp是个模板页面 里面只有一个页面布局 -->
<definition name="base" path="/tiles/template.jsp">
<!-- 向模板页面的指定位置插入其他页面 -->
<put name="header" value="/tiles/header.jsp"/>
<put name="menu" value="/tiles/menu.jsp"/>
</definition>
<!-- 继承至上面的添加了部分内容的模板 再添加新的内容 -->
<definition name="register" extends="base">
<put name="body" value="/tiles/regBody.jsp"/>
</definition>
<!-- 继承至上面的添加了部分内容的模板 再添加新的内容 -->
<definition name="login" extends="base">
<put name="body" value="/tiles/loginBody.jsp"/>
</definition>
</tiles-definitions>
(3) template.jsp页面 :
<%@page contentType="text/html;charset=utf-8"%>
<%@taglib uri="http://struts.apache.org/tags-tiles" prefix="tiles"%>
<html>
<body>
<table width="100%" height="100%">
<tr height="10%" bgcolor="gray">
<td colspan="2" align="center">
<!-- 指定此地可以插入新的页面 -->
<tiles:insert attribute="header"/>
</td>
</tr>
<tr height="90%">
<td width="15%" bgcolor="blue">
<!-- 指定此地可以插入新的页面 -->
<tiles:insert attribute="menu"/>
</td>
<td width="85%" align="center">
<!-- 指定此地可以插入新的页面 -->
<tiles:insert attribute="body"/>
</td>
</tr>
</table>
</body>
</html>
(4) 使用的时候 页面格式如下 register.jsp :
<%@taglib uri="http://struts.apache.org/tags-tiles" prefix="tiles"%>
<!-- definition属性指定tiles-defs.xml文件中装配完成的页面名称 -->
<tiles:insert definition="register"/>
<!-- 若在tiles-defs.xml文件中没有声明 则要这么写 :
<tiles:insert page="/tiles/template.jsp">
<tiles:put name="header" value="/tiles/header.jsp"/>
<tiles:put name="menu" value="/tiles/menu.jsp"/>
<tiles:put name="body" value="/tiles/regBody.jsp"/>
</tiles:insert>
-->
一个客户在浏览器的地址栏输入了如下:
URL: http://www.tarena.com/webdev/account/deposit?accno=1212&amt=1000
调用 EG 的方法 F 可以获得初始参数interestRate的值。
在accountServlet中调用HttpServletRequest的getRequestURI返回H ,
调用getQueryString返回B ,调用getContextPath返回 A ,
调用getServletPath返回 C ,调用getPathInfo返回 D 。
A. /webdev
B. accno=1212&amt=1000
C. /account
D. /deposit
E. Servletconfig
F. getInitParameter
G. HttpServlet
H. /webdev/account/deposit
Struts 2
1, Struts2的体系和struts1的体系差别很大,因为struts2使用WebWork的设计核心,而不是原来struts1的设计核心。Struts2大量使用拦截器来处理用户请求,从而允许用户的业务逻辑控制器与Servlet API分离。
2, struts2框架的大致处理流程如下:
①浏览器发送请求,例如请求 /mypage.action[U13] /report/myreport.pdf等
②核心控制器FilterDispatcher根据请求决定调用合适的Action
③WebWork的拦截器链自动对请求应用通用功能,例如 workflow,validation或文件下载和上传
④回调Action的execute方法(其实可以是任意方法),该方法execute方法先获得用户的请求参数,然后执行某种数据操作,调用业务逻辑组组件来处理用户请求。
⑤Action的execute方法处理结果信息将被输出发哦浏览器中,可以是html页面,pdf还有jsp,Velocity,FreeMarker等技术模板。
3,在struts2 中的web.xml配置 加拦截器(拦截器是整个struts的核心技术)
<?xml version="1.0" encoding="GBK"?>
<web-app version="2.4" xmlns="http://java.sun.com/xml/ns/j2ee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd">
<!-- 定义Struts2的FilterDispathcer的Filter -->
<filter>
<filter-name>struts2</filter-name>
<filter-class>org.apache.struts2.dispatcher.FilterDispatcher</filter-class>
</filter>
<!-- FilterDispatcher用来初始化struts2并且处理所有的WEB请求。 -->
<filter-mapping>
<filter-name>struts2</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
</web-app>
3, struts.xml的配置
<struts>
<!-- include节点是struts2中组件化的方式 可以将每个功能模块独立到一个xml配置文件中 然后用include节点引用 -->
<include file="struts-default.xml"></include>
<!-- package提供了将多个Action组织为一个模块的方式
package的名字必须是唯一的 package可以扩展 当一个package扩展自
另一个package时该package会在本身配置的基础上加入扩展的package
的配置 父package必须在子package前配置
name:package名称 extends:继承的父package名称
abstract:设置package的属性为抽象的 抽象的package不能定义action 值true:false
namespace:定义package命名空间 该命名空间影响到url的地址,例如此命名空间为/test那么访问是的地址为
http://localhost:8080/struts2/test/XX.action -->
<package name="com.kay.struts2" extends="struts-default" namespace="/test">
<interceptors>
<!-- 定义拦截器 name:拦截器名称 class:拦截器类路径 -->
<interceptor name="timer" class="com.kay.timer"></interceptor>
<interceptor name="logger" class="com.kay.logger"></interceptor>
<!-- 定义拦截器栈 -->
<interceptor-stack name="mystack[U14] ">
<interceptor-ref name="timer"></interceptor-ref>
<interceptor-ref name="logger"></interceptor-ref>
</interceptor-stack>
</interceptors>
<!-- 定义默认的拦截器 每个Action都会自动引用 如果Action中引用了其它的拦截器 默认的拦截器将无效 -->
<default-interceptor-ref name="mystack"></default-interceptor-ref>
<!-- 全局results配置 -->
<global-results><result name="input">/error.jsp</result> </global-results>
<!-- Action配置 一个Action可以被多次映射(只要action配置中的name不同)
name:action名称 class: 对应的类的路径 method: 调用Action中的方法名 -->
<action name="hello[U15] " class="com.kay.struts2.Action.LoginAction">
<!-- 引用拦截器 name:拦截器名称或拦截器栈名称 -->
<interceptor-ref name="timer"></interceptor-ref>
<!-- 节点配置 name : result名称 和Action中返回的值相同
type : result类型 不写则选用superpackage的type struts-default.xml中的默认为dispatcher
-->
<result name="success" type="dispatcher">/talk.jsp</result>
<result name="error" type="dispatcher">/error.jsp</result>
<result name="input" type="dispatcher">/login.jsp</result>
<!-- 配置Action返回cancel时重定向到Welcome的Action-->
<result name="cancel" type="redirect-action">Welcome</result>
<!—异常处理 result表示出现异常时返回的name为success的结果处理—》
<exception-mapping exception=”java.lang.Exception” result=”success”/>
<!-- 参数设置 name:对应Action中的get/set方法 -->
<param name="url">http://www.sina.com</param>
</action>
</package>
<!—引用国际化文件的base名-->
<constant name=”struts2.custom.i18n.resources”value=”messageResource”
</struts>
4,struts2的action编写
例1 HelloWorld.jsp
<% @ page contentType = " text/html; charset=UTF-8 " %>
<% @ taglib prefix = " s " uri = " /struts-tags " %>
[U16] < html >
< head >
< title > Hello World! </ title >
</ head >
< body >
< h2 >< s:property value ="message[U17] " /></ h2 >
</ body >
</ html >
例1 classes/tutorial/HelloWorld.java
import com.opensymphony.xwork2.ActionSupport;
public class HelloWorld extends ActionSupport {
private String message[U18] ;
public String getMessage() {
return message;
}
public void tring setMessage(String message) {
this.message=message;
}
@Override
public String execute[U19] () {
message = " Hello World, Now is " + DateFormat.getInstance().format( new Date());
return success;
}
}
例1 classes/struts.xml中HelloWorld Action的配置
< package name ="ActionDemo" extends ="struts-default" >
< action name ="HelloWorld" class ="tutorial.HelloWorld" >
< result > /HelloWorld.jsp </ result >
</ action >
</ package >
4, 拦截器的编写
编写权限控制器
public class AuthorityInterceptor extends AbstractIntercepto[U20] r {
private static final long serialVersionUID = 1358600090729208361L;
//拦截Action处理的拦截方法
public String intercept(ActionInvocation invocation) throws Exception {
ActionContext ctx=invocation.getInvocationContext(); // 取得请求相关的ActionContext实例
Map session=ctx.getSession();
String user=(String)session.get("user"); //取出名为user的session属性
//如果没有登陆,或者登陆所有的用户名不是aumy,都返回重新登陆
if(user!=null && user.equals("aumy")){
return invocation.invoke();[U21]
}
//没有登陆,将服务器提示设置成一个HttpServletRequest属性
ctx.put("tip","您还没有登录,请登陆系统");
return Action.LOGIN;
}
}
Struts2.xml的配置文件
<struts>
<include file="struts-default.xml"/>
<!--受权限控制的Action请求配置-->
<package name="authority" extends="struts-default">
<interceptors>
<!--定义一个名为authority的拦截器-->
<interceptor class="com.aumy.struts.example.intercepter.AuthorityInterceptor" name="authority"/>
<!--定义一个包含权限检查的拦截器栈-->
<interceptor-stack name="mydefault">
<interceptor-ref name="defaultStack"/> <!--配置内建默认拦截器-->
<interceptor-ref name="authority"/> <!--配置自定义的拦截器-->
</interceptor-stack>
</interceptors>
<default-interceptor-ref name="mydefault" />[U22]
<!--定义全局Result-->
<global-results>
<result name="login">/login.jsp</result>
</global-results>
<action name="show" class="com.aumy.struts.example.LoginAction"
method="show">
<result name="success">/show.jsp</result>
</action>
<action name="add" class="com.aumy.struts.example.LoginAction" method="add">
<result name="success">/add.jsp</result>
</action>
</package>
</struts>
还要一种方法拦截器,可以对某个action的方法进行拦截 编码类似action拦截器
public class MyInterceptor3 extends MethodFilterInterceptor {
@Override
protected String doIntercept(ActionInvocation invocation) throws Exception {
System.out.println("use MethodFilterInterceptor");
String result = invocation.invoke();
return result;
}
}
只是在配置的时候和其他interceptor有些区别:
<interceptor name="myInterceptor3" class="com.langhua.interceptor.MyInterceptor3">
<param name="excludeMethods">execute,login</param> <!-- execute,login两个方法不需要拦截
<!—addmessage 方法需要拦截 可以指名多个方法,只需要用逗号隔开
<param name="includeMethods">addmessage</param>
</interceptor>
6,拦截结果的监听器,用来精确定义在execute方法执行之后,在处理物力资源转向之前的动作,这个监听器是通过手动注册到拦截器内部的
public class MyListener implements PreResultListener[U23] {
public void beforeResult(ActionInvocation invocation, String resultCode[U24] ) {
System.out.println(“放回的逻辑视图是:”+resultCode);
}
}
然后再在拦截器里面调用 invocation.addPreResultListener(new MyListener());
监听器是在这个拦截器完成别的拦截器之后调用的
5,struts2的标签库 相比struts1,struts2提供了大量的标签,且更加强大 ,且主要有,1 UI 用户界面标签(html页面显示) 2 非UI 主要是数据访问,逻辑控制的标签 3 Ajax支持的标签
6,Struts2 的(客户端和服务端)校验更为方便更容易国际化处理
7,国际化处理:
8,支持文件的上传和下载
Struts1和Struts2的区别和对比:
Action 类:
• Struts1要求Action类继承一个抽象基类。Struts1的一个普遍问题是使用抽象类编程而不是接口。
• Struts 2 Action类可以实现一个Action接口,也可实现其他接口,使可选和定制的服务成为可能。Struts2提供一个ActionSupport基类去实现常用的接口。Action接口不是必须的,任何有execute标识的POJO对象都可以用作Struts2的Action对象。
线程模式:
• Struts1 Action是单例模式并且必须是线程安全的,因为仅有Action的一个实例来处理所有的请求。单例策略限制了Struts1 Action能作的事,并且要在开发时特别小心。Action资源必须是线程安全的或同步的。
• Struts2 Action对象为每一个请求产生一个实例,因此没有线程安全问题。(实际上,servlet容器给每个请求产生许多可丢弃的对象,并且不会导致性能和垃圾回收问题)
Servlet 依赖:
• Struts1 Action 依赖于Servlet API ,因为当一个Action被调用时HttpServletRequest 和 HttpServletResponse 被传递给execute方法。
• Struts 2 Action不依赖于容器,允许Action脱离容器单独被测试。如果需要,Struts2 Action仍然可以访问初始的request和response。但是,其他的元素减少或者消除了直接访问HttpServetRequest 和 HttpServletResponse的必要性。
可测性:
• 测试Struts1 Action的一个主要问题是execute方法暴露了servlet API(这使得测试要依赖于容器)。一个第三方扩展--Struts TestCase--提供了一套Struts1的模拟对象(来进行测试)。
• Struts 2 Action可以通过初始化、设置属性、调用方法来测试,“依赖注入”支持也使测试更容易。
捕获输入:
• Struts1 使用ActionForm对象捕获输入。所有的ActionForm必须继承一个基类。因为其他JavaBean不能用作ActionForm,开发者经 常创建多余的类捕获输入。动态Bean(DynaBeans)可以作为创建传统ActionForm的选择,但是,开发者可能是在重新描述(创建)已经存 在的JavaBean(仍然会导致有冗余的javabean)。
• Struts 2直接使用Action属性作为输入属性,消除了对第二个输入对象的需求。输入属性可能是有自己(子)属性的rich对象类型。Action属性能够通过 web页面上的taglibs访问。Struts2也支持ActionForm模式。rich对象类型,包括业务对象,能够用作输入/输出对象。这种 ModelDriven 特性简化了taglib对POJO输入对象的引用。
表达式语言:
• Struts1 整合了JSTL,因此使用JSTL EL。这种EL有基本对象图遍历,但是对集合和索引属性的支持很弱。
• Struts2可以使用JSTL,但是也支持一个更强大和灵活的表达式语言--"Object Graph Notation Language" (OGNL).
绑定值到页面(view):
• Struts 1使用标准JSP机制把对象绑定到页面中来访问。
• Struts 2 使用 "ValueStack"技术,使taglib能够访问值而不需要把你的页面(view)和对象绑定起来。ValueStack策略允许通过一系列名称相同但类型不同的属性重用页面(view)。
类型转换:
• Struts 1 ActionForm 属性通常都是String类型。Struts1使用Commons-Beanutils进行类型转换。每个类一个转换器,对每一个实例来说是不可配置的。
• Struts2 使用OGNL进行类型转换。提供基本和常用对象的转换器。
校验:
• Struts 1支持在ActionForm的validate方法中手动校验,或者通过Commons Validator的扩展来校验。同一个类可以有不同的校验内容,但不能校验子对象。
• Struts2支持通过validate方法和XWork校验框架来进行校验。XWork校验框架使用为属性类类型定义的校验和内容校验,来支持chain校验子属性
Action执行的控制:
• Struts1支持每一个模块有单独的Request Processors(生命周期),但是模块中的所有Action必须共享相同的生命周期。
• Struts2支持通过拦截器堆栈(Interceptor Stacks)为每一个Action创建不同的生命周期。堆栈能够根据需要和不同的Action一起使用
Ajax部分
1定义:多种应用异步通信,能够数据的局部刷新,使用户有不间断的体验
ajax 的四项技术:
javascript, css, dom, XMLHttpRequest
2四者之间的关系:
1. javascript 创建XMLHttpRequest 对象并用它与web server 通信获得数据;
2. javascript 使用dom 的api 将数据更新到页面;
3. javascript 将css 应用到HTML 的元素上
Ajax的传输步骤------------------------------à
3 ajax 的编程步骤:
1. 创建XMLHttpRequest 对象xhr;
2. 使用xhr 的open 函数打开资源;open("GET or POST" , "传向的页面"+如果是GET要加参数(不用加/));
3. 使用xhr 的onreadystatechange 属性注册处理应答的回调函数的句柄;(为什么只传句柄?如果传display()的话相当于传入的是函数值,
而传入display()的话是将整个函数传给它,当有变化时交个这个函数来处理传入display()还会出错?)
4. (在使用POST 方法使用)使用xhr 的setRequestHeader 设置请求头。通常设置content-type
请求头,可能的值是:application/x-www-form-urlencoded 和text/xml;
5. 使用xhr 的send 方法发送请求;
6. 编写回调函数处理应答:在此函数里通过xhr 的readyState 属性判断通信是否结束(等于4 结束);然后再通过xhr 的status 属性判断web server 是否正确处理应答(等于200 正确),如果正确处理应答,应答的文本存放在xhr 的responseText 属性中,应答是xml 再将生成的xml 文档放在xhr 的responseXML 中 传XML文档只能用POST方法传
res.getCharactorEncoding();可获得res的字符编码
res.setCharactorEncoding("UTF-8");
用DOM api 解析XML 文档的步骤:
1. 创建DocumentBuilderFactory:
DocumentBu ilderFactory dbf = DocumentBuilderFactory.newInstance();
2. (可选)设置dbf 的属性:
设置合法性检测: dbf.setValidating(true);
设置处理名字空间: dbf.setNamespaceAware(true);
3.创建DocumentBuilder:
DocumentBu ilder db = dbf.newDocumentBuilder();
4a.解析XML 文档:
Document doc = db.parse(xmlResource);
4b.生成XML 文档:
Document doc = db.newDocument();
4 XMLHTTPRequest的属性和方法介绍
方法属性:
open(string method, string url, boolean asynch, string username, string password):post还是get,url地址,同步还是异步 后面三个参数是可选的
void send(content):
string getAllResponseHeaders()
void setRequestHeader(string header, string value):这个方法为HTTP请求中一个给定的首部设置值。它有两个参数,第一个串表示要设置的首部,第二个串表示要在首部中放置的值。需要说明,这个方法必须在调用open()之后才能调用。
string getResponseHeader(string header):
onreadystatechange :每个状态改变时都会触发这个事件处理器,通常会调用一个JavaScript函数、回调函数
readyState:请求的状态。有5个可取值:0 = 未初始化,1 = 正在加载,2 = 已加载,3 = 交互中,4 = 完成
responseText:服务器的响应,表示为一个串
responseXML:服务器的响应,表示为XML。这个对象可以解析为一个DOM对象
statusText:HTTP状态码的相应文本(OK或Not Found(未找到)等等)
5 kettasAjax 封装XMLHttpRequest 四个方法说明:
1.kettasAjax.doGetText(url, textHandler, async):
用GET 方法发出请求到url, 返回的文本responseText 作为回调函数textHandler 的参数。
textHandler 的签名类似function(txt).
2.kettasAjax.doGetXml(url, xmlHandler, async):
用GET 方法发出请求到url, 返回的XML Document 也就是responseXML 作为回调函数
xmlHandler 的参数。xmlHandler 的签名类似function(doc).
3.kettasAjax.doPostText(url, textHandler, body, async):
用POST 方法将请求体body 发送到url, 返回的文本responseText 作为回调函数textHandler
的参数。textHandler 的签名类似function(txt).
4.kettasAjax.doPostXml(url, xmlHandler, body, async):
用POST 方法将请求体body 发送到url, 返回的XML Document 也就是responseXML 作为
回调函数xmlHandler 的参数。xmlHandler 的签名类似function(doc).
6 AJAX全称为"Asynchronous JavaScript and XML"(异步JavaScript和XML),是指一种创建交互式网页应用的
网页开发技术。它容许网页的部分刷新,比起传统的整个页面刷新,传输的数据量大大减少,这样就显著提高了
用户浏览web页面的速度。
1) 获取ajax核心对象XMLHttpRequest的标准JavaScript函数 :
function createXhr(){
// 判断是IE浏览器还是其他浏览器,然后返回对象
if(window.ActiveXObject){
return new ActiveXObject("Microsoft.XMLHTTP");
}else if(window.XMLHttpRequest){
return new XMLHttpRequest();
}else{ throw new Error("Does not ajax programming");}
}
注 : 下面的笔记中,都用 createXhr()这个函数获取XMLHttpRequest对象
2) 实例 : 在网页中验证电子邮箱输入的格式
(1) 网页文件
<title>Validate Email Format</title>
<!-- createXhr()以及其他工具函数都在utils.js中 -->
<script type="text/javascript" src="js/utils.js"></script>
<script type="text/javascript">
//以下是标准的步骤,要牢记
var xhr = null;
function validateEmail(){
xhr = createXhr();// 获取XMLHttpRequest对象
// 要提交到服务器的请求,escape函数用于除去字符串两端空格,$函数根据id获得对象(此方法在util.js中) vm是servlet的访问地址
var url = "vm?mail=" + escape($("mail").value);
/* 指定发送的方式、接受请求的页面(或servlet)、是否采用异步回调
* 参数也可以是("GET", url, true)
* true是默认的,故可省略不写,表示采用异步回调
*/
xhr.open("GET", url);
// 指定异步回调函数
xhr.onreadystatechange = displayStatus;
// 若采用POST方式提交请求,这里就必须发送请求体,这里是GET方式,故为NULL
//请求体的设置xhr.setRequestHeader(“Content-Type”,”application/x-www-form-urlencoded”);
xhr.send(null);
}
//异步回调函数的实现
function displayStatus(){
// readyState是服务端状态, 0代表无状态,1代表建立连接,2准备发送数据,3正在发送数据 ,等于4表明对请求响应完毕,该返回客户端的数据都返回了
if(xhr.readyState == 4){
// status是客户端状态,等于200表明数据已经存在于缓存,可以直接拿来使用了
400 url出错,500内部出错
if(xhr.status == 200){
var status = $("mailFormatStatus");
// 获得服务器返回的文本数据
var mailStatus = xhr.responseText;
if(mailStatus == "true"){
status.innerHTML = "valid email format";
}else{ status.innerHTML = "invalid email format"; }
}else{// 获得客户端状态值以及对应的描述性文本
errorHandler(xhr.status, xhr.statusText);}
}else{ status.innerHTML = "please wait..."; }}
</script>
</head>
<body>
<h2>Please enter an email address to validate</h2>
<input type="text" id="mail" onblur="validateEmail()"/><br/>
<div id="mailFormatStatus"></div>
</body>
</html>
(2) Servlet文件ValidateEmailFormatServlet.java片段
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
// 注意这里不是"text/html" 因为我们只需要返回纯文本字符串即可 故用"text/plain"
response.setContentType("text/plain;charset=gbk");
String mail = request.getParameter("mail");
String status = "false";
if(mail != null){
Pattern p = Pattern.compile("\\S+@\\S+");
Matcher m = p.matcher(mail);
if(m.matches()) status = "true";
}
PrintWriter out = response.getWriter();
out.print(status);
out.flush();
//这里关闭与否 应该无关痛痒
out.close();
}
(3) web.xml片段
<servlet>
<description>
</description>
<display-name>
ValidateEmailFormatServlet</display-name>
<servlet-name>ValidateEmailFormatServlet</servlet-name>
<servlet-class>
com.kettas.ajax.xhr.day1.ValidateEmailFormatServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>ValidateEmailFormatServlet</servlet-name>
<url-pattern>/vm</url-pattern>
</servlet-mapping>
3) 向服务器请求获得xml文件(对象)
本例中,得到从服务器返回的xml对象,然后将数据填充到表格
<title>Online super store</title>
<script type="text/javascript" src="js/utils.js"></script>
<script type="text/javascript">
var xhr = null;
function initStore(){
xhr = createXhr();
xhr.open("GET", "xmlListProduct.jsp");
xhr.onreadystatechange = display;
xhr.send(null);
}
function display(){
if(xhr.readyState == 4){
if(xhr.status == 200){
fillStoreTable(xhr.responseXML);
}else{
errorHandler(xhr.status, xhr.statusText);
}}}
function fillStoreTable(productsRoot){
// 将xml数据,填充表格,具体过程略
...
}
</script>
</head>
<body onload="initStore();">
<table id="storeTable">
<tbody id="storeBody">
</tbody>
</table>
</html>
xmlListProduct.jsp文件内容如下 :
<!-- 注意,客户端需要的是xml文件,故这里是"text/xml" -->
<%@page language="java" contentType="text/xml; charset=ISO-8859-1"
pageEncoding="ISO-8859-1"%>
<%@taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%>
<jsp:useBean id="store" class="com.kettas.ajax.xhr.day1.SuperStore" scope="application"/>
<products>
<c:forEach var="product" items="${store.products}">
<product>
<id>${product.id }</id>
<name>${product.name }</name>
<price>${product.price}</price>
</product>
</c:forEach>
</products>
7 封装ajax的调用步骤 :
获得XMLHttpRequest对象,然后向服务器发送请求,最后对服务器返回的数据进行处理。这个过程
千篇一律,每次按照相同的步骤书写,故将它们封装起来是当务之急。这样的话,调用起来会方便
得多。
封装的具体实现如下所示(十分经典的javascript代码,掌握并牢记) :
function Request(l){
var xhr = createXhr();// 私有属性 XMLHttpRequest对象
var listener = l; // 私有属性 此对象负责对服务端响应作反应
// 保险起见 若初始化时没有赋予对象 则赋予一个新创建的对象
if(!listener) listener = new ResponseListener();
// 公有属性 请求体内容的类型 默认的表示发送的是表单数据
// 若发送的是xml类型数据 则this.contentType = "text/xml";
// 实际上 这个是无关痛痒的 都用默认的类型即可
this.contentType = "application/x-www-form-urlencoded";
// 公有方法 以GET方式发送请求
this.doGet = function(url,async){
sendRequest("GET", url, null, async);
}
// 公有方法 以POST方式发送请求
this.doPost = function(url, requestBody, async){
sendRequest("POST", url, requestBody, async);
}
// 私有方法 向服务器发送请求
function sendRequest(method, url, requestBody, async){
if(async == undefined) async = true; // 如果没有说明是否采用异步回调 则采用异步回调
// 如果async有值 并且async != ( false || 0 || null ) 则采用异步回调
else if(async) async = true;
else async = false; // 其他情况( async = false || 0 || null ) 则不采用异步回调
// 指定发送的方式、接受请求的页面(或servlet)、是否采用异步回调
xhr.open(method, url, async);
if(async) xhr.onreadystatechange = callback; // 指定异步回调函数
// 设置请求体的内容类contentType =application/x-www-form-urlencoded”
xhr.setRequestHeader("Content-Type", contentType);
xhr.send(requestBody); // 发送请求
// 若没有采用异步回调 则执行此函数
if(!async) listener.complete(
xhr.status, xhr.statusText, xhr.responseText, xhr.responseXML
);
}
// 私有函数 异步回调函数的实现
function callback(){
switch(xhr.readyState){
case 0: listener.uninitialized(); break;
case 1: listener.loading(); break;
case 2: listener.loaded(); break;
case 3: listener.interactive(); break;
case 4: listener.complete(
xhr.status, xhr.statusText, xhr.responseText, xhr.responseXML
); break;
}}
// 私有函数
function createXhr(){
if(window.ActiveXObject){
return new ActiveXObject("Microsoft.XMLHTTP");
}else if(window.XMLHttpRequest){
return new XMLHttpRequest();
}else{
throw new Error("Does not ajax programming");
}}}
// 此对象处理服务端响应
function ResponseListener(){
this.uninitialized = function(){}
this.loading = function(){}
this.loaded = function(){}
this.interactive = function(){}
// 最后一个响应状态 响应完毕
this.complete = function(status, statusText, responseText, responseXML){}
/* 继承上面的对象
* 对于其它四个状态 一般是不需要处理响应的 故这里不予理会
* 只是覆盖响应完毕后的处理方法
*/
function ResponseAdapter(){
this.handleText = function(text){}
this.handleXml = function(xml){}
this.handleError = function(status, statusText){
alert("Error: " + status + " " + statusText);
}
// 覆盖父类的方法
this.complete = function(status, statusText, responseText, responseXML){
if(status == 200){
this.handleText(responseText);
this.handleXml(responseXML);
}else{
this.handleError(status, statusText);
}
}
}
// 注意 javascript的继承机制
ResponseAdapter.prototype = new ResponseListener();
ResponseAdapter.prototype.constructor = ResponseAdapter;
// 对以上部分实现功能的组织调用类
if(!kettasAjax) var kettasAjax = {};
// 采用GET方式发送请求 对返回的文本数据进行处理
if(!kettasAjax.getText){
kettasAjax.getText = function(url, handleText, async){
var l = new ResponseAdapter();
l.handleText = handleText;
var req = new Request(l);
req.doGet(url, async);
}}
// 采用GET方式发送请求 对返回的XML数据进行处理
if(!kettasAjax.getXml){
kettasAjax.getXml = function(url, handleXml, async){
var l = new ResponseAdapter();
l.handleXml = handleXml;
var req = new Request(l);
req.doGet(url, async);
}
}
// 采用POST方式发送请求 对返回的文本数据进行处理
if(!kettasAjax.postText){
kettasAjax.postText = function(url, requestBody,handleText, async){
var l = new ResponseAdapter();
l.handleText = handleText;
var req = new Request(l);
req.doPost(url,requestBody, async);
}
}
// 采用POST方式发送请求 对返回的XML数据进行处理
if(!kettasAjax.postXml){
kettasAjax.postXml = function(url, requestBody, handleXml, async){
var l = new ResponseAdapter();
l.handleText = handleXml;
var req = new Request(l);
req.doPost(url, requestBody, async);
}
}
注 : 以后就使用这个kettasAjax对象,会方便异常
8 采用kettasAjax对象,采用不同的方式向服务器发送不同类型的数据
(1) html文件如下 :
<title>Send Request Parameters</title>
<script type="text/javascript" src="js/utils.js"></script>
<script type="text/javascript" src="js/kettasAjax.js"></script>
<script type="text/javascript">
function handleForm(httpMethod){
if(httpMethod == "GET"){
// 采用GET方式向服务器发送表单数据
kettasAjax.getText("echo.jsp?" + getQueryString(), display);
}else{
// 采用POST方式向服务器发送表单数据
kettasAjax.postText("echo.jsp", getQueryString(), display);
}}
// 显示服务器返回的数据
function display(txt){
$("response").innerHTML = txt;
}
// 获得表单数据的字符串组织形式
function getQueryString(){
var name = escape(getEavById("name"));
var age = escape(getEavById("age"));
var password = escape(getEavById("password"));
var queryStr = "name=" + name +"&age=" + age + "&password=" + password;
return queryStr;
}
// 采用POST方式向服务器发送xml数据
// 由于发送的是xml形式字符串 故服务端不能以getParameter的方式读取 要以readLine方式读取
function handleXmlForm(){
kettasAjax.postText("handleXml", getXmlFromForm(), display);
}
// 获得表单数据的xml字符串表现形式
function getXmlFromForm(){
var name = getEavById("name");
var age = getEavById("age");
var password = getEavById("password");
var xmlStr = "<params>"
+ "<name>" + name + "</name>"
+ "<age>" + age + "</age>"
+ "<password>" + password + "</password>"
+ "</params>"
return xmlStr;
}
</script>
</head>
<body>
<form action="#">
Name: <input type="text" id="name"/><br/>
Age: <input type="text" id="age"/><br/>
Password: <input type="password" id="password"/><br/>
</form>
<button onclick="handleForm('GET')">Get</button>
<button onclick="handleForm('POST')">Post</button>
<button onclick="handleXmlForm()">Send Parameters as XML</button><br/>
<div id="response"></div>
</body>
</html>
(2) echo.jsp内容如下 :
Http method: ${pageContext.request.method}, and parameters
are name: ${param.name}, age: ${param.age}, password: ${param.password}
(3) Servlet文件HandleXmlServlet片段如下 :
protected void doPost(HttpServletRequest request,
HttpServletResponse response) throws ServletException, IOException {
response.setContentType("text/plain");
StringBuilder sb = new StringBuilder();
// 由于客户端发送的是xml形式的字符串 故要获得BufferedReader对象用于读取
BufferedReader reader = request.getReader();
PrintWriter out = response.getWriter();
String line = null;
// 可能客户端是采用多行发送的 故不能只读取一行了事
while ((line = reader.readLine()) != null)
sb.append(line);
// 解析xml数据
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
try {
DocumentBuilder db = dbf.newDocumentBuilder();
// 由于不存在.xml文件 故只能以内存字节流的方式初始化
Document doc = db.parse(
new ByteArrayInputStream(sb.toString().getBytes())
);
String name = getElementData(doc, "name");
String age = getElementData(doc, "age");
String password = getElementData(doc, "password");
out.print("Parameters are name: " + name
+ " age: " + age
+ " password: " + password
);
} catch (Exception e) {
e.printStackTrace();
out.print("error");
}
out.close();
}
9 页面部分更新,只更新表格部分
(1) html文件的实现 :
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Online super store</title>
<script type="text/javascript" src="js/utils.js"></script>
<script type="text/javascript" src="js/kettasAjax.js"></script>
<script type="text/javascript">
function initStore(){
kettasAjax.getText("listProduct.jsp", display);
}
function display(txt){
$("store").innerHTML = txt;
}
</script>
</head>
<body onload="initStore();">
<div id="store"></div>
</body>
</html>
(2) listProduct.jsp内容如下 :
<%@ page language="java" contentType="text/html; charset=GBK"
pageEncoding="GBK"%>
<%@taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%>
<jsp:useBean id="store" class="com.kettas.ajax.xhr.day1.SuperStore" scope="application"/>
<table border="1">
<tbody>
<c:forEach var="product" items="${store.products}">
<tr>
<td>${product.id }</td>
<td>${product.name }</td>
<td>${product.price}</td>
</tr>
</c:forEach>
</tbody>
</table>
10 于Ajax中的Json
Json 是一种线上协议,轻量级的xml 一种文体格式
Json的功能,简单的说,就是实现字符串和对象之间的转换。要使用其功能,在客户端,要引入
json.js文件,在服务器端,则要引入json.jar这个包。
Json对象和数组 {}大括号代表一个对象,,【】代表数组
(1) Json在客户端的应用实例 :
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>JSON test</title>
<script type="text/javascript" src="js/json.js"></script>
<script type="text/javascript" src="js/utils.js"></script>
<script type="text/javascript">
// 创建一个对象,注意格式 {}
var product = {
id: 1, name: "core java", price: 100 };
// 创建一个对象字符串,注意格式
var s = '{"id": 1, "name": "George", "age":30}';
// 实现对象和字符串之间的互换 只须stringify和parse这两个方法即可
function display(){
// 将对象转换为字符串,发送给服务器处理
byId("json").innerHTML = JSON.stringify(product);
// 将字符串转换为对象,把服务端的字符串组装成对象
var student = JSON.parse(s);
alert(student.id + "\t" + student.name + "\t" + student.age); }
</script>
</head>
<body onload="display()"><h2>
<div id="json"></div>
</h2>
</body>
</html>
(2) Json在客户端和服务器端同时应用 :
1 客户端html文件 :
<html><head>
<title>Query Price</title>
<script type="text/javascript" src="js/json.js"></script>
<script type="text/javascript" src="js/kettasAjax.js"></script>
<script type="text/javascript" src="js/utils.js"></script>
<script type="text/javascript">
function query(){
// obj4form是一个工具函数 根据表单id 将用户输入表单的数据封装为一个对象
var car = obj4form("carInfo");
// 直接向服务器发送对象字符串
//"queryPrice"是访问servlet地址,JSON.stringify(car)发送对象字符串,display为服务器返回内容
kettasAjax.postText("queryPrice", JSON.stringify(car), display);
}
function display(txt){
// 将从服务器返回的对象字符串转换为对象
var ret = JSON.parse(txt);
var model = ret.model;
var price = ret.price;
$("result").innerHTML = model + " " + price
}
</script>
</head>
<body>
<h2>Please enter car information</h2>
<!-- 利用输入的汽车id和生产厂商信息 得以查询汽车的价格 -->
<form action=#" id="carInfo">
Id: <input type="text" id="id"/><br/>
Make: <input type="text" id="make"/><br/>
</form>
<button onclick="query()">Query Price</button>
<div id="result"></div>
</body>
</html>
2 Servlet文件QueryPriceServlet的片段 :
protected void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
response.setContentType("text/plain");
BufferedReader reader = request.getReader();//得到客户端的字符串
PrintWriter out = response.getWriter();
StringBuilder sb = new StringBuilder();
String str = null;
while((str = reader.readLine()) != null)
sb.append(str);
try { // 将客户端发送的对象字符串转化为JSONObject对象
JSONObject jsonObj = new JSONObject(sb.toString());
// 获得对象中的数据 注意格式
int id = jsonObj.getInt("id");
String make = jsonObj.getString("make");
// 不用make.equals("Audi") 避免了make == null的情况下抛异常 小技巧
if(id == 1 && "Audi".equals(make)){
// 给客户端回应一个对象字符串 注意格式// 首先创建一个新的JSONObject对象
jsonObj = new JSONObject();
// 向这个对象中填充数据
jsonObj.put("model", "A8");
jsonObj.put("price", 860000);
// 将对象转化为对象字符串 回发给客户端
// 这个对象字符串的格式为 : '{"model" : "A8" , "price" : 860000}'
out.print(jsonObj.toString());
}
} catch (Exception e) {
e.printStackTrace(); throw new ServletException(e);
}finally{ out.close();
}}
(3) 在服务器端将java中的实体bean、collections、array以及map对象转换为对象字符串 :
要实现这个功能,可以借助内部工具类JsonUtil.java,其中的几个方法是很有用的,如 :
public static JSONObject bean2Json(Object bean),
public static JSONObject map2Json(Map map),
public static JSONArray array2Jarr(Object value),
public static JSONArray collection2Json(Object value)
测试类片段如下 :
public class TestJsonUtil {
public static void main(String[] args) {
SuperStore ss = new SuperStore();
// ss.getProducts()方法返回一个商品对象的集合
JSONArray jarr = JsonUtil.collection2Json(ss.getProducts());
System.out.println(jarr.toString());
}
}
11 关于Ajax中的dwr框架 :
有了dwr框架,操作html页面感觉就是在直接操作java代码。实际上,还是通过服务器端执行的。
要使用此框架提供的功能,必须引入dwr.jar这个包。
思想:在web上的servlet动态生成javascript代码
(1) 最简单的实例
java类文件 :
package com.kettas.ajax.dwr.day4;
public class Hello {
public String sayHello(String user){
try{
Thread.sleep(2000);
}catch(Exception e){
}
return "Hello, " + user;
}
}
配置文件dwr.xml(与web.xml处于同一个位置) :
<dwr>
<allow>
<!-- javascript="hello"指定客户端对象名称为hello
creator="new"指定java对象的创建方式
scope="application"指定java对象的放置位置 -->
<create javascript="hello" creator="new" scope="application">
<param name="class" value="com.kettas.ajax.dwr.day4.Hello"></param>
</create>
</allow>
</dwr>
客户端html文件的内容 :
<!-- 服务器动态生成的js文件 内有hello对象 -->
<script type="text/javascript" src="dwr/interface/hello.js"></script>
<!-- dwr引擎包 -->
<script type="text/javascript" src="dwr/engine.js"></script>
<!-- 引入dwr提供的工具包 -->
<script type="text/javascript" src="dwr/util.js"></script>
<script type="text/javascript">
// element.style.visibility = "hidden";
// element.style.visibility = "";
// CSS内容 对象的隐藏属性
/*
// 这样写的话 仅此函数就可以了
function sayHello(){
// 直接给参数赋内部函数
hello.sayHello($("user").value, function(txt){
dwr.util.setValue("helloStr", txt);
});
}*/
/*
// 也可以这么写 最简便
function sayHello(){
// hello是在dwr.xml配置的,hello.sayHello相当调用服务端的方法,($("user").value 参数,display回调方法,返回值就是display(text)中的text值
hello.sayHello($("user").value, display);
}
*/
// 这么写 是最完整的
function sayHello(){
//dwr.util.useLoadingMessage("Wait a minute...");
// 给参数赋一个对象
var cb = {
callback: display, // 指定回调函数
timeout: 500, // 指定等待时间 如果超出这个时间 则出错
errorHandler: display, // 指定处理错误的回调函数
// ???
name: "George"
};
// 在客户端调用方法 始终比服务端对应方法要多一个参数
// 这个参数最终的实现是一个回调函数
// 这个回调函数的参数就是服务端方法返回的对象(在客户端是javascript对象)
hello.sayHello($("user").value, cb);
}
function display(txt){
// 调用工具包中的工具函数 为容器添加文本
dwr.util.setValue("helloStr", txt);
}
</script>
</head>
<body>
<h2 id="h2">Please enter a user who you want to say hello to:</h2>
User: <input type="text" id="user"/><button onclick="sayHello()">Say Hello</button><br/>
<div id="helloStr"></div>
</body>
</html>
在web.xml文件中不妨加上如下内容作为测试用 :
<servlet>
<servlet-name>dwr</servlet-name>
<servlet-class>org.directwebremoting.servlet.DwrServlet</servlet-class>
<init-param>
<param-name>debug</param-name>
<param-value>true</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>dwr</servlet-name>
<url-pattern>/dwr/*</url-pattern>
</servlet-mapping>
(2) 实例 : 人员管理
Person实体类 : Integer id; String name; String address; float salary;
人员管理类PersonMgmt :
package com.kettas.ajax.dwr.day5;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
public class PersonMgmt {
private Map<Integer, Person> people = new HashMap<Integer, Person>();
private static int count = 10;
public PersonMgmt() {
Person fred = new Person("Fred", "1 Red Street", 2, 100000.0f);
Person jim = new Person("Jim", "42 Brown Lane", 3, 20000.0f);
Person shiela = new Person("Shiela", "12 Yellow Road", 4, 3000000.0f);
people.put(new Integer(fred.getId()), fred);
people.put(new Integer(jim.getId()), jim);
people.put(new Integer(shiela.getId()), shiela);
}
public Collection<Person> getPeople() {
return people.values();
}
public Person getPerson(Integer id) {
return people.get(id);
}
public void add(Person p) {
//如果p中没有值时执行
if (p.getId() == -1)
p.setId(count++);
people.put(p.getId(), p);
}
public void remove(Person p) {
people.remove(p.getId());
}
}
配置文件dwr.xml :
<dwr>
<allow>
<create javascript="personMgmt" creator="new" scope="session">
<param name="class" value="com.kettas.ajax.dwr.day5.PersonMgmt"></param>
</create>
<!-- 由于客户端传入的是javascript的Person对象
故这里要声明对应的服务器端java的Person对象
否则服务端将无法将对象从客户端类型转换为服务端类型
<convert》转换器,对象到字符串之间的转换 -->
<convert match="com.kettas.ajax.dwr.day5.Person" converter="bean"></convert>
</allow>
</dwr>
客户端html文件内容 :
<title>Person Management</title>
<script type="text/javascript" src="dwr/engine.js"></script>
<script type="text/javascript" src="dwr/util.js"></script>
<script type="text/javascript" src="dwr/interface/personMgmt.js"></script>
<script type="text/javascript">
// 初始化显示人员信息
function init(){
personMgmt.getPeople(fillTable);
}
// 函数数组
var peoplebodyCellFuncs = [
// 函数参数为人员对象 返回值即我们要在单元格中显示的内容
// 此数组的长度即表主体显示内容的列数
function(person) { return person.name;},
function(person) { return person.address;},
function(person) { return person.salary;},
// 在第四列添加两个操作按钮
function(person) {
var div = document.createElement("div");
div.appendChild(createBtn("personId", person.id, prepareEdit, "Edit"));
div.appendChild(createBtn("personId", person.id, removePerson, "Remove"));
return div;
}
];
// 编辑人员信息
function prepareEdit(){
var personId = this.personId; // 获得此人id 注意这里this的使用
personMgmt.getPerson(personId, fillFields) // 调用服务端方法 获得此人对象
}
// 填充人员信息编辑表
function fillFields(person){
// person对象如下 : {id : 1, name : "yinkui", address : "xf", salary : 50000}
// 相当于执行 : document.getElementById("id").value = 1
// document.getElementById("name").value = "yinkui" ...
dwr.util.setValues(person);
}
// 删除一个人
function removePerson(){
var personId = this.personId; // 获得此人id 注意这里this的使用
// 由于对应服务端java方法中 删除一个人只需要此人id
// 故 创建person对象时只需要赋予id
// 这样传到服务端 创建的java的Person对象顶多是其他属性为null
var person = {
id: personId
};
personMgmt.remove(person, init); // 调用服务端方法删除此人 然后刷新人员信息表的显示
}
// 创建按钮的函数
function createBtn(attrName, attrValue, onclick, label){
var btn = document.createElement("button");
btn[attrName] = attrValue; // 按钮对象中存放对应人员id值
btn.onclick = onclick; // 为按钮添加事件
btn.appendChild(document.createTextNode(label)); // 为按钮添加显示内容
return btn;
}
// 填充人员信息表
// 其中people对象是从服务端返回的人员对象数组
// 其格式为[{id : 1, name : "yinkui", address : "xf", salary : 50000}, {...}, ...]
function fillTable(people){
dwr.util.removeAllRows("peoplebody");// 调用工具函数 清空表主体中的所有行
// 调用工具函数 通过人员对象数组 为表主体添加行
// 其中peoplebodyCellFuncs是一个函数数组
dwr.util.addRows("peoplebody", people, peoplebodyCellFuncs);
clearPerson();// 初始化人员信息编辑表的内容
}
// 添加新人员
function writePerson(){
var person = {
id: -1,
name: null,
address: null,
salary: null
};
// 注意此工具函数的使用
// 其作用与dwr.util.setValues({...})恰恰相反
// 相当于执行 : person.id = document.getElementById("id").value
// person.name = document.getElementById("name").value
dwr.util.getValues(person);
personMgmt.add(person, init); // 调用服务端方法 然后刷新人员信息表的显示
}
// 初始化显示人员信息编辑表
function clearPerson(){
// 注意此工具函数的使用 参数为对象
// 相当于执行 : document.getElementById("id").value = -1
// document.getElementById("name").value = null ...
dwr.util.setValues(
{ id: -1, name: null,
salary: null, address: null
}
);
}
</script>
</head>
<body onload="init()">
<h1>Dynamically Editing a Table</h1>
<h2>People Management</h2>
<!-- 人员信息列表 -->
<table border="1">
<!-- 表头 -->
<thead>
<tr> th>Name</th>
<th>Address</th>
<th>Salary</th>
<th colspan="2">Actions</th></tr>
</thead>
<!-- 表的主体 -->
<tbody id="peoplebody"></tbody>
</table>
<h4>Edit Person</h4>
<!-- 对人员信息进行编辑 或是添加新人员 -->
<table>
<tr><td>ID:</td>
<td><span id="id">-1</span></td></tr>
<tr><td>Name:</td>
<td><input id="name" type="text" /></td></tr>
<tr><td>Salary:</td>
<td><input id="salary" type="text" /></td></tr>
<tr><td>Address:</td>
<td><input type="text" id="address" /></td></tr>
<tr><td colspan="2" align="right">
<input type="button" value="Save" onclick="writePerson()" />
<input type="button" value="Clear" onclick="clearPerson()" /></td></tr>
</table></body></html>
12,关于DOJO的框架:
含义:Dojo是一个非常强大的面向对象的JavaScript的工具箱, 建议读者能够去补充一下JavaScript下如何使用OO进行编程的, 这对于你以后阅读Dojo Source有很大的用处
作用:①处理浏览器的不兼容问题,可以方便建立互动的互动用户见面。②更容易统一页面风格。③封装与服务端的通信,动态从服务器下载javascript
开发流程:①把dojo工具包考到js文件下
②引用 dojo 的启动代码
<script type="text/javascript" src="/yourpath/dojo.js" />
③声明你所要用到的包
<script type="text/javascript">
dojo.require("dojo.math");引入数学方法
dojo.require("dojo.io.*"); 引入io
dojo.require("dojo.event.*"); 引入事件
dojo.require("dojo.widget.*");引入页面
function init(){ 初始化
var btn = dojo.widget.byId("helloBtn");
dojo.event.connect(btn, "onClick", "sayHello");事件源,产生关联,时间方法
}
function sayHello(){
dojo.io.bind(
{ url: "sayHello.jsp",处理事件的url
handler: callback, 回调函数
formNode: "myForm" 表单内容
});}
function callback(type, data, evt){
document.getElementById("result").innerHTML
= data;
}
dojo.addOnLoad(init);加入事件
</script>
</head>
<body>
<h2>DOJO Test</h2>
<form action="#" id="myForm">
User: <input type="text" name="user" />
</form>
<button dojoType="Button" widgetId="helloBtn">DOJO Say Hello</button>
<div id="result"></div>
</body>
</html>
13,关于Ext框架
参考项目文档
14补充:Dom部分 创建标签 var td=document.createElement(“td”); td.apperdchild(childName)
javascript的高级部分
1,javascript的对象和继承 由于javascript是采取prototype机制的“伪继承”,prototype必须显示引用“基类”对象,所以注定了javascript只能实现“弱继承”,或者叫做“对象继承”
注意这里的次序关系需要很明确,prototype的赋值必须在对象构造之外。
function classA()
{ classA.prototype.name=”xxx”;属性
classA.prototype.methodA = function() {...}这是一个对象方法
classA.methodA = function(){...} //这是一个类方法相当于java的static方法
}
function classB()
{ classB.prototype.methodA = function(){...}
}
classB.prototype = new classA();
//注意这里声明B继承自A出现在function classB()函数体之外,并且先于
//classB对象的构造被执行。
私有成员的设置
Var name=“zhang”;
this.getName=function(){return name;}
this.setName=function(myname){name=myname; }
Spring
一:Ioc
1含义:为解决企业应用开发的复杂性而创建的开源框架,用基本的javaBean来完成EJB的事情 从大小和开销方向spring都是轻量级的
2,用途
① Ioc容器可以将对象之间的依赖关系交由spring管理,进行控制
② AOP:方便进行面向切面的编程,是oop的扩展,想加什么功能直接加
③ 能够集成各种优秀的框架,struts hibernate等
3,spring 组成内容
4准备配置工作
①下载SpringFramework的最新版本,并解压缩到指定目录。
在IDE中新建一个项目,并将Spring.jar将其相关类库加入项目
② 配置文件 bean.xml
③在classpath创建日志输出文件。log4j.properties
④org.springframework.beans及org.springframework.context包是Spring IoC容器的基础
5 Spring 基础语义
1)IoC (Inversion of Control)=DI (Dependency Injection)控制反转和依赖注入
它是一种基于接口的编程,bean由容器创建在需要的时候拿来用即可,主要是采用反射来实现,其核心组建就是BeanFactory 但实际开发常用XmlBeanFactory
2)依赖注入的几种实现类型
Type1设值注入:通过类的setter方法完成依赖关系的设置,就是给bean类中属性加set方法
Type3 构造子注入:即通过构造函数完成依赖关系的设
public class DIByConstructor {
private final DataSource dataSource;
private final String message;
public DIByConstructor(DataSource ds, String msg) {
this.dataSource = ds;
this.message = msg;
}}
3)几种依赖注入模式的对比总结
Type2 设值注入的优势
1. 对于习惯了传统JavaBean开发的程序员而言,通过setter方法设定依赖关系显得更加直
观,更加自然。
2. 如果依赖关系(或继承关系)较为复杂,那么Type3模式的构造函数也会相当庞大(我们需
要在构造函数中设定所有依赖关系),此时Type2模式往往更为简洁。
3. 对于某些第三方类库而言,可能要求我们的组件必须提供一个默认的构造函数(如Struts
中的Action),此时Type3类型的依赖注入机制就体现出其局限性,难以完成我们期望的功
能。
Type3 构造子注入的优势:
1. “在构造期即创建一个完整、合法的对象”,对于这条Java设计原则,Type3无疑是最好的
响应者。
2. 避免了繁琐的setter方法的编写,所有依赖关系均在构造函数中设定,依赖关系集中呈现,
更加易读。
3. 由于没有setter方法,依赖关系在构造时由容器一次性设定,因此组件在被创建之后即处于
相对“不变”的稳定状态,无需担心上层代码在调用过程中执行setter方法对组件依赖关系
产生破坏,特别是对于Singleton模式的组件而言,这可能对整个系统产生重大的影响。
4. 同样,由于关联关系仅在构造函数中表达,只有组件创建者需要关心组件内部的依赖关系。
对调用者而言,组件中的依赖关系处于黑盒之中。对上层屏蔽不必要的信息,也为系统的
层次清晰性提供了保证。
5. 通过构造子注入,意味着我们可以在构造函数中决定依赖关系的注入顺序,对于一个大量
依赖外部服务的组件而言,依赖关系的获得顺序可能非常重要,比如某个依赖关系注入的
先决条件是组件的DataSource及相关资源已经被设定。
理论上,以Type3类型为主,辅之以Type2
类型机制作为补充,可以达到最好的依赖注入效果,不过对于基于Spring Framework开发的应用而言,
Type2使用更加广泛。
5)bean.xml配置文件
Bean Factory,顾名思义,负责创建并维护Bean实例。
Bean Factory负责根据配置文件创建Bean实例,可以配置的项目有:
1. Bean属性值及依赖关系(对其他Bean的引用)
2. Bean创建模式(是否Singleton模式,即是否只针对指定类维持全局唯一的实例)
3. Bean初始化和销毁方法
4. Bean的依赖关系
6)XmlBeanFactory两中注入方式的配置
①property-------àset方法的注入配置
<p:bean id=”hello” class=”com.kettas.HelloIFImp”>
<p:property name=”user” value=”xxx”></p:property>
</p:bean>
②constructor---------à构造方法的注入配置
<p:bean id="hello2" class="com.kettas.spring.ioc.day1.HelloIFImpl">
<p:constructor-arg index=”0” value="world"></p:constructor-arg>
<p:constructor-arg type="java.lang.String"”ref="calendar"></p:constructor-arg>
</p:bean>
说明: index=”0”构造方法第一个参数,用index可以稍微减少冗余,但是它更容易出错且不如type属性可读性高。你应该仅在构造函数中有参数冲突时使用index。
7) 依赖的目标类型分成三种形式
1) 基本类型+String
<value>data</value>类型自动转化
2) 对其他bean 的引用
<ref bean="target"/>
3) 集合类型 list props set map
list set properties配置类似
<p:property name="intList">
<p:list>
<p:value>1</p:value>
<p:value>2</p:value>
</p:list>
</p:property>
<p:property name="objMap">
<p:map>
<p:entry>
<p:key>
<p:value>1</p:value>
</p:key>
<p:ref local="hello2"/>
</p:entry>
</p:map>
</p:property>
<p:property name="pros">
<p:props>
<p:prop key="1">red</p:prop>
<p:prop key="2">green</p:prop>
</p:props>
</p:property>
Xml配置文件属性的说明
<bean id="TheAction" ⑴ class="net.xiaxin.spring.qs.UpperAction" ⑵ singleton="true" ⑶
init-method="init" ⑷destroy-method="cleanup" ⑸depends-on="ActionManager" ⑹ >
<property…>
</bean>
⑴ id
Java Bean在BeanFactory中的唯一标识,代码中通过BeanFactory获取
JavaBean实例时需以此作为索引名称。
⑵ class Java Bean 类名 即真正接口的实现类
⑶ singleton bean的作用域(创建模式(prototype还是singleton))
单例(Singleton)模式,如果设为“true”,只维护此Java Bean的一个实例,反之,如果设为“false”, BeanFactory每次都将创建一个新的实例返回。默认为true
实现方式是第一次getBean时放入Map中保存,第二次再用时直接在Map中拿,类名为key,实例为value。Bean的其他作用域还有prototype:原型模式:在获取prototype定义的bean时都产生新的实例,其生命周期由客户端维护。Session对每次HTTPsession中都回产生一个新的实例。Global session 仅在使用portletcontext的时候才有效,常用的是singleton和prototype
⑷ init-method
初始化方法,此方法将在BeanFactory创建JavaBean实例之后,在向应用层返回引
用之前执行。一般用于一些资源的初始化工作。在javaBean中创建init方法,再添加属性init-method=“init”就行
⑸ destroy-method
销毁方法。此方法将在BeanFactory销毁的时候执行,一般用于资源释放。与init用法类似
⑹ depends-on
Bean依赖关系。一般情况下无需设定。Spring会根据情况组织各个依赖关系的构建工作(这里
示例中的depends-on属性非必须)。
只有某些特殊情况下,如JavaBean中的某些静态变量需要进行初始化(这是一种Bad
Smell,应该在设计上应该避免)。通过depends-on指定其依赖关系可保证在此Bean加
载之前,首先对depends-on所指定的资源进行加载。
⑺ <value>
通过<value/>节点可指定属性值。BeanFactory将自动根据Java Bean对应的属性
类型加以匹配。
下面的”desc”属性提供了一个null值的设定示例。注意<value></value>代表一
个空字符串,如果需要将属性值设定为null,必须使用<null/>节点。
⑻ <ref>指定了属性对BeanFactory中其他Bean的引用关系。
8)使用抽象bean 定义抽象类Abstract=“true”抽象bean不能实例化,一个类可以创建多个bean
抽象bean的配置和一般bean的配置基本一样只是在增加了Abstract=“true”抽象bean是一个bean的模板,容器会忽略抽象bean的定义,不会实例化抽象bean,故不能通过getBean()显示的获得抽象bean的实例也不能将抽象bean注入其他bean的依赖属性。
抽象bean的配置和继承
通过Abstract属性配置抽象bean
<bean id=”fatherTemple” class=”abstractClass” abstract=”true”>
<!—注入属性à
<property name=”name” ref=”xxx”/>
</bean>
<!—通过parent属性定义子beanà
<bean id=”childTemple” parent=”fatherTemple”>
<property name=”name2” ref=”yyyy”/> -定义自己的属性
</bean>
说明:子bean配置可以增加新的配置信息,并可以定义新的配置覆盖父类的定义
子类和父类中至少有一个class属性否则不知道实现类,父类的class可以不写
9)bean在容器上的生命周期
初始化两种方法 1使用init-method属性指定那个方法在bean依赖关系设置好后自动执行
2实现initializingBean接口 实现该接口必须实现void afterPropertiesSet()throws Exception那么就不用设置init-method方法了,注意:最好使用init-method方法,减少代码的侵入性,如果两种方法都实现则先实现接口再init方法(一般写入日志文件)
销毁两种方法和初始化一样也有两种方法:1,destroy-method和1实现DisposableBean接口
6,Spring容器,最基本的接口就是BeanFactory 负责创建,配置,管理bean 它有一个子接口ApplicationContext并将功能扩展。
理论上bean装配可以从任何资源获得,包括属性文件,关系数据库等,但xml是最常用的XmlBeanFactory , ClassPathXmlApplicationContext , FileSystemXmlApplicationContext ,
XmlWebApplicationContext应用系统配置源。Spring中的几种容器都支持使用xml装配bean,包括:
XmlBeanFactory , ClassPathXmlApplicationContext ,FileSystemXmlApplicationContext ,
XmlWebApplicationContext
BeanFactory接口包含如下的基本方法:
Boolean containsBean(String name): 判断Spring容器是否包含id为name的bean定义。
Object getBean(String name): 返回容器id为name的bean.
Object getBean(String name, Class requiredType): 返回容器中id为name,并且类型为requiredType的bean.
Class getType(String name): 返回容器中id为name的bean的类型.
ApplicationContext有三个实现类实现资源访问
FileSystemXmlApplicationContext:基于文件系统的xml配置文件创建ApplicationContext,
ClassPathXmlApplicationContext :以类加载路径下的xml的配置文件创建ApplicationContext(更为常用)
XmlWebApplicationContext:对使用servletContextResource进行资源访问
获得容器的应用的方式
①InputStream is = new FileInputStream("bean.xml");
XmlBeanFactory factory = new XmlBeanFactory(is);
或者BeanFactory factory = new XmlBeanFactory(
new ClassPathResource("classpath:bean.xml"))
Action action = (Action) factory.getBean("TheAction");
②ApplicationContext bf=new ClassPathXmlApplicationContext("classpath:bean.xml")
Action action = (Action) factory.getBean("TheAction");
③ApplicationContext bf=new FileSystemXmlApplicationContext(“classpath:bean.xml”)
第一种BeanFactory启动快(启动是不创建实体,到用时才创建实体),
第二种ApplicationContext运行快(在加载时就创建好实体)更为常用,继承BeanFactory
5)工厂bean和bean工厂
FactoryBean(工厂bean):是bean的加工工厂,是对已知Bean的加工,是一个接口,要实现三个方法:
① Object getObject()可以对bean进行加工添加功能
②Class getObjectType()③Boolean isSingleton()
Bf.getBean(“ab”)只是得到MyFactory.getObject()的object对象 所以最后要强转。
Beanfactory bean工厂 就是生产bean的工厂,注入:
由于Spring IoC容器以框架的方式提供了工厂方法的功能,并以透明的方式给开发者,不过在一些遗留系统或第三方类库中,我们还会碰到工厂方法,这时用户可以使用Sping使用工厂方法注入的方式进行配置。
静态工厂方法:
很多工厂类方法都是静态的,这意味着用户在无须创建工厂类实例的情况就可以调用工厂类方法。因此静态工厂方法比非静态工厂方法的调用
更加方便。我们将carFactory类的getCar()方法调整为静态的然后再Spring配置如下:
<bean id=”car” class =”carFactory” factory-method=”getCar”/>
用户直接通过class属性指定工厂类, 然后在通过factory-method指定对应的静态工厂方法创建bean。
如果静态工厂方法需要参数则用<p:constructor-arg index=”1”value="calendar"></p:constructor-arg>传入
实例工厂方法:
有些工厂是非静态的,即必须是实例化工厂类才能调用工厂方法。
下面我们实例化一个工厂类CarFactory类来为Car类提供实例。
package com.car;
public class CarFactory
{ public Car getCar(){return new Car();}}
工厂类负责创建一个或多个目标类实例,工厂类方法一般以接口或抽象类变量的形式返回目标类。工厂类对外屏蔽了目标类的实例化步骤。调
用甚至不知道如何具体的目标类是什么。
下面我们在Spring 配置文件中进行配置
<!--工厂Bean生成目标Bean-->
<bean id=”carFactory” class=”com.CarFactory”/>
<!--工厂Bean目标Bean-->
<bean id=”car” factory-bean=”carFactory” factory-method=”getCar”/>
factory-bean=”carFactory”指定了工厂类Bean,factory-method=”getCar”指定了工厂类Bean创建该Bean的工厂方法。
和静态工厂类似如果工厂方法需要参数则用
<p:constructor-arg index=”0”value="calendar"></p:constructor-arg>传入
7,使用ApplicationContext ApplicationContext覆盖了BeanFactory的所有功能,并提供了更多的特,容器创建时就创建了singleton Bean
相对BeanFactory而言,ApplicationContext提供了以下扩展功能:
1. 国际化支持:继承MessageSource接口,提供国际化支持
2. 资源访问:支持对文件和URL的访问。
3. 事件传播:事件传播特性为系统中状态改变时的检测提供了良好支持。
4. 多实例加载:可以在同一个应用中加载多个Context实例,即加多个配置文件
1,国际化处理的步骤
1) 写相应的国家资源文件如:ApplicationResources_zh.properties
注意字符的转化类似struts的国际化
2) bean.xml 的配置
<p:bean id="messageSource[U25] " class="org.springframework.context.support.ResourceBundleMessageSource">
<p:property name="basename" value="com.kettas.spring.ioc.day3.ApplicationResource" />
</p:bean>
<p:bean id="msc" class="com.kettas.spring.ioc.day3.MessageSourceConsumer" />
</p:beans>
3)实现类MessageSourceConsumer
具体的实现类implements MessageSourceAware。注入messageSource ms
得到字符:String name = ms.getMessage("name", null, Locale.CHINA);name是资源文件的key值
Locale.CHINA是中文,Locale.ENGLISH英文
2. 资源访问:即外部资源文件的获取;资源文件
两种引入外部资源的方式
①<!-- <p:bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<p:property name="location" value="com/kettas/spring/ioc/day3/jdbc.properties"></p:property>
</p:bean> -->
②,引入解析:xmlns:context="http://www.springframework.org/schema/context"
<context:property-placeholder location="com/kettas/spring/ioc/day3/jdbc.properties"/>
使用<p:property name="driverClassName" value="${jdbc.driver}"></p:property>
jdbc.driver是资源的key值
其它:ApplicationContext.getResource方法提供了对资源文件访问支持,如:
Resource rs = ctx.getResource("classpath:config.properties");
File file = rs.getFile();
3. 事件传播:事件机制是观察者模式的实现
事件框架两个重要的成员:
1) ApplicationEvent:容器事件,必须由ApplicationContext发布
2) ApplicationListener:监听器:可有其他任何监听器bean担任
3) ApplicationContext是事件源必须由java程序显示的触发
1)事件流程:
2)代码实例:
1, 事件源.
public class LogEventSource implements ApplicationEventPublisherAware
{
private ApplicationEventPublisher publisher; public void setApplicationEventPublisher(
ApplicationEventPublisher publisher){
this.publisher = publisher;
}
public void fireEvent(){
LogEvent evt = new LogEvent(this, "Test message");
publisher.publishEvent(evt);
}
}
2, 事件监听,
public class Logger implements ApplicationListener {
private Log logger = LogFactory.getLog(Logger.class);
public void onApplicationEvent(
ApplicationEvent evt) {
logger.info("Event type: " + evt.getClass().getName());
if(evt instanceof LogEvent){ logger.info(((LogEvent)evt).getMessage());
}}
}
3)配置文件
<p:bean id="les" class="com.kettas.spring.ioc.day3.LogEventSource"> 有相应的事件方法
</p:bean>
<p:bean class="com.kettas.spring.ioc.day3.Logger"></p:bean> 处理事件的后台
</p:beans>
说明:LogEventSourc有相应的事件方法publisher.publishEvent(evt)主动触发容器事件; Logge处理事件的后台
4. 多实例加载
BeanPostProcessor bean后处理器 实现BeanPostProcessor接口 对bean实例进一步增加功能,实现两个方法processBeforeInitialization(Object bean,String name)方法(该方法的第一个参数是将进行后处理的实例bean,name是该bean的名字)和ProcessaAfterInitialization(Object bean,String name).在init()或destory之前做一些处理.Spring的工具类就是通过bean的后处理器完成的。
BeanFactoryPostProcessor 容器后处理器:实现接口BeanFactoryPostProcessor只负责处理容器本身 ,实现的方法是 postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory)参加资源的引入和获取,可以修改bean工厂bean的定义相当一个再配置的过程。类似BeanPostProcessor,ApplicationContext可以自动检测到容器中的容器后处理器,但是BeanFacory必须手动调用。
5,web中如何使用spring
1),加入相应的jar包
2),Web.xml的配置
<listener>
<listener-class> org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
或者
( <servlet><servlet-name>context</servlet-name>
<servlet-class> org.springframework.web.context.ContextLoaderServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet> )
通过以上配置,Web容器会默认自动加载/WEB-INF/applicationContext.xml初始化
ApplicationContext实例,如果需要指定配置文件位置,可通过context-param加以指定:
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/myApplicationContext.xml</param-value>
</context-param>
3) Servlet中应用
WebApplicationContext wac= WebApplicationUtils.getRequiredWebApplication(Context(getServletContext()));
HelloIf h=( HelloIf)wac.getBean(“hello”);
6,BeanFactoryLocator工厂的工厂,主要的两个实现类ContextSingletonBeanFactoryLocator和SingletonBeanFactoryLocator
BeanFactoryLocator bfl = SingletonBeanFactoryLocator.getInstance();
BeanFactoryReference bfr = facLoc.useBeanFactory("il8b");
// BeanFactory fac = bfr.getFactory();
MessageSourceConsumer msc=(MessageSourceConsumer)bfr.getFactory().getBean(“xxxx”);
配置文件只能是beanRefFactory.xml且放在根目录下
<p:bean id=”il8n” class=”org.springframework.context.support.ClassPathXmlApplicationContext”>
<p:constructor-arg><p:value>spring/il8n.xml</p:value></p:constuctor-arg>
</p:bean> 应用另外一个配置文件即另外一个beanFactory
7, .让spring完成自动装配Autowiring 解决<ref>标签为javaBean注入时难以维护而实现的
下面是几种autowire type的说明:
● byname : 试图在容器中寻找和需要自动装配的属性名相同的bean或id,如果没有找到相应的bean,则这个属性未被装配上。配置文件中的id/name中查找
● byType : 试图在容器中寻找一个与需要自动装配的属性类型相同的bean或id,如果没有找到,则该属性未被装配上。
相当set方法注入
● constructor : 试图在容器中寻找与需要自动装配的bean的构造函数参数一致的一个或多个bean,如果没找到则抛出异常。构造方法注入
● autodetect : 首先尝试使用constructor来自动装配,然后再使用byType方式。
Dependeney_cheching 依赖检查 一般和自动装配配套使用 四种类型:name,simple,object ,all
缺点:spring不一定能很准确的找到javaBean的依赖对象,大型应用一般不用,且配置文件可读性差
8,BeanFactoryAware和BeanNameAware
实现 BeanFactoryAware 接口的 bean 可以直接访问 Spring 容器,被容器创建以后,它会拥有一个指向 Spring 容器的引用。
BeanFactoryAware 接口只有一个方法void setBeanFactory(BeanFactorybeanFactory)。配置和一般的bean一样
如果某个 bean 需要访问配置文件中本身的 id 属性,则可以使用 BeanNameAware 接口,该接口提供了回调本身的能力。实现
该接口的 bean,能访问到本身的 id 属性。该接口提供一个方法:void setBeanName(String name)。
9,spring2.5标签的使用(新特性)对属性,方法的注入 减少配置文件是工作量
1)属性,方法,构造函数的标签注入
1)@Autowired @Autowired按byType自动注入 是对属性的注入,按照类型匹配原则(set和constructors)
2)@Resource(name="target"):@Resource默认按 byName自动注入罢了。@Resource有两个属性是比较重要的,分别是name和type,Spring将@Resource注解的name属性解析为bean的名字,而type属性则解析为bean的类型。所以如果使用name属性,则使用byName的自动注入策略,而使用type属性时则使用byType自动注入策略。如果既不指定name也不指定type属性,这时将通过反射机制使用byName自动注入策略。--à常用
3)bean lifecycle :@PostConstruct--àinit注入 @PreDestroy ----àdestory方法注入
4)ClassPath 类路径扫描,就是注入bean
2)Bean的标签注入spring2.5为我们引入了组件自动扫描机制,他可以在类路径底下寻找标注了@Component、@Service、@Controller、@Repository注解的类,并把这些类纳入进spring容器中管理。它的作用和在xml文件中使用bean节点配置组件是一样的。要使用自动扫描机制,我们需要打开以下配置信息:
@Service用于标注业务层组件bean、@Service("studentBiz")。 @Controller用于标注控制层组件(如struts中的action)、
@Repository用于标注数据访问组件,即DAO组件。@Repository("dao")
而@Component泛指组件,当组件不好归类的时候,我们可以使用这个注解进行标注,即就是一般的bean。
二,AOP相关术语
1
▲AOP是OOP的延续,是Aspect Oriented Programming的缩写,意思是面向切面的编程。并不是全部的AOP框架都是一样的。他们连接点模型的功能可能有强弱之分,有些可以字段,方法,构造函数级别的,有些只能是方法的比如spring aop 最主要的三种aop框架:AspectJ Jboss AOP Spring Aop 前面两种都可以对字段方法构造函数的支持。Sping和AspectJ有大量的协作
▲Aop添加的主要功能有:事务管理,安全,日志,检查,锁 等
▲Spring对Aop支持的4种情况:
★经典的基于代理的Aop(各个版本的spring) ★@AspectJ注解驱动的切面(spring 2.0)
★纯POJO切面(spring 2.0) ★注入式AspectJ切面(各个版本的spring)
2 名词解释:
☆关注点 (Concern):关注点就是我们要考察或解决的问题。如订单的处理,用户的验证、用户日志记录等都属于关注点。
关注点中的核心关注点 (Core Concerns) ,是指系统中的核心功能,即真正的商业逻辑。如在一个电子商务系统中,订单处理、客户管理、库存及物流管理都是属于系统中的核心关注点。
还有一种关注点叫横切关注点 (Crosscutting Concerns) ,他们分散在每个各个模块中解决同一样的问题,跨越多个模块。如用户验证、日志管理、事务处理、数据缓存都属于横切关注点。
在 AOP 的编程方法中,主要在于对关注点的提起及抽象 。我们可以把一个复杂的系统看作是由多个关注点来有机组合来实现,一个典型的系统可能会包括几个方面的关注点,如核心业务逻辑、性能、数据存储、日志、授权、安全、线程及错误检查等,另外还有开发过程中的关注点,如易维护、易扩展等。
☆切面 (Aspect):切面是通知和切入点的结合,通知和写入点定义了切面的全部内容—它的功能,在何时何地完成功能。在Spring 2.0 AOP中,切面可以使用通用类(基于模式的风格XML Schema 的风格) 或者在普通类中以 @Aspect 注解(@AspectJ风格)来实现。
下面的例子是基于 xml schema 风格来定义一个 Aspect( 红字部分 ) :
<aop:aspect id="aspectDemo" ref="aspectBean">
< aop:pointcut id = "myPointcut" expression = "execution(* package1.Foo.handle*(..)" />
< aop:before pointcut -ref = "myPointcut" method = "doLog" />
</aop:aspect >
这个定义的意思是:每执行到 package1.Foo 类的以 handle 开头的方法前,就会先执行 aspectBean 的 doLog 方法
☆连接点 (Join point):连接点就是在程序执行过程中某个特定的点,比如某方法调用的时候或者处理异常的时候。这个点可以
是一个方法、一个属性、构造函数、类静态初始化块,甚至一条语句。切面代码可以通过这些点插入到程序的一般流程之中,从而添加新的行为。 而对于 SPRING 2.0 AOP 来说他是基于动态代理的,故
只支持方法连接点,这个一定要记住~!每一个方法都可以看成为一个连接点,(AspectJ和Jboss可以提供其他aop的实现如,
字段构造函数等)只有被纳入某个Point Cut的 JointPoint 才有可能被 Advice 。 通过声明一个org.aspectj.lang.JoinPoint
类型的参数可以使通知(Advice)的主体部分获得连接点信息。JoinPoint 与CutPoint 之间的关系见下面的CutPoint 的讲解
☆切入点 (Pointcut):如果说通知定义了切面的“什么”和“何时”那么切入点就定义了“何地”。切入点指一个或多个连接点,可以理解成连接电点的集合 .我们通常使用明确的类和方法或是利用正则表达式定义这些切入点。 Advice 是通过 Pointcut 来连接和介入进你的 JointPoint 的。
比如在前面的例子中,定义了
< aop:pointcut id = "myPointcut" expression = "execution(* package1.Foo.handle*(..)" /> 那个类的那个方法使用
那么这就是定义了一个PointCut ,该Pointcut 表示“在package1.Foo类所有以handle开头的方法”
假设package1.Foo类里类似于:
Public class Foo {
public handleUpload(){..}
public handleReadFile(){..}
}
那么handleUpload 是一个JointPoint ,handleReadFile 也是一个Joint,那么上面定义的id=”myPointcut” 的PointCut 则
是这两个JointPoint 的集合
☆通知 (Advice):通知定义了切面是什么,以及何时使用,去了描述切面要完成的工作,通知还要解决何时执行这个工作的问题。它应该在某个方法之前调用还是之后调用,或者抛出一个异常。故通知有各种类型Advice。定义了切面中的实际逻辑(即实现 ) ,比如日志的写入的实际代码。换一种说法Advice 是指在定义好的切入点处,所要执行的程序代码 。
通知有以下几种:
§前置通知( Before advice ) :在切入点匹配的方法执行之前运行使用@Before 注解来声明。implements MethodBeforeAdvice实现的方法是public void before(Method method, Object[] args, Object target)
§返回后通知( After returning advice ) :在切入点匹配的方法返回的时候执行。使用 @AfterReturning 注解来声明
§抛出后通知( After throwing advice ) : 在切入点匹配的方法执行时抛出异常的时候运行。使用 @AfterThrowing 注解来声明
§后通知( After (finally) advice ) :不论切入点匹配的方法是正常结束的,还是抛出异常结束的,在它结束后(finally)后通知(After (finally) advice)都会运行。使用 @After 注解来声明。这个通知必须做好处理正常返 回和异常返回两种情况。通常用来释放资源。
§环绕通知( Around Advice ) :环绕通知既在切入点匹配的方法执行之前又在执行之后运行。并且,它可以决定这个方法在什么时候执行,如何执行,甚至是否执行。在环绕通知中,除了可以自由添加需要的横切功能以外,还需要负责主动调用连接点 ( 通过 proceed) 来执行激活连接点的程序 。 请尽量使用最简单的满足你需求的通知。(比如如果前置通知也可以适用的情况下,就不要使用环绕通知)
§环绕通知使用 @Around 注解来声明。而且该通知对应的方法的第一个参数必须是 ProceedingJoinPoint 类型 。在通知体内(即通知的具体方法内),调用 ProceedingJoinPoint 的 proceed() 方法来执行连接点方法 。
☆引入 (Introduction):引入是指给一个现有类添加方法或字段属性,引入还可以在不改变现有类代码的情况下,让现有的 Java 类实现新的接口 (以及一个对应的实现 )。相对于 Advice 可以动态改变程序的功能或流程来说,引入 (Introduction) 则用来改变一个类的静态结构 。比如你可以使用一个引入来使bean实现 IsModified 接口,以便简化缓存机制。举例来说,我们可以创建一个Audiable通知类,记录对象在最后一次被修改是时的状态,这很简单,只要一个方法,setLastModified(Date),和一个实例变量来保存这个状态,这个新的方法和实例变量然后就可以引入到现在的类,从而在不修改他们的情况下让他们有新的行为和状态。
☆目标对象( Target Object )被一个或者多个切面(aspect)所通知(advise)的对象。也有人把它叫做 被通知(advised) 对象。 既然Spring AOP是通过运行时代理实现的,这个对象永远是一个 被代理(proxied )对象。
☆AOP 代理( AOP Proxy )“代理”是向目标对象应用通知之后被创建的对象,对客户端来说目标对象和代理对象是一样的。AOP框架创建的对象,用来实现切面契约(aspect contract)(包括通知方法执行等功能)。 在Spring中,AOP生成被代理的类有两种:如果目标对象实现的是一个接口则是用JDK的 java.lang.reflect.Proxy动态代理;如果不是接口则用CGLIB库生成目标生成目标类的子类在创建这个子类的时候spring织入通知,并且把这个子类的调用委托给子类,这样就要注意两点,1 最好是用接口实现代理,cglib只是实现没有接口也可以通知的情况。2,被标记final的方法和类不能通知因为无法创建子类。。
☆织入( Weaving )把切面(aspect)连接到其它的应用程序类型或者对象上,并创建一个被通知(advised)的对象,这样一个行为就叫做Weaving。 这些可以在编译时(例如使用AspectJ 编译器),类加载时和运行时完成。 Spring 和其他纯 Java AOP 框架一样,在运行时完成织入 。
其实织入的方式有3种:
1、编译器织入: 切面在目标类编译时植入,这需要特殊的编译器。AspectJ 主要就是是使用这种织入方式 。
2、类加载器织入: -切面在目标类加载到JVM的时候进行织入,这需要特殊的ClassLoader。它可以在目标类被加载到程序之前增强类的字节代码。比如AspectWerkz ( 已并入 AspecJ ) 及 JBoss 就使用这种方式 。
3、运行时织入: -即在 java运行的过程中,使用Java提供代理来实现织入。根据代理产生方式的不同,运行时织入又可以进一步分为J2SE动态代理及动态字节码生成两种方式。由于J2SE动态代理只能代理接口,因此,需要借助于一些动态字节码生成器来实现对类的动态代理。大多数 AOP 实现都是采用这种运行时织入的方式,包括 Spring 。
3:AOP的工作模式
代理主要有静态代理和动态代理
静态代理:在代理中实现接口并创建实现类对象,在对实现类的方法增加功能(不常用)
动态代理:实现implements InvocationHandler接口。实现方法
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable{
System.out.println("=========");
Object o=method.invoke(this.ins, args);
System.out.println("---------");
return o;
}
流程图:在配置文件的配置
配置文件代码:
<!-- pointcut definition -->
<p:bean id="cf" class="com.kettas.spring.aop.day4.MyClassFilter">
<p:property name="classes">
<p:set>
<p:value>com.kettas.spring.ioc.day1.HelloIF</p:value> ---
</p:set>
</p:property>
</p:bean>
<p:bean id="mm" class="com.kettas.spring.aop.day4.MyMethodMatcher">
<p:property name="methodNames">
<p:set>
<p:value>sayHello</p:value>
</p:set>
</p:property>
</p:bean>
<p:bean id="pc" class="com.kettas.spring.aop.day4.MyPointcut">
<p:property name="classFilter" ref="cf"></p:property>
<p:property name="methodMatcher" ref="mm"></p:property>
</p:bean>
<!--advice 要继承implements MethodInterceptor-->
<p:bean id="timingAdvice" class="com.kettas.spring.aop.day4.TimingInterceptor"></p:bean>
<!-- advisor 把通知和切入点结合在一起- 最好给代理增加功能->
<p:bean id="timingAdvisor" class="org.springframework.aop.support.DefaultPointcutAdvisor">
<p:property name="advice" ref="timingAdvice"></p:property>
<p:property name="pointcut" ref="pc"></p:property>
</p:bean>
<!—target 目标 -->
<p:bean id="helloTarget" class="com.kettas.spring.ioc.day1.HelloIFImpl">
<p:property name="cal">
<p:bean class="java.util.GregorianCalendar"></p:bean>
</p:property>
<p:property name="user" value="world"></p:property>
</p:bean>
<!-- proxy -->
<p:bean id="hello" class="org.springframework.aop.framework.ProxyFactoryBean">
<p:property name="target" ref="helloTarget"></p:property>
<p:property name="interceptorNames">
<p:list>
<p:idref bean="timingAdvisor"/> 增加一种服务
<p:idref bean="xxxAdvisor"/> 增加另一种服务
</p:list>
</p:property>
<p:property name="proxyInterfaces"> 要和目标类实现共同的接口
<p:value>com.kettas.spring.ioc.day1.HelloIF</p:value>
</p:property>
</p:bean>
</p:beans>
简化配置:有可能只是目标类不一样,其他的都是一样的。解决每一个目标类都需要一个复杂的代理过程配置的问题,可以简化配置的问题 抽象ProxyFactoyBean的方法 如:
<!-- 抽象proxy --定义抽象的类,只是没有目标类,其他的通知和接口都一样>
<p:bean id="helloBase" class="org.springframework.aop.framework.ProxyFactoryBean" abstract=“true”>
<p:property name="interceptorNames">
<p:list>
<p:idref bean="timingAdvisor"/> 增加一种服务
<p:idref bean="xxxAdvisor"/> 增加另一种服务
</p:list>
</p:property>
<p:property name="proxyInterfaces"> 要和目标类实现共同的接口
<p:value>com.kettas.spring.ioc.day1.HelloIF</p:value>
</p:property>
</p:bean>
</p:beans>
真正的代理
<!—target 目标 继承抽象方法 只用写目标类就可以了 -->
<!-- proxy -->
<p:bean id="hello" parent=” helloBase”>
<p:property name="target" ref="helloTarget"></p:property>
</p:bean>
4:AOP的自动代理
Spring的aop机制提供两类方式实现类代理。一种是单个代理,一种是自动代理。
单个代理通过ProxyFactoryBean来实现(就如上面的配置),
自动代理:自动代理能够让切面定义来决定那个bean需要代理,不需要我们为特定的bean明确的创建代理从而提供一个更完整的aop实现 通过BeanNameAutoProxyCreator或者 DefaultAdvisorAutoProxyCreator实现。
◆采用单个代理方式 (费时费力,配置复杂臃肿)
下面就采用自动代理
实现代理bean的两种方式:
1,“基于Spring上下文的里声明的通知者bean的基本自动代理”:通知者的切点表达式用于决定哪个bean和那些方法需要被代理
2,”基于@AspectJ注释驱动切面的自动代理”:切面里包含的通知里指定的切点将用于选择哪个bean和哪个方法要被代理
第一种:<!——自动代理增加此行,容器会自动根据通知要匹配的切入点,为包含切入点的类创建 代理。注意这个bean没有id,因为永远都不会直接引用它——>
<bean class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator"/> feedom.net
第二种 自动代理@AspectJ切面
然而aspectJ提供可一种基于jdk1.5注解技术的方式,使得配置文件更少,更方便。能够把POJO类注释为切面这通常称为
@AspectJ.我们利用@AspectJ注释,我们不需要声明任何额外的类或Bean就可以把POJO转换成一个切面例如:
@Aspect 定义切面不再是普通的POJO了 在POJO类中加注释
public class AspectJMixAspect {
private Log logger = LogFactory.getLog(AspectJMixAspect.class);
@Pointcut("execution(* *..HelloIF.*(..)) || execution(* *..TestBeanIF.*(..))")定义切入点那些类的那些方法添加
public void allMethods() { }
@Pointcut("execution(* *..TestBeanIF.toDate(..)) && args(dateStr)")
public void toDate(String dateStr) {
}
@Around("allMethods()") 环绕通知
public Object timing(ProceedingJoinPoint pjp) throws Throwable {
long begin = System.currentTimeMillis();
Object ret = pjp.proceed();
long end = System.currentTimeMillis();
String methodName = pjp.getSignature().getName();
String targetClass = pjp.getTarget().getClass().getName();
logger.info("Around advice: It took " + (end - begin) + "ms to call "
+ methodName + " on " + targetClass);
return ret;
}
@Before("allMethods()")
public void logBefore(JoinPoint jp) {
logger.info("Before advice: " + jp.getSignature().toLongString());
}
@AfterReturning(value="toDate(dateStr)", returning = "date", argNames = "date, dateStr")
public void afterReturning(Date date, String dateStr) {
logger.info("call method toDate(" + dateStr + ") and return " + date);
}
@AfterThrowing(value="toDate(dateStr)", throwing="ex", argNames="dateStr, ex")
public void afterThrowing(String dateStr, ParseException ex){方法名任意但参数要和argNames=""中的参数顺序一样,
}
}
配置文件
<?xml version="1.0" encoding="UTF-8"?>
<p:beans xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:p="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd ">
<!-- target -->
<p:bean id="hello" class="com.kettas.spring.ioc.day1.HelloIFImpl">
<p:property name="cal">
<p:bean class="java.util.GregorianCalendar"></p:bean>
</p:property>
<p:property name="user" value="world"></p:property>
</p:bean>
<p:bean id="testBean" class="com.kettas.spring.aop.day5.TestBean">
<p:property name="pattern" value="yyyy-MM-dd"></p:property>
</p:bean>
<!-- apsect bean -->
<p:bean id="aspectJAspect" class="com.kettas.spring.aop.day5.AspectJMixAspect"></p:bean>
<aop:aspectj-autoproxy></aop:aspectj-autoproxy> 声明自动代理bean需要命名空间:aop="http://www.springframework.org/schema/aop"
</p:beans>
5,定义纯粹的POJO切面 不在普通的bean中加注释,而是在配置文件中配置
<!-- target -->
<bean xxx ></bean>
ß-Pojo bean-->
<bean id =” aspectJAspect”class=” com.kettas.spring.aop.day5.AspectJMixAspect”/>
<aop:config>
<aop:aspect ref=” aspectJAspect”> 将aspectJAspect定义为切面 下面定义加方法的类和方法
<aop:pointcut id="allMethods" expression="execution(* *..HelloIF.*(..)) or execution(* *..TestBeanIF.*(..))"/>
<aop:advisor pointcut-ref="allMethods" advice-ref="timingAdvice" />
<aop:advisor pointcut-ref="allMethods" before-method=“logBefore” />
<aop:advisor pointcut-ref="allMethods" advice-ref="logAfterAdvice" /> 引入外部的通知
<aop:advisor pointcut="execution(* *..TestBeanIF.toDate(..))" advice-ref="logThrowingAdvice" />
</aop:config>
14,注入AspectJ切面
为什么要用AspectJ:AspectJ提供了Spring AOP很多不能实现的多种切点类型(比如属性,构造方法切入,由于不能实现构造方法的切入spring aop就不能实现对象创建过程的通知)
AspectJ是一个代码生成工具(Code Generator)。AspectJ有自己的语法编译工具,编译的结果是Java Class文件,运行的时候,classpath需要包含AspectJ的一个jar文件。AspectJ是AOP最早成熟的Java实现,它稍微扩展了一下Java语言,增加了一些Keyword等
public aspect TestAspectJ {
public TestAspectJ();
public pointcut writeOperations():
execution(public boolean Worker.createData()) ||
execution(public boolean Worker.updateData()) ||
execution(public boolean AnotherWorker.updateData()) ;
before() : writeOperations() {
XXXXXX; advice body
}
after() : writeOperations() {
XXXX;// advice body
}
}
配置文件
<bean class=”xxx/TeatAspectJ” factory-method=”aspectof”>
<property name=”” ref=””/></bean>
说明机制:这个<bean>和其他的bean并没有太多区别,只是多了 factory-nmthod属性 要想获得切面的实例,就必须使用factory-method来调用 aspectof()方法,而不是调用TestAspectJ的构造方法,简单来说Spring不使用《bean》声明来创建TestAspectJ实例,它已经被AspectJ运行时创建了,Spring通过aspectof()工厂方法获得切面的引用,然后利用<bean>元素对她执行属性注入
上述代码关键点是pointcut,意味切入点或触发点,那么在那些条件下该点会触发呢?是后面红字标识的一些情况,在执行
Worker的createData()方法,Worker的update方法等时触发
Pointcut类似触发器,是事件Event发生源,一旦pointcut被触发,将会产生相应的动作Action,这部分Action称为Advice。
Advice在AspectJ有三种:before、 after、Around之分,上述aspect Lock代码中使用了Advice的两种before和after。
所以AOP有两个基本的术语:Pointcut和Advice。你可以用事件机制的Event和Action来类比理解它们
其中advice部分又有:
Interceptor - 解释器并没有在AspectJ出现,在使用JDK动态代理API实现的AOP框架中使用,解释有方法调用或对象构造或者字段访问等事件,是调用者和被调用者之间的纽带,综合了Decorator/代理模式甚至职责链等模式。
Introduction - 修改一个类,以增加字段、方法或构造或者执行新的接口,包括Mixin实现。
Spring2.5 aop新特性
在配置文件中的配置:就和POJO中的配置一样
在java类中的配置:就和自动代理类配置一样 ,只是在配置的时候注意加上<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
注意: execution(* *..HelloIF.*(..)))的含义 任意权限(*),任意返回值(*),任意包下(。。),类名(HelloIF),任意方法(*),任意参数(。。)。 星号(*)代表之间没有逗号和点号,(。。)代表可以有逗号和点号
5, 切入点指示符:前面的execution就是一个切入点指示符。Spring AOP还支持的切入点指示符有
Within:配置特定的连接点 。 this:Aop代理必须是指定类型的实例 如this(org.crazyit.service.AccountService)匹配实现了AccountService接口的所有连接点. Target:和this的表达类似。实现限定目标对象必须是指定类型的实例。
Bean:匹配实例内方法执行的连接点 bean(tradeService)可以用* 通配符
三:Data Access 数据库的模板操作
1,好处:支持事务的管理 2有统一的异常处理 RuntimeException 3 Support各种数据库
2,数据源的配置:在spring中数据源的配置有
★有JDBC驱动程序定义的数据源 :在org.springframework.jdbc.datesource包有两个实现类
DriverManagerDateSouce。每个请求时都建立一个连接
或者SingleConnectionDataSource 配置api中的set方法,用户名,密码,url driver
★由JNDI查询的数据源 :在tomcate配置数据源,还有Jbose等都可以 通过jndi引用就可以了
<bean id=”dataSource” class=”…jndi.jndiObjectFactory”>
<property name=”jndi” value=”/jdbc/rantzDataSource”/>
<property name=”resourceRef” value=”true”/>
★连接池的数据源:Spring 没有提供数据源连接池,但是DBCP项目提供了一个,下载相关的jar到lib中
配置类似第一种JDBC驱动的配置
3 JDBC :我们知道,Spring JDBC的主要目标是为了简化JDBC的编程,方便我们构建健壮的应用程序。这里,它的一个基
本设计理念,就是将JDBC编程中变化的和不变化的分开。 在JDBC中,什么是变化的?毫无疑问,SQL语句是变
化的。那什么是不变化的?正确的使用JDBC的方式是不变化的。使用JDBC模板
提供了三个模板:JdbcTemplate 最基本的jdbc模板 使用索引参数查询数据库
NamedParameterJdbcTemplate ,查询时把值绑定到SQL里的命名参数而不是使用索引参数
SimpleJdbcTemplate利用java5的特性,比如自动装箱,通用,可变参数列表来简化JDBC模板的使用
以JdbcTemplate为例子说明:在已经配置datasource的情况下就能使用 JdbcTemplate有相关的查询,插入,等方法
在xml中配置模板JdbcTemplate bean
<bean id=”jdbcTemplate” class=”org.springframework.jdbc.core.JdbcTemplate”>
< property name=”dataSource” ref=”dataSource”>
</bean>
在dao层的bean中加入JdbcTemplate引用就可以了,这样就可以使用它来访问数据库
如:class JdbcDao{
Private JdbcTemplate jdbcTemplate;
//setJdbcTemplate 方法注入
Public User getUserById(long id){
List users=jdbcTemple.query(sql,new Object[]{Long.valueOf(id)}),new RowMapper(){
Public Object mapRow(ResultSet rs,int rowNum) throws SQLException
User user=new User();
user.setId(rs.getInt(1)); ….;
return user;
}
Return users.size()>0?(user)users.get(0):null;
}
}
说明:一个字符串,包含用于从数据库里选择数据的 SQL 语句。
一个 Object 数组,包含与查询里索引参数绑定的值。Sql几个问号就绑定几个值
一个 RowMapper 对象,它从ResultSet 里提取数值并构造一个域对象。封装对象返回结果
然后像下面的设置一样注入模板JdbcTemplate属性就可以了
<bean id=”jdbcDao”class=”…/JdbcTemplate”>
<property name=” JdbcTemplate” ref=” jdbcTemplate”></bean>
JdbcDaoSupport
注意:可以使用Spring 对JDBC 的DAO 支持类 JdbcDaoSupport
思想: 全部 DAO 对象创建一个通用父类,在其中设置JdbcTemplate 属性,然后让全部DAO 继承这个
类,使用父类的JdbcTemplate 进行数据访问,这样可以减少配置量。Spring 的JdbcDaoSupport 就是用于编写基于JDBC 的DAO 类的基类,我们只需让自己的DAO 类继承它即可 例如上面的
class JdbcDao extends JdbcDaoSupport{
Public User getUserById(long id){
List users=getJdbcTemple().query(sql,new Object[]{Long.valueOf(id)}),new RowMapper(){
Public Object mapRow(ResultSet rs,int rowNum) throws SQLException
User user=new User();
user.setId(rs.getInt(1)); ….;
return user;
}
Return users.size()>0?(user)users.get(0):null;
}
}
在配置文件与配置不继承JdbcDaoSupport 的DAO 没有什么区别
<bean id=”jdbcDao”class=”…/JdbcTemplate”>
<property name=” JdbcTemplate” ref=” jdbcTemplate”></bean>
4 在Spring 里集成Hibernate
1)下载相关的Hibernante的jar包到lib文件 注意最好是3.0版本以上
2)使用Hibernate 模板 HibernateTemplate 有各种增删改查的方法
▲与 Hibernate 进行交互的主要接口是org.hibernate.Session。这个Session 接口提供了基本的数据访问功
能,比如从数据库保存、更新、删除和加载对象。获得 Hibernate Session 对象引用的标准方式是实现。Hibernate 的SessionFactory 接口。SessionFactory负责打开、关闭和管理Hibernate Session,以及其他一些功能。
3)为了让事情简单一些,Spring 提供了HibernateDaoSupport,用法类似JdbcDaoSupport,它能够让我们把会话工厂Bean 直接装配到DAO 类
两种获得session的配置 bean.xml
<!-- 自己加载 hibernate -->
<bean id="dataSource"[U26] class="org.apache.commons.dbcp.BasicDataSource">
<property name="driverClassName">
<value>oracle.jdbc.driver.OracleDriver</value>
</property>
<property name="url">
<value>jdbc:oracle:thin:@localhost:1521:XE</value>
</property>
<property name="password">
<value>kettas</value>
</property>
<property name="username">
<value>kettas</value>
</property>
</bean>
<bean id="factory" class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
<property name="dataSource">
<ref bean="dataSource"/>
</property>
<property name="mappingResources">
<list>
<value>com/kettas/entity/shoppingcart.hbm.xml</value>[U27]
</list>
</property>
<property name="hibernateProperties