科帮网

登录/注册
您现在的位置:论坛 盖世程序员(我猜到了开头 却没有猜到结局) 盖世程序员 > JavaWeb方面的所有知识听课记录
总共48087条微博

动态微博

查看: 5668|回复: 12

JavaWeb方面的所有知识听课记录

[复制链接]
admin    

1244

主题

544

听众

1万

金钱

管理员

  • TA的每日心情

    2021-2-2 11:21
  • 签到天数: 36 天

    [LV.5]常住居民I

    管理员

    跳转到指定楼层
    楼主
    发表于 2015-06-02 12:42:30 |只看该作者 |倒序浏览
    一、       javaWeb基础4 z7 F9 t' [" a9 \
    第一天:4 C- ?9 _- f# n, C# V9 o
    1.Eclipse详解:( C3 r' ]/ E4 X5 ^
    (1).Bad versionnumber in .class file:编译器版本和运行(JRE)版本不符合。高的JRE版本兼容低版本的编译器版本。8 [, E7 T1 p* q+ n
    : B4 d$ E% e/ I8 \2 S6 \! x! Q
    (2).当程序有错误的时候,使用Debug as 运行程序。双击语句设置断点。程序运行到此处停止。点击跳入方法的内部代码。点击跳过,执行下一条代码,点击跳出,跳出方法。观察变量的值,选中变量右击选择watch. 跳入下一个断点。查看断点,调试完后一定要清除断点。结束运行断点的jvm.
    - [4 A/ O. R( y, a& j
    7 a5 y% }6 q* }9 I6 q2.HashSet和hashCode和equals方法
    5 n: J. u( ]/ U, E7 i- ~java系统首先调用对象的hashCode()方法获得该对象的哈希吗,然后根据哈希吗找到相应的存储区域,最后取出该存储区域内的每个元素与该元素进行比较.两个equals相等,hashCode()相等。需要重写equals,hashCode()方法.更改数据的值,hashCode()的值也更改了,并未删除.内存泄露.有个东西不在被用,但是未被删除,导致内存泄露.  k, k4 f) |- @: ]& D2 K
    ; Q  P, o3 u0 p
    3.Junit测试框架
    * l  A/ Y& u0 Q; j(1).在测试类,方法前加注解:@Test,不然出现初始化异常。% Z# z: W% [$ Z9 h- w* B
    " V  h) T# ]) ?. v. [
    (2).方法before,after前加@Before,@After注解。在测试方法之前和之后运行方法。
    $ }# }- M' |, `+ j5 ^1 l  u5 N( i, z
    3 w( b1 C: O- j$ `0 p5 T6 A* ?  U(3).静态方法beforeClass,afterClass方法前加上注解@BeforeClass,@AfterClass,类加载的时候运行
    : S' a0 V1 h" E" v( D
    ; \4 N5 p" p8 L7 j) q1 C) y(4).Assert断言。判断两个对象是否相等。期望值和实际值相等。
    $ y/ x2 @& z, t1 ^- U6 c& L+ T% d
    , J+ i7 B5 D3 T/ I- T4.得到配置文件的路径
    9 p! m5 M3 A5 k' g2 F1 Z通过类加载器 reflect.class.getClassLoader.getResourceAsStream();在class指定目录下查找指定的类文件进行加载.编译器把src中的.java文件编译成class文件,所有非.java文件,原封不动的搬过去.但是这种方法是只读的.' }' W' i2 Z) v8 {  F) B: m7 w
    通过类的信息reflect.class.getResourceAsStream();相对路径
    : M9 q! H( D# J+ n, Q) ]/ ]一般用绝对路径,使用配置文件告诉用户路径.
    - L7 e) M. a3 v一定要记住要用完整的路径,但是完整的路径不是硬编码的,是计算出来的.
    7 S. ]- r# d7 t3 h2 A3 V+ \* W7 l! W) k  p
    5.反射/ u6 c/ e( t; B* k, e( G
    (1).反射主要用于框架开发, n% V8 a+ u, a1 V( d

    7 h  [1 @7 q6 b. i6 [7 d(2).一个类有多个组成部分,例如:成员变量,方法,构造方法等,反射就是加载类,并解析类的各个组成部分。0 P% r( [. p/ `$ ^0 B+ k/ z
    ( O7 Y1 ~: m) p' h
    (3).加载类使用Class.forName()静态方法,给类的完整名称,包名和类名。
    ; R8 s' m  n8 J6 L9 h
    ; J, G$ R3 Q" E- D) k! [7 }7 A2 g2 o(4).Class提供了解析public的构造方法,方法,字段等方法以及private。字段封装数据,方法执行功能
    7 u9 t1 Y% d( _; ~
    " B/ y  U% J+ M, a(5).静态方法无需传递对象,method.invoke()2 q% i5 I3 S; O5 B

    - d$ X) T+ ~7 e9 T9 @(6).升级时保持兼容性,main函数的解析有点麻烦,反射解析数组参变量的时候的问题。启动Java程序的main方法的参数是一个字符串数组,即public static void main(String[] args),通过反射方式来调用这个main方法时,如何为invoke方法传递参数呢?按照jdk1.5的语法,整个数组是一个参数,而按照jdk1.4的语法,数组中的每个元素对应一个参数,当把一个字符串数组作为参数传递给invoke方法时,javac会到底按照哪种语法进行处理呢?jdk1.5肯定要兼容jdk1.4的语法,会按jdk1.4的语法进行处理,即把数组打散成若干个单独的参数。所以,在给main方法传递参数时,不能使用代码mainMethod.invoke(null,newString[]{"xxx"}),javac只把它当做jdk1.4的语法进行理解,而不把它当做jdk1.5的语法解释,因此会出现参数类型不对的问题。解决的方法:( v* y# t% \3 z6 ]+ f) \
    mainMethod.invoke(null,newObject[]{new String[]{"xxx"}});
    # k" u7 F5 n1 S/ C- `mainMethod.invoke(null,(Object)newString[]{"xxx"});编译器会做特殊处理,编译时不把参数当做数组看待,也就不会把数组打散成若干个参数了.$ p% o( V# {, B$ D
    " v: f5 ~1 _9 a0 q" ]0 Q
    (7).对数组进行反射:相同的维数(不是数组元素的个数,例如都是一维数组,不关心数组的大小),相同的8种基本数据类型时数组有相同的字节码.
    + b. M5 U% P: `* W& X
    ( J7 o# f( H$ X+ i# z; ?; I! [! T6. 泛型
    / y, i- \3 b  w, m1 @(1).泛型是对象类型,不能是基本类型,泛型存在源代码级别上,给编译器看的,生成class文件就不存在泛型了
    9 A7 w. {% i, P$ v& g+ t) g7 @! O/ i8 U& l3 e' M. h- C, s- a
    (2).参数类型变量,实际类型变量,泛型类型,参数化的类型8 P. N, s) ]. j3 M8 \
    ( y5 M7 r7 g% Q: ^7 R4 w
    (3).自定义泛型方法:public <T> void method(T args){};public<T,K,V> void method(T a,K b,V c){};' y1 H# n7 k. s6 ]

    : K5 ?. t& I% {# h(4).静态方法public static <T> void method(T t){};泛型类不作用静态方法
    9 y. j! g+ Z. W% ^4 C
    3 V" u: Z/ }" C% X1 ^7.可变参数
      s+ W4 q: `9 x* _. L" ?* Y1 R(1).可变参数就看成数组,可以使用增强for循环
    % z5 T* \& {1 }5 b. E1 C, d3 |
    8 z. F/ x) l7 g! [(2).可变参数列表为最后一个参数4 u2 o) b! a  f
    " m% h9 I( o+ z; O
    (3).可以给可变参数传递一个数组
    ) S! f! F, k" a% i3 w9 L
    2 K- |; e6 e1 ~3 ~0 o! t(4).可变参数的类型是基本类型还是对象类型
    6 ^4 p* }2 M3 @3 k5 Z4 Y8 S& P% [8 E0 C) G% N
    8.课程介绍. N, K6 t% p: ]' j
    (1).在谷歌心目中,“云”必须具备以下条件:数据都存在网上,而非终端里,软件最终消失,只要你的“云”设备拥有浏览器就可以运行现在的一切,“云”时代的互联网终端设备将不仅仅是PC,手机,汽车,甚至手表,只要有简单的操作系统加个浏览器就完全可以实现,由于数据都在“云”端,企业的IT管理越来越简单,企业和个人用户也不同在担心病毒,数据丢失等问题。  a. M) |/ Q6 k( c) I" t2 i
    / g. q! N: f( K2 h7 l
    (2).李开复描述了这样一个场景,只要你的PC或手机等终端里安装了一个简单的操作系统和完整功能的浏览器,开机后输入自己的用户名和密码,你存在“云”中的应用软件和数据就会同步到终端里。9 n8 I( _: a7 ~
    & r; Q, q8 {, n/ s$ n) u- e
    9.快捷键
    $ J- X% k& P2 i(1).配置快捷键:window->preferences->key
      M* W4 z6 h) r! g. F2 V(2).Alt+/ :内容提示
      H: g5 k$ j& V+ E          Ctrl+1 :快速修复8 @7 g7 J" K* ~
              Ctrl+Shift+O :快速导入包
    ! o- |) {1 n9 e) C+ R7 d* m          Ctrl+Shift+F :格式化代码
    7 u7 u) c' W  P, S- v          Alt+方向键 :跟踪代码, W9 A* w/ U. K; k1 k" S
              Ctrl+Shift+/ :添加注释
    3 D$ q4 f# y  [3 a5 i1 f          Ctrl+Shift+\ :取消注释
    $ X# e6 l7 G" W8 H          Ctrl+Shift+X :更改为大写
    9 T  O" P" U  E9 v' f' l          Ctrl+Shift+Y :更改为小写& z4 P0 G- x: j7 e& }2 F- r& E
              Ctrl+Shift+向下键 :复制代码
    1 c# k  B3 {2 J6 _6 N0 q; E- I5 F7 q' \          Ctrl+Shift+向上,向下  :改变代码行的顺序0 R- C- e" o) w" e+ C' K
              Ctrl+T  :查看继承关系+ w( Q: y+ [$ s% ?/ r/ e
              Ctrl+Shift+T :查看源代码/ U3 Z% Y& A' G! A3 |! D
              Ctrl+Shift+L :查看所有的快捷键! p# r* P( T( M/ K1 A
    3 y4 e: a4 |) L" E" `3 m2 ^; c# Y
    10.类加载器及其委托机制的深入分析
    % i+ q  f3 f; s; Q5 C8 b3 y/ _* Y(1).Java虚拟机中可以安装多个类加载器,系统默认三个主要类加载器,每个类负责加载特定位置的类:BootStrap,ExtClassLoader,AppClassLoader
    : x$ T$ F$ p7 m. u8 e
    & ]4 d, p. Y# o3 x) M/ n(2).类加载器也是Java类,因为其他java类的加载器本身也要被类加载器加载,显然必须有第一个类加载器不是java类,这正是BootStrap(内嵌到JVM的内核中,使用C++语言编写的)
    ' H$ W0 u' [$ p6 i% u4 R& M3 P% {& h! H
    (3).Java虚拟机中的所有类装载器采用具有父子关系的属性结构进行组织,在实例化每个类转载器对象时,需要为其指定一个父级类装载器对象或者默认采用系统类装载器为其父级类加载器
    ' ?0 B. ?3 ]4 ~" z* T(4).! {. Y& g( b# G, T3 {5 Q
    public class ClassLoaderTest{( a2 U" S% \  D/ e, n
    public static void main(String[] args){! k+ U4 Z0 P+ X6 R4 R" u
    System.out.println(ClassLoaderTest.class.getClassLoader().getClass().getName());
    + @6 U# J: y& w8 C* f//输出为sun.misc.Lanuncher$AppClassLoader;' \- {6 P5 E4 L# B8 P- _! S2 e- [0 m
    System.out.println(System.class.getClassLoader());1 }& m5 G: w1 T
    //输出为null,因为类System是由BootStrap加载的;
    . Y" S2 p. R. k7 I6 q- ?4 u. A( ]}: z- v$ l0 d' i$ ]
    }
    , M. |# j0 L* E
    & X! ~8 I! @+ g2 E* B(5).BootStrap->ExtClassLoader->AppClassLoader
    " Y* y) S9 J3 U9 UClassLoader loader= ClassLoaderTest.class.getClassLoader();% S' y, g, m/ K
    while(loader!=null){( ^  |2 V+ Q- A3 b, q
    System.out.println(loader.getClass().getName());, W) ]1 R( g) X
    loader = loader.getParent();//往上顺序打印) H, L8 z: p+ _7 ^7 j! I7 @
    }
    : ]% l1 |4 f* V/ o! K: k; U) ^System.out.println(loader);//最后打印老祖宗5 p; W, ^  M% s( x4 X  ~0 O
      [0 M# F+ P8 k8 i4 I; i
    (6).
    9 [9 g, d6 h. ~7 rBootStrap------>JRE/lib/rt.jar  V7 b/ l& `) R8 Z6 A5 \/ h
    ExtClassLoader----->JRE/lib/ext/*.jar
    / ]" R5 M- n" T0 FAppClassLoader------>ClassPath指定的所有jar或目录" Q+ g  v' @/ N- g0 T) d
    用Eclipse的打包工具将ClassLoaderTest打包成itcast.jar,然后放在jre/lib/ext目录下,在eclipse中运行这个类,运行结果显示为ExtClassLoader,此时的环境状态是classpath目录有ClassLoaderTest.class,ext/itcast.jar包中也有ClassLoaderTest.class,这时我们在打印ClassLoaderTest类的类加载名称,发现是ExtClassLoader,而不是AppClassLoader.
    2 E7 `1 |! |0 P4 z$ S' y7 G+ `
    ; _, K9 t2 t! e9 Q8 S(7).类加载的委托机制:# v4 F2 Q0 {$ h4 g1 C) ~; z
    当Java虚拟机要加载一个类时,到底派出哪个类加载器去加载呢:! Q$ y5 I) E/ c3 V( e2 u# Z
    首先当前线程的类加载器去加载线程中的第一个类
    ) e# z5 `* Q2 r+ D% O如果类A中引用了类B,Java虚拟机将使用加载类A的类加载器来加载类B: c; t2 l5 s, Z( t9 z% m5 N- ^9 P& N
    还可以直接调用ClassLoader.loadClass()方法来指定某个类加载器去加载某个类8 [( {& [0 x, _1 Q! ^
    每个类加载器加载类时,又先委托给其上级类加载器
    ) I% P" x: `9 }) r. H# A当所有祖宗类加载器没有加载到类,回到发起者类加载器,还加载不了,则抛ClassNotFoundException,不是再去找发起者类加载器的儿子,因为没有getChild方法,即使有,那么多个儿子,找哪一个呢?
    8 g: c7 m8 i+ n# c对着类加载器的层次结构图和委托加载原理: [. @, s8 q* u. L" T0 o0 Y; u
    % ?, N) B) h6 N9 v, L  \  {
    (8).Thread类有一个方法setContextClassLoader(ClassLoader classLoader)//加载当前的类.7 ^( C5 f3 a, P) d' j
    0 O9 G" ^+ b( C! p5 F
    (9).每个类加载都首先会委托送到BootStrap,那么BootStrap很累,这样,那为什么不多搞几个BootStrap呢,之所以不这样做,是因为,便于统一管理,因为所有的类都会找BootStrap,可能这时有几个相同的类进行加载,那么BootStrap,不会多次将他们的class文件加载内存中,只会加载一份即可.这样效率就高了.
    ( |4 y+ D) B2 B$ i5 N# V8 J& j  I
    (10).
    9 z, l( K, g5 h. m% cpublic class MyClassLoader{' G  [* r; T+ Z% a7 o0 Q+ a+ \% B& F% s
    public static void main(String[]args){
    * Y( q. {7 ~: A- z+ r  L5 c1 V6 \String srcPath=args[0];& ]" ]/ E7 m6 Q( e7 ^# ~
    String destDir=args[1];//得到目录& F+ q3 h  Z3 E) S
    String destFileName =srcPath.substring(srcPath.lastIndexOf('/')+1);//得到文件名( o. Q$ _0 V$ o6 B
    String destFilePath=destDir+"\\"+destFileName;
    4 Q% L5 A; P6 s6 W& HFileInputStream fis = newFileInputStream(srcPath);7 U5 G- S! d6 e
    FileOutputStream fos=new FileOutputStream(destPath);
    9 x' U) x2 ^/ S/ Mcypher(fis,fos);
    7 p% K. h; c  R) H5 Afis.close();
    5 e( g% ?: a7 K% F; d7 n* Q5 Cfos.close();1 r* G) u  r4 F3 G
    }) `$ U4 M& w, H4 O# c
    private static void cyp(InputStreamips,OutputStream ops){9 c5 B5 s  C6 y$ r( Y% |1 D
    int b =-1;
    7 b  d. b$ y/ a  _. u: vwhile((b=ips.read())!=-1){
    9 e" Y8 f$ B, t1 _6 c$ m  h2 n; ?ops.write(b^0xff);//对内容进行异或处理1 F" k) \) Y- P% j9 {. t9 s
    }% O: k+ @) @0 b
    }2 G* z" I- k! o# y  X, h
    }/ c4 r8 o" I1 H( ]- @( R
    class ClassLoader extends Date{, G* J* m2 C6 i/ x: p
    public String toString(){
    # W! v5 S3 }- C/ `) K8 I4 U8 `return "hello,itcast";9 q+ \  T7 r* b* O# @& U
    }: R  h/ z1 _# E  k" o- j% d
    }
    8 }  H6 U+ \2 w1 B6 dargs[0]:ClassLoader.class的绝对路径
    ! `/ z2 e/ s9 V  D) s  Dargs[1]:itcastlib5 o+ Z- y: P/ p2 T
    有包名的类不能调用无包名的类.( ?. M- ^% W- o
    1 R1 L8 k4 _9 ]5 T7 |$ H' w+ }
    (11).编写自己的类加载器:: _1 p0 T! T6 L$ ~, ?1 E- L
    知识讲解:
    , r) W$ h: O/ E0 r/ X: m自定义的类加载器必须继承ClassLoader(抽象类)& ~& r$ K" I$ |1 \( Y
    覆盖findClass方法
    $ ^; f) q0 C- t8 ydefineClass方法:得到class文件转换成字节码6 o+ H" D3 F6 o; F
    编程步棸:/ m/ m" q: o9 n- S; V( c
    编写一个文件内容进行简单加密的程序
    " L  u% L! B9 r1 I) W8 h7 {编写了一个自己的类加载器,可实现对加密过的类进行装载和解密
    3 K9 _. R) _: ^; _; d编写一个程序调用类加载器加载类,在源程序中不能用该类名定义引用变量,因为编译器无法识别这个类,程序中可以出了使用ClassLoader.load方法之外,还可以使用设置线程的上下文类加载器或者系统类加载器,然后再使用Class.forName
    - O% i1 A) W9 @7 V" `) _1 L) `* f- @
    (12).: u6 b, h# ?; }- ?5 W  F6 E
    模板设计模式:
    5 F. `5 u% D; }# K1 c0 o: F父类--->loadClass(相同的)
    ) N. p0 i. [: E8 s8 e子类1(自己干的)
    $ T' ?" p$ h0 l' a% T) P子类2(自己干的)
    , w: K) l5 ~( j; D6 p* Y* O覆盖findClass方法(自己干)0 I7 K' Z; o$ \* f
    & k8 t- L7 ]' {3 g! c
    (13).
    % F  ?( o( I- v2 V/ Vpublic class MyClassLoader extendsClassLoader{
    : }# l' B. s, T1 `  J3 J6 X8 P7 Vpublic MyClassLoader(){
    ' m$ E. A: a& ?, x}' u3 G; @0 {# I2 w7 g
    public MyClassLoader(String classDir){* W2 J* ~+ w) G0 Z5 }" Z1 w. i
    this.classDir = classDir;; v0 {" R' j! S9 [. |- T: Y
    }  N7 H3 Q- @+ A
    @Override
    4 B/ _3 \+ P" \# A1 _: w+ T" N- nprotected Class<?> findClass(Stringname){
    ! ]& {5 h7 H- IString classFileName = classDir +"\\" + name + ".class";. q/ V2 g# W5 J* g- G7 p4 Y
    FileInputStream fis = new FileInputStream(classFileName);$ P/ c5 K5 r. @# C& s8 k* a
    ByteArrayOutputStream bos = newByteArrayOutputStream();: z8 z" J/ |/ ~% a2 s1 {# t
    cypher(fis,bos);
    : ?8 L8 r/ O" }fis.close();3 X# r7 y1 H  v- z8 x0 z
    byte[] bytes = bos.toByteArray();1 M1 Z  G8 H. m5 e6 F1 G+ |
    defineClass(bytes,0,bytes.length);
    % d( E  _/ u* Kreturn super.findClass(name);9 Q8 A, t$ E% J. k* [5 S, `. P
    }, Q( ?: v, R# L0 d/ ^; j
    public static void main(String[] args){
    " Z6 s* t! k2 Z/ a4 \9 aClass clazz = newMyClassLoader("itcastlib").loadClass("ClassLoaderAttachment");; y: @' m# j% l$ h8 i; ]9 q: n9 M
    ClassLoaderAttachment d1 =clazz.newInstance();0 @/ e9 L0 p, T# C  B# l
    }
    7 E1 a0 p$ R& Y8 u7 x4 W, j}
    8 C) I+ d1 R1 J
    4 E, v4 o0 P# p) ?$ l8 _(14).windows->showview->problem查看错误.
    : k; o9 a& s. |  g. P: Q  c
    4 M& `9 a7 l5 D( o+ w  s. {(15).可以查看Servlet的类加载委托继承树! N) O! K5 o( S8 g. w* Z
    传智博客(JavaWeb方面的所有知识)听课记录(经典).txt (219.71 KB, 下载次数: 1) 3 \! ~6 s" S" U. K0 n
    4 u, ^; {5 T6 C1 U

    ; p( |/ c7 ]" V

    科帮网 1、本主题所有言论和图片纯属会员个人意见,与本社区立场无关
    2、本站所有主题由该帖子作者发表,该帖子作者与科帮网享有帖子相关版权
    3、其他单位或个人使用、转载或引用本文时必须同时征得该帖子作者和科帮网的同意
    4、帖子作者须承担一切因本文发表而直接或间接导致的民事或刑事法律责任
    5、本帖部分内容转载自其它媒体,但并不代表本站赞同其观点和对其真实性负责
    6、如本帖侵犯到任何版权问题,请立即告知本站,本站将及时予与删除并致以最深的歉意
    7、科帮网管理员和版主有权不事先通知发贴者而删除本文


    JAVA爱好者①群:JAVA爱好者① JAVA爱好者②群:JAVA爱好者② JAVA爱好者③ : JAVA爱好者③

    admin    

    1244

    主题

    544

    听众

    1万

    金钱

    管理员

  • TA的每日心情

    2021-2-2 11:21
  • 签到天数: 36 天

    [LV.5]常住居民I

    管理员

    沙发
    发表于 2015-06-02 12:42:55 |只看该作者
    11. 枚举
    # |5 D4 ~& P& o' ^(1).枚举的作用限定指定的值,没有枚举前设计一个类,将构造函数设置成私有的,变量设置成静态常量
    , L% W; k: d! a; U- y$ ]
    / q7 P* o) L/ a$ S5 o(2).枚举可以有构造函数(私有的),字段,方法8 k9 o7 R" I1 n- E( I3 g

    $ d, F1 o+ E6 J7 E2 _; X$ Y(3).可以定义set,get方法,获取变量的值。" [! y0 Z! Z; n& y/ L7 \
    (4).带有抽象方法的枚举,不能new出新的对象了。在new对象的时候就重写抽象方法,使用匿名内部类$ E+ i# L% s+ ~; e, @, c& p

    2 p- B2 Q# h4 P" }( q8 ~, R9 [% v(5).枚举中的每个枚举值代表枚举类的一个对象。
    + D1 y# v5 w4 ?5 ~ 5 f8 t2 P+ l7 Q
    (6).枚举也可以实现接口,或继承抽象类7 S6 Q# X5 }8 @  A  W4 w& C

    % g# M# U4 l3 j" c# {2 ^# [(7).JDK5中的switch拓展为除了接受int,short,char,byte外,也可以接受枚举类型。
    8 o3 j+ ^. o: O
    : ?) V! ~3 ~" W4 G(8).枚举的方法,name,ordial,valueOf,将字符串转换成枚举值。表单提交数据的时候。values返回枚举的所有的枚举值
    3 F/ Y8 \: Z7 \# s
    1 l( t; F+ M2 R5 r( Y0 e7 {! W( M3 _12.内省
    2 S; ~% V2 J3 J, r( `(1).内省:Introspector,专门操作Bean的属性。
    / v6 U6 m% H8 [+ i$ L6 d
    ' n0 |. |/ _. ^6 I(2).Bean的属性:只有字段提供了set/get方法,就是属性。只要有get/set的方法,就有一个属性,所以属性是有get/set方法决定的  }% p1 a* V- T- D, P
    5 ?  @2 c/ T. |% m
    (3).任何类都继承了Object类,又因为Object中有一个class属性# u# F. g! i- M; C/ @

    - |8 O( G& _  h- N! |* L) R(4).内省的入口类:Introspector,方法getPropertyDescriptors()获取Bean的属性
    ) |: u; K0 w8 `; Y. F+ U1 C0 e $ j$ |" t: G: v9 \
    (5).操作Bean的指定属性  O( w$ i2 k/ R+ a
    # X: S8 ]! D" C  w
    (6).BeanUtils框架操作Bean
    ; T( F& Z( i* o2 y0 F& H5 E! W7 e 1 x5 ]- c$ E- R4 c: f
    (7).在工程中新建一个文件夹,将架包复制到文件夹中,还需另外的架包loging,选中架包,右击build path加入到工程的环境中
    ) Q6 i" a6 d2 W6 \
    0 Z/ c( F" E' P: M$ y* z(8).BeanUtils使用方便,类型自动转化,只支持8中基本数据类型
    ( ?1 ]5 J! ~- D/ P & f; V& n- f8 \) d4 l, k
    (9).给BeanUtils注册类型转换器,ConvertUtils.register()
    ; J2 y3 X0 E2 j$ t1 v
    3 I% M8 x: ]" @(10).将Map中的数据整合到对象中,BeanUtils.populate()方法
    ) r8 e# r' g4 P* `
    4 E6 v3 {% _9 |7 g0 L$ o13.配置Java模板代码
    2 C1 Y( C, C$ L: o+ g1 g+ [window->preferences->java->Editor->Template:编辑模板代码:line_selection,光标cursor,右击选择source with
    : k% Q3 h( T9 H3 p2 I2 A- M 1 y& X. `3 a- p* J; z" b: M
    14.享元模式: ?% O. H" d  {8 Y
    相同的对象只实例化一个,实例:桌面上的图标,word中字符,有很多小的对象,有很多相同的属性,不同的属性叫做外部行为,相同的属性叫做内部行为integer的缓冲池
    ) q1 A' A3 c% S, K% m5 s ! c+ R1 h0 _, Y# y
    15.注解6 L0 m! C+ I4 y' ~% i( ]' P
    (1).@SuppressWarning("deprecation")过时注解
    & D8 ]1 m0 P% t - ^) l9 @% u" X# k" W# I
    (2).@Deprecated注解,表示该方法是否过时,架包升级时添加的注解
    7 K6 ]0 V6 S; ]1 {
    1 Y  u: k0 c4 d1 Z. A5 a8 A(3).注解相当于一种标记,通过反射了解你的类及各种元素上有无何种标记: H  K( K* ]* O$ ^& G% V3 B
    / F2 J' D/ b/ k& r/ L8 A1 ?9 M
    (4).注解相当于一个类:@interface Annotation{};注解类,应用注解类的类,对应用了注解类的类进行反射操作的类7 g4 B; b, K1 @3 o7 R9 \2 e/ j
    * k7 b; \3 H2 f  c# J4 F( e
    (5).AnnotationTest.class.getAnnotation(ItcastAnnotation.class)得到类AnnotationTest上的注解ItcastAnnotation,注解上使用注解叫做元注解,元数据,元信息, s5 e& p( m8 R+ _! F4 ~

    " w% s! l9 q/ e5 @7 K* g(6).@Retention(RetentionPolicy.RUNTIME)(保持到运行阶段),@Retention(RetentionPolicy.SOURCE)(保持在源文件阶 段),@Retention(RetentionPolicy.CLASS)(保持在class文件中):源代码->class文件->(类加载)内存中的文件(字节码)) y' x. n8 F5 F) q
    + W6 P% {. |/ n. x* k
    (7).@Override注解保持到SOURCE,@SuppressWarning注解保持到SOURCE,@Deprecated注解保持到RUNTIME(只有将该类调到内存中才知道该类中的方法是否过时了)
    4 i7 j% G2 f* e
    ' T% q7 V" A# [2 q6 W$ M& x(8).@Target({ElementType.METHOD,ElementType.TYPE})注解只能标记到方法上或类,接口等类型上 (9).注解的属性:String color();类有个color属性,还有一个特殊的属性value,属性的默认值default,数组的属性值,枚举的属性值,注解的属性值
    5 Q/ F( @/ j& g. [/ I' r3 F! S第二天:# m; N, P: k" \
    1.dom4j解析XML文档* R" X5 [8 b2 Q, M" |2 _. }
    (1).Dom4j是一个简单、灵活的开放源代码的库,Dom4j是由早期开发JDOM的人分离出来而后独立开发的,与JDOM不同的是,dom4j使用接口和抽象基类,虽然Dom4j的api相对要复杂一些,但是他提供了比JDOM更好的灵活性) a5 V5 }8 ^5 z* x
      M6 I( d0 I  j2 h5 ]" c1 _& Y
    (2).Dom4j是一个非常优秀的Java XML API,具有性能优异、功能强大和极易使用的特点,现在很多软件采用的Dom4j,例如hibernate,包括sun公司自己的JAXM也使用了Dom4j1 }# l- F1 G+ e% @  I; Q& R: X

    ; D0 }/ n5 _4 [$ }9 G(3).使用Dom4j开发,需要下载dom4j相应的jar文件
    / a8 X9 O' I; Q" ], Q/ S ; U% D2 J2 Q, H7 h
    2.  XML语法
    , _# ~/ k* J0 _7 W, ^( x' R(1).编写XML文档时,需要先使用文档声明,声明XML文档的类型,使用IE校验XML文档的正确性.
    ; R6 f( o# v( K8 b/ d
    , X  K" J( ~1 O3 f% u, n% V(2).XML文档中的"中国"保存为本地的码表的"中国"
    ) I' T' r# c; R$ R0 p% a
    ! I# {% y, V& N9 e1 a* X(3).在XML文档中,空格和换行都作为原始内容被处理
    3 a- R  e$ ~2 r7 D% I - n+ Q( d% s% g" k* C$ y
    (4).XML区分大小写,一个标签可以有多个属性,每个属性都有它自己的名称和取值,在XML技术中,
    7 k4 @3 `1 ?, v' X6 B% S标签属性所代表的信息也可以被表示子标签表示
    3 `3 P7 b; I0 ~0 d
    0 Y* ?; P3 l% k8 P8 `4 T2 ^) t(5).XML文件中的注释采用:"<!--  >"格式; @) O- Z1 k1 U2 E8 `, I' H# h

    + R# E( R% d( V1 `0 y4 k7 L(6).CDATA区域内的内容,XML解析程序不会处理,而是直接原封不动的输出% m2 y9 [: Q( J$ n: L, n& f2 G: @0 j
    ) s% @) h3 D" N4 J. q
    (7).转义字符: "<":&lt; ">":&gt;! |2 P7 e% U- O. O, l$ ^+ }

    * U0 {' z  c7 s. k- d3.  Xml语言
    ) Z3 U7 a5 O4 A0 f/ q在xml语言中,它允许用户自定义标签,一个标签用于描述一段数据,一个标签可分为开始标签和结束标签之间,又可以使用其他标签描述其他数据,以此来实现数据关系的描述.用于配置文件,以描述程序模块之间的关系" d8 f! g! D" K; A
    4.  XML约束
    - n( k* _5 U; `- c! }) q) ?(1).在XML技术里,可以编写一个文档来约束一个XML文档的书写规范) `+ L+ }$ y* L( D# s
    , x% S: {0 s1 c- F$ b% m

    " A) B) [! o7 E9 _% \(2).常用的约束技术:XML DTD 和 XML Schema
    8 D  M. `9 P+ H6 ]/ x( r8 A 0 O' R( [# p: K& V1 |2 o
    (3).如果dtd文件中含有中文,一定要存为utf-8格式.
    ; G- S; I* _4 q1 k1 {6 I 6 y: t! u( B. h9 n0 t: u& i) a
    (4).IE5以上的浏览器内置了XML解析工具:Microsort.XMLDOM,开发人员可以编写javascript代码,利用这个解析工具装载xml文件,并对xml文件进行dtd验证.创建xml文档解析器对象:
    0 W/ W) D( A' F# t+ s! cvar xmldoc = newActiveXObject("Microsoft.XMLDOM");3 R6 c3 q$ O4 S/ u
    开启xml校验1 L# A) q- V5 D5 S5 k4 n
    xmldoc.validateOnParse= "true";8 t+ V% U6 y# D' c5 u$ X. ?
    装载xml文档5 c: m; I( |  O; B2 w  s8 B
    xmldoc.load("book.xml");  O! ~" k+ ?. V/ H; ]
    获取错误信息
    1 }- A0 c: j$ x7 cxmldoc.parseError.reason;xmldoc.parseError.line
    5 f9 k  r, w6 O2 j
    8 N8 k1 J0 c% a(5).将DTD文件导入到eclipse中,可进行校验xml是否遵循dtd文件" @3 v$ D8 R" D/ n% m
    在xml文件中编写DTD
    - ~* d1 d* V, H/ B6 v3 }7 m( z ) o5 k! ?- E1 j9 X8 o
    第三天:
    0 I# R5 V% D/ d9 S/ G8 A1.HTTP请求头各个头字段详解
    1 S# w4 }  K2 |! a+ C请求头详解:3 p; i9 \% K) K- m4 K: n
    Accept:text/html,image/*用于告诉服务器,客户机支持的数据类型," T9 P( _$ x; T/ m" a+ v% L: L3 C$ |
    Accept-Charset:客户机采用的编码2 @6 M4 a7 p- Z% Q/ l! Q& d
    Accept-Encoding:客户机支持的压缩格式
    $ n0 H0 ]2 B0 g, A6 m& ~Accept-Language:客户机的语言环境,不同国家访问的内容也是不同的,就是通过这个头实现的,用于实现国际化
    ( z8 x- F: \, T% C4 BHost:www.it315.org:8080:告诉服务器,想访问的主机名1 F3 l) P: D9 D4 p# R4 {" B* X' `; ]) r
    If-Modified-Since:客户机通过这个头告诉服务器,资源的缓存时间,提高访问效率
    0 m4 |0 V: Q  J$ J4 p+ dReferer:客户机告诉服务器,他是从哪个资源来访问服务器的(防盗链),通过检查该头是否是从本网站点击过来的,如不是的,就让他跳到本网站的首页来
    ; }6 j% Q6 v( nUser-Agent:客户机告诉服务器,客户机的软件环境
    - E& _1 h1 c; eCookie:客户机通过这个头向服务器带点数据
    4 ]& z1 N/ {# g* j% F. O* \
    7 g9 m8 V# c. q2.Http请求行和请求方式% X0 G! \& m) H+ c0 {
    一个完整的HTTP请求包含:一个请求行,若干个请求头,以及实体内容,请求行,请求头,之后空一行,带有请求信息(如表单提交数据为post方式)如下所示
    8 L; B  e" g( d; O3 {Get/books/java.html HTTP/1.1   :请求行,用于描述客户端的请求方式,请求的资源名称,以及使用的HTTP的协议版本号
    * K% Y) A0 s/ t0 X- F5 ~5 `5 dAccept:*! e0 q5 s' }; z2 R" @+ R
    Accept-Language:en-us
    ' m2 ?/ v( ^1 J& k: g) w& CConnection:Keep-Alive
    1 K7 y" f$ k" @Host:localhost' L6 t+ c  h2 Z6 Q3 ~* e3 I
    Referer:http://localhost/links.asp1 |3 N! N7 z7 g, e  X
    User-Agent:Mozilla/4.0
    9 y5 t* a+ W4 f$ \Accept-Encoding:gzip,deflate
    6 |5 _  s5 q; M# Q# P3 _8 y" Q7 k, n以上的内容为多个消息头,用于描述客户端请求哪台主机,以及客户端的一些环境信息等) T" J* \  \9 S) o, t
    请求行中的GET称之为请求方式,请求方式:post,get,head,options,delete,trace,put,常用的有:get,post,用户没有设置,默认情况下浏览器向服务器发送的都是get请求,例如在浏览器直接输地址访问,点连接访问都是get,用户如想把请求方式改为post,可通过更改表单的提交方式实现.不管post或get,都用于向服务器请求某个web资源,这两种方式的区别主要表现在数据传递上:请求方式为get方式,则可以再请求的URL地址后以?的形式带上交给服务器的数据,多个数据之间以&进行分割,同时在URL地址后附带的参数是有限制的,其数据容量通常不能超过1k,若请求方式为post方式,则可以再请求的实体内容中向服务器发送数据,post方式的特点:传送的数据无限制
      _2 H! i" Y$ k1 |# P& b<ahref="1.html?name=aaaaaaa">连接</a>get方式
    : e) o( ?- ~3 i9 P/ y 6 p* I. }. \5 @. I( B4 x
    3.Http响应头字段详解/ C' X5 `* o: q  k
    响应头:; n( j; c+ q' t7 J
    Location:
    ) X0 L+ K, d- ^这个头配合302状态码使用,用于告诉用户找谁
    , Q" y: e0 }* j: W,response.setStatus(302),response.setHeader("Location","/day04/1.html");请求重定向,判断浏览器的地址栏的地址是否发生变化,实例是用户登录
    + C: D' d9 u/ fServer:服务器通过这个头,告诉浏览器的类型' _% }: \5 Y3 N* E) W6 X
    Content-Encoding:服务器通过这个头,数据的压缩格式,收费是靠数据出口量算的, 所以为了省钱和效率高,要进行数据压缩,jdk中的GZIPOutputStream类,压缩类流,包装流和底层流,最好将包装流关了或者刷新,数据写入到底层流中去,4 Q4 m! ^' l: M+ a5 ?+ S5 [
    response.setHeader("Content-Encoding","gzip");response.setHeader("Content-length",gzip.length+"");
    6 y( e7 @$ E4 g7 MContent-Length:服务器会送的数据的长度8 A- r# L3 ~+ X8 L( d
    Content-Type:服务器会送数据的类型,response.getOutputStream().write();服务器会送数据都是二进制,通过这个头,可以告诉浏览器这个二进制是什么类型,this.getServletContext().getResourceAsStream("/1.bmp");intlen = 0;byte buffer[] =new byte[1024];OutputStream out =response.getOutputStream();while((len=in.read(buffer))>0){out.write(buffer,0,len)},在服务器的目录下的web.xml中查看各个数据类型的respose.setHeader("content-type","")的写法.
    7 G* c6 C1 D2 m: KRefresh:告诉浏览器隔多长时间刷新一次,response.setHeader("refresh","3;url=""")控制浏览器隔三秒跳到指定的网页上0 g+ U  j& p' U, Z$ n4 f8 E7 Y. }
    Content-Disposition:告诉浏览器以下载的方式打开文件,response.setHeader("content-disposition","attachment;filename=3.jpg);
    # P; i, N+ b) X# O: y) C% M) @. o1 oTransfer-Encoding:告诉浏览器数据的传送格式: x3 [$ {. h: r) \. b0 ^
    ETag:缓存相关的头,服务器根据数据的内容生产一个字符串,客户机第二次访问服 务器时带来的ETag的值和服务器的值一样,就拿缓存给客户,实时更新,
    + Y6 u9 H0 O1 }' o( i- sExpires:高速浏览器,把会送的资源缓存多少时间,-1或0,则是不缓存的  _# F2 [6 ^6 o) \
    Pragma:no-cache- [+ F; G% j: f# r
    Cache-Control:no-cache, v+ x) q' {2 E4 w! t
    控制浏览器不要缓存数据,当数据不发生改变时,就要缓存,当实时性要求很高的数据不能缓存., t0 S7 {: I& x& r" [+ _* O6 ]7 q

    " z7 F7 Q" R7 z4.Http响应状态行
    : f- k, H1 s; L# z! s5 s2 r  GHTTP /1.1 200OK  :状态行,用于描述服务器对请求的处理结果; ~1 F7 C& w% E
    Server:Microsoft-IIS/5.0   Y% P9 M1 @. S& f- B
    Date:..../ V+ Y" x1 V+ p
    Content-Length:2291
    1 x& D: a: P, F$ yContent-Type:text/html: h" z: O' ]7 [8 C
    Cache-control:private
    ' n& ^# `( v: D多个消息头:用于描述服务器的基本信息,以及数据的描述,服务器通过这些数据的描述信息,可以通知客户端如何处理等一会它会送的数据
    ; V6 b. P  B( ]9 @8 O5 p# l  e<HTML>( W4 K- k! {; U! i) y
    <BODY>: [; B& s3 p+ I7 R7 e; J) p
    实体内容:代表服务器向客户端会送的数据
    ! r  ~! ]; r% Z4 R具体:! j* Z1 L  H& N# F7 z
    状态行:4 \) M$ Q+ |& u: n1 B; A
    格式:HTTP版本号  状态码  原因叙述<CRLF>
    " `! L% Y- p. ~& s  v# }举例:HTTP1.1  200     OK
    ( ~; D2 \( E3 d9 J4 |3 Z8 G8 H状态码用于表示服务器对请求的处理结果,他是一个三位的十进制数,响应状态码分为5类,; m9 Y0 G) S! o. l1 v' e; D' j
    100-199:表示接收请求,要求客户端继续提交下一次请求才能完成整个处理过程4 U; z+ {% P9 P' |6 }0 Q
    200-299:表示成功接收请求并已完成整个处理过程,常用200
    - s1 O& ^+ Q9 ^  N8 Q5 t7 u  `* [300-399:为完成请求,客户需进一步细化请求,例如,请求的资源已经移动一个新地址,常用302(你请求我,我叫你去找别人),307和304(拿缓存)3 L; j4 d; J9 e; f0 R4 O
    400-499:客户端的请求有错误,常用404,403(没有权限访问)
    ! G7 s0 [- M8 s" Q500-599:服务器端出现错误,常用500- j4 F" @' Z2 t. X9 I! j7 V
    + ~) N7 g/ k. n9 Q1 m, l9 @3 H
    5.http协议概述
    0 q$ A3 M/ {% r& z(1).浏览所有的服务器:" c3 y. p* G8 O/ l) a
    在命令行中敲入
    ) `; [7 Q/ t) Otelnet localhost 80801 [7 J: X5 _: O! l0 _
    GET /aa/1.html HTTP/1.1+ W8 y; C7 J1 k' u
    Host:& I+ F2 a  {9 n5 A( s
    (2).在HTTP1.0协议中,客户端和Web服务器建立连接后,只能获得一个web资源。在HTTP1.1协议,允许客户端与web服务器建立连接后,在一个连接上获取多个web资源。
    2 Q  E( j# S. ?(3).在命令行中敲入以上的命令,HTTP/1.0获取一次资源,就断开连接,而HTTP/1.1不会断开连接
    * n/ ^* i1 h% O" Y* Y) ~/ i: c6 b(4).一个web页面中,使用img标签引用了三幅图片,当客户端访问服务器中的这个web页面时,客户端总共会访问几次服务器,即向服务器发送了几次HTTP请求:1 w% l! D: q" k0 K& y
    <img src="1.jpg">- N: M3 @$ b- x5 d
    <img src="2.jpg">  k. T4 r" a( _; K
    <img src="3.jpg">- ?8 h. \3 r4 F+ i, w/ Y
    共四次请求,浏览器认识img标签,浏览器解析img,则img一次请求,最好将这三张图片整合到一张图片上,这样就只有两次请求了# K: F$ L/ v1 O; e! Y' g+ H' O" o
    (5).<scriptsrc="1.js">8 Y- J5 f% h3 u+ Z, ?$ h
    <scriptsrc="1.js">7 s: @9 _) A1 g: L; ~1 N
    共三次请求,可以将多个js文件整合到一个js文件,避免多次向服务器发出请求& z5 _) \7 h' Y$ E, Z
    6. tomcat服务器目录结构8 s4 p: ^" v/ T
    bin:存放启动和关闭Tomcat的脚本文件(命令)
    2 H5 `3 r& n2 q) rconf:存放Tomcat服务器的各种配置文件(核心文件:server.xml)% H3 z  ?' J% D, v8 ?0 P1 L- k7 t6 j
    lib:存放Tomcat服务器的支撑jar包
    $ S) N: \& ]" l2 x1 ?# hlogs:存放Tomcat的日志文件(catalina文件)
    " s! [- E4 t8 z7 R) v5 `9 Q" Ttemp:存放Tomcat运行时产生的临时文件
    + E% D% D8 [! M" I' V0 Q: rwebapps:web应用所在目录,即供外界访问的web资源的存放目录
    ( ^  @2 P! W$ Q/ r. B) e5 x( m7 bwork:Tomcat的工作目录,jsp翻译成servelet文件
    ( Z1 [! z+ k5 ?3 t0 P# m
    . e* ?" [1 c9 f; e7 p/ }7.web服务器启动问题
      {+ g4 V) j& ~: V* a(1).tomcat是使用Java编写的,启动的时候需要JVM,java环境设置不正确的话可能导致服务器不能启动,还有就是8080端口被占用了.
    4 ~# _4 m$ M' l
    9 {3 C$ [/ F: W! c(2).在conf->中server.xml中对tomcat服务器进行配置
    ( D2 @0 P* g% \# {0 K' S
    & [6 _( [! B5 P# J(3).我的电脑->计算机管理->服务->停止windows服务2 |' |& E. M, V' U+ y

    * @' w! H- |  P9 D8 N% F7 [(4).http://www.sina.com/是url;www.sina.com是主机名;sina.com是域名,在域名下可以搭建多个网站$ i! P* J# e, ~& x
    + L% F. Q  r( t& v" f
    (5).tomcat文件下的webapps下新建一个web资源
    5 S: P4 K% n# \  g  G+ o. f : R9 O  V7 X7 h
    (6).Cataline_home环境变量的设置问题,服务器的目录,启动的服务器的目录要和服务器存放的目录一致: K5 L$ @2 L, r# o  m8 Y

    / k" _( `2 i4 K/ H$ D8. web开发相关概念和常见web服务器8 Z5 p: G9 t) E
    (1).自己开发服务器:
    1 I% P9 C/ N& O9 L0 Q8 t& npublic class Server
    - x) `% h& J2 U- E* @{
    & r' d, ?8 _$ a  v1 S. |public static void main(String[] args)throws Exception{
    ( I0 @! ?3 b1 C) p. ^# MServerSocket server = newServerSocket(9999);//服务器建立端口
    % a) R6 p0 F/ \Socket sock server.accept();//客户端访问3 t# o) u* j% |
    FileInputStream in = newFileInputStream("c:\\a\\1.html");
    / I% r$ r# f1 H: q* w& WOutputStream out = sock.getOutputStream();
    " @( ?& s1 f; E$ @" Z% R5 q3 c! d: qint len = 0;- J' n+ u! \  e3 N& D3 k4 o: R
    byte buffer[] new byte[1024];
    7 p; w/ ~( h% b$ `( {5 M" vwhile((len = in.read(buffer))>0){+ ~; }, p2 Q& [2 L6 O& p5 C
    out.write(buffer,0,len);  Y/ x8 S3 z- v" `
    }in.close();
    ! S+ x& I+ A: J7 ?) Lout.close();4 G9 D' G/ v: P3 I3 T
    sock.close();
    , G! a5 Y  v* d5 ~' Z0 wserver.close();% R8 e* s8 e8 H
    }& M2 }$ Q) R0 A+ L" ]
    }
    & W2 ~4 }: T  w: t+ F(2).WebLogic是BEA公司的产品,是目前应用最广泛的Web服务器,支持J2EE规范,是商业产品,需要收费9 D% t* K- u% u& i
    (3).常用的Web服务器IBM公司的WebShpere,也遵循J2EE规范,也要收费
    ' R4 D( K/ u1 _+ v(4).Tomcat服务器只支持JSP以及Servlet规范,不支持EJB,如果要支持可以使用JBOSS
    % A' G$ O$ E( V/ k, K  }: x& t(5).http:80,smtp:25,pop3:110,ftp:23,https:443
    ; c" H2 ]3 U# e6 r) ~6 a, u  j
    4 e- F. v! R5 N* A; k9 r9.  web应用和虚拟目录. A) ?: r5 L4 F- ^2 L' t! {
    (1).web应用虚拟目录映射:自己开发好的web应用项目到部署到服务器上的web应用项目的关系目录,在服务器的配置文件中配置,虚拟目录,<Context path="/itcast"docBase="c:\news"/>,"/itcast"就是虚拟目录,"c:\news"是磁盘上web应用的目录,修改配置文件后一定要重启服务器.http://localhost:8080/itcast/1.html,浏览c:\news\1.html文件.当path=""时,就是缺省设置
    3 U1 a( {3 v7 n3 V , P, w0 H' {/ r9 Q$ e' {; M
    (2).tomcatdocument中reference中观察tomcat的配置文件设置方法.在Catanena\localhost\新建一个context.xml文件.这种方法无需重启服务器.0 w5 X7 w  I( v" S" c- U" u% F
    <ContextdocBase="c:\news"/>,http://localhost:8080/1.html;多级虚拟目录:文件名:a#b#c.xml,http://localhost:8080/a/b/c/1.html;缺省的虚拟目录,把文件名改成ROOT.xml重启服务器,覆盖原先的缺省设置.+ y5 f$ f, p+ b

    - U6 g, S% o9 }) n, j; P0 Z0 V(3).webapps文件夹中的web应用可以让外界访问,又服务器自动映射目录,把news文件放到webapps中去,http://localhost:8080/news/1.html,当服务器和webapps不在一个目录时,此方法不可以.  f1 {7 l; Q9 N" }

    3 ^! P. D. J2 s! K" l10.  web应用组织结构
    - X! d6 a9 k$ e  A5 ~(1).mail:Web应用所在目录
    7 f" |$ h" A! Jhtml,jsp,css,js文件等,这些文件一般存在web应用根目录下,根目录下的文件外界可以直接访问,java类,jar包,web应用的配置文件存在这个目录下WEB-INF,这个文件夹的文件名一个单词都不能写错,这个文件要新建,在这个文件夹下载新建一个classes文件夹,放置java程序,新建一个lib文件夹,放置架包,该目录下的文件外界无法非法直接访问,由web服务器负责调用.每个web应用应该有个web配置文件,是最重要的一个配置文件,一切配置可以通过这个文件进行配置web.xml4 G/ e7 n- l" {: O
    - q, d! e1 U/ J1 h7 N, T
    (2).对于web.xml文件,可以从server.xml中拷贝,拷贝头和尾部分.
    红红火火恍恍惚惚
    回复

    使用道具 举报

    admin    

    1244

    主题

    544

    听众

    1万

    金钱

    管理员

  • TA的每日心情

    2021-2-2 11:21
  • 签到天数: 36 天

    [LV.5]常住居民I

    管理员

    板凳
    发表于 2015-06-02 12:43:13 |只看该作者
    11.  web资源访问流程+ Y1 b3 {+ c  p1 s7 Z* ~. R5 o
    (1).服务器认识war包,自动解压,放到webapps中,自动生成应用.8 G, N; S  m- g3 O6 \
    3 f+ J1 o0 r" s* c
    (2).配置context元素的reloadable="true",应用程序修改,不需要重新发布应用,建议不要配置,因为只要一修改,就要重新加载,出现问题.+ W1 B" ^4 m! x
    ; Z$ F. v/ G0 ^5 v" R1 F
    (3).context.xml中<Context>中配置,被所有的web应用使用.
    " j) @! R5 }8 x
    + T; x6 F% f0 ~12.  配置虚拟主机3 ~+ H& [! Z* G9 {8 @' g) G+ ~6 |0 i
    (1).在一个tomcat服务器中可以放置多个网站,所谓配置虚拟主机,就是在tomcat服务器中配置一个网站
    , n  D  _$ |. i5 W 9 b2 K' J3 S8 H9 }  T! q
    (2).如需在Web服务器中配置一个网站,需使用Host元素进行配置,例:<Hostname="site1"appBase="c:\app"></Host>,配置的主机(网站)要想被外部访问,必须在DNS服务器或windows系统中注册.
    ' u; y; |3 B) K2 o
    9 ]0 e; k; g8 _8 D5 _; D  v0 c: S7 r' V(3).在server.xml新建一个主机,新建一个Host标签,<Host name=www.sina.com appBase="c:\sina"><Contextpath="/.mail" docBase="c:\sina\main"/></Host>
      k3 Q# s+ z1 u) u
    4 j) d0 H) z& ~+ y9 N(4).互联网访问流程:ie中输入错误!超链接引用无效。 返回给ie,接着使用ip地址访问sina.com. }- p( s! y% D0 G, N3 J
      K0 P5 K% s0 a% h
    (5).ie开始会访问windows的host文件,如果这个文件知道这个地址,就不会去问DNS,没有则会去访问DNS服务器:/windows/system32/drivers/etc/hosts文件进行配置.8 ?0 q+ h0 b" z  c& m- W
    ; x3 `4 d9 ?! r9 ]$ y5 {
    13. 软件密码学和配置tomcat的https连接器
    . }  w+ C* E2 |; R% O8 F. l(1).对称加密:加密和解密的密码是相同的.
    ( [6 n3 w$ B5 |5 L0 W4 t/ D " F" k" ^( l# [: z3 y
    (2).非对称加密:接受者生成一对公钥和私钥,公钥加密的数据只能私钥解,私钥加密的数据只能公钥解,然后接受者将公钥传给发送者,用接受者的公钥进行加密,然后接受者用私钥解密.然而,在传送公钥的途中可能被黑客拦截,然后黑客自己生成自己的公钥和私钥,然后将公钥送给发送者,发送者如何知道公钥是不是接受者的公钥?这是在互联网上有个叫CA的机构,接受者产生一对公钥和私钥,然后让CA认证,CA给接受者一份数字证书,这时发送者受到的是数字证书,发送者验证数字证书是不是接受者的数字证书.信任点就是CA.网上银行出示一份数字证书(公钥),浏览器可以验证数字证书正确,注册,当你填写信息时,浏览器就是用这份数字证书验证.但是数字证书可能被黑客拦截,怎么确定数字证书是接受者发的还是黑客发的,数字签名(私钥加密),发送者自己生成一对公钥和私钥,发送者想让接受者相信是自己加密的,发送者就需要签名(使用自己的私钥),然后只有接受者使用公钥解密,就相信是发送者将内容加密的.MD5算法得到数据指纹.然后对数据指纹加密.将密文和指纹加密信息传给接受者,可以判断在传送过程没有被篡改,用密文生成数据指纹,和传过来的数据指纹对比.% J8 W; p1 X8 `

    & ?: o! {" s8 E$ H3 G8 D第四天:
    ' m) u, q5 s5 W1 L1.Eclipse开发servlet
    & H" @/ h% |% E9 O  y(1).web工程的名称,该工程部署时,在webapps目录下就会有一个example的web应用
    / A8 L) a8 o+ [! u' C. c
    8 M8 ~" t1 \5 z/ i  ?(2).src文件夹,Java程序的开发目录,该目录下编写的所有程序在部署时,会自动部署到example/web-inf/classes目录下5 F2 w5 m8 i3 e, P3 b

    . s6 m% d: d6 O(3).WebRoot文件夹,webroot对应于web应用的根目录,该目录下的所有子目录和子文件夹在部署时,会原封不动的发布到web应用目录下
    : n6 `+ ~0 D& `8 V2 l0 u
    + I+ F6 n+ G$ N' m* i(4).Web rootfolder:WebRoot应用的根目录,Context root URL:映射到服务器的目录,在WebRoot目录下新建Web资源,应用发布时,将src文件夹中的源文件编译成class到webRoot\web-inf下的classes文件夹
    6 w0 J, f$ ^$ c1 U
    2 t. q" m- x9 u; Z2 E(5).servlet导入源码,到tomcat服务器的目录下查找文件夹
    1 t6 B/ ^$ \) \) k7 S5 J0 X 1 e' C  N6 D) X
    (6).为servlet配置对外访问路径,# E* c6 D3 y2 J9 g8 o7 {* E& ^
    <servlet><servlet-name>! O. L) |5 v- C* a# T
    别名
    : x* ^- ?6 F) g# J! R5 u5 x</servlet-name><servlet-class>. X; p# P1 ~/ l# V) I
    类名(全称)
    & G5 a5 ~6 w; |, ~( w</servlet-class></servlet>
    # {4 X4 e) J" f8 C: }, J! {<servlet-mapping><servlet-name>
    & J) z! D2 U4 T0 F! \( Y别名
    9 o! g9 c5 Y9 S) j</servlet-name><url-pattern>/aa<url-pattern></servlet-mapping>
    ( f( x: a9 `8 D/ y7 s. ~ 3 Y; u  n# a; z% u" g
    (7).还可以更改tomcat的运行java环境
    7 k: u  r: Q1 T0 w4 s3 U- f2 J
    . M# a5 V7 Y6 g. e( ]7 ~, y2. HttpServlet和一些开发细节
    5 w6 B6 }0 U& b7 I; j2 @1 NServlet接口sun公司定义了两个默认实现:/ M' o6 Z2 n" W9 ^" M. H8 [
    GenericServlet,HttpServlet5 r: O( F5 t3 `6 u
    httpservlet指能够处理HTTP请求的servlet,它在原有的servlet接口上添加了一些与http协议处理方法,它比servlet接口的功能更为强大,因此开发人员在编写Servlet时,通常应继承这个类,而避免直接去实现Servlet接口6 B/ r7 M1 T9 d# X! E
    httpservlet在实现servlet接口时,复写了service方法,该方法体内的代码会自动判断用户的请求方式,如为get请求,则调用httpservlet的doGet方法,如为post请求,则调用doPost方法,因此,开发人员在编写Servlet时,通常只需要复写doGet或doPost方法,而不要去复写service方法.可以查看httpservlet的源码,
    0 p9 y* y  i5 ~) G0 {: o修改servlet的模板代码.进入myeclipse,搜索Servlet.java文件,打开修改文件.$ r9 O2 R* M) o$ q' V3 }  {: D

    : y# g  E% w2 V- Q" t  c3 q& x3.ServletConfig对象以及它的开发场景
    $ D7 s. ?. o9 ]) e1 r7 O1 s2 m" J8 U% Z(1).在servlet的配置文件中,可以使用一个或多个<init-param>标签为servlet配置一些初始化参数,当servlet配置了初始化参数后,web容器在创建servlet实例对象时,会自动将这些初始化参数封装到servletconfig对象中,并在调用servlet的init方法时,将servletConfig对象传递给servlet,进而,程序员通过servletconfig对象就可以得到当前servlet的初始化参数信息.: {0 f+ J. @+ H
    / Q# l7 u9 r: e; A: |' D
    (2).web服务器创建的容器对象. w' e$ \  i: M' {% L6 Y
    request,response,servletconfig,servletcontext,session,cookie
    / S: k" F; H: ]$ a4 t
    5 j# I3 i: @. D5 I+ I8 N(3).servletconfig) \+ z: K0 h; h: q/ s1 o
    配置参数信息." j; b! J( g% ?
    <init-param><param-name>data</param-name><param-value>xxxx</param-value></init-param>/ D4 k  F3 }$ X# s: G

    % X1 Y( p9 O: f* H(4).覆写init()方法带有servletconfig参数的,服务器自动将参数信息封装到servletconfig对象中.调用init()方法时,servlet可以得到这些参数信息.在servlet中定义一个servletconfig对象,将init()传递过来的servletconfig接收到.private ServletConfig config;public voidinit(ServletConfig config){this.config = config;}String value =config.getInitParameter("data");System.out.println(value);参数信息不在程序中,而是在配置文件.同时this.getServletConfig();代替privateServletConfig config;因为父类已经定义了ServletConfig对象,只需调用父类的getServletConfig方法即可.
    & F6 p, {+ m# m( S- ]/ D- t7 f6 N) o
    . B9 ^% `" r- N7 N(5).可以配置多个参数信息,同时为得到所有的参数信息:4 @1 A2 ]1 W" y! i- i9 N2 j
    Enumeration e =this.getServletConfig().getInitParameterName();  X5 i+ s! U# [' L) d- }& q
    while(e.hasMoreElements())5 L4 g! D1 t# @
    {0 O5 Z; }- L# h  K
    String name = (String)e.nextElement();& W5 Z" w2 h4 s5 `
    String value =this.getServletConfig().getInitparameter(name);0 K% @, {/ S* X* M
    System.out.println(value);
    " F$ w- `3 Q% `6 j}8 I- j& G4 h' G$ y- Z6 @& F
    (6).开发过程中,参数不能在程序中写死,需要在配置文件中给出参数信息.实例:字符集,连接不通的数据库.读取不同的配置文件.  B+ A: n) D+ E
    7 S. R1 p( |1 V- K4 m
    (7).struts就是个实例.可以到struts实例中查看struts的web.xml文件
    " ]4 r/ q% G. `( m; s 9 t! n, U& ~& c9 ^
    4.  ServletContext读取web应用中的资源文件& V- u/ E4 u( y: N/ {
    (1).在web开发中,用于资源文件的类型一般为两种xml,property,如果数据之间是没有关系就是用property,有关系就使用xml,数据库的连接关系没有关系,一般用property文件,1 e0 l! N( d' v9 p+ \1 X7 C

    3 ]$ V5 E+ T$ z* K4 I(2).db.properties文件,url=jdbc:mysql://localhost:3306/test;username=root;password=root;读取db.properties文件.通用代码:
    ( W5 g! ^- I& S8 T" HInputStream in
    5 r% T# L: z3 C' c) Z$ Q5 ~= this.getServletContext().getResourceAsStream("/WEB-INF/classes/db.properties");
    1 E+ _8 W; y$ X斜杠就代表web应用根目录,Properties props = new Properties();props.load(in);把文件load到Properties对象中,用Map对象存储String url =props.getProperty("url");
    9 f! }9 V& M) u3 H3 |( m' \String username =props.getProperty("username");
    ' k& z# R9 m! S; Z% v4 A* `1 DString password = props.getProperty("password");5 k  d+ Z6 ^7 ^1 ^% p
    2 ^5 Y! Y5 ~$ T
    (3).web应用中不使用FileInputStream in = newFileInputStream("src/db.properties");采用的是相对路径,相对路径中有这个资源,这是相对路径相对的是jvm的启动目录.当时jvm的启动目录是服务器的启动目录:服务器/bin
      _( K8 @# t* W
    , i1 u# d5 Z8 L* a& [$ T% j(4).另外的读取资源的方法:! T; }8 o8 f; m$ p2 g& T
    String path = this.getServletContext().getRealPath("/WEB-INF/classes/db.properties");得到硬盘的绝对路径 此时就可以使用FileInputStream in = new FileInputStream(path);需求:下载,可以得到资源的名称,读取文件时客户机带来的,当前读取的资源文件的名称.可以从path中截取出来.8 J3 Y3 x5 b5 G( @) V. P$ z9 \. ~
    String filename =path.substring(path.lastIndexOf("\\")+1);* U  N" P. s0 @

    / I2 P. u6 X% C6 U) j0 i! d(5).在servlet中用dao对象操作数据库,dao对象需要properties文件的内容即数据库的连接信息.dao一般放到cn.itcast.dao包中.不能将servlet中的context对象传递给dao对象,web层和服务层不能有交织了.如果读取资源文件的程序不是servlet的话,此时要通过类装载器读取资源,在UserDao对象中InputStream in =UserDao.class.getClassLoader().getResourceAsStream,资源文件("db.properties");一般只需读取一次,有个习惯:将读取文件代码放在static{};静态代码块中.通常抛出初始化错误throw new ExcepitonInitaliterError(e);文件不能太大,类加载器要装载到内存中
    ! C1 U# D$ i5 M3 ~
      @; s* v: y( V; W# \(6).有人可能把properties文件改了.然后读取的内容是原始的,还是修改的,读取的是原始的,因为类装载的方式,但是类只装载一次.但是想读取更新后的内容:String path =UserDao.class.getClassLoader.getResource("db.properties").getPath();然后用FileInputStream去读取.  N& o9 I5 `  l3 u* N& |

    : L* t* K- f1 o0 o/ V% k1 O" R5.  ServletContext对象" ]4 N9 S9 X. u, Y7 p1 d
    (1).web容器在启动时,它会为每个web应用程序都创建一个对应的ServletContext对象,它代表当前web应用,servletconfig对象中维护了servletcontext对象的引用,开发人员在编写servlet时,可以通过servletconfig.getServletContext方法得到servletContext对象.同时也可以context = this.getServletContext();得到父类的context查看servletContext api 文档了解servletContext对象的功能.1 q3 K* b2 u) h& R) i3 A4 q

    8 B6 J$ }( v1 V# E3 O$ v( j(2).servletContext为所有的servlet对象实现数据共享.是全局概念.方法getContext()为了得到别的web应用.getInitParmeter()方法可以得到web应用的配置信息.getNameDispatcher()得到一个转发对象.getRealPath()方法得到资源的硬盘真实路径,方法getResource()得到资源的路径,只是返回的是URL地址.getResourcePaths()方法返回给定目录下的所有资源.getServletContextName()方法返回web应用的名称.setAttribute(),getAttribute(),removeAttribute();
    7 v! Y, ~# F: R. g9 m+ }
    - r5 Q8 ?1 K, b+ N- S" _  V5 w3 d9 k(3).由于一个web应用中所有servlet共享同一个servletcontext对象,所以多个servlet通过servletcontext对象是实现数据共享。servletcontext对象通常也被称之为context域对象.可以获取web应用的初始化参数,实现servlet的转发,利用servletcontext对象读取资源文件:得到文件路径,读取资源文件的三种方式,.properties文件(属性文件),context域,request域,session域,page域.5 U% K# Z  a  N2 d( r

    " q( M' z2 U3 S6 {* F7 |(4).实例:聊天室,资源共享.开个浏览器说,开个浏览器读
    : t$ P( _7 `" F% O0 h 4 S: i7 m7 f7 n6 g7 q5 K$ w+ T
    (5).<context-param><param-name>data</param-name>
    3 [4 V. J& T3 F) X* x, X5 S5 i! B: P<param-value>XXXX</param-value></context-param>
    ; D0 R# T1 ?& `; q和config不同,config是给某个servlet初始化参数,而context是所有的servlet初始化参数
    : A) D& K. g% |# r 8 ?; _1 |3 @4 ^2 N1 R9 T; }
    (6).servletconfig中的初始化信息中连接数据库信息,应该写成context的初始化信息,因为可能有多个servlet需要数据库连接
    4 s& e& S$ U5 Q5 R 5 d/ U) P( `" |! u8 I$ u2 o
    (7).转发和重定向:转发:你找我,我帮你去找他,重定向:你找我,我告诉你,你自己去找他;如果是转发:只有一次请求,地址栏不发生改变重定向:两次请求。servlet中不适合浏览器的数据的输出,因为要数据的排版,应该在html,jsp中输出数据.servlet要将输出数据要转发给jsp显示.% r* o& o, L& j0 Q$ B1 c% {: j
    RequestDispatcherrd = this.getServletContext().getRequestDispatcher("/1.jsp");/ C! r: ^: e5 h- a
    rd.forward(request,response);
    " Y+ i+ n4 I$ y7 [3 q在jsp中取数据,& s+ N/ h: R" P1 D$ g! b
    <% String.data=application.getAttribute("data");out.write(data);%>servletcontext可以,但是在开发过程中是有问题的:有一个人来访问数据,把它的数据存到context中,当他在jsp中取数据的时候,此时又来一个人,也来存数据,可能覆盖前一个人的数据,那前一个人取得数据可能是后来的那个人的数据,因为context是共享资源,存在线程安全问题.在开发过程中一般用request域带数据.
    ! O5 H: t  m# G) \& t0 d% K5 p 9 I  K1 \4 S1 o  w4 V2 }) e# }
    (8).服务器启动时,为每个web应用创建servletcontext对象,当服务器停止时,servletcontext对象被销毁.  `! y. O% ^# ]

    # f# G. u# c  d6.  Servlet的生命周期8 s% ]2 }( S+ L  ~
    ie浏览器,web服务器,Servelet程序
    ) S& g/ `$ Q9 }! C% G" n浏览器输入地址->连接web服务器,发送HTTP请求,解析出客户机想访问的主机名,解析客户机想访问的web应用和web资源-->使用反射技术创建一个servlet实例对象(servlet第一次访问的时候被创建)-->调用servlet.init()完成对象的初始化->调用servlet.service()响应客户端的请求,服务器创建request,response容器,service方法执行->向response容器中写入客户机请求的数据->服务器从response中取出数据构建一个HTTP响应回写给客户机-->回写http响应# x5 P6 D% T3 [. u& X  G% r
    客户机第一次访问服务器,生成一个servlet,第二次在访问同一个servlet就不会再创建了,当关闭服务器时servlet就消亡了
    / m  ^% i* o; B" D0 ]# W( N
    ) O8 F' j2 P: N# ?% ~" @, g7.  Servlet的线程安全# [+ S; F! \# i7 X+ E" Z# S9 P
    (1).当多个客户端并发访问同一个servlet时,web服务器会为每一个客户端的访问请求创建一个线程,并在这个线程上调用servlet的service方法,因此service方法如果访问了同一个资源的话,就有可能引发线程安全的问题,如果某个servlet实现了SingleThreadModel接口,那么servlet引擎将以单线程模式来调用其service方法。SingleThreadModel接口中没有定义任何方法,只要在servlet类的定义中增加实现SingleThreadModel接口的声明即可.对于实现了SingleThreadModel接口的servlet,servlet引擎仍然支持对该servlet的多线程并发访问,其采用的方式是产生多个servlet实例对象,并发的每个线程分别条用一个独立的servlet实例对象。实现SingleThreadModel接口并不能真正解决servlet的线程安全问题,因为servlet的引擎会创建多个Servlet实例对象,而真正意义上解决多线程安全问题是指一个servlet实例对象被多个线程同时调用的问题,事实上,在servlet api2.4中,已经将SingleThreadModel标记为Deprecated(过时的).标准的解决方案是同步方式sychronized6 b3 k! }, y7 u+ n. `9 _( s
    ; c; @* I: p/ a+ ?! l
    (2).如果线程向person的静态list集合中加入了数据(aaa),数据用完后,一般要移除静态集合中的数据(aaa),否则集合中的数据越来越多,就会导致内存溢出。对象销毁了,静态资源的字节码仍然驻留在内存中.# b: U0 ~* }; K3 k& W

    * U; r. b4 e7 _. c(3).web中的异常一般不能抛,要catch住.
    ' ?" ~4 X( w1 K9 C+ n( d7 H# }8 x8 U 8 x( t/ ?5 |/ L5 Q
    8.  Servlet开发的一些重要细节
    " m! M, w% w" s7 [, N3 C2 s- c(1).由于客户端是通过URL地址访问web服务器中的资源,所以Servlet程序若想被外界访问,必须把servlet程序映射到一个URL地址上,这个工作在web.xml文件中,使用<servlet>元素和<servlet-mapping>元素完成
    ! D+ M. S  D. J, x$ l2 a " x+ }& z4 d2 u5 T
    (2).<servlet>元素用于注册Servlet,它包含有两个主要的子元素:<servlet-name>和<servlet-class>,分别用于设置Servlet的注册名称和Servlet的完整名.
    + I, w$ J3 R  d! x一个<servlet-mapping>元素用于映射一个已注册的Servlet的一个对外访问路径,它包含有两个子元素:<servlet-name>和<url-patteren>,分别用于指定Servlet的注册名称和Servlet的对外访问路径$ G  R3 |+ _" B: T% S6 L! q7 ^
    (3).进入myeclipse->Web更改Web-Content-root
    9 g1 A$ s" g- [/ m( g: w
    + z9 F, P# t  m. l) p* ?(4).一个servlet可以映射到多个对外访问路径.只需多复制个
    : g6 z8 {- s; L& @" O  Y8 a<servlet-name><url-patteren>(伪静态)( {' q9 R: I! ~& x' ]
    + o) ^8 G# O( O( z7 m% K4 l
    (5).同一个Servlet可以映射到多个URL上,即多个<servlet-mapping>元素的<servlet-name>子元素的设置值可是同一个servlet的注册名1 p: U( g) W: m: l( k
    4 R- T8 h. G! g2 M
    (6).在servlet映射到的URL中可以使用*通配符,但是只能有两种固定的格式:一种格式是:"*.扩展名",另一种格式是以正斜杠/开头并以"/*"结尾的.& b4 i% w: V( f8 n( j' J6 O( D% c
    <servlet-mapping><servlet-name>anyName</servlet-name>5 }+ j$ o7 Z' x6 \
    <url-pattern>*.do</url-pattern></servlet-mapping>0 Y( E6 e* L6 y
    匹配所有后缀名为do的访问路径(也可以不需要后缀名, 匹配所有的文件)<servlet-mapping><servlet-name>anyName</servlet-name><url-pattern>/action/*</url-pattern></servlet-mapping>服务器在更改web.xml时,无需重启,<WatchedResource>WEB-INF/web.xml</WatchedResource>在服务器的conf文件夹下的context.xml配置文件中,监视web.xml是否发生改动5 O7 @, e4 U9 A

    6 M+ P7 s; \* i7 `# [5 ~% \(7).servlet1 映射到 /abc/*
    , Y. T, A" ?' m6 `3 x$ z" p          servlet2 映射到 /*9 w; W- `  H. G& E8 i5 c( d0 \
              servlet3 映射到 /abc
    # |2 `: p) D8 A. S7 @           servlet4 映射到 *.do9 C& {" s5 I3 ]
               问题:4 |0 k" u' T$ ]; }
    1.当请求URL为"/abc/a.html","/abc/*"和"/*"都匹配,但是servlet引擎会调用servlet12 Q* d" K3 `$ G
    2.当请求URL为"/abc"时,"/abc/*"和"/abc"都匹配,但是servlet引擎会调用servlet3) B7 Z/ ?8 z8 a( P: {! t. i- e9 s
    3.当请求URL为"/abc/a.do"时,"/abc/*"和"*.do"都匹配,但是servlet引擎会调用servlet15 o! w4 s, p, b; ]5 C4 V1 T* p
    4.当请求URL为"/a.do"时,"/*"和"*.do"都匹配,但是servlet引擎会调用servlet2+ W$ j- E& p* X: N' L4 R1 J( c9 ]
    5.当请求URL为"/xxx/yyy/a.do"时,"/*"和"*.do"都匹配,但是servlet引擎会调用servlet2
    - c% _) f) q7 S# P; }总结:谁长的最像,谁先匹配,同时*的优先级最低.8 q4 R$ {& z/ V( N) r1 K* A
    9 V/ m. r( ?& a. Z* N) U
    (8).Servlet是一个供其他Java程序(Servlet引擎:服务器端调用servlet的程序)调用的Java类,它不能独立运行,它的运行完全由servlet引擎来控制和调度.针对客户端的多次servlet请求,通常情况下,服务器只会创建一个servlet实例对象,也就是说servlet实例对象一旦创建,他就会驻留在内存中,为后续的其他请求服务,直至web容器退出,servlet实例对象才会销毁.在servlet的整个生命周期内,servlet的init方法只会被调用一次,而对一个servlet的每次访问请求都导致servlet引擎调用一次servlet的service方法,对于每次访问请求,servlet引擎都会创建一个新的httpservletrequest请求对象和一个新的httpservletresponse响应对象,然后将这两个对象作为参数传递给她调用的servlet的service方法,service方法再根据请求方式分别调用doXXX方法.
    % {1 I/ g  e3 a1 X) i ) n8 B6 C# j8 H0 z3 V5 c
    (9).右击,可以选择父类的方法,进行重写父类的方法
    % P$ c( Y  z7 P" E * M0 M7 p3 D( n( A1 t) b( M
    (10).服务器启动时,servlet并未创建,只有当访问web资源时会创建一个servlet,调用init()方法.一个servlet为多个请求服务,当服务器停了,servlet就被摧毁了,调用destory()方法.2 P1 Q( d6 d( ~2 F( o; S
    / h5 l$ z8 [& s+ z  g/ T5 n
    (11).针对客户端的每一次请求,服务器端都会为用户创建一个request和reponse,他们的生命周期很短,如果是并发的请求,大量的请求可能创建大量的request和reponse对象,可能导致服务器运行变慢,但是不是并发的话,可能不会创建那么多个对象
    0 b& ]$ O6 Z, }2 M0 e- ~* p
    + P. U6 ]! M5 ^/ ~5 i& b5 E(12).如果在<servlet>元素中配置一个<load-on-startup>元素,那么web应用程序在启动时,就会装载并创建servlet的实例对象,以及调用servlet实例对象的init()方法,<servlet><servlet-name>invoker</servlet-name><servlet-class>...</servlet-class><load-on-startup>2</load-on-startup>;用途为web应用写一个initservlet,这个servlet配置为启动时装载,为整个web应用创建必要的数据库表和数据.(即启动服务器时就创建了servlet),struts就是一个非常大的servlet,其中数字的大小就是启动的优先级,数值越小优先级越高.struts中的数值为2,担心用户需要扩张,所以不使用1.: f& V; R, z) }" s
    1 q6 K$ q. r7 B7 Y6 a) Y6 O
    (13).如果某个servlet的映射路径仅仅为一个正斜杠/,那么这个servlet就成为当前web应用程序的缺省servlet;凡是在web.xml文件中找不到匹配的<servlet-mapping>元素的URL,它们的访问请求都将交给缺省servlet处理,也就是说,缺省servlet用于处理所有其他servlet都不处理的访问请求.在<tomcat的安装目录>\conf\web.xml文件中,注册了一个名称为org.apach.catalina.servlets.DefaultServlet的Servlet,并将这个servlet设置为缺省servlet;当访问tomcat服务器中的某个静态html文件和图片时,实际上是在访问这个缺省servlet。这个用途主要用于:用户请求一个数据(html,jpg,avi等)服务器将启动一个缺省的servlet提供给客户数据.在浏览器看到的任何内容都是数据都需要由一个servlet来为客户提供数据,包括错误信息.2 l! ~. L1 p' T2 h
    9 D! K( N' [6 S; B4 f, ]7 y, ?
    (14).配置文件修改了,不需要重启服务器,但是源程序修改了,要重启服务器.# v7 J; R* q) l/ ]  N1 I
    9.  Servlet开发入门$ q3 S' I3 t* H$ @
    (1).jsp就是servlet,servlet是sun公司提供的一门用于开发动态web资源的技术,编写一个java类,实现servlet接口.获得servlet启动信息,通过getServletConfig(),service的两个参数,ServletRequest req,ServletResponse res,这个方法是服务器调用的,获取客户机的对象,只需找request,输出信息找response.getOutputStream();1 O1 [) }  Y$ W
    (2).进入服务器的目录,新建一个web应用,在应用中新建一个java,class,lib,在文件夹java下新建一个servlet文件,
    ( L6 ^$ d0 S+ N6 O8 K" }8 Z  Zpackage cn.itcast
    ) y/ |7 D7 c) _& x2 Apublic classFistServlet extendsGenericServlet{
    ! Q) a8 `0 E2 j+ m0 n7 a- vpublic void service(! `% N7 q: {" N
    ServletRequest req,ServletResponseres)throws ServletException,java.io.IOException{: f% x/ y3 L% A' A2 o( a
    OutputStream out = res.getOutputStream();
    9 M+ a* o- ~% ?+ U# j& }out.write("Servlet".getBytes());" U0 k% A. [) {" H% t& ?+ L! u
    }
    7 W+ y8 D- p- |$ J( p" U* R1 @}' S. x+ }7 R) b- m
    (3).servlet所需的架包导入,tomcat能运行servlet,则在服务器的lib目录下含有架包,为servlet配置对外访问路径web.xml,代码可以到服务器的web.xml文件中拷贝
    红红火火恍恍惚惚
    回复

    使用道具 举报

    admin    

    1244

    主题

    544

    听众

    1万

    金钱

    管理员

  • TA的每日心情

    2021-2-2 11:21
  • 签到天数: 36 天

    [LV.5]常住居民I

    管理员

    地板
    发表于 2015-06-02 12:43:29 |只看该作者
    第五天:6 k. g7 t) j6 ^$ C' `
    1.     HttpServletResponse简介
    % g  F! L* V% ]- o* [(1).web服务器收到客户端的http请求,会对每一次请求,分别创建一个用于代表请求的request对象,和代表响应的reponse对象.request和reponse对象既然代表请求和响应,那我们要获取客户及提交过来的数据,只需要找request对象就行了,要向客户机输出数据,只需要找response对象就行了.
    " s, X  h5 ^* G+ j
    4 P7 _2 X7 d, ]. ^  d0 A# I(2).reponse的相关方法,构建一个http响应的方法,getStatus(),setHeader(),getWriter(),getOutputStream();字节流可以写任何数据
      A, S6 Z& g( k- t
      F6 [4 j2 {; d$ w3 U+ J2.request获取请求头和请求数据
    8 A! X! O1 q0 x( |7 w) S(1).用户点击链接和表单提交向服务器提交数据
      x* Q  X: K5 E# U8 v: k 9 W$ O: C, v& _$ ~
    (2).在服务器端获取数据:
    . F, [6 b% ]9 ~* W9 o. zrequest.getParameter("username");
    ) K! \3 I- d8 S) `- krequest.getParmeterName();6 W/ ?' _2 f- e3 i: Q9 u
    得到所有的名称的枚举.
    ; r; K% Q0 \1 o$ |% S1 g0 Krequest.getParameterValues();
    0 d0 }  t, Q" X% u得到名称相同的值, H6 l$ z* o3 v9 Z9 y
    (3).在用户提交的数据中,可能抛空指针异常,加强代码的健壮性.
      D8 j2 `* L9 H5 B  C8 N- f4 J
    - r+ D/ `' f5 c3 J: T$ |(4).Mapmap=request.getParameterMap();将用户的数据封装到对象中User,先获取Map,再用JavaBeans将Map转化成对象User,其中map关键字是请求参数名称,关键字的类型是String,值的类型是String[],因为可能有多个相同名称对应的值就有多个,所以是个数组;Map将值填充对象User,迭代Map,得到关键字和对应的值,利用反射技术将这些数据填充到User对象中.设计一个对象代表表单.BeanUtils.copyProperties(user,formbean);将表单beans拷贝到user对象beans中;BeanUtils.populate(user,map);对象上的字段必须是八种基本类型,不是的话,要自己写个转换器.. H/ l$ d. ^' ^! z. n
    $ X0 g1 b; H; Y: x; j' u, o
    (5).服务器以断点的方式运行进行调试,
    7 h7 `8 F( z# ~( T5 A# d2 E
    # q: [+ y) X, Y(6).request.getInputStream();获取输入流,进行获取数据,进行文件上传.
    9 e- a: V& l# S1 ?  f) J- u2 x3.  request简介.txt
    9 u9 o  ?! N2 w: u: o(1).HttpServletRequest对象代表客户端的请求,当客户端通过HTTP协议访问服务器时,HTTP请求头中的所有信息都封装在这个对象中,开发人员通过这个对象的方法,可以获得客户这些信息.5 F' e( O- ?6 Z

    + L7 L" ]8 M- S4 E0 j8 a) l(2).getMethod();获取请求方式,getRequestURI();获取客户想我访问的资源;getHeader();getDateHeader();getHeaderNames();获取所有的头信息,getHeaders();与getHeaderNames();不同的是:可能有同名的头信息,此时就要用getHeaders()方法,a-xx;y-xx;getParameter获取客户带来的数据name="aaa";password="rot";getParameterNames();getParameterValues();getInputStream():将用户的数据当做流读入,如:客户进行文件上传;
      x% p! u- M9 H- u& p* M " k. D: l! p5 N& @. e7 E
    (3).getRequestURL():/day07/servlet/demo1;! Q! Q9 q) Z. l5 q' ]
    getRequestURI():http://localhost:8080/day07/servlet/demo11 M3 p' j9 A5 z
    用在权限拦截上,不同的网页有不同访问权限,做个拦截器,过滤器,还有是在记录页面的访问次数.getQueryString()客户携带的查询信息,地址栏后的类容,getRemoteAddr();得到访问者的ip地址;getRemoteHost():主机在DNS注册了,打印了主机名.getRemotePort();来访者的端口,ie也是一个程序,需要一个端口getLocalAddr();返回web服务器的ip地址,getMethod():获取请求方式,默认是get方式% P8 x( C8 g! ]* e+ j8 Q2 N) ]- [
    6 p$ C' I" C! I$ z6 v/ f
    4.request乱码  T, A1 v2 `# M7 h  a
    (1).在文本框中敲入中文,浏览器提交数据的码表就是浏览器使用哪个码表打开.request容器也是默认码表是iso8859码表,所以在获取数据时,request.setCharsetsEncoding("UTF-8");这种改变码表的方式只对post方式有效,对get方式无效.这种方式解决的是post方式提交的数据.
    2 W: q+ ], v6 C  c6 f
    ) r& j9 a- M. }$ k(2).但是这种方式不能用于get方式,如果是get提交,必定是乱码,所以必须在servlet中手动的改变乱码,newString(username.getBytes("iso8859"),"UTF-8");; y! w* z3 q  s1 u! ?
    + M& k3 O! D7 M; T% j( A5 A; x
    (3).用户使用超链接提交数据时,带有中文,其实超链接就是一个get方式,所以要使用get方式去解决乱码问题.
    0 G0 n2 z$ ^+ x) Q) |& y
    1 L' T* R$ L* H3 H  F(4).更改服务器的配置,解决乱码问题,但是在开发中严禁这种方式的使用,从浏览器中进入tomcat,修改连接器,HTTP->URLEncoding,然后到tomcat目录下的server.xml中修改连接器Connector,在其中添加一个属性:URLEncoding="UTF-8";还有一种方法,在连接器中添加一个属性,useBodyEncodingForURI="true";是将post方式解决乱码的方法可以用于解决get方式乱码.所以还要事先设置好request容器的码表., G# {4 q! M  G+ H( [+ \

    " }2 K9 U, _) ~5 f! l(5).request.setCharacterEncoding("UTF-8");
    . p  N, {3 N* G/ I3 iString username =request.getParameter("username");1 v- E6 F0 D3 [0 J7 j: \, N
    response.setCharacterEncoding("gb2312");) r+ ~7 i. f, L* ~
    response.setContentType("text/html";charset="gb2312");
    3 L4 W6 C# I8 z% h  Tresponse.getWriter.write(username);不会有乱码
    # E+ c8 c5 U1 n" b5.request实现请求转发和mvc设计模式.txt
    ) L0 @. S9 r0 b' p. v+ G(1).request是个域对象,request作用的范围是请求范围中,主要用于设计mvc中.7 `7 z- T; H1 ~, d: L2 n

    # \. c+ x. q6 j: f$ \! S(2).servlet中转发到jsp显示数据,% r7 a- [+ ~. N' Z% v( l, I
    request.getRequestDispatcher("/mesage.jsp").forward(request,response);
    ; l2 D' W, k8 F; i" N在开发中一般都是用这种方式,而不是用ServletContext的转发方式.每一次请求都对应一个request,正好,request的作用域是一个请求,所有request的转发,都是一个请求,都在这个作用域中.( R& @, S3 z" G

    ! C/ t8 t% m1 o9 W3 {(3).request.getParameter()方法是获取客户机带来的数据,而request.getAttribute()方法是获取request域中获取数据.
    3 ?% m$ G, n5 \
    7 {# R2 m, `" T& T(4).forward方法用于将请求转发到requestDispatcher对象封装的资源,如果在调用forward方法之前,在servlet程序中写入的部分内容已经被真正的传送到了客户端(关闭response流),forward方法将抛出IlegalStateExcepiton异常,如果在调用forward方法之前向servlet引擎的缓冲区(response)中写入了内容,只要写入到缓冲区中的内容还没有被真正输出到客户端,forward方法就可以被正常执行,原来写入到输出缓冲区中的内容将被清空,但是,已经写入到HTTPServletResponse对象中的响应头字段信息保持有效.
    ' v; N& E- T, \" [2 R0 [( a/ U$ v 8 l- ^# Z1 W+ N# v8 M. ^$ Y
    (5).if(true){request.getRequestDispatcher("/index.jsp").forward(request,response)}
    8 t8 K& s4 G& H6 e; c: }& S. t8 `" [跳转到index.jsp向客户机写数据0 D7 T  m& _; T7 R# {/ u( k% g
    request.getRequestDispatcher("/index.jsp").forward(request,response)也要向用户写数据。所以一个良好的习惯是跳转之后一定要return;
    5 A8 e" b  ~7 ^ 1 u9 k0 P- i2 Z" e& {+ R
    (6).String data ="aaa";response.getWriter().write(data);不要关闭这个流,这是可以request.getRequestDispatcher("/index.jsp").forward(request,response);理论上应该是两部分内容的组合,即data和jsp中的内容,但是实际上只显示jsp中的内容,因为jsp的内容将覆盖data内容,但是消息头不改变,在这个过程中创建了两个servlet,因为jsp也是一个servlet,但是只有一起请求.不会创建两个容器.
    + X- X  u3 k8 c- R: f5 f3 B) [7 _( M; g . P# c3 L, g0 x# z/ Z9 F
    6.     request实现页面包含
    ) Z2 |% R# Y( W! n# p& k所有的网页都有相同的网头和网脚,此时只需要将网头和网脚设置共同的部分html,request.getRequesetDispatcher("head.jsp").include(request,response);response.getWriter().writer("hhh");request.getRequestDispatcher("foot.jsp").incude(request,response);被包含页面不能出现全局架构标签,因为可能重复.实现页面包含的作用
    ; l* I- P9 ?0 ^6 O! s9 f 6 Q; {0 }" T7 p$ m
    7.response的outputstream输出数据的问题# ^* L8 H2 t" c9 Z6 v9 d
    (1).在程序中的"中国"变成了UTF-8码表,但是浏览器默认的是gb2312打开内容,在程序中发送一个头,告诉浏览器用哪个码表打开.1 s9 H: s! Y# d! M
    response.setHeader("Content-type","text/html;charset=UTF-8");4 f( }5 j& w  z8 z
    ; f5 u  o9 K7 p6 o1 P* |- ]
    (2).html中的标签<meta>可以模拟一个http响应头.<meta http-equiv='conten-type'content='text/html;charset='UTF-8'>;
    0 e( u- x- F% Q: _  I" q0 ~ " c; w1 F6 c0 S* C/ ]
    (3).当Content-type写成content-type就变成下载了.: h. U! R( F, c- K, O$ k, W

    , ^# F6 O) V6 E$ n  f3 n(4).out.write(1);在浏览器上显示的并不是1,因为1对应的码表.
    * ~, N& b! t5 z! Q) h) F& `8 u ' ~7 _7 m9 \" U2 m" j9 p
    8.response的writer输出数据的问题.txt
    ' {2 s/ G) ]& q9 t4 }(1).String data ="中国"rintWriter out =response.getWriter();out.writer(data);有问题显示乱码5 ]" f% Q0 ]; f" f# f
    2 m* a* N( l% ~
    (2).servlet将"中国"写入response中,但是response用的是iso8859码表,对应没有" 中国",所以在浏览器中看到乱码,response.setCharacterEncoding("UTF-8");设置response的码表为UTF-8;同时也要告诉浏览器以utf-8码表打开." o+ e/ H3 O0 \

    % v% M+ D  W# p( ~* i  d4 n5 p(3).简便的调用response.setContentType("text/html;charset="UTF-8");可以代替以上两条设置码表的代码.
    % b- Q3 s4 f/ u& N4 {, o; z( f
    0 a* s0 l+ }( o2 c# J* [9.response实现请求重定向和response的一些细节2 h! g% D: }0 a3 f9 f3 `1 Q8 |
    (1).怎么实现请求重定向:发送状态码302,
    5 k9 _7 u6 @7 F; Q) e$ v; _response.setStatus(302);response.setHeader("location","/day06/index.jsp");
    4 v8 z5 P- C# F! H& }; @$ {同时sun公司提供了一个方法response.sendRedirect("/day06/index.jsp");重定向的特点是地址栏发生改变.可能加重服务器的负担,因为有多次请求,一般用于用户登录,因为转发,地址栏不变,用户看不到是否跳到首页了,重定向的地址栏变化,可以告诉用户跳到首页,还有一种是购物,点击购买,服务器的一个servlet帮你买成了,servlet会跳到购物车显示页面,显示你购买的东西.当使用转发时,用户点击刷新,可能又买了一次,重新干一次就不好了,所以用重定向,其他情况下都最好使用转发,减轻服务器的负担; u: T2 ^& i0 L$ ]9 Y- C- X
    $ s$ j* Y2 K% f. O& x
    (2).getOutputStream和getWriter方法分别用于得到输出二进制数据,输出文本数据的servletoutputstream,printwriter对象,但是这两个方法是互斥的,条用了其中的任何一个方法后,就不能再调用另一个方法,servlet程序向ServletOutputStream或printwriter对象中写入的数据将被servlet引擎从response里面获取,servlet引擎将这些数据当做响应消息的正文,然后再与响应状态行和个响应头组合后输出到客户机,servlet的service方法结束后,servlet引擎将检查getWriter或getOutputStream方法返回的输出流对象是否已经调用过close方法,如果没有,servlet引擎调用close方法关闭该输出流对象
    0 Q' _( ^& O, M* a( [ ! a! U- C1 i2 {# Z" H
    (3).当在demo1中使用了getOutputStream()方法,转发到demo2,但是在demo2中使用了getWriter();就是在一条调用链中不能存在这两个方法,当使用重定向了,有两次请求,产生了两个response对象所以不会有问题,但是转发就有问题了.' F+ t7 p# Z1 K7 k/ J6 f3 f4 [5 X& C

    7 M, L+ m0 g! J3 A(4).无需关心流的关闭,servlet引擎会自动关闭,但是你自己定义的流需要自己关闭,引擎不会去关闭的.- X# J2 B% x1 t3 C2 Y' A1 H% N( ]
    / \( W( F0 _! [) E* ?* ~1 Q
    10.response实现文件下载5 L2 R1 d; _8 X0 H& T  X$ P; r
    (1).在webroot应用下新建一个download文件夹,存放下载资源.
    4 ^5 q6 [2 p/ H2 ?3 D( r当下载的文件是中文文件,则文件名需要经过url编
    6 i5 h+ ]0 M; v" J码,URLEncoder.encode(filename,"UTF-8");filename是下载文件名., z' P5 l+ N* T  m' _
    ! O) V% y# X' {' K  ~
    11.web工程中各类地址的写法" k, [7 Z  o+ J) H
    (1).request.getRequestDispatcher("/form1.html").forward(request,response);给服务器用的/ _: e3 m- @- Q9 G  \8 [! x; E

    5 h6 i( o1 d' _2 h* D+ W% h(2).response.sendRedirect("/day05/form1.html");给浏览器用的
    4 b2 Y6 ]% H/ T; [7 ~7 j
    1 j+ q4 B+ u/ J3 }$ s7 T5 P(3).this.getServletContext().getRealPath("/form1.html");给服务器用的/ l9 D! ~% @, f/ `4 A. w
    2 z7 G' L' N4 t6 v* \6 W/ j
    (4).this.getServletContext().getResourceAsStream("/form1.html");给服务器用的$ e+ R* _$ E1 U' k- W/ U/ h! C
    . Z8 e# Q5 q) U1 a# V9 i
    (5).<ahref="/day05/form1.html">ddd</a>给浏览器用的. n/ Z0 {' o) ?$ Q% Q/ b9 v! `2 I/ u

    ) u* \& o) u" S( k(6).<formaction="/day05/form1.html"></form>给浏览器用的+ Z/ V0 [7 ^5 H6 A# K% W0 j; h$ u) m5 z

    ' |" ]8 f1 U- F- t5 _7 N总结:写地址以斜杠开头,如果地址是给浏览器用的,这个斜杠代表一个网站,如果是给服务器用的,这个斜杠代表web应用.浏览器向服务器发送请求,就是给浏览器用的
    $ E4 U. d  ?6 z" o / Y! T0 c  H* S, `
    12.   防盗链
    + I/ _# p4 q/ U3 f% p& A& u/ Y* H首先检查你是否从我指定的网页来的,没有的话,String referer =request.getHeader("referer");直接在地址栏上粘贴地址,还有就是refer.startsWith("http://localhost");检查是否从首页跳转过来的.首页上放广告,再有凤姐日记的链接.网站就是靠广告运行的.
    1 t% u5 o: Y" @/ k- X' s% G9 E
    $ n1 A& z# v. A6 a( {13.输出随机认证码图片% z+ O# A- Q. T! F) x$ ]6 J0 e
    (1).BufferedImage:内存中的一幅图片.构造函数指定了图片的长和宽,像图片上写随机数,getGraphics()方法返回一个图形的对象,然后向该图像上写数据.在把内存中的这幅图片输出到浏览器,ImageIO图像的输入输出;write();方法,指定一个和浏览器相关的输出流.
    2 |+ u) D, R# J2 C
    % ~" v7 r- P+ t9 x/ R& R(2).告诉浏览器以图片的方式读取数据.
    - X. C, x6 R; U8 J" U / x% R3 s8 a% H& E! C: b6 j
    (3).<imgsrc="/day04/servlet/ResponseDemo3">;
    0 m+ d' [! J# w5 T  v( }2 r3 A 9 Y8 U% w' X& h% Q9 t4 U) M3 @2 H2 ^' K
    (4).刷新后图片为改变,没有控制浏览器是否进行缓存,浏览器默认是进行缓存的,所以每次拿的都是缓存图片,没有改变,所以要控制浏览器不要缓存,
    0 M8 V5 P1 b, R# `9 @4 ?response.setDateHeader("expries",-1);response.setHeader(Cache-Control","no-cache");response.setHeader("arma","no-cache");必须三个都要设置好,才起作用.刷新有两个作用:重新向服务器发出请求,还有就是把上一次的事情在干一次.(防止表单重复提交);
    & s2 _1 \5 W9 u7 C* k 5 G+ S# O6 h  B5 L$ J' A) e0 _) h
    (5).<imgsrc="/day05/servlet/ResponseDemo4" onclick="change(this)"alt="换一张"style="cusor:hand"><br/><scripttype="text/javascript">function change(img){img.src = img.src+"?"+new Date().getTime()}</script>如果后面不跟随机数,还是拿缓存了.7 S* \! [2 U* Q4 C1 l$ B
    0 q  @9 S* p5 P: R( L7 _2 T
    14. 用Expires头控制浏览器缓存
    % C' d; M+ r+ g, g* q" V(1).控制浏览器缓存,在servlet向客户会送的数据不变,就需要进行缓存,不需要向服务器发送请求,只需要到硬盘的缓存存放文件夹中读取缓存
    . @' x' u  V8 i( I1 w
      g0 p$ F$ P: w4 r(2).Internet选项->常规->点击设置->查看文件,浏览器把index.jsp缓存了,缓存时间是当前时间值:System.currentTime()+1000*3600;/ s1 Y3 I- G% y8 S5 T3 K% [

    * j; z  C) u6 f9 L* I& `15. 用refresh控制浏览器定时刷新# P) A; ]* ?; Y. E* Q! p7 v
    (1).刷新头:response.setHeade("refresh","3;url='/day04/index.jsp'");控制浏览器每隔3秒刷新一次,
    + ~% \# \$ C* t7 H2 |9 W
    % P: @0 p. U7 Z4 e4 R, I7 ^(2).假设是一个用于登陆的servlet:提交用户名和密码,需要到数据库查询是否登陆成功,response.getWriter("恭喜你,登陆成功,本浏览器将在3秒后,跳到首页,如果没有调,请点击<a href="  ">点击</a>");
    # a7 a7 ?3 f% ~( o# |, D3 J% I/ }/ { 9 {: u3 m, S( y9 ?$ e8 w
    (3).this.getServletContext().getRequestDispatcher("/message.jsp").forword(request,response);
    4 X7 c- ^7 W9 M! k" y
    7 d, Z. S2 W% h1 n" F$ u(4).servlet怎么告诉jsp定时刷新.字符串写给jsp"<metahttp-equiv='refresh'conten='3;url=/day08/index.jsp'>"
    红红火火恍恍惚惚
    回复

    使用道具 举报

    admin    

    1244

    主题

    544

    听众

    1万

    金钱

    管理员

  • TA的每日心情

    2021-2-2 11:21
  • 签到天数: 36 天

    [LV.5]常住居民I

    管理员

    5#
    发表于 2015-06-02 12:43:43 |只看该作者
    第六天:
    + w: t& j+ H. M" c3 f0 w0 i5 P1 ~3 v1.  Cookie的细节
    1 V( \( `0 A% Z$ Y% ^一个Cookie只能标识一种信息,它至少含有一个标识该信息的名称(name)和设置值(value),一个web站点可以给以一个web浏览器发送多个Cookie,一个web浏览器也可以存储多个web站点提供的Cookie,浏览器一般只允许存放300个Cookie,每个站点最多存放20个Cookie,每个Cookie的大小限制为4KB,如果创建了一个cookie,并将他发送到浏览器,默认情况下它是浏览器之后即被删除,若希望浏览器将该cookie存储到磁盘上,则需要使用maxAge,并给出一个以秒为单位的时间,将最大失效设置为0,则是命令浏览器删除该cookie,注意,删除cookie时,path必须一致,否则不会删除.cookie不能太大,不然可能造成网络拥堵,也可以将cookie删除.当两个cookie的名称相同时,删除其中一个,就是删除两个.即这两个cookie必须相同,setPath();也要设置.可以到ie保存Cookie的文件中查看文件.javascript也可以删除cookie
    ' f9 c7 o" O6 {9 T$ {
    5 v" e5 T, N. R5 M. K7 T2.Cookie显示用户上次访问网站的时间
    5 J0 B7 V$ U* D3 x' y; ]7 A(1).javax.servlet.http.Cookie类用于创建一个Cookie,response接口也定义了一个addCookie方法,它用于在其响应头中增加了一个相应的Set-Cookies方法,它用于获取客户端提交的Cookie,使用Cookie封装用户的数据.0 C  `1 S6 @9 O% q: i

    ) A8 T, y, ~! f(2).构造函数,Cookie(name,value);给Cookie一个名称name,用户下一次访问时,带着上一次访问的Cookiet,所以在Request.getCookies();返回一个Cookie数组,即用户的所有Cookie,Cookie的有效期默认是浏览器的进程过程,可以通过setMaxAge()方法设置有效期,案例:用户在一段时间内自动登录。方法setPath();用于设置访问哪个web资源时携带Cookie,方法setDomain(),设置访问哪个域名时携带Cookie.IE默认禁止这种第三方Cookie,你访问我的网站,我给你带回一个的网站的Cookie.) ?) l/ Z& E( K% O
    4 r( N/ t# T( v
    (3).用户第一次访问时,没有携带Cookie,Cookie cookie[] =request.getCookies();cookie可能为空,8 M) Y4 |! @9 s
    首先要判断一下是否为空,4 i9 q6 ~# H$ f* e! m
    for(inti=0;cookies!=null&&i<cookies.length;i++){if(cookies[i].getName().equals("lastAccessTime")
    2 U. d* W: T  x{long cookieValue= Long.parseLong(cookies[i].getValues()}};' |; g  V" h3 Z4 B3 T1 z4 ~7 l
    新建一个
    ! K4 M. i* s# P* w7 p+ ?( E+ [0 sCookie:Cookiecookies = new Cookie("lastAccessTime",System.currentTimeMillis());
    % Q1 ]5 U# z8 N5 |$ f1 d! J设置Cookie有效期:cookie.setMaxAge(,,,);  c2 b* X. u1 N" F: ?) k
    设置有效路径(默认的是"/day04/servlet");cookie.setPath("/day04");
    & w( E3 J" x4 e1 v& u
    % d; n- y+ ]% L9 k4 v# C7 V4 _6 s7 g3.session的工作原理& D4 J5 s/ P+ Y* h2 O# C
    (1).如果把Cookie禁止,则不能购买东西,Internet选项->隐私->高级( V+ L; Z' _# Y9 D6 i4 {3 S

    2 ~7 J/ g% B/ U! p- f" ?' q9 ?! O(2).URL重写,你访问我的服务器,都是超链接点击过来的,不同用户访问首页时,就帮用户创建session得到session的id号,然后将id号放在超链接URL中携带过来,不是Cookie携带了,所以所有的URL要重写,
    # h% f- D" j! i' ?5 @request.getSession();Stringurl = response.encodeURL("/day03/servlet/SessionDemo")此方法自动将session的id号加入该地址中.<a href = '+url+'>点击</a>3 x/ B% s& u; E! K2 q' n

      m3 ]* H" R: v(3).如果没有禁止Cookie,浏览器带一个Cookie来了,就不会进行URL重写. ^1 C/ c" K: S- C' i
    & m3 G2 S/ |6 P/ ?8 _
    4.session的一些细节问题
    5 k& j7 Q8 \( j" h2 z(1).一个浏览器就占用一个session,开一个新的网页,不创建session,超链接弹出的新窗口也是共享一个session,同时开多个浏览器,创建多个session,这个问题也不是绝对的,不同的浏览器,功能不一样,将session的id号写到浏览器进程,
    # L4 E$ t9 h3 j' l( p- w
    8 E) R. W6 s2 k4 ?9 d5.Session简介* V. M  U0 ?3 ^( d5 {, j
    (1).默认情况下,一个浏览器独占一个session对象
    $ `' E. }- r/ [+ S* o6 E, O - D  f* p; g" r
    (2).HttpSessionsession = request.getSession();
    0 q* P, u( M) c+ y; b( Nsession.setAttribute("name","value");
    ; g* j9 C. Z' D/ y: wsession.getAttribute("name");+ n# d+ J% r+ p1 ^. A4 g' p

    ) g- |. p! ^. _( P(3).写好过滤器,就可以解决web应用中的乱码问题
    # K, k( L" u% Z* ?% ~; I$ d5 I
    2 v+ z6 S8 x$ M/ }(4).一个session只为一个会话服务.(只打开一个浏览器窗口)
    - [! s1 L1 I# P% h# l$ X& [
    - D% [$ W- m6 f3 [$ D& v6 i(5).session的生命周期,第一次访问getSession()方法时,session就被创建,session是30分钟没有人用了(即使浏览器不关闭),服务器就将其删除.而不是结束会话服务.当然这个时间可以实现的.9 t$ m  e* h3 W) C4 R+ O& {7 K. l
    <session-config><session-timeout>10</session-timeout></session-config>单位是分钟,同时在代码中也可以控制,session.invalidate();request.getSession(true)方法是指:有session,不创建,没有session就创建,request.getSession(false)方法是指:只获取session,不创建session,显示购物车的时候,没必要创建session只需要获取session.2 W+ ?1 O( @( |+ J8 O
    : d2 x( a" O2 s* a. H8 {
    (6).request.getSession()方法怎么知道获取指定用户的session,即session的工作原理:servlet1中:session=request.getSession(),产生一个session的id号,以Cookie的形式回写给用户,下一次用户就带着session的id号来,所以该方法就知道获取指定用户的session,但是这个Cookie没有写有效期,会话结束后,这个id号就没有了,下一次访问,服务器又要创建session,买东西的过程中,关闭浏览器,下一次再接着买,上次买的东西,都没有了,所以解决这个问题,只需设置到这个Cookie的有效期即可.sessionid = session.getId();回写一个Cookie,名称为JSESSIONID,值为sessionid,覆盖默认的Cookie.8 d% @$ U! y. U: I7 k+ t

    ) I& l$ \, X* l" R/ |+ b" S6.防止表单的重复提交
    . n# U# V+ E2 _, B9 O(1).用javascript防止表单的重复提交,<script type="text/javascript">variscommitted = false;function dosubmit(){if(!iscommitted){iscommitted =true;return ture}else return false;}</script>. ^/ u4 A8 \9 f
    <formaciton="/day01/servlet/Demo1" method="post"onsubmit="return dosubmit()">客户端阻止,第一个问题是,用户可以查看页面的源代码,修改js代码,用户也可以点击刷新,把上次干的事在干一次,也可以点击后退按钮.但是在开发过程中还是用js,
    6 K3 C4 k: Y" k2 T/ G! [2 \ ( `# A( i( ?& R" V/ r
    (2).在服务器端防止表单提交,表单是由程序给出,为用户提交的表单提供一个随机数(表单号),服务器端,查看表单的id号,检查表单是否提交过了,如果提交了,删除该表单的id号,产生令牌的发生器class TokenProcessor{}为了保证令牌是独立的,一般讲其设置为单例模式,是一个对象创建的随机数重复性低,还是多个对象创建的随机数重复对象低,是后一种重复性低,为了得到随机数的长短一样,就要用数据的指纹(摘要),数据的指纹都是一样大的,是128位,类MessageDigest dis =MessageDigest.getInstatnce("md5");使用md5码,byte[] md5 = dis.digest(input);input是传过来的数据,用字节数组变成字符串返回,用到新的算法:base64编码,任何数据的base64码变成ASCII码,原理:将数据字节变成四个字节,0011 0010 1100 1101 0010 1001,这三个字节变成四个字节,每六位装到一个字节,少两位,就在前面补两个零,为:00001100 00101100 00110100 00101001,最小数为0,最大数为63,不会超过64,base64自己定义了一个码表,比如:0:a,1:b,3:c ........63: 所以任何数据都可以变成键盘上对应的字符,人们熟悉的字符,应用在数据的传输过程中,需要给数据的带一个结束符号和开始符号,所以把要传输的数据变成base64,开始符号和结束符号使用base64中没有的字符即可,BASE64Encoder encoder=newBASE64Encoder();return encoder.encoder(md5);这个方法可能在api中查找不到,2 O! [; f: b+ u+ S& T3 r

    : P5 ~) b9 X$ B5 O) p(3).在servlet中产生一个令牌String token 然后跳到jsp中,这是将token存到session域中,而不是request中,因为token是id号,以后还要使用,在jsp中<input type="hidden"name="token" value="${token}">在用户不知道的情况下将令牌带过去,
    ; P. {4 \# \' q, R) s5 } 1 l" c9 P8 @9 g! Z3 b; \+ r0 C
    (4).将已经提交过的表单号删除,struts中就是这样防止表单的重复提交的.
      I$ F; }0 R7 ]5 N$ |1 H
    % h( F6 ^" k7 M7 P5 H' e7.会话管理- l; R- q0 A+ J7 s
    (1).会话可简单理解为:用户开一个浏览器,点击多个超链接,访问服务器多个web资源,然后关闭浏览器,整个过程称之为一个会话,
    ) U$ J- a+ w4 p$ R2 O
    + c* }2 m  {% d7 W(2).会话过程中要解决的一些问题:每个用户与服务器进行交互的过程中,各自会有一些数据,程序想办法保存每个用户的数据,例如:用户点击超链接通过一个servlet购买一个商品,程序应该保存用户购买的商品,以便用户点结账servlet时,结账servlet可以得到用户商品,为用户结账。# Q. z( ?, Z4 x; q8 m8 h% k5 x
    / m8 \  ?& D6 X/ e5 }( \1 I# s- c
    (3).如果用户的数据存到request中,结账servlet是一个重新的浏览器请求,所以结账servlet与购买servlet是两次请求,不是共享request,所以结账servlet不能得到购买servlet的内容.ServletContext存在并发问题
    : h, X+ O" \$ @2 I
    " b/ }" L. u4 v& [' V(4).一般使用Cookie和Session;Cookie是客户端技术,程序把每个用户的数据以cookie的形式写给用户各自的浏览器,当用户使用浏览器再去访问服务器中的web资源时,就会带着各自的数据去,这样,web资源处理的就是用户各自的数据,你找我买个的东西,买完了东西,你带回去(可能将东西cookie放在ie缓存,在本地硬盘中),当你来结账时,把东西在带来,即访问结账请求,浏览器拿缓存(cookie);
    - I" p- N. L6 c4 x+ w4 i
    1 U2 _# h# [1 v( }3 ?& V' M+ C(5).Session是服务器端技术,利用这个技术,服务器在运行时可以为每个用户的浏览器创建一个其独享的Session对象,由于session为用户浏览器独享,所以用户在访问服务器的web资源时,可以把各自的数据放在各自的session中,当用户再去访问服务器中的其他web资源时,其他web资源再从用户各自的session中取出数据为用户服务.用户的数据存在服务器端.购买Servlet:session =request.getSession();Session.setAttribute(name,object);结账Servlet:Session=request.getSession();obj=Session.getAttribute(name);不会再为这个用户创建一个session,而去拿服务器中该用户的session2 Z0 a7 j' u& H% q3 Z; y- b: \4 l
    , H7 i$ ^* {& K; F; C" G
    8.三个域对象的总结
    % Z/ [8 I- Q& E(1).request,session,servletContext三个域:request:程序显示完,数据没有用了,就用request(转发);session:程序显示完数据后,该数据还有用,就用session,servletContext:显示完了,除了该自己用,还要给别人用(聊天室)
    0 x4 F2 _/ F% Y. ?+ f5 \ ) b& U& E5 ^% U  M
    (2).md5码,数据指纹,保存用户名的密码,为了安全性,将密码的指纹保存,根据md5码反推,需要时间很长,也可以暴力破解,因为密码的长度是一定的,所以一般把数据加上一个随机数,还有一种用途是进行数据完整性的校验,
    回复

    使用道具 举报

    admin    

    1244

    主题

    544

    听众

    1万

    金钱

    管理员

  • TA的每日心情

    2021-2-2 11:21
  • 签到天数: 36 天

    [LV.5]常住居民I

    管理员

    6#
    发表于 2015-06-02 12:43:56 |只看该作者
    第七天:) U8 E+ }7 M# F2 M: q% Y0 \" M; k
    1.     jsp入门和jsp运行原理
    6 I1 X$ ?& ?* N9 ?1 ]; _% O(1).<%Date date= new Date();out.write(date.toLocaleString();%>显示当前时间.
    % Q. w+ ^( v1 I& I 2 U$ _; r/ D  B5 D% ~, |# s
    (2).jsp的运行原理:首先将jsp中翻译成servlet放在服务器的work目录下,调用servlet的service方法,在这个方法中将jsp中内容通过流out.write()写到浏览器中,jsp中的java代码将原封不动的运行,可以查看jsp的servlet代码,查看所有定义的对象,不仅有out对象,还有其他对象,JspWriter就相当于PrintWriter1 l: o# E+ \; ^) @( a; F' h

    ' u6 i- s2 Z1 b  l/ X& E3 T(3).最佳实践:一种技术可以用在多个地方,但是只有一个最佳的地方就是最佳实践.
    2 N  O2 @( g! |( N8 h5 J( D( F' c9 P
    6 ~1 t  N7 x) G7 J. b  B; v9 G% ?. s8 z2.     jsp语法page指令详解' L4 _* \% F8 s- v1 |8 o3 a
    (1).修改jsp的模板,进入到MyEclipse,搜索servlet.java,在这个目录下,点开jsp找到Jsp.vtl; H! j' ?  _' p; a; n+ P. ^5 [# F! V

    ; k  \5 s+ T" M4 G(2).jsp指令是为jsp引擎,引擎就是将jsp翻译成servlet的程序,他们并不直接产生任何可见输出,而是告诉引擎如何处理jsp页面中的其余部分,在jsp2.0规范中共定义了三个指令:page指令,include指令,taglib指令
    " `( @, p5 n' S" j2 i% z
    . d' p' @) x3 x  z2 v(3).指令的语法:<%@ 指令属性名=“值”%>,不会产生任何输出,举例:<%@ pagecontentType="text/html;charset=gb2312"%>,如果一个指令有多个属性,这多个属性可以写在一个指令中,也可以分开写,例如:<%@ pagecontenType="text/html";charsetgb2312"%><%@ pageimport="java.util.Date"%>也可以写作<%@ pagecontentType="text/html;charset=gb2312" import="java.util.Date"%>
    ) N; P- j# O% w% O
    3 Q1 K% M: \) S/ r(4).page指令用于定义JSP页面的各种属性,无论page指令出现在JSP页面中的什么地方,它的作用都是整个JSP页面,为了保持程序的可读性和遵循良好的变成习惯,page指令最好是放在整个JSP页面的起始位置.
      O# f' C# C( ~' \# u8 {; ]; O
    - N9 N- F. V' q! |- z- x" c(5).JSP2.0规范中定义的page指令的完整语法:
    ! _/ }) \' V7 J+ I' O<%@ page! R& j3 Y0 _. ~2 p. D& _9 I; p
    [language="java"]
      H" z7 s: _  {3 M7 ?! [* D+ o[extends="package.class"]& _- W7 B$ W4 L; b2 F
    [import="{package.class|package.*},..."]" u" I& r% h3 R4 D, g5 y$ J% G
    JSP引擎自动导入下面的包:java.lang.*;javax.servlet.*;javax.servlet.jsp;javax.servlet.http;可以在一条page指令的import属性中引入多个类或包,其中的每个包或类之间使用逗号分隔:<%@ pageimport="java.util.Date,java.sql.*,java.io.*"%>
    7 G! c) c, j3 E2 K2 J, k/ [  n[session="true|false"]:session是否创建,如果为true,则翻译成servlet时,就创建一个session,否则不创建,设置true,后就可以在JSP中片段中使用session对象,否则不能使用,默认值为true,因为session的周期比较长,为了减轻服务器的负载6 Y$ F" v( Z* @9 x/ H" Z
    [buffer=none|8kb|sizeKb]:默认值为none,jsp的out直接写到response中,没有缓冲了2 y0 S1 y; ~( N' l$ N; v* W3 _7 q
    [autoFlush="true|false"]:自动刷新,默认值为true" }- v* e) C  q% T  S
    [isThreadSafe="true|false"]:JSP是否是线程安全的,设置true后,将发现翻译后的servlet就继承了SingleThreade接口,一个servlet只为一次请求服务,不会出现线程安全问题  U3 E# ]# x( [
    [info="text"]:带信息
    5 I: z+ p# f1 o2 K[errorPage="relative_url"]:指定JSP错误的处理页面,errorPage属性的设置值必须使用相对路径,如果以"/"开头,表示相对于当前web应用程序的更目录(注意不是站点根目录),否则,表示相对于当前页面,errorPage="/errors/error.jsp";也可以在web.xml文件中使用<error-page>元素为整个web应用程序设置错误处理页面,其中的<excepiton-type>子元素指定异常类的完全限定名,<location>元素指定以"/"开头的错误处理页面的路径.作用域不同一个是在一个页面,一个是全局的配置,<error-page><exception-type>java.lang.ArithmeticException></exception-type><location>/errors/error.jsp</location></error-page>;<error-page><error-code>404</error-code><location>/errors/404.jsp</location></error-page>;如果设置了某个JSP页面的errorPage属性,那么在web.xml文件中设置的错误处理将不对该页面起作用,全局性的优先级高,会覆盖页面的配置
    ) ^  {) [9 s( y8 @0 Q/ X[isErrorPage="true|false"]:这个页面是否为错误处理页面,默认值为false,好处是:服务器在翻译成servlet时将异常封装成一个对象,九大隐式对象中包含一个Excepiton,但是这个对象并不是任何时候都有,只有设置这个参数后,才有,记住
    & n( Y: g- Y  T. |# A7 t9 _9 }[contentType="mimeType[ ;charset=charachterSet]"|"text/html;charset=ISO-8859-1"]页面的类型- v; V$ w% O; h9 T$ l: x# ]( C- a& v
    [pageEncoding="charachterSet|ISO-8859-1"]:页面的编码
    3 Z* Q* G0 n9 r3 d; O' c[isELIgnored="true|false"]:是否忽略EL表达式,EL表达式时JSP2.0的新语法,使用EL表达式时需要将其设置为false.默认都支持EL表达式& w1 g; D  H: q, q& }% g4 I+ i0 x
    2 O/ B% @# t1 S% x
    (6).jsp乱码问题:Jsp程序存在有与Servlet程序完全相同的中文乱码问题,输出响应正文时出现的中文乱码问题,读取浏览器传递的参数信息时出现的中文乱码问题,Jsp引擎将JSP页面翻译成Servlet源文件时也可能导致中文乱码问题,jsp引擎将jsp源文件翻译成servlet源文件默认采用UTF-8编码,而jsp开发人员可以采用各种字符集编码来编写jsp源文件,因此,jsp引擎将jsp源文件翻译成servlet源文件时,需要进行字符编码的转换,如果jsp文件中没有说明他采用的字符集编码,jsp引擎将把它当做默认的ISO8859-1字符集编码处理,通过page指令的contentType属性说明JSP源文件的字符集编码,page指令的pageEncoding属性说明JSP源文件的字符集编码$ @2 a, Z: ?+ D7 u0 g% B$ L

    5 G5 m" _6 o  ^& E. T! ?8 W(7).tomcat6已经不存在乱码问题了,在jsp中是"中国",将"中国"保存到硬盘中,默认的查本机的码表,web服务器,翻译jsp的时候就是用iso8859-1码表,所以翻译后的servlet中的"中国"将是乱码,所以要改变服务器的编码,所以就用pageEncoding="gb2312"设置服务器翻译的码表,翻译到servlet后"中国"是正常的,但是此时还要设置浏览器以哪个码表打开所以需要设置:contentType="text/html;charset=UTF-8",其实最后的一个设置不需要设置了,因为response是以UTF-8编码的
    & ^' u3 l, o: S$ g" O * ?; M( W- V" ]5 i0 c. c
    3.     jsp中常用标签6 x4 h; U- }3 d0 R: d
    (1).jsp标签也称之为Jsp Action(jsp动作)元素,它用于在jsp页面中提供业务逻辑功能,避免在jsp页面中直接编写java代码,造成jsp页面难以维护.; ]& P( Y* u' ?  K" B, X
    0 s! J$ f" z7 a! ?
    (2).<jsp:include>,<jsp:forward>:<jsp:forwardpage="/index.jsp"></jsp:forward>应用场景:通常为网站配置首页,首页是个servlet,显示是jsp,配置首页的时候,servlet是无效的,但是jsp是有效的,所以可以使用这个标签从jsp跳转到servlet.<jsp:includepage="/1.jsp"></jsp:include>是动态包含,翻译成两个servlet,在运行时进行合并,静态包含是在源代码的基础上进行合并<jsp:param>标签是设置参数,<jsp:forward page="/servelet/ServletDemo"><jsp:paramname="username" value="xxxx"/></jsp:forward>,跳转到servlet时带数据过去,value可以是一个脚本表达式<%=x%>,x为变量." ^6 _+ p1 a) E1 I) r6 K4 P
    0 Z3 C% }( l/ `6 Z) s) I
    4.     div+css
    ' {' v% l% N  j) x(1).盒子模型:每一块数据都用div套起来,可能控制div的边框,属性border,上边框:Border-top;下边框:Border-bottom;左边框:Border-left;右边框:Border-right,Padding-top,Padding-bottom,Padding-left,Padding-right;控制数据在div盒子中的位置,Margin-top,Margin-bottom,Margin-left,Margin-right:控制一个盒子在页面的位置,
    + z0 C. K, u1 p! `& j3 x
    1 Q# M- g. F: z, }+ T(2).div是个行级元素,默认的是div单独排在一行,有时想将两个div排成一行,这是就要用到定位技术:Float和Position两种技术
    % @& h+ v4 g1 X. ~& k7 [  r + F6 {$ @9 ~0 o( N
    (3).<style>: J- g6 @3 X3 A) O
    body{margin:4px 3px 2px 1px;}
    / m6 k6 b4 h: D   #father{# y: f. C, B, b
    background-color:#FFFFEE;8 U( q# I. M, b; H3 ~5 R
    width:100;
    8 Y! r- l4 n  Z3 b* Jheight:100px;; ]4 n" ~, [- Y: m# P
    border:1px dashed green;//边框是绿色的虚线," ~/ k! x6 X# g3 c5 ^
    }
    , y/ n. J# R" W1 U; d" m#son1{float:left;}
    2 o; h# u  q& v: _0 o#son2{float:left;}
    ! a4 x& d0 I, U& a#son3{float:left;}: E2 h' ~1 i& ^
    </style>
    % S$ K# X8 v) ]: A: ]<body>2 w% H. H. V  d& p2 C
    <div id="father">2 w, M# q: ]# N2 J7 U
    <divid="son1">aaaaaaa></div>/ k# h4 T4 |2 w6 s, ^. C3 a
    <divid="son2">bbbbbb</div>
    ! ?! T% l- O3 U$ w<div id="son3">ccccc</div>2 o1 D5 O% J1 l. @3 W  z+ O; v
    </div>5 }& K4 T: M5 ?
    </body>
    ' V/ z0 {, g( v' R! o; j这个代码是有问题的,当添加<div id="son4">ddddd</div>时,也会浮动,受到前面几个div的影响,所以要清楚浮动效果<divid="clear"></div>,#clear{clear:both;}
    4 f" y- [/ s6 ~4 m& h  Q. ? * [. z4 \- G0 g
    (4).position技术就是将#son1{position:relative;left:60%}4 B3 K3 i8 B; v: A/ }$ Q( k

    6 T& o# t1 S# ^, h  b5 N5.jsp九大隐式对象! _- T5 u6 b  J! K
    (1).每个jsp页面在第一次被访问时,web容器都会把请求交给JSP引擎(就是一个Java程序)去处理,JSP引擎先将JSP翻译成一个_jspServlet(实质上也是一个servlet),然后按照servlet的调用方式进行调用,由于JSP第一次访问时会翻译成servlet,所以第一次访问通常会比较慢,但是第二次访问,JSP引擎如果发现JSP没有变化,就不在翻译,而是直接调用,所以程序的执行效率不会受到影响,JSP引擎在调用JSP对应的_jspServlet时,会传递或创建9个与web开发相关的对象供_jspServlet使用,JSP技术的设计者为便于开发人员在编写JSP页面时获得这些web对象的引用,特意定义了9个相应的变量,开发人员在JSP页面中通过这些变量就可以快速获得这9大对象的引用,8 y+ Q5 S2 a3 f$ H% x/ v8 j
    & m( A! L7 z7 t; {! G( k7 D
    (2).HttpServletRequestrequest,HttpServletResponse response,ServletContextapplication,ServletConfig config JspWriter out,Exception,PageContextpageContext,Object page,Session session,具体的可以查看文档3 s7 _" r7 v8 m* q  r
    5 |5 E) a- c- M4 M# d2 g4 Z$ }
    6.jsp映射和jsp常见错误处理
    6 e6 W, [5 a- ]8 h在web.xml中
    4 s; s0 \' |9 z7 }" u8 W) t* L5 C<servlet><servlet-name>xxx</servlet-name><jsp-file>/1.jsp</jsp-file></servlet><servlet-mapping><servlet-name>xxx</servlet-name><url-pattern>/12.html</url-pattern>
    ; c7 t, N7 t4 b# N* t' b
    ( E, r6 \% E7 n% L2 j+ d( k( R7.jsp语法
    7 o; ]5 N4 }( p1 a. \  P% w8 v(1).jsp语法:jsp模板元素,jsp表达式,jsp脚本片段,jsp注释,jsp指令,jsp标签,jsp内置对象,如何查找jsp页面中的错误; t  d6 v* j# u; l

    3 j6 j8 [* x- l(2).jsp脚本表达式(expression)用于将程序数据输出到客户端:语法:<%=变量或表达式%>举例:当前时间:<%=new java.util.Date()%>;jsp引擎在翻译脚本表达式时,会将程序数据转成字符串,然后再相应位置用out.print(...)将数据输出给客户端,jsp脚本表达式中的变量或表达式后面不能有分号(;)& [' \' u( v' b2 X) d

      q" w7 j: }" Z) l: t: U(3).jsp脚本片段<% %>,中写入java代码,原封不动的变成java代码
    : G2 D# L6 }1 r7 `8 h ! B4 C& ]. ]7 a
    (4).变量可以再多个脚本片段中是可以相互访问的.
    # f  x  p9 F5 C) C8 } 5 T2 Y( d& B% Q. q/ Q9 X2 r
    (5).jsp声明:jsp页面中编写的所有代码,默认会翻译到servlet的service方法中,而jsp声明中的java代码被翻译到_jspService方法的外面,语法:<%! java代码%>(多个感叹号),所以,jsp声明可用于定义jsp页面转换成的Servlet程序的静态代码块、成员变量和方法,多个静态代码块、变量和函数可以定义在一个jsp声明中,也可以分别单独定义在多个jsp声明中,jsp隐式对象的作用范围仅限于Servlet的_jspService方法,所以在jsp声明中不能使用这些隐式对象,<% public void run(){} %>这种语法是错误的,<%! public void run(){}%>这种语法是正确的.5 p" ^" B4 ^! Y& _, Z1 }  k9 f

    , q7 M  f) c; D. g! t0 s! D(6).注释:<%-- --%> jsp中的注释不会打给浏览器,但是html注释会打给浏览器.
    7 l. V( j/ a3 | & A7 p2 G9 P# o
    8. out隐式对象
      `' H: S, V6 P# Z. K) i9 y(1).out隐式对象用于向客户端发送文本数据,out对象是通过调用pageContext对象的getOut方法返回的,其作用和用法与ServletResponse.getWriter()方法返回的PrintWriter对象非常相似,JSP页面中的OUT隐式对象的类型为JspWriter,JspWriter相当于一种带有缓存功能的PrintWriter,设置JSP页面的page指令的buffer属性可以调整它的缓存大小,甚至关闭它的缓存,只有向out对象中写入了内容,且满足如下任何一个条件时,out对象才去调用servletresponse.getWriter方法,并通过该方法返回的PrintWriter对象将out对象的缓冲区中的内容真正写入到servlet引擎提供的缓冲区中:设置page指令的buffer属性关闭了out对象的缓存功能,out对象的缓冲区已经满了,整个JSP页面结束.% o. Z9 b, \) c" g
    / t+ J; O1 E8 |; z6 b7 U
    (2).out.write("hahahahhahha");response.getWriter().write("wowowowo");理论上是先看到hahahah在看到wowowowowwo,但是实际是wowowowowowo hahahahhaha,原因就是out有缓存,JspWriter向缓冲区中写入内容,respose的缓冲中是wowowwowowwo,当jsp结束后,response缓冲区发现JspWriter的缓冲区中有内容,就将其刷新到response,所以先看到wowowowowwo,然后就是hahahahahhaha,就是response写入的内容最先显示,所以在开发过程中不要两种方式都使用,一般使用out.write();/ m2 m8 W2 r! l" C! j6 \- Y; P& K1 T
    / L1 t& \% T8 J. J+ }. a, I& J( N( Q1 [5 u
    9.pageContext对象% ]- s5 H- U, W/ e( R; c
    (1).pageContext对象是JSP技术中最重要的一个对象,它代表JSP页面的运行环境,这个对象不仅封装了对其他8大隐式对象的应用,它自身还是一个域用来保存数据,并且,这个对象还封装了web开发中经常涉及到的一些常用操作,例如引入和跳转其他资源,检索其他对象中的属性等. h7 l6 [2 k+ g! w/ ]' a

    4 m$ ~4 R0 ^4 @8 _(2).它的方法:getException()返回exception隐式对象,getPage()方法返回page隐式对象,getRequest()返回request隐式对象,getResponse()返回response隐式对象,getServletConfig返回config隐式对象,getServletContext返回application隐式对象,getSession()方法返回session隐式对象,getOut()方法返回out隐式对象,pageContext封装了其他8中内置对象,用途是在自定义标签中,jsp中最好不要出现java代码,有时想显示数据,又不能有java代码,String data -(String)request.getAttribut("data");out.write(data);在页面中写入一个标签<flx:viesData/>传递一个pageContext对象就相当于传递8大隐式对象.2 \8 k' r: G1 s& h/ W! y: ?5 R

    4 L2 X9 S1 y$ }* K4 n. r) f(3).pageContext也是一个域对象,pageContext.setAttribut("data","aaa");域的生命周期是整个页面的周期,作用域是整个页面,四个域中,作用范围最小的一个,request就是一个请求范围内,session就是在一个会话范围内,servletContext整个web应用
    0 k) a7 r+ w5 h/ g
    1 h/ q& h/ l2 N5 e8 G(4).代表各个域的常量:$ ^1 F' a" e0 a
    PageContext.APPLICATION_SCOPE,PageContext.SESSION_SCOPE,PageContext.REQUEST_SCOPE,PageContext.PAGE_SCOPE
    ' R: D8 t( q( D. p- W2 z# i
    ; W* a5 p: ~7 L# k! w+ r(5).封装了访问其他域的方法,方法public java.lang.Object getAttribute(java.lang.Stringname,int scope)是个重载的方法:Stringdata=(String)pageContext.getAttribut("data",PageContext.REQUEST_SCOPE);管理所有域的入口3 s3 E+ @: V" o

    , L. q% i5 r$ T(6).最重要的一个方法:findAttribute:查找各个域中的属性,pageContext.findAttribut("data")首先从page,request,session,application逐级查找,找不到返回空.用于el表达式,el的执行就是按照这个方法的原理执行的., P, t1 W! w" d* x1 ~; C! h

    : H% p5 m" Z9 B0 r8 ](7).引入和跳转到其他资源,PageContext类中定义了一个forward方法和两个include方法来分别简化和代替RequestDispatcher.forward方法和include方法,方法接受的资源如果以"/"开头,就代表当前的web应用; N) T- N3 S) x$ a- G7 @
    ( _7 z+ M2 y- ?8 i5 {0 w! t
    10.jsp语法include指令详解5 v, F' a3 M+ e( M8 I
    (1).include指令用于引入其他JSP页面,如果使用include指令引入了其他JSP页面,那么JSP引擎将把这两个JSP翻译成一个Servlet,所以include指令引入通常称之为静态引入8 t* `) a# ~" e) _# o+ Y

    7 D) F) w! e* {5 L+ Q- Z2 `(2).语法:<%@ include file="relativeURL"%>其中的file属性用于指定被引入文件的路径,路径以"/"开头,表示代表当前web应用,细节:被引入的文件必须遵循JSP语法,被引入的文件可以使用任意的扩展名,即使其扩展名为html,JSP引擎也会按照处理jsp页面的方式处理它里面的内容,为了见明知意,JSP规范建议使用jspf(JSP fragments)作为静态引入文件的扩展名,由于使用include指令将会涉及到2JSP页面,并会把2个JSP翻译成一个servlet,所以这2个JSP页面的指令不能冲突(除了pageEncoding和导入包除外);7 w7 m" s6 ^! G! a. \

    4 H+ ~3 R8 N$ u: O, i; d(3).查看源文件,不是格式良好的html页面,就将被包含的页面的头删除
    ) J( q4 S% |8 e0 ]1 A) n! Z0 t
    # Q. U0 ^* b; b$ G  S7 c(4).动态包含:request.getRequestDispatcher("/public/head.jsp").inlude(request,response);它会把三个JSP翻译成三个servlet,静态包含只翻译一个Servlet,
    2 i! E& B9 k4 c
      a# j; `4 S/ ~: q1 d(5).在开发过程中两种包含都用到:静态包含编译时包含,性能高,动态包含是运行时包含
    红红火火恍恍惚惚
    回复

    使用道具 举报

    admin    

    1244

    主题

    544

    听众

    1万

    金钱

    管理员

  • TA的每日心情

    2021-2-2 11:21
  • 签到天数: 36 天

    [LV.5]常住居民I

    管理员

    7#
    发表于 2015-06-02 12:44:08 |只看该作者
    第八天:
    ' W: ~* }8 b* u% p! j+ U9 ]1.  sun公司的jstl标签库
    6 W. K! t  S0 f6 Z" z1 l(1).JSTL标签库:核心标签,国际化标签,数据库标签,XML标签,JSTL函数(EL函数)
    " N5 s% H& {4 f! { 0 \0 F# a3 z$ M: {* {  [% w4 n
    (2).sun公司的所有标签都放在standard.jar架包中,
    ( F+ |5 Q- D5 q; F+ C 6 I8 ^& @- u, ]& n; ^/ a8 W
    (3).<cut>标签用于输出一段文本内容到pageContext对象当前保存的"out"对象中,
    : |0 t( {$ j0 V9 N/ q' s
    . A. z4 \7 V+ _# W(4).<c:set>标签用于把某一个对象存在指定的域范围内,或者设置web域中的java.util.Map类型的属性对象或JavaBean类型的属性对象的属性.
    ( a- X( L8 y5 ~% N, E4 k- b - A* {2 K, t) F/ k( P0 J7 o7 R
    (5).<c:remove>标签用于删除各种web域中的属性5 g- U3 ^/ m6 y! t- z4 u
    + G7 A' n: B: X  m
    (6).<c:catch>标签用于捕获嵌套在标签体中的内容抛出异常
    7 |- V: G+ V6 P7 G' y 6 \7 N( ]  A+ R3 }1 S
    (7).<c:if>,<c:foreach>标签) r, S; f3 k/ o& y3 B
    + ?5 \. w1 j  W3 c; |) x
    2.标签案例-打包自己的标签库6 {0 D" ]/ m6 {# _' W8 s
    (1).首先建一个java工程,将所有的标签处理器类考到这个java工程中,然后将tld文件存放到meta-inf文件夹中,然后将java工程导出为jar
    * z7 H* M: `  ?) g6 [
    / I  m7 q0 `% l1 L. L% _* m3.标签案例-开发foreach" d8 r$ X  H# I. M& U* O
    (1).List list =new ArrayList();然后迭代输出list中的数据: u9 Y# W+ n% b

    " s# A9 \" e0 m7 w+ m0 g! H* |(2).<c:foreachvar="str" items="$(list)">$(str),var属性记录list中数据的类型.var的类型为String& y0 T1 q% X  k

    . _0 K* _# [- R6 W7 K; z" D( O4 @& n(3).在doTag()方法中,List list = (List)items;Iteratro it =list.iterator();while(it.hasNext()){Object value =it.next();this.getJspContext().setAttribute(var,value);this.getJspBody().invoke(null);标签中的内容执行回到pagecontext的域中拿去属性var的值,然后输出}
    6 {9 b: S7 K; ^ 9 Y% X9 L( b* d- N9 T5 Z$ Z3 n, ~
    (4).将foreach功能加强可以迭代数组,Map,Collection判断是不是Map,还是Collection,还是Object[],但是要进行类型转换,可能有很多重复代码,所以都直转型Collection collection即可,if(items instanceof Collection){collection =(Collection)items;}if(itemsinstanceof Map){Map map (Map) items;collection = map.entrySet();} if(itemsinstanceof Object[]){Object obj[] =(Object[])items;CollectionArrays.asList(obj);}当碰到基本数据类型时,就需要迭代数组,将数组中的数据放到collection中,
    " J" Z8 M+ H# s - G5 Y6 u2 ]5 j0 U3 |; A. q
    (5).sun公司的foreach标签在standard架包中,在web-inf,c.tld,查找forEach标签对应的类
    # f  d; W( w) c$ P5 s' t+ E $ l3 \- ?; I  U9 p
    (6).反射技术,Class类中有一个方法isArray()判断是否是数组类型,不仅适用于任何类型,在反射类中有一个类Array,它有个方法/ w( ~7 W; F- P, ?' a8 O; ]
    getLength(),for(inti=0;i<length;i++){Object value =Array.get(items,i);this.collection.add(value);}
    $ L6 V% k% ?# w# I$ @1 ^对任意数组进行操作.  Y- O8 I, d* d# b
    1 O: q9 e# d6 s: ?% y
    4.标签案例-开发if-else标签* Q9 z: x/ u- @+ o
    (1).<c:iftest="$(user!=null)">aaa</c:if><c:else>bbb</c:else>这两个标签是共享一个变量,即让他们两个共享同一父标签<c:choose></c:choose>,共享的变量在父标签中定义,则此时需要写三个标签处理器类,
    1 C  p' E) g: e" Y. _2 j9 C  P ! q! t. s$ a1 f/ M5 E# Z& D
    (2).同时在父标签中要让其标签体执行,所以在父标签中的doTag()方法,this.getJspBody().invoke(null);
    , H$ G/ {' b9 i/ }& F, \
    0 ~, B: M- r& H' Y6 D(3).得到父标签的变量,所以在子标签的doTag()方法中Choose parent = (Choose)this.getParent();就可以得到父标签的处理器类
    , o0 u( _/ I) [7 V( R+ o: L2 C" F 6 B. P8 \6 l7 U" K! {8 n* g' W6 @
    5.  标签案例-开发防盗链标签
    1 A% ^  o' U* x# ~就是将前面的request的课程中的方法定义为标签处理类即可
    % v6 s% K4 j6 z! j& ~, W
    2 ]6 L6 n0 @! {6.  标签案例-开发转义2 Q6 A2 w0 b1 r
    (1).比如超链接的原始输出,2 I. G5 x1 o, w  h2 ^
    StringWriter sw =new StringWriter();JspFragment jf = this.getJspBody();jf.invoke(sw);Stringcontent = sw.getBuffer().toString();服务器的webapps/examples/web-inf/htmlFilter,java中的方法filter();
    : w( J& s4 z9 p7 G
    - s: V/ \, K' l3 c  P7.  标签简介和开发第一个标签
    6 K2 w7 a' i' _(1).自定义标签主要用于移除jsp页面中java代码,使用自定义标签移除jsp页面中的java代码,只需要完成以下两个步棸:编写一个实现Tag接口的Java类,把页面java代码移动这个java类中(标签处理类),编写标签库描述符(tId)文件,在tId文件中对标签处理器类描述成一个标签,. F! i1 \: K& V' F) Z

    7 ]8 l3 [( Q1 V3 u& \& U(2).<%request.getRemoteAddr();out.print(ip);%>,显示用户的IP,使用自定义标签,首先将java代码移动到java类,继承Tag接口,标签有开始标签和结束标签,doEndTag()方法,doStartTag()方法,标签可能有爸爸,所以方法getParent()将父标签作为一个对象传给jsp引擎,方法setPageContext()方法最重要,就是传递一个PageContext对象,他同时也携带了其他8个对象,所以将java代码移动到doStartTag();Tag是个接口,所以使用其继承类,TagSupport,自己编写的类只需要覆盖doStartTag();准备好request,response,所以要得到PageContext,又因为服务器在执行doStartTag()方法之前,默认执行了setPageContext()方法了,所以可以写为:HttpServletRequest request =(HttpServletRequest)this.pageContext.getRequest();还有获取out对象,用于输出.移动完java代码后,第二步:就是在标签库中将标签处理器描述标签,可以到tomcat的webapps中自带的一些例子,web-inf/jsp2/taglib.tld.+ d5 [: Z5 L) E$ k* q+ l. l
    1 O0 K* Y8 F% a# t% R) O/ e8 |
    (3).<tag><name>viewIP</name><tag-class>cn.itcast.web.tag.ViewIPTag</tag-class><body-content>empty</body-content></tag>.<url><http://www.itcast.cn</url>
    : |. D- z2 e, ^4 m+ K 7 t6 T9 |' ~& s3 S" I9 @$ b* H
    (4).导入标签<@taglib url="http://www.itcast.cin"prefix="itcast"%>:prefix是前缀,可以容易定位到描述标签的文件
    ( h2 X8 N1 i1 H, l& t
    6 y" w& d  m$ B/ Q. m(5).<itcast:viewIP/>即可itcast就是前缀prefix,viewIP就是tag的名称name& X" U3 ^, v) M7 T; u8 F8 F* e& x
    # v) I7 _: d1 @- a' B$ C0 m+ @
    (6).tld文件的位置必须放在web-inf中.
    - x& ?0 R, M. j  y& v; a% b( M ( S; J7 L0 k0 \+ v$ V* D
    8.  传统标签库功能详解  ?: c# z9 w4 n+ l. ^' K5 t% B6 b
    (1).控制标签体内容是否输出:覆写父类的doStartTag()方法,public int doStartTag(){returnTag.EVAL_BODY_INCLUDE}执行标签体,然后再添加tld文件,<body-content>JSP</body-content>,如果不执行只需要return Tag.SKIP_BODY即可& G! G2 r# }0 t# [# k

      s8 l  _2 \* C) }$ R) N) T( L(2).控制整个JSP是否输出,就在JSP开始头定义一个标签,doEndTag()的返回值是否继续执行余下的JSP内容,只需覆盖父类的doEndTag()方法,public int doEndTag(){return Tag.SKIP_PAGE}不执行JSP内容,然后添加tld文件,一定要将该定义的标签放在JSP开始的部分.返回Tag.EVAL_PAGE就可以执行余下的JSP内容.
    9 e4 S" F/ v+ c6 x2 l
      x0 A' s3 Y/ W(3).控制某一部分内容重复执行,只需将重复类容套在自定义的标签中,Tag接口找不到指定的方法,必须要使用其子接口IterationTag接口,其内部中有个方法:doAfterBody(),这个方法在标签体结束后执行,如果返回EVAL_BODY_AGAGIN,就接着执行,直到返回SKIP_BODY,所以继承IterationTag接口,但是TagSupport继承IterationTag,所以还是继承TagSupport即可,首先覆写方法doStartTag()方法,返回Tag.EVAL_BODY_INCLUDE;然后覆写方法doAfterBody(){x--;if(x>0)return IterationTag.EVALBODY_AGAIN;elsereturn IterationTag.SKIP_BODY;}其中x应该定义为成员变量,而不是在doAfterBody的局部变量,因为doAfterBody()方法是多次执行的,不是只执行一次., }/ L; T- o/ h# n
    * y# o* `) h1 k* L
    (4).修改jsp页面的内容,将显示的内容修改成大写的,将内容套在自定义标签中,接口BodyTag接口,此接口继承了IterationTag接口,覆写BodyTagSupport方法doStartTag()返回EVAL_BODY_BUFFERED,会将标签体最为一个对象通过方法setBodyContent传给标签处理器类,然后在doEndTag()方法中BodyCotned bc = this.getBodyContent();得到标签体,String content = bc.getString();content=content.toUpperCase;然后通过pageContext输出,最后要记得返回EVAL_PAGE,执行余下的jsp内容,就拿到标签体中的内容,然后对内容进行操作.7 B4 P( V3 R" Q& C: M5 u7 z" P4 ^

    7 `2 l  ], }* R8 P; M$ \(5).IterationTag接口的子类为TagSupport类,TagSupport类的子类为BodyTagSupport类,同时BodyTagSupport类是实现BodyTag接口,同时BodyTag接口继承了IterationTag接口,IterationTag接口继承了Tag接口,同时Tag接口和SimpleTag接口都继承JspTag接口,但是Tag接口时JSP2.0以前定义的,现在不用了,而现在主要用的就是SimpleTag接口,其子类为SimpleTagSupport类.所以称Tag接口为传统标签,SimpleTagSupport为简单标签
    * @$ S/ I0 [' A/ R
    6 A9 z+ \" S0 e6 ]) a9.  简单标签库功能详解
    0 C  }" m* G3 o- G(1).SimpleTag接口集成了以前的Tag,IterationTag,BodyTag三个传统标签的功能,所以SimpleTag有个默认实现类SimpleTagSupport
    0 N. J( @9 r( I2 x * M/ R; f7 x* L3 F
    (2).JspContext就是PageContext,方法,setJspBody(JspFragment jspBody),服务器通过这个方法将PageContext对象传递给标签处理器,然后调用doTag()方法,没有开始和结束标签,doTag()方法中抛出个异常,返回SKIP_PAGE控制整个页面不输出,
    % E4 l9 B4 h  {* g) P
    5 j: ]6 o0 u9 P* E/ w9 M(3).简单标签的一般逻辑都在doTag()方法中,JspFragment jf = this.getJspBody();jf.invoke(this.getJspContext().getOut());简单标签中写成<body-content>scriptless</body-content>,JSP2.0规定了jsp中不能写脚本代码了,所以不死JSP,而是scriptless,jf是得到了一个标签体,jf.invoke()标签体运行,在this.getJspContext().getOut()的out对象写入浏览器# n0 h* y3 x# ]5 y* n( u; Z
    * |) o; G9 A6 h3 z9 K* E3 R
    (4).如果想让标签体不执行,jf.invoke()方法不执行就可以了0 x& L! P0 V4 e$ {9 f! k9 f
    8 L! o5 O3 C( ~5 T: o  ]
    (5).jf就是标签体,可以写成:for(int i=0;i<5;i++)jf.invoke(null);这样写也行,就是默认的也是写给浏览器,重复将标签体
    : ~7 k  I5 [$ b* H. E , E: o& G8 q4 d/ p) F) I  J/ T
    (6).StringWritersw = new StringWriter();jf.invoke(sw);将jf的标签体的内容输入到自己的缓冲,可以自己定义一个缓冲流,String content = sw.toString();content =content.toUpperCase();2 n2 Z! p7 _# P( ~# j/ \

    - {. t9 F5 J8 {(7).控制整个标签是否执行,只需在doTag()方法中抛出异常,throw new SkipPageException();即可.同时这个标签也要放在jsp页面的开始处,jsp余下的内容就不执行了+ o% [  j' R1 E4 E9 `- `

    * {" q/ y. |9 r8 L% {. [(8).jsp运行时遇到简单标签,实例化标签处理器类,然后通过调用对象setJspContext()方法,将PageContext对象传递给标签处理器类,接着调用setParent()方法将父标签传递给标签处理器类,然后把标签体的内容封装JspFragment对象传递给标签处理器类,然后调用doTag()方法执行自定义标签,执行完后,标签就变成垃圾了,等待垃圾回收器回收,这一点和传统的自定义标签不同, y  @- P, w" @/ V* X( `& @4 [
    3 t$ ^% u. |: {$ {/ ^7 U/ q
    10.开发带属性的标签
    ; o+ v' N2 l# i3 p. P8 b7 I+ y2 c! L(1).要想让一个自定义标签具有属性,通常需要完成两个任务:在标签处理器中编写每个属性对应的setter方法,在TLD文件中描述标签的属性,为自定义标签定义属性时,每个属性都必须按照JavaBean的属性命名方式,在标签处理器中定义属性名对应的setter方法,用来接收JSP页面中调用自定义标签时传递过来的属性值,例如属性url,在标签处理器类中就要定义相应的setUrl(String url)方法,在标签处理器中定义相应的set方法后,JSP引擎在解析执行开始标签前,也就是调用doStartTag方法前,会调用set属性方法为标签设置属性
    - y- s8 {# m" k( z5 u 5 r3 L0 n1 _0 \' @3 |, w
    (2).在tld文件中声明:$ N; F0 t& g5 [$ H- X4 ]
    <body-content>scriptless</body-content>,<attribute><name>count</name><required>true</required><rtexprvalue>true</retexprvalue>这个设置为true说明不仅可以传递变量,也可以传递脚本表达式</body-content>9 u' q, P$ c/ a7 l! _
    " J9 o- E* `( T, M3 z, p6 d
    (3).count="5"属性传递过去的是字符串,但是count是int型的,服务器自动转型,但是只支持8中基本类型
    1 G( _: b- o) z/ C
    ! |) L3 C9 Y4 z5 M) Q* {5 k11.自定义标签功能概述9 p7 d; q2 w! q9 Q" i2 d* d) m
    (1).自定义标签功能扩展,开发人员在编写JSP页面时,经常还需要在页面中引入一些逻辑例如:控制jsp页面某一部分内容是否执行,控制整个jsp页面是否执行,控制jsp页面内容重复执行,修改jsp页面内容输出,
    0 q# T+ _" G" j4 _% J4 U% K
    ) Q6 n* S- Z0 G. `(2).希望有段jsp代码只有权限的人才能访问,就是控制某一部分内容是否执行,格式化页面输出的内容.
    $ d9 A. x; p# h" T6 s1 y
    + ]0 N! k7 \7 V  H1 s. J& x' G12.自定义标签运行原理
    2 [5 B6 b3 l$ C1 m(1).ie->web服务器->1.jsp(1.servlet)[<itcast:viewIP/>]首先实例化标签处理器类,然后调用自定义的继承Tag接口的类的方法,调用setPageContext方法,把页面的pageContext对象传递给标签处理器类,如果有父类标签,就把标签对象通过setParent方法传递给标签处理器类,完成以上初始化工作后,就开始执行开始标签,doStartTag()方法,如果标签有标签体,服务器一般会执行标签体,然后执行结束标签.然后标签调用release()方法,释放标签占用的资源,一般是web应用停止时调用,服务器接着执行jsp余下的内容,web服务器会将web-inf中的tld文件加载到内存中,
    6 v( y. w% Q8 G6 r% n; e' U 6 O8 r) `  j8 l+ b
    (2).jsp翻译成Servlet中可以看到service方法,调用自定标签的动作放在方法中,首先获取pageContext,将标签处理器类的对象创建出来,
    回复

    使用道具 举报

    admin    

    1244

    主题

    544

    听众

    1万

    金钱

    管理员

  • TA的每日心情

    2021-2-2 11:21
  • 签到天数: 36 天

    [LV.5]常住居民I

    管理员

    8#
    发表于 2015-06-02 12:44:34 |只看该作者
    第九天:
    & y, s' ]9 M! g+ P- H& y3 U7 a1.     el表达式和jstl快速入门
    6 O4 Q# E4 W- i1 I; {! Q- x6 U3 p(1).EL表达式用于获取数据,在jsp页面中可以使用${标示符}的形式,通知JSP引擎调用pageContext.findAttribute()方法,以标示符为关键字从各个域对象中获取对象,如果域对象中不存在标示符所对应的对象,则返回结果为""(注意不是null),EL表达式中也可以使用$(customerBean.address)的形式来访问JavaBean对象的属性,结合JSTL标签,EL表达式也可以轻松获取各种集合中的元素,EL表达式也可以使用类如${1==1}的新办公室进行简单的逻辑判断
    / v( J$ m' S" x! ?& L3 V+ z 6 j8 Y! v% \+ @
    (2).<% Person p= new Person();p.setName("aaa");request.setAttribute("person",p);%>  ${person.name}
    ) ?" d7 J5 P  r) B0 i3 ` 5 e& {; V' a2 t1 @7 j0 _" `
    (3).用el表达式在取数据时,通常用.号,若取不出来,就用[],${map['aaa'].name};: J8 M3 N' \6 v0 {& T, A! p. h  b

      r, S6 w) o/ b  R* P0 }(4).${pageContext.request.contextPath}获取当前web应用的路径,<a href=${pageContext.request.contextPath}>点击</a>2 G# W. l% g! F/ b$ d, |
    ( R, W  i8 O$ u6 Z( }4 Z
    (5).<c:forEachvar="entry" items="${map}">${entry.key}{entry.value.name} </c:forEach>
    ' x$ H/ N% C9 b( d, ]* F) G
    * _7 h" t+ `8 x! E4 g' K& Q+ f! h2.     jsp和javabean7 G" s" O2 G2 a' b$ I) K; O
    (1).javabean是一个遵循特定写法的Java类,它通常具有如下特点:这个Java类必须具有一个无参的构造函数,属性必须私有化,私有化的属性必须通过public类型的方法暴露给其他程序,并且方法的命名也必须遵守一定的命名规范
      U! z2 k3 B1 t - v% f& k( p3 L" A& A# k0 w
    (2).javabean在j2ee开发中,通常用于封装数据,对于遵循以上写法的javabean组件,其他程序可以通过反射技术实例化javabean对象,并且通过反射那些遵守命名规范的方法,从而获知javabean的属性,进而调用其属性保存数据+ U/ j& a  q# F3 V

    7 X' a7 [7 i4 d; _$ }5 |(3).属性的修改器和访问器就是set,get;1 ~! J( V. J8 E8 X

    ( k4 D- y7 D5 n/ l0 B(4).JSP技术提供了三个关于JavaBean组件的动作元素,即JSP标签,他们分别是:<jsp:useBean>标签:用于在JSP页面中查找或实例化一个JavaBean组件,<jsp:setProperty>标签:用于在JSP页面中设置一个JavaBean组件的属性,<jsp:getProperty>标签:用于在JSP页面中获取一个JavaBean组件的属性# ]$ h7 z! `$ O, L, U- C+ P
    5 _0 L" a, I3 b+ o) ?
    (5).<jsp:useBean>标签用于在指定的域范围内查找指定名称的JavaBean对象:如果存在则直接返回该JavaBean对象的引用,如果不存在则实例化一个新的JavaBean对象并将它以指定名称存储到指定的域范围中,常用语法:<jsp:useBean id="beanName"class="package.class"scope="page|request|session|application"/>;id属性用于指定JavaBean实例对象的引用名称和其存储在域范围中的名称,class属性用于指定JavaBean完整的类名(即必须带有包名)scope属性用于指定JavaBean实例对象所存储的域范围,其取值只能是page,request,session和application等四个值中的一个,其默认值是page,从page中查找,查到不到就创建一个javabean,查找到了就返回定义的javabean,反正最后肯定有一个javabean
    " `) C1 {7 V- @5 i; S
    2 l6 y# b3 e$ m(6).<jsp:useBean....>body</jsp:useBean>标签体只会在javabean实例化时执行.3 P0 m) s0 m, t

    0 T2 L0 }$ {! R/ a# i3.     mvc开发模式* @8 w1 {1 h7 c% ]/ t
    (1).按照三层结构开发流程:servlet,jsp是web层,service,javabean是业务逻辑层(service层),dao是数据访问层(dao层),通常在层与层之间定义接口,所以在dao层和service层之间定义dao接口,同理在web层和service层之间定义一个service接口,dao层使用jdbc,hibernate编写,当dao层修改了,service的代码不需要修改,因为在service中使用的的是dao接口
    : \) N5 g  X5 F" w& \0 m8 ~ . k! t! G+ b3 ~3 k' d  h( V
    (2).组织包接口:Cn.itcast.domain:存放javabean类,cn.itcast.dao存放dao层的类,cn.itcast.dao.impl:存放dao层接口,cn.itcast.service:存放service层类,cn.itcast.service.impl:存放service层接口,cn.itcast.web.controller:存放servlet的类,cn.itcast.web.listener:存放监听器类,cn.itcast.web.filter:存放过滤器类,cn.itcast.web.util:存放工具类,cn.itcast.juit.test:存放测试类,jsp页面放在web-inf下的jsp文件夹下.* G4 j( V# C+ w) p0 N- \; F7 l' Z
      c2 y6 s6 E1 j0 T% {  A' C) {) G) D2 ^

    9 e2 M. p: n. L! y! i
    : f2 b( y4 N8 I7 l  ^8 I二、       Struts1& e# W" [& @7 e* u/ V0 _4 }7 Z  T
    1.     ActionForm的工作流程分析
    + ~( T& K4 `" J# l  ](1).ActionForm的工作原理:处理ActionForm的一般步骤:
    9 I5 x/ v% ~( Z3 C: Q第一步:检查Action的映射,确定Action中已经配置了对ActionForm的映射+ A8 V$ K' e6 n' P. m1 H! P$ l4 I
    第二步:根据name属性,查找form-bean的配置信息0 `3 j. @  M. k4 \) h/ p
    第三步:检查Action的form-bean的使用范围,确定在此范围下(request,session),是否已经有此form-bean的实例
    , }8 q8 D2 w. i' B第四步:假如当前范围下,已经存在了此form-bean的实例,而是对当前请求来说,是同一种类型的话,那么就重用4 s% u8 F/ k) {) O
    5 B$ ^/ {1 I# P
    第五步:否则,就重新构建一个form-bean的实例(调用构造方法),并且保存在一定作用范围' C0 U/ o; z8 D8 e. J3 [, v% @% e
    第六步:form-bean的reset()方法被调用
    % I' N3 F; D$ B7 ~第七步:调用对应setter方法,对状态属性赋值2 ]" l8 `: |: P' L/ D
    第八步:如果validate的属性设置为true,那么就调用form-bean的validate()方法
    6 {2 \) N9 E8 A3 j9 z# c; A第九步:如果validate()方法没有返回任何错误,控制器将ActionForm作为参数,传给Action实例的execute()方法并执行) _9 ^) V. T2 P
    注意:直接从ActionForm类继承的reset()和validate()方法,并不能实现什么处理功能,所以要自己重新覆盖方法
    ( U# ^+ Q4 S+ t% h/ o7 N0 K 9 i1 ]( Q" @6 M/ g# e& p* m
    2.     ActionForm相关的attribute属性
    1 J) h3 k5 b: U$ |& X(1).配置文件简介:使ActionServlet,ActionMapping,Action,ActionForm这几个不同层次的组件相互协调工作,这些配置文件是在系统启动的时候,读入到内存中,供控制器使用的
    + Q  _/ D* n8 F$ p+ |$ s
    8 J. u! u2 u4 m4 v0 |(2).<action-mappings>元素帮助进行框架内部的流程控制,可将请求URL映射到Action类,将Action对象与ActionForm对象相关联,<action-mappings>元素内可定义多个<action>子元素,<action>元素,所描述的是特定的请求路径和一个相应的Action类之间的映射关系,有以下属性:attribute:设置和Action关联的form bean在request/session内的属性key,通过request/session的getAttribute(attribute)方法返回该form bean实例,用来存取form的关键字,缺省值与name一样.而不是attribute的值.(ActionForm),input:当表单验证失败时将请求转发的URL8 o$ u! V+ }; d4 J+ N

    , p5 f3 ~' Y& S! w; d(3).ActionForm相关的input属性
    5 V! t. F2 V+ F5 h, tinput属性是用来记录路径:当validate校验不通过,即ActionForm的validate方法返回为ActionErrors对象,且该对象携带一些错误信息,就跳转到指定的错误页面(action)的路径,一般是结合validate=true是结合使用的,当为false时,input属性就没有意义了.
    1 o8 ^0 K( f; {ActionForm获取用户的请求参数,其属性的名称必须要和请求参数名相同,必须要定义对应的get/set方法7 I( G. s8 i3 b* N" C
    ! k3 M# t" V8 T1 L" k
    3.     ActionForm相关的validate属性
    . M) a0 F5 h5 t2 g$ d(1).数据校验:判断用户带来的数据是否符合规定的格式.+ x' v! C- E6 o

    $ b+ K5 x& J1 z5 h. _7 M(2).服务器端的校验:看ActionForm有没有调用validate(),可以返回ActionErrors对象,此对象返回一个错误封装对象,没有错误就返回null,缺省的情况是返回null,所以子类要覆盖这个方法.也可以通过在actionForm子类的配置中设置validate="false"的值,使该方法不调用, j5 h  g, j# \+ e$ h4 y, t
    复位:是总控制器恢复bean属性的默认值6 f, \+ J$ J8 r( K
      X- X1 h* U9 k+ T
    4.     ActionForward的有关问题$ u. z8 l+ c2 Y# q# p
    (1).ActionForward对象的配置对象,这些配置对象拥有独一无二的标识以允许他们按照name属性等来检索,ActionForward对象封装了向前进的URL路径且被请求处理器用于识别目标视图
    5 P6 Y- _9 K+ O5 b5 p ; a- V1 N, O  p+ F
    (2).其内部的方法就是两种方法:request.Dispatch().forward();和response.sendRedirect(),前面讲到的重定向和转发.+ Q: Q. ^* D2 H/ j
    ActionForward:Redirect是false,就是容器内跳转,就是Request.Dispathcer.forward();在这种情况下path为相对路径是true,就是容器外跳转,就是Response.sendRedirect(),在这种情况下path为绝对路径,要在域名前加上协议头错误!超链接引用无效。1 K/ e  Y, |! Z# O8 ~$ ]5 v
    % N* Q# B8 |$ D) A8 p8 g0 I$ k
    6.ActionMapping的深入研究
    ( S' g7 X0 `4 I$ u(1).ActionMapping:每个<action>元素都与类ActionMapping的一个实例对应,代表着请求信息,该类就是对<action>元素中的信息进行封装保存.该类含有这些信息的访问器和修改器.9 b/ h1 \3 O* I1 t! D( S

    : [: p; U! m9 T8 A# z# p" r. Q(2).mapping.getName();mapping.getPath();mapping.getType();mapping.findForwards();返回局部跳转的页面信息,即就在一个action内部.
    ' l8 Z- E1 D- m# ^, H ( o; q/ n1 L: x# Y' v
    7.Action的深入研究和分析- L  d! D1 O, L4 o& Q$ Z% y7 ?
    (1).怎么测试Action实例化了,根据构造函数即可,public Action(){System.out.println("Action isrunning!");}Action在发出请求时初始化,不是在读取配置时初始化,每个action只会初始化一次,即使在不同的会话中(打开两个浏览器).内存中只有一份,资源可以共享和重用,但是不安全.对以多个请求只创建一个action,含有并发问题.需要进行同步.struts2.x是安全的,
    # |; T4 N$ d8 J4 P1 {( H " I( [, B( _* m1 r3 o  N2 A
    (2).安全的话注意两点:第一、不要用类变量或实例变量共享只是针对某个请求的数据,第二、注意资源操作的同步性& z* q  C5 ]9 r7 n

    2 r5 \6 r+ }9 K# _/ n/ J: E% @(3).一个action被调用多少次?这是就可以使用action的不安全性,定义一个变量count即可
    ) Z  U2 S3 s+ |- b) F+ P! w
    ' e. A7 [# n8 Z8 k. B! d8.bean-message标签的讲解1 ^1 A- C) ^( ^* G) _: e1 X$ b8 f  q
    bean-message:国际化信息,key是信息的关键字,其对应value是资源文件
    : k2 Z1 Q" g! x8 C8 Ibean-message的使用:
    $ Q7 \# z' t4 S+ o第一步:定义资源文件,5 R3 U; a7 l8 o; G+ i) Z
    com.itcast.ApplicationResources.properties;com.itcast.ApplicationResources_zh-cn;ApplicationResoruces是基名,后面可以跟不同国家的名称
    8 L6 }3 V! R  `& D! N# p1 L+ P. ^, _第二步:在struts-config中添加:<message-resourcesparameter="com.itcast.ApplicationResources"key="myKey"/>6 h& n. {) {# K9 n. l
    例如:<message-resourcesparameter"cn.itcast.ApplicationResource" key="myKey">写的是基名,在资源文件中定义greeting=welcome username=root password=root键值对key属性是防止所有的properties文件混淆3 N. x4 `# _1 ^/ m$ T- R
    第三步:在页面中使用:bean:message test<br> <bean:message bundl
      h5 E6 x/ Q; P) ^) T; He="myKey"key="userName"/> <bean:message bundle="myKey"key="password"/>
    7 I  z$ f8 Q- }2 Q7 ?( H% g. h( @例如:: X+ k& K- s9 C
    <tr>- C9 |6 v9 J5 C3 q) p* B2 {8 W
    <td colspan="2">
    3 s$ O1 q- f5 ^% l8 h3 v" n<bean-message bundle="myKey"key="greeting"/>
    7 \# p) X- S" d' E</td>
    * L8 w1 {9 {4 M9 k8 d! y</tr>' B/ w! G, [) f, u- P& N
    0 Y) y# ]2 _; h
    <tr># V% T/ f! u! A/ E) C
    <td colspan="2">
    8 ]7 D& A( X' T<bean-message bundle="myKey"key="username"/>
    / w+ n* {  E% q( L. w</td>
    7 [6 B8 \! j4 i7 J1 y: n8 ~</tr>/ r% ?) ?2 j: a9 l# ?. _7 m* {

    3 ]% k' r& h! u7 S% o3 V2 a$ h第四步:切换浏览器的语言类型,工具-->Internet选项->常规->语言% H- L1 C- P6 A5 V1 D
    / j& r, X9 |" q3 v
    在struts-config.xml文件内的<message-resources>标签中也可以不指定key属性,那么,该<message-resources>标签指定资源包将成为struts的默认资源包;响应的,在JSP文件内的<bean:message>标签中特可以不指定bundle属性,这表示要在struts的默认资源包中去查找信息.8 ]' R1 k8 u7 P# M/ L
    2 u$ m% o8 J; p$ ^, V* h
    9.bean-write标签的讲解0 {. Q) F, o5 `- G9 U$ A
    <bean-writescope="request" name="user"property="username"/>
    ( q, F7 c2 l3 t7 x% `从哪个域中输出指定bean以及bean的一些属性4 b5 h" Z3 r% G1 O& u7 d$ S

    ; P' Y* N: @, D8 i10. DispatchAction的讲解. Q! @1 N$ l4 D% u
    (1).实现一个模块,需要对学生信息进行CRUD操作:  T% A2 k* d/ Y2 d
    AddStudentAction
    4 l. f! `& s2 F0 [; S- H2 u9 [DeleteStudentAction2 D+ @/ E* y; _6 x  Z9 g. ]; z
    UpdateStudentAction
    1 C6 j0 U; T0 K1 lQueryStudentAction4 K* [7 k9 ]7 ^! s. ~( W0 X; h
    分别调用四个类的execute方法,但是四个定义四个类有点过多.所有可以合并相关的Action7 u/ v" J8 ]% s' X, [

    7 N) Z+ n& s6 G, g$ e(2).特殊的Action的使用ispatchAction,起作用是可以减少Action的数量,使用方法:继承DispatchAction,在里面添加上需要使用的所有方法,参数和返回值与原来的execute方法完全一样,配置文件中带上parameter属性,如parameter="opertationType";使用的时候才用如下形式:/*.do?operationType=add
    5 C0 l# h; v+ {0 K) B; ]8 C0 F* `
    ( r; Q0 n% }4 Y4 X8 g: g9 H+ F(3).一个Action可以调用CRUD的方法,DispatchAction中的四个方法的参数和返回类型都和原先的execute方法一样,就是将execute复制四份,同时将方法的名称改为addStudent,deleteStudent,updateStudent,queryStudent即可,然后在这四个方法中编写响应的代码.
    1 e" U+ j+ }# Z7 d; g7 U' q ! z- ?9 q( e/ ~& Y
    (4).在地址栏中输入..../DispatchAction.do?operationType=addStudnt,就是访问增加学生页面
    # d4 e' l6 Y' B, p " ^3 Y9 ^* F6 }0 D
    (5).第一步:可以开发一个DispatchAction的子类,这里的方法注意必须与原来的execute方法同参数同返回值
    3 r0 q1 b; [+ M2 a第二步:配置parameter属性& l' N7 k$ B) W3 c) Y

    & z- {; |' t3 O: ~' U' x: v11.ForwardAction的讲解( K: L  q, l/ X8 W4 s
    (1).访问jsp访问形式:需要统一访问模式*.do;所有的jsp在web-inf下,所以请求jsp之前需要先访问action
    6 z' Z; h) C) W 9 u# ^, b& L; ~/ a! n. k8 m
    (2).因为每个jsp对应一个Action,可能要许多Action,所以我们只定义一个跳转Action:ForwardAction 访问:/testForwardAction.do,目的是统一以*.do形式访问所有的模块;<action path="/testForwardAction"forward="/TestForwardAction.jsp"/>
    6 x* E. G' Q6 H7 E. ~% B; k6 X
    9 M9 C* L( g2 P9 B4 r12. logic_iterate标签的讲解
    - _5 }) r+ Q  w(1).逻辑标签:逻辑库的标记能够用来处理外观逻辑而不需要使用scriptlet,struts逻辑标签库包含的标记能够有条件的产生输出文本,在对象集合中循环从而重复的产生输出文本,以及应用程序流程控制,它也提供了一组在jsp页面中处理流程控制的标记,这些标记封装在文件名为struts-logic.tld的标记包中,逻辑标记库定义的标记能够执行下列三个功能:条件逻辑,重复,转发/重定向响应9 |8 n, I8 B2 l5 t+ w
    ( g; i; y1 k% R8 }
    (2).Logic:iterate的使用:单重循环,双重循环,对指定的集合的循环,这个集合必须是一个Iterator,Collection,Map,Array;
    + l' I# q+ w3 p7 D' C
    5 {7 z( X0 I: ]9 F) c( W(3).- ]7 l4 T4 _: ?4 Y6 D8 E
    <%String[]usernames={"aa","bb","cc","dd"};
    8 S7 S1 Y+ a: m request.setAttribute("usernames",usernames);%>
    ) M6 G4 z( A, m: O& t <logic:iterate id="username" scope="request"name="usernames">* x1 B: ?! L  C' Q9 I, }3 t) B- g; p
      ${username}- ~; y9 U6 h' a* f/ o
    </logic:iterate>. |; g- Y- H1 s$ z, l! n
    name所指代的bean必须是一个集合类型
    , r3 f" [9 T# L6 i+ J1 V6 pname+property:每一个人的所有爱好:
    & }# w* g: v9 r8 s, p2 _User mengfanlong = new User();
    1 D. z3 u- X. [( B- m- Cmengfanlong.setUsername("mengfanlong");: T, V/ ^+ b: l9 W6 a
    String[]mengfanlongFavorites={"sports","sleep","study"};
    1 X& A. V3 i7 ]( _4 d8 ^& Amengfanlong.setFavorites(mengfanlongFavorites);5 ?! F) i+ @/ \6 n! L
    用ArrayList存储用户
    : d% P* L$ c  Q5 ^. K+ n9 A# Q<logic:iterate id="user"scope="request" name="userList">' I+ L4 z, k+ x( @! J
    ${user.username}# v! m7 a* ]: T8 x7 o5 e- S( x
    <logic:iterate id="favorite"name="user" property="favorites">1 _, W! ^, d5 ^7 Y+ r6 u, A$ U
    ${favorite}& s$ B4 I" ]5 _8 {+ Q) ^( s( R
    </logic:iterate>
    ' N6 q( B: }) R3 z9 N</logic:iterate>. F! L* A/ }) w8 [

    0 x. ]! s( F( x3 f* p2 ^(4).id是迭代时的临时属性,还有其他属性:length,offset,indexId等控制循环变量- r1 p. S) C8 G# P, x
    4 s& V6 K: s+ v$ d4 h  b2 d& O
    13.struts插件的讲解9 @3 W& A1 F% |( v
    (1).插件(Plugin)生命周期方法:init,destroy,用户提供setter方法,告诉ActionServlet中心控制器把属性设置# _. ^( |+ W1 ]' H, w5 U, Y
    / ]0 }0 n* e# ?4 y/ N
    (2).应用:在struts启动时把hibernate加载进来,就是要把hibernate的配置文件读进来,同时打开hibernate的sessionfactory" M/ ^  C6 A8 j6 N
    需要struts.jar包+hibernate.jar包
    0 |$ k0 d- V$ b1 Z$ b& o3 Q设计一个类 HibernatePlugin,实现plugin接口,读取hibernate配置文件,打开SessionFactory;; ?2 U) @% d5 t% V: N" b$ X) i
    在struts-config.xml配置文件中添加一对<plugin>标签,在plugin中加上子标签
    $ p/ T" T) L. i, u# b # J# J' o6 o5 Q4 @) [( R" N6 V  ~
    (3).3 ?0 k' _5 k9 n) o
    <plug-inclassName="cn.itcast.HibernatePlugin">
    ! }% k" L# y0 _! n7 \  <set-property property="hibernateConfigFile"value="/WEB-INF/hibernate.cfg.xml"/>
    % i. s  Q1 |4 L( m: b </plug-in>3 ]* l6 Z' O1 v5 x/ |6 D- G
    ) ]0 [+ p  z  f  ~  I4 ^' D) k
    (4).struts当服务启动时启动,插件在struts启动时读取配置文件时启动.可以在中心控制器ActionServlet的init方法对plugin初始化,destroy销毁了4 Z# q7 o' `" F, K8 b# E+ m4 l$ Z# ~) q. T

    0 _) M; D* o  c(5).服务启动-->ActionServlet->读取struts-config.xml->根据各种标签的内容进行一系列的初始化(plugin,ActionMapping)
    8 o% n2 P* b; ?& ?: M 9 `' T- m$ e, P) Z# f/ ~# l5 l
    14.struts的MVC组件
    ( L0 W9 @; @2 h1 F, Y* L4 c(1).组件:ActionServlet,ActionClasses,ActionMapping,ActionForward,ActionFormBean4 y% K$ g1 B9 V. a, l5 O! p
    6 d# k+ p/ P  n5 Q9 }
    (2).struts中的MVC:' t# e" g" z6 d8 D, n5 J: p
    第一:模型:本质上来说在struts中model是一个商业逻辑类,开发者实现商业逻辑1 l3 U5 i) y( t4 M
    第二:视图:View是由与控制器Servlet配合工作的一整套JSP定制标签库构成,利用他们可以快速建立应用系统的界面
    ; S, X5 G3 H% Q& t9 y第三:控制器:前端控制器是一个Servlet,它将客户端请求转发到相应的后端控制器Action类
    / B! @* k) H/ }
    - r5 F  [; U- Z5 m% G" L+ G! A15.struts的工作原理
      d  H  e2 R( b3 d9 u第一步:初始化,读取struts-config.xml,Struts框架总控制器(ActionServlet)是一个Servlet,在web.xml中配置成自动启动的Servlet,读取配置文件(struts-config.xml)的配置信息,为不同的struts模块初始化相应的ModuleConfig对象:ActionConfig、ControlConfig、FormBeanConfig、ForwardConfig、MessageResourceConfig- }: H  e1 K2 j2 n3 M
    第二步:等待Http请求:用户提交表单或调用URL向Web应用服务器提交一个请求,请求的数据用HTTP协议上传给Web服务器9 o% o' r0 N) P3 _
    第三步:填充FormBean:实例化、复位、ActionServlet拿到用户的数据填充FormBean、校验、保存等,(*.do)从ActionConfig中找到对应该请求的Action子类,如没有对应的Action,控制器直接转发给JSP或静态页面,如有相应的Action且这个Action有一个相应的ActionForm,ActionForm被实例化并用HTTP请求的数据填充其属性,并且保存在ServletContext中(request或session中),这样它们就可以被其它Action对象或者JSP调用
    ; v' L; s7 c2 u; k2 b第四步:将请求转换到具体Action处理:控制器根据配置信息ActionConfig将请求派发到具体的Action,相应的FormBean一并传给这个Action的execute()方法.(后台控制器)/ M: O' j$ S5 {, P! t( ?" [( `
    第五步:调用后台的业务功能类完成商务逻辑:Action一般只包含一个execute方法,它负责执行相应的业务逻辑(调用其他业务模块),完成后返回一个ActionForward对象,控制器通过该ActionForward对象来进行转发工作.& q% ?' X! u! b8 k4 t
    第六步:返回响应对象:Action根据业务处理的不同结果返回一个目标响应对象给总控制器(前端控制器actionservlet),该目标响应对象对应一个具体的JSP页面或另一个Action# F/ c# l, K9 ?6 T4 @2 Y
    第七步:转换Http请求到目标响应对象(jsp):总控制器根据业务功能action返回的目标响应对象,找到相应的资源对象,通常是一个具体的JSP页面
    7 ^4 ]( u# g8 q/ P( \; i% ?第八步:Http响应:前几步都在服务器端,这一步在客户端,目标响应对象将结果展现给用户目标响应对象(jsp)将结果页面展现给用户( |" u) ]' d& |( m$ o/ V  [. F
    控件:总控制器ActionServlet的作用最大,Action(execute)ActionForm,配置文件,将各个模块连接起来
    7 n$ [/ W# m9 C- D注意web.xml和struts-config.xml的区别,总控制器ActionServlet要在web.xml中注册,ActionForm中的属性,Action等的信息都在struts-config.xml中注册.
    - {# @" V" B' l4 D( o/ [" O0 j- g
      z2 H- S1 e: x, P7 [/ C6 i7 }16.Struts相关基础理论介绍
    7 I3 K3 w% {& U3 O( P6 u- J$ R9 m# q(1).案例:实现一个用户登录功能,如果用户输入的用户和密码都正确,就跳转到登录成功页面,否则跳转到登录错误页面
    : w5 m+ a6 F1 q
    / i! T, ~3 i& ?4 \% s(2).知识点:为什么要使用Struts,FrameWork的概念,Struts的概念和体系结构,Struts的工作原理,Struts的组件,Struts配置文件简介,Struts标记库,错误处理框架,校验框架,高级特性,Struts优缺点
    9 Y: B7 ~6 e8 s" ^' q) G" L, s
    - |) e$ E* P' [: @' y(3).框架(Framework):人们用于解决相同或者相似类型问题的方案,可重用性,可扩展性,可收缩性
    # y1 K& ~4 K5 B- L1 e
    3 w) `/ U" |+ b# F$ H+ ]7 d% Q(4).Struts是Apache组织的一个开源项目,主要是采用了servlet和jsp技术来实现的,是基于Sun JavaEE平台开发的# w' C5 _5 w8 u* h& r7 Z
    % }4 e$ O' V  O( k# A: t
    17.struts中的异常处理
    5 r  w2 r, Y7 u! y3 P; ^2 i(1).异常的作用:增加健壮性,模块间传递信息9 S: y, F9 V9 A: l+ U
    (2).配置异常:定制异常有两种:全局异常(所有Action使用的)和局部异常(一个Action使用的),
      ^+ d% w' q  |(3).全局异常的定义方法:
    6 R6 @) _! \0 f0 F4 W<global-exceptions>
    " X3 t1 b# B2 z$ z5 W <exceptionkey="user.login.invalideUser" path="/Login.jsp"type="com.future.struts.MyException"/>
    " ]& z& W9 M) U</global-exceptions>
    $ F+ ^/ G3 {% B/ L1 f# ^$ d, b& e局部异常的定义方法:* Q1 n/ r' F, y+ X( K
    <action-mappings>
    , A$ F& a# x1 q! q<action attribute="loginForm" name="loginForm"path="/loginAction" scope="request"type="com.future.struts.LoginAction" validate="false"/>7 i# @4 e, G! ^; m
    <exceptionkey="user.login.invalideUser" path="/Login.jsp"/>' b+ H: l% E, l- U9 r
    </action-mappings>
    3 h  w' ?1 f7 h- O' o8 ]
    / v7 c* K1 O7 R+ C  ?$ ^(4).当Action的execute方法抛出异常时,调用异常处理器类ExceptionHandler
    # @( c* w- j* F5 m" h: M
    ' {* u8 X' e, e+ y( M7 b6 J0 C(5).怎么使用:( ]  C( D5 o  v" P0 K% I
    第一步:配置<exception>可以指明path,key,type,Path是指出现异常后的跳转页面,Key是指异常信息的键,对应的值在资源文件当中,Type所要处理的异常
    5 q, k: A: y8 R7 f. ^  C: g4 }第二步:在相应的action中的execute方法抛出异常& }8 p2 A9 L/ v; }
    第三步:在异常处理页面(path所指页面)使用html:errors标签打印提示信息1 T7 c; Z& X) e: d5 ?' X
      s: _5 K* _* [/ [3 B. b
    18.struts注册程序的组件设计和流程分析
    % l5 D$ t: H6 |4 j" W. N/ Z: }0 _(1).编写action,actionform,首先编写actionform,因为action中要用到actionform,在struts-config.xml中注册
    & N: W! o/ D& ?6 ~actionform,action<form-bean name="addStudentForm"type="cn.itcast.AddStudentForm"></form-bean>  <action path="/addStudentAction"type="cn.itcast.AddStudentAction"name=""></action>2 C* z; a, Q  h9 d8 R
    ,还有跳转页面:2 d) y# n- R( ~
    <forwardname="addStudentSuccess"path="/AddStudentSuccess.jsp"/><forwardname="addStudentFailure" path="/AddStudent.jsp"/>
    % A0 C# w- \  r2 d2 c0 u" s. o
    , S  A# m: S/ p3 e(2).对action的编写要控制线程安全: K" s2 w4 Y$ u
    ! f3 G# ^; g  a0 h: s' [; J
    (3).ActionServlet将请求参数封装成FormBean的属性时进行转换而导致的结果,ActionServlet内部调用BeanUtil这个工具包来将字符串类型的请求参数转换成FormBean中对应的属性的类型,然后再将转换结果装配到FormBean中,这时候可以打印出FormBean中各个属性的结果看看,就大概清楚BeanUtil转换后的结果了,我们在以后的项目实战课程中有对此问题的详细分析和巧妙的解决方案
    - X+ `: p1 \. E# m) h' P- j4 C
    ; ~% y* f4 B& \19.搭建struts开发环境
    % f1 U3 B9 A8 B. Z/ F' {导入jar包,使用相关类,建立一个配置文件:struts-config.xml 放在web-inf下,web.xml注册struts中心控制器---ActionServlet,注意事项:struts配置文件的位置,预先加载控制器,可以到struts自带的例子程序,从这些例子程序中的web-inf项目下拷贝struts-config.xml文件
    : F7 Q, S; p: c
    , f5 ?4 v, _" Y# K: c- ~/ c& G" _, C20.动态FormBean的讲解' \# S5 e3 j- [: H+ |+ \
    (1).动态form不需要用户自己写代码ActionForm
    8 d7 J4 _: a. n  P" B" r, @
    1 _- C( p  m. s" w(2).    <form-bean name="dynaFormForm"type="org.apache.struts.action.DynaActionForm">
    7 \. N, S' V- X' Z! k. }1 f<form-property name="userName"type="java.lang.String"/>/ q$ T: P+ v% H0 Q
    <form-property name="password"type="java.lang.String"/>
      {1 g& ?( P9 I<form-property name="age"type="java.lang.Integer"/>
    : w: L) w; q2 e, M9 o. |  l</form-bean>
    0 r' [) `+ I+ R; a: y2 C% c需要告诉form-bean的名称,属性名,属性的类型等信息,其中type必须为包装类型.; B+ m% x* O6 p7 H& S. e6 e
    7 [# }$ p: Z% i2 R/ z0 m
    (3).提交一个表单数据,用数据填充动态表单,在Action中的execute方法中," h; k- \' |3 `
    DynaActionForm addStudentForm =(DynaActionForm)form;
    ' P* S. q0 `+ `( I$ r( n& ^String sname =(String)addStudentForm.get("sname");
    : c' Y" V5 q3 ]4 G0 J3 rjava.sql.Date birth =(java.sql.Date)addStudentForm.get("birth");
    / v6 p; k' a8 G! I/ x' b2 R' L
    3 A1 {' H  G4 X0 D8 t21.分析struts程序的执行流程
    # W: w, [0 r6 @% `% M9 J(1).ie->服务器->控制器ActionServlet(前端控制器)(送ActionForm给后端控制器)->LoginAction(后端控制器,第一句要进行转型LoginForm loginForm = (LoginForm)form;)->前端控制器->显示页面; Q, a& M  ]6 P" r; c: `

    $ Q* G# r* d5 Z1 N+ ](2).if(loginForm.getUsername().equals("itcast")){returnURLKeyWord= "loginSuccess";}else{returnURLKeyWord="loginFailure";}2 e: ~3 I. E7 o. N1 E! |
    % [/ T% {5 c9 T' D) F
    (3).前端控制器是根据struts-config.xml配置文件找到后端控制器,跳转到成功或者失败的页面也是根据struts-config.xml配置文件的.后端控制器才是真正执行代码.
      |$ T7 m. |7 @# O9 C; u& v; I$ M ' ~% {. h% ]. V! U3 F) S9 L
    (4).问题:谁来填充From?,什么时候填充?,根据什么内容来填?
    ) b3 L  Q# q; S      ActionServlet怎样把请求派发给Action?! c+ s& r+ a2 {3 z* h
          Action运行完后怎样跳转?( a; }- I- K1 U, k% z

    + {: Q  \1 a" ~5 t2 q( ]& n22.分析自己写struts框架的思路
    0 ?1 ~4 O+ G$ \; s/ Z) e开发以下类:
    8 N7 i- {+ n: l. ]$ ]0 XActionServlet:读取配置文件dom4j,填充form,派发请求:调用对应的action的execute方法,查找响应,跳转
    - v! }; C$ G0 A) F" NActionForm{reset(),validate()}
    ! f7 Q/ y1 V% i5 [, m! O( n- Z* P  OAction{execute(ActionMapping,ActionForm,HttpServletRequest,HttpServletResponse}
    2 e' {# O+ }+ _$ B6 G3 D9 J% SActionMapping{path,name,type,validate}使用HashMap存储
    , y- V! o7 z) f" u5 K- }ActionForward{name,path}使用HashMap存储
    " A4 @7 W" B$ g2 r配置文件struts-config.xml% q9 w  k: R, x5 {6 m- t

    : S0 _/ H' a9 G5 w: d4 ?23.配置全局跳转
    / S$ j6 H2 C) b% n: j7 A) p8 rActionA----->Error.jsp7 ?3 s  I& r% |0 i) z- Y3 |
    ActionB----->Error.jsp: |5 @0 N# ?+ X+ ~9 E8 U
    ActionC----->Error.jsp
    9 Q# R- L$ L, Z; m9 L! }' B以前的方法是在三个Action中都写上forward! s9 s2 g+ Q7 m. E* v  x  H
    / e  Z4 B& o; m
    全局跳转:Action A,B,C---->Error.jsp,在struts-config.xml文件中添加标签:<global-forwards><forward name="error"path="Error.jsp"/></global-forwards>
    : a1 n6 D: @  R 2 e1 q. P" s; K2 l" L7 `* f
    24.通过bean_define标签入门struts标签库
    + n8 F3 H6 N3 Y$ G- r8 H! [(1).JSP视窗组件所使用的struts标记库由四类标记组成:Bean标记:用来在JSP页面中管理bean,Struts-bean.tld;逻辑标记:用来在JSP页面中控制流程,Struts-logic.tld;HTML标记:用来生成HTML标记,在表单中显示数据,使用会话ID对URL进行编程Struts-html.tld;tiles标记:使用动态模板构造普通格式的页struts-tiles.tld% X) s8 h8 `$ E: f" D& k) Z" i2 J

    8 U' s1 t% I5 `(2).Bean标记,这个标记库中包含用于定义新bean、访问bean及其属性的标记,Bean标记库将标记定义在四个子类别中:创建和复制bean的标记,脚本变量定义标记,bean翻译标记,消息国际化标记.( [# Y7 s& q1 }) v. L

    ! B' V# t  ~, Q' w$ H  g  M; A' @(3).Bean:define:从已经有的变量或者变量的属性定义一个新的变量:id,name,property,scope(老bean),toScope(新bean),5 W3 W4 s8 N8 t
    查找:到scope中查找name的变量获取它的property,
    # y3 l0 j2 }9 R1 O/ J( Z定义:定义一个新变量的名字(id),同时这个属性是必须的; b$ `$ }* q+ b  R9 u
    保存:将新变量保存到toScope
    + K! @& O1 B. k5 ]* i, y) U- i( g 4 O, A! Z/ ^7 ]" y; W  f" a
    25.通过代码了解ActionForm的基本工作流程( I: @' Q. [) u3 @
    (1).通过ActionForm的构造函数来观察是否被实例化了,通过reset()方法来观察是否执行了复位方法.set()方法,观察他们三者的运行顺序,. L6 {% @# N# `* n- h1 h' E+ p* I

    . d4 c" h. ?* J7 b- l(2).首先调用构造方法,然后调用reset方法,然后在调用set方法,复位只需要只对请求复位.! Q- m" z% v. I& u
    / n5 C, ~6 A5 ~2 `! L0 E% X
    26.用struts开发简单的登陆示例程序
    ; n, L' A  k* U( s, I9 ^. h(1).建一个类,继承ActionForm,注册,修改配置文件struts-config.xml
    ! G1 [- A$ g: U) o. y% s4 a
    : C7 G% K1 B' f: t$ o: y(2).public classLoginForm extends ActionForm{
    0 Y) U) E2 u  \9 r" k! f% Z2 `# K           private String username = null;
    9 X/ Y- @7 _% y; G0 y           private String password = null;
    8 _) i( ]9 c1 X5 S0 n+ D}
    ! P# d: w& P- Z9 W+ y ! ~  [, q, x8 R# E. E
    (3).在struts-config-xml文件中添加一个标签<form-beans></form-beans>1 K2 b" F( @( G! _& D0 X) h6 j
    0 o9 O4 W& K6 T5 |/ j
    (4).开发Action,建立一个类,继承Action,覆盖execute方法,需要强制转型,调用其他模块,跳转(根据关键字,关键字参照action中forward标签中的name属性),注册:修改配置文件struts-config.xml,Path:指明调用者(jsp)能通过中心控制器ActionServlet找到Action,Type:指明该action类全名,Name:该action引用的form的名称
    % L2 I) ]. }* [( m! J4 s
    4 i3 T0 L8 f! `6 E(5).public classLoginAction extends Action{覆盖execute方法}2 s$ L# B3 B- T' R( _# m. V& W

    ) W; {7 v+ b/ W5 f6 |7 B27.用监听器探索ActionForm如何被存储
    0 K  z! R, R/ `9 `; x(1).怎样检查ActionForm被存储了,有两种方法:一种是从过程去查看,另一种从结果去查看.
    * S4 Q5 j& u, i ! I8 Y4 o" f. e. O
    (2).从过程查看,通过监听器,从结果查看execute方法
    ! R5 _9 j/ z/ J; D+ h, e - `* z$ `, i# i. g# J7 M/ x" p8 N
    (3).制作监听器:定义一个类,AttributeListener,然后实现两个接口,HttpSessionAttributeListener,HttpRequestAttributeListener,在web.xml中写入:<listener><listener-class>cn.itcast.AttributeListener</listener-class></listener>监听session,request域中属性的变化.) K" b' \# O% L
    3 M. M( x: I4 B' d% z& u- a
    28.在execute方法中分析ActionForm的问题
    , Q5 x. B4 z) y/ P+ _, Z: `/ O(1).从结果查看,
    - y% t' W$ R9 u% bAddStudentFormaddStudentFormInSession
    ! s2 H2 b! i# a4 h8 q/ x; o=(ActionForm)request.getSession().getAttribute("addStudentForm");  L& p3 H( W8 |2 a" I
    或者
    ; I) v3 w8 Q9 @AddStudentFormaddStudentFormInScope
    " I- X$ B  h/ F2 G' y5 M$ L=null;if(mapping.getScope().equals("request")){addStudentFormInScope. x( k7 U4 U8 _- C2 c/ ~* @  }7 X
    =(AddStudentForm)request.getAttribute("addStudentForm");}else{addStudentFormInScope: M0 b3 I. j; P4 ~6 z: S
    =(AddStudentForm)request.getSession().getAttribute("addStudentForm");}从不同的域中获取ActionForm4 j. K  R8 O, g  m; O" @* ?' ?

    - M9 l; X. N* |" E  e(2).参数form是和存储在域中的form是相同的(ActionForm);; {* e' y: ^/ k, u& y- G
    7 Z! j4 o( R4 _: r$ y! F
    (3).没有sname成员变量,但是有setName()方法,所以准确来说是看是否有setName();页面中的控件<input type="text"name="name">中的name是否和ActionForm中成员变量的名称一样,其实不是看成员变量,而只是看标准的set方法,通过反射技术执行.
    红红火火恍恍惚惚
    回复

    使用道具 举报

    admin    

    1244

    主题

    544

    听众

    1万

    金钱

    管理员

  • TA的每日心情

    2021-2-2 11:21
  • 签到天数: 36 天

    [LV.5]常住居民I

    管理员

    9#
    发表于 2015-06-02 12:45:13 |只看该作者
    三、       Struts2% |/ z/ u- y0 m- {: {
    1.  Action名称的搜索顺序
    : m. P' w5 [2 V1 a0 n(1).获取请求路径的URL,例如URL是:http://server/struts2/path1/path2/path3/test.action4 ~" k8 o2 B4 N; P
    4 N1 L- E1 `2 Z) c
    (2).首先寻找namespace为/path1/path2/path3的package,如果不存在这个package则执行步棸3,如果存在这个package,则在这个package中寻找名字为test的action,当在该package下寻找不到action时就会直接跑到默认namespace的package里面中寻找action(默认的命名空间为空字符串),如果在默认namespace的package里面还寻找不到该action,页面提示找不到action
    % S. M( j8 t+ Z) K$ } . t+ ^. r1 [% `% {( h2 d) a
    (3).寻找namespace为/path1/path2的package,如果不存在这个package,则转至步棸4,如果存在这个package,则在这个package中寻找名字为test的action,当在该package中寻找不到action时就会直接跑到默认namespace的package里面去找名字为test的action,在默认的namespace的package里面还寻找不到该action,页面提示找不到action
    + c! L& F. B! v4 @0 a; a2 n( O7 e " x! ?3 q, I; U0 i  g6 [
    (4).寻找namespace为/path1的package,如果不存在这个package则执行步棸5,如果存在这个package,则在这个package中寻找名字为test的action,当在该package中寻找不到action时就会直接跑到默认namespace的package里面去找名字为test的action,在默认namespace的package里面还寻找不到该action,页面提示找不到action% O, n& X" C2 c  z3 X
    6 \9 a, O& W8 Q9 L( f0 K1 g
    (5).寻找namespace为"/"的package,如果存在这个package,则在这个package中寻找名字为test的action,当在package中寻找不到action或者不存在这个package时,都会去默认namespace的package里面寻找action,如果还是找不到,页面提示找不到action
    : j$ u  ~0 j9 P7 F5 ]
    ; z" I2 p: ^' e7 y3 [* j(6).在struts2中默认的处理后缀是.action,不加后缀也可以) ^4 O+ L2 T1 y

    9 D; O- X, C8 f6 p2.  Action配置的各项默认值+ x- ~" A0 a# x4 o; X% }
    (1).struts1中:<actionpath="/control/employee/addUI"forward="/WEB-INF/page/employeeAdd.jsp"/>实现请求转发,action将请求转发给视图jsp& ]* c& d& i. E& X- b& D5 l8 r/ {
    ; r  j' t9 ]: I" p0 K
    (2).在struts2中,<actionname="addUI"><result>/WEB-INF/page/employeeAdd.jsp</result></action>,不需要设置addUI的类路径class属性了
    7 ?: @/ e1 V/ [/ w! Y + p  H1 i8 |3 E3 c2 b
    (3).Action配置中的各项默认值:+ W3 f' t, S- \( _8 a, ?
    <package name="itcastnamespace="/test" extends="struts-default">
    + V% ~/ Q' s5 `; A<action name="hellowrold"class="cn.itcast.action.HelloWorldAction"method="execute">' A  j$ a' v2 t0 R/ w$ t
    <result name="success">/WEB-INF/page/hello.jsp</result>  Z1 c9 P! ]! U3 {+ A5 q5 @
    </action># e+ t1 e7 [& f9 z* F! }
    </package>
    # V9 c4 g& U8 A( b" W如果没有为action指定class,默认是ActionSupport,可以查看ActionSupport的源代码,首先交给ActionSupport类处理." L; F4 Q) e- l# F! C/ u4 O
    如果没有为action指定method,默认执行action中的execute()方法,这个方法的返回值为"success";- v8 }6 [2 ]" L
    ! G1 w/ K& B1 x) H
    (4).如果没有指定result的name属性,默认值为success,正好和execute方法的返回值相同,所以可以实现视图的转发. X7 X7 F3 G% B

    1 H5 ^. S) z. z2 O3.OGNL表达式9 B* J, {8 W: U5 e
    (1).OGNL表达式:OGNL是Object Graphic Navigation Language(对象图导航语言)的缩写,它是一个开源项目,struts2框架使用OGNL作为默认的表达式语言# }/ o  _9 A0 W8 R- N
    第一:相对EL表达式,它提供了平时我们需要的一些功能,如:3 B# j$ S1 I. ~/ l6 P& z2 c# R
    支持对象方法的调用,如:xxx.sayHello();0 P- F. k; g0 r. J
    第二:支持类静态方法调用和值访问,表达式的格式为@[类全名 (包括包路径) ]@[方法名|值名],例如:@java.lang.String@format('foo %s,'bar')或@cn.itcast.Constant@APP_NAME
    ) l8 d7 C; b2 o% X第三:操作集合对象
    & y$ n6 \/ T. o9 y, P9 F9 u
    2 v# c* A6 l( l7 k8 b' z2 B5 X(2).OGNL有一个上下文(Context)概念,说白了上下文就是一个MAP结构,他实现了java.utils.Map接口,在struts2中上下文(context)的实现为ActionContext
    - S+ H+ h3 E- Z. l! i! ? ; K4 |  y. I  Q/ u& \1 a
    (3).struts2中的OGNLContext是实现者为ActionContext,它的结构为:OGNL Context:ValueStack(值栈,他是根对象),parameters,request,session,application,attr,当struts2接受一个请求时,会迅速创建ActionContext,ValueStack,action,然后把action存放进ValueStack,所以action的实例变量可以被OGNL访问5 y+ G5 W# n+ q9 F

    . ^' G. I6 _$ |/ ?0 U8 K(4).当要访问某个对象只需在其前面加上一个'#',例如:#request,当然有一个特殊的例子,就是根对象,会省略'#',OGNL会设定一个根对象(root对象),在struts2中根对象就是ValueStack(值栈),如果要访问根对象(即ValueStack)中的对象的属性,则可以省略#命名空间,直接访问该对象的属性即可.& P* d" e( w/ Y6 J

    3 H' J) {+ F3 D  h! d0 _(5).在struts2中,根对象ValueStack的实现类为OgnlValueStack,该对象不是我们想象的只存放单个值,而是存放一组对象,在OgnlValueStack类里有一个List类型的root变量,就是使用它存在一组对象,在root变量中处于第一位的对象叫栈定对象(存放action),通常我们在OGNL表达式里直接写上属性的名称即可访问root变量对象的属性,搜索顺序是从栈定对象开始寻找,如果栈定对象不存在该属性,就会从第二个对象寻找,如果没有找到就从第三个对象寻找,依次往下访问,直到找到为止。大家注意:struts2中,OGNL表达式需要配合struts标签才可以使用,如:<s:property value="name"/>,value属性接受的是OGNL表达式,搜索是否含有name属性
    " C0 B3 }8 e. i % y7 B7 \' D9 z0 K, K: B
    (6).由于ValueStack(值栈)是struts2中OGNL的根对象,如果用户需要访问值栈中的对象,在JSP页面可以直接通过下面的EL表达式访问ValueStack(值栈)中对象的属性:
    - ?- Q& K% u, Y- B) t' Q${foo}获得值栈中某个对象的foo属性
    % S; @/ `$ M- A% m' f如果访问其他Context中的对象,由于他们不是根对象,所以在访问时,需要添加#前缀1 D& K# B# _0 J/ A6 w2 }
    第一:application对象:用于访问ServletContext,例如#application.userName或者#application['userName'],相当于调用ServletContext的getAttribute("username");! ?& E3 f6 m+ w. P, G. k# P
    第二:session对象:用来访问HttpSession,例如#session.userName或者#session['userName'],相当于调用session.getAttribute("userName");
    # O" a/ c) L! ~5 c6 R5 R第三:request对象:用来访问HttpServletRequest属性(attribute)的Map,例如#request.userName或者#request['userName'],相当于调用request.getAttribut("userName");, F  V: B2 I% x$ s3 u8 `. @9 K
    第四:parameters对象:用于访问HTTP的请求参数,例如#parameters.userName或者#parameters['userName'],相当于调用request.getParameter("username");
    ; T! x. D: W$ Z7 D" s第五:attr对象:用于按page->request->session->application顺序访问其属性.
    5 R* r7 y3 W1 v/ r+ C4 K , u/ {# N) y2 F  }
    (7).为何使用EL表达式能够访问valueStack中对象的属性:原因是struts2对HttpServletRequest做了进一步的封装,简单代码如下1 H- D0 r3 O; ~5 }4 I& \- I' U
    public class StrutsRequestWrapper extends HttpServletRequestWrapper{
    % T+ d. _% ~2 f3 X& O# `% O& |: |publicStrutsRequestWrapper(HttpServletRequest req){
    5 E+ j, G( M2 [2 f! p+ ^super(req);: \  _1 I. L( L$ t) m
    }( ^; E: v# |9 I0 Q
    public Object getAttribut(String s){; u3 ]1 d6 r; A
    ActionContext ctx = ActionContext.getContext();7 x* @2 G! s& v. l; h) M; ^
    Object attribute = super.getAttribute(s)//先从request范围获取属性值
    # t' l2 ?1 S. R# lif(ctx !=null){. Q- n" p2 S) ^8 L3 E
    if(attribute==null){+ e1 e& d+ u+ I9 s
    ....
    , V" X8 y. t1 d! c; V: C& A+ dValueStack stack =ctx.getValueStack();
    5 L$ `7 y% s3 r- Eattribute=stack.findValue(s);//寻找规则就是前面的寻找规则  l; c& t- d. a
    ....1 d6 U' B5 h- w" G
    }" Y2 W/ I1 T6 c' }
    }
    4 L+ }5 f! ?+ L7 r+ T( t6 }0 A# greturn attribute1 ^+ l) N( U2 W9 r! C# a1 Y
    }8 x% ^2 c7 `1 F. Z. k' d
    }1 J' V2 u/ F; I- @
    EL表达式只能访问ValueStack对象中的内容- o( L+ {/ s) ^% R/ ]2 G
    " u7 q0 w+ t2 \
    (8).采用OGNL表达式创建List/Map集合对象,如果需要一个集合元素的时候(例如List对象或者Map对象),可以使用OGNL中同集合相关的表达式,使用如下代码直接生成一个List对象:
    + O* z4 L) F: h. G7 d1 ?<s:set name="list"value="{'zhangming','xiaoi','liming'}"/>把当前迭代的对象放在值栈的栈顶: }$ ~& {! w5 I2 _- n6 ?
    <s:iterator value="#list">  X$ `0 C! ^+ d% X1 `
    <s:property/><br>; K3 k+ N' U, I( }. F; |3 Q
    </s:iterator>
    + m3 b4 v5 q6 r5 P+ v. ZSet标签用于将某个值放入指定范围
    7 y' r7 Q/ b6 d! q# oscope:指定变量被放置的范围,该属性可以接受application,session,request,page或action.如果没有设置该属性,则默认放置在OGNL Context中.' ^' N1 j3 p- d
    value:赋给变量的值,如果没有设置该属性,则将ValueStack栈顶的值赋给变量  ]2 \3 r- j$ c: H# ^/ G. H) C0 J
    生成一个Map对象:  U- m% |9 L; s: X" n6 y# G
    <s:set name="foobar"value="#{'foo1':'bar1','foo2':'bar2'}"/>
    7 o* ^5 y& J9 z, Z5 S<s:iteratorvalue="#foobar">//迭代标签,把当前迭代的对象放在值栈的栈顶(entry对象),foobar是Map对象和request等对象是同等地位,访问时需要使用'#'
    1 \' q  b  ?% m$ q. M2 |<s:property value="key"/>=<s:propertyvalue="value"/><br>
    : S4 @1 _% K# S! g( H</s:iterator>
    ; o. @) e. Y1 M1 ?. h数字不用任何符号,字符串使用单引号('),对于Map采用的是maps.entrySet()这个方式进行迭代的.
    7 H# k, B4 _9 d5 z/ k$ K6 R! F1 w
    % [& e3 T$ X, Y' p. G(9).property标签用于输出指定值:& l4 g2 a0 y7 s- s7 q6 D/ a
    <s:set name="name"value="kk"/>
    ; L+ I- f( ^& @( R- f" J<s:property value="#name"/>  }2 [8 g3 k8 ^5 j0 j. ^
    default:可选属性,如果需要输出的属性值为null,则显示该属性指定的值7 b# C1 j! Q4 c, v( a
    escape:可选属性,指定是否格式化HTML代码
    + V4 Y2 C% @$ I* Jvalue:可选属性,指定需要输出的属性值,如果没有指定该属性,则默认输出ValueStack栈顶的值7 j3 |2 o4 Z2 a. |
    id:可选属性,指定该元素的标识; q/ g" g7 e3 }
    8 t! N4 E. S; t  L& J* G) K& ^
    (1)0.对于集合类型,OGNL表达式可以使用in和not in两个元素符号,其中in表达式用来判断某个元素是否在指定的集合对象中,not in判断某个元素是否不在指定集合对象中,如下所示:
    6 f+ E4 y( l1 s/ e) fin表达式:
    $ T; d2 j$ A. ?) B% M8 y0 H<s:if test="'foo' in {'foo','bar'}"># N& R$ y$ P2 @2 b+ P( v9 V! _: m

    % D! K$ d$ P: E" O</s:if>- ~2 @. ~. a7 f8 E/ E: @
    <s:else>; k! [5 h- J5 ]+ \1 R& q# I
    不在. N: y! e  q& K. z" }
    </s:else>) Q$ o* G. g' c% V% T( a
    no in 表达式' f$ Q# c5 ^) n, R& O* H
    <s:if test="'foo' not in {'foo','bar'}">+ h& H8 w* p. ], K
    不在
    ' w3 `( I3 c) S& r* N0 M</s:if>
    3 q% {% t* c+ j5 T1 k0 b<s:else>
    ; c" X/ ?( ?" k% X2 ]% g$ Q* f. @. x6 ^" l+ l/ {, H
    </s:else>, I( H* [+ W& @5 f, f

    2 i& |9 f* l8 H& ^; P! m(11).OGNL表达式的投影功能,除了in和not in之外,OGNL还允许使用某个规则获得集合对象的子集,常用的有以下3个相关操作符:
    " {: `6 ~/ X& O3 }: H* c第一:?:获得所有符号逻辑的元素$ K. k- _2 g$ T2 i; ?
    第二:^:获得符合逻辑的第一个元素
    ) h; V7 Q, G- i. t第三:$:获得符合逻辑的最后一个元素' p& w; e3 C& K  a0 M
    例如代码:( D  l6 V8 ]4 W0 u% N
    <s:iterator value="books.{?#this.price>35}">/ I& R/ w" B5 g# Z4 D: W9 ^
    <s:propertyvalue="title"/>-$<s:propertyvalue="price"/><br>& w# F! x( X9 |) R1 D# n# n+ U
    </s:iterator>
    ! W( \, x) Y  M! D' O在上面代码中,直接在集合后紧跟{}运算符表明用于取出该集合的子集,{}内的表达式用于获取符合条件的元素,this指的是为了从大集合books筛选数据到小集合,需要对大集合books进行迭代,this代表当前迭代的元素,本例的表达式用于获取集合中价格大于35的书集合.
    $ e/ G, f+ m2 B: Bpublic class BookAction extends ActionSupport{
    0 f* \8 G- H7 v+ fprivate List<Book> books;
    0 M2 y" S% |7 c# t& l7 E$ p: C5 z2 l' ` 8 ?  u4 l6 V: w" h1 R' h
    @Override7 M# t2 }) J* R0 [5 w2 k: x. @; n
    public String execute(){9 r8 D$ L$ G$ }% V' I. q2 F
    books = new LinkedList<Book>();' j9 A4 A7 Z6 u
    books.add(new Book("aadfsd","spring",23));" q9 n, j; n+ z
    books.add(newBook("basdfd","ejb3.0",15));6 l2 L) S# g4 j' f
    }* m! K2 k/ `* e( D, ?
    }) B: s* t8 n" |! A4 [9 a: M
    " T- p( `, k$ E# N& U$ ~
    4.result配置的各种视图转发类型; z2 f4 m" Q# P! w
    (1).在struts1中有两种视图转发类型:容器内转发,容器外转发(重定向);
    0 a4 g* [+ \# A; O+ Z; t<actionpath="/control/employee/manage".../>
    " _; ~3 K' m. T6 [<forwardname="add">/index.jsp</forward>
    . ~. k7 Z# i7 ^6 k5 n) h" a8 e<forward name="add"redirect=""/index.jsp</forward>
    ( k% H3 q8 W2 j</action>
    : i# ]. I" U) I: H" K 2 b4 i) ?% `( ?. n# I
    (2).struts2中的视图转发类型:result配置类似于struts1中的froward,但是struts2中提供了多种结果类型,常用的类型有:dispatcher(默认值)、rdierect、redirectAction、plainText;dispatcher对应于struts1中的容器内部请求转发,redirect对应于struts1中的容器外部转发(重定向)
    % S5 M* Y' k6 P, e: k8 \ ' @' C* f3 G/ l) l/ F  \: Y
    (3).<actionname="helloworld"class="cn.itcast.action.HelloWorldAction">
    ) R4 i. o+ @! I9 ]0 @<resultname="success">/WEB-INF/page/hello.jsp</result>8 d" T" j0 r5 y, ], a% I! P+ p7 b
    </action>
    2 I, w& {9 T4 d% w在result中还可以使用${属性名}表达式访问action中的属性,表达式里的属性名对应的action中的属性,如下:<resulttype="redirect">/View.jsp?id=${id}</result>,使用重定向可能需要将Action中的数据属性代入视图页面,这种方式太重要了,很实用.这种表达式叫做ognl表达式,struts1中是没有的,只能将属性值在代码中写死了,不像struts2中的这个表达式,很灵活.当传递的属性是中文时,需要进行URLEncoder.encode("传智播客","UTF-8")编码.+ [' A5 q- F) O$ C
    6 [7 \! C. t' A& y- J0 i  S
    (4).下面是redirectAction结果类型的例子,如果重定向的action中同一个包下:
    7 k" t% [; \; h7 R( U! m<resulttype="redircetAction">helloworld</result>
    $ M  a1 v4 v7 W* O$ w9 N如果重定向的action在别的命名空间下:
    * y  g3 q/ }9 [6 X<resulttype="redirectAction">8 i7 y+ ?1 u" q1 q" j
    <paramname="actionName">helloworld</param>! W5 o7 u0 O! w7 ~# J8 r4 `
    <paramname="namespace">/test</param>" {1 j7 L# H4 n- t: \: X$ H& q
    </result>4 Z5 C* h% l4 E  p$ n8 Z! l9 d
    当添加用户完后,可以回到一个用户列表,此时可以重定向到action
    : Y: _$ P  u0 [/ E2 r
    ' U" V4 h7 O" E8 w7 J4 N( |! W, m(5).plaintext显示原始文件内容,例如:当我们需要原样显示JSP文件源代码的时候,我们可以使用此类型
    : |9 Z+ L# p; b& r* X! ^<result name="source"type="plainText">
      l8 G7 u: O% |' b3 e' H<paramname="location">/xxx.jsp</param>. N0 m! i% x, ?& f+ o7 ^
    <paramname="charSet">UTF-8</param><!--指定读取文件的编码-->4 B9 g% X0 t/ u" v
    </result>
    # z) P. ]  c  r1 d$ n. [+ F在Eclipse中jsp是用UTF-8编码存放的,当读取jsp的内容时,是用本地字符编码的,可能出现乱码,所以要设置读取文件的编码集.+ C+ J3 u' V) }4 Q& `

    , R* X  c. Q  ~. \# |(6).浏览器重定向的JSP不能放在web-inf目录中,而请求转发的JSP可以放在web-inf目录中/ @: d/ ~2 N" i+ {. B

    ) G. S' F9 c& `- @: L( ?. g(7).struts2中的全局视图:
    ! Z9 a0 m4 c2 Q8 f<global-results>- u) m1 h$ K9 w7 `6 P$ N2 P
    <resultname="index.jsp"></result>" j$ j" Y2 d: G/ ]6 X3 \
    <global-results>
    " C" H, j# a: r& ]7 I和struts1中的全局视图是很相似的
    7 R7 j+ J! D( j  l6 c' F我们可以定义一个包,然后将全局视图的配置放到这个包中! J7 f: p0 C5 C

    ! H! W) ^. z4 ^5.struts2常用标签
    9 x( ^9 |- v" |; J0 _! f( e(1).property标签用于输出指定值:
    * K# Z5 d' ^8 [, m/ ]<s:setname="name" value="kk"/>% F" p* a( Z& E
    <s:propertyvalue ="#name"/>8 C& a$ n& N3 L2 ?7 g
    default:可选属性,如果需要输出的属性值为null,则显示该属性指定的值( ^" Y7 [, v# s% s; H
    escape:可选属性,指定是否格式化HTML代码
      x% f. I. I2 s, W4 ]' [value:可选属性,指定需要输出的属性值,如果没有指定该属性,则默认输出ValueStack栈顶的值9 o' q4 |# u7 Q+ R) j
    id:可选属性,指定该元素的标识% ^1 ~0 \4 S: R  X7 n

    8 D$ [, }% M/ l(2).iterator标签用于对集合进行迭代,这里的集合包含List、Set和数组
    4 b+ p% b. U& e( \<s:set name="list"value="{'aa','bb','cc'}"/>: b" X: q6 i& m+ v7 m) I& f# Z
    <s:iterator value="#list"status="st">
    # h: |8 ]9 N- B! O) q( V3 k<font color=<s:iftest="#st.odd">red</s:if><s:else>blue</s:else>>9 n" R! p8 @3 {/ R
    <s:property/></font><br>; a+ v- ?5 J0 M1 n
    </s:iterator>7 Y8 Y0 t. }, L& V+ a& e1 m
    value:可选属性,指定被迭代的集合,如果没有设置该属性,则使用ValueStack栈顶的集合,
    / m# y6 X1 G1 k  G+ Cid:可选属性,指定集合里元素的id(已被标注为过时)9 _. f- Q1 E9 t) Q' k
    status:可选属性,该属性指定迭代时的iteratorStatus实例,该实例包含如下几个方法:
    , \: ?8 k. _1 p& H( V6 Uint getCount(),返回当前迭代了几个元素6 ~6 M5 S/ s; f! b' p( t: S/ I$ p% q
    int getIndex(),返回当前迭代元素的索引
    # ?$ w4 O. o. J- V& l+ Nboolean isEven(),返回当前被迭代元素的索引是否为偶数
    ( m1 E, m  Q( ]  Yboolean isOdd(),返回当前被迭代元素的索引是否是奇数. T( l6 t3 F5 S% O/ K- y  f
    boolean isFirst(),返回当前被迭代元素是否是第一个元素3 D0 y( E, I: Y
    boolean isLast(),返回当前被迭代元素是否是最后一个元素
    - X; V- }, `% Y- [9 J
    : X: h4 M/ M2 }- ^2 e(3).<s:setname="age" value="21" scope="request"/>1 B( L3 {) ^3 N5 ~, V- K$ c
    <s:iftest="#request.age==23">
    $ A" _# h) B. c) F23- Q  q" p& b4 j: F* U  V$ J
    </s:if>
    : X8 ~9 A0 n0 |' r<s:elseif test ="#age==21">: E4 c" G5 D( j5 z$ A
    21; L+ H% I4 h2 `" e- I) o6 U- U
    </s:elseif>
    5 L. q! E$ |- ?  _* [* e<s:else>. R. r# Q6 ~" Q4 N9 D9 x
    都不等
    ' e0 u* [) m+ N: F* z</s:else>
    5 R& R5 L& x6 O4 F$ m. [ # E, \6 F; `1 G' X7 D$ y: Z5 A) D
    (4).url标签:6 U  N4 ~& z) b
    <s:url action="helloworld_add"namespace="/test">
    1 H8 e( g$ J# p# c: S<s:param name="personid"value="3"/>
    $ z5 G5 G5 ]( \" K' P; n</s:url>3 W( \3 Z$ N: U
    生成类似如下路径:
    ) a; c3 @/ G, l; q) y2 ?+ G/struts/test/helloworld_add.action?persionid=36 Y( B6 S: U7 j- a0 R
    8 E  I+ w% J: h8 P9 ~7 n
    当标签的属性值作为字符串类型处理时,"%"符号的用途是计算OGNL表达式的值
    ' c  l9 q) g( d% f0 p# f0 Z1 f<s:set name="myurl"value='"http://www.foshanshop.net"'/>1 n" J8 W- a( Z- ^* V" D
    <s:url value="#myurl"/>( v% T) `0 G& S+ @4 s. \8 ?
    <s:url value="%{#myurl}"/>
    + e% A, P' k6 a. ?! a7 E0 T输出结果:( L2 s& K) b/ F$ c( ]
    #myurl2 J8 g! H; R. H. Q1 F- R+ q
    http://www.foshanshop.net! }' E* U. s( t

    ; W* v. Z0 U+ Y. B/ O$ Q$ x(5).表单标签checkboxlist复选框
    - R5 h+ H6 N, ~如果集合为list( ?' \' k" T$ V6 r$ g
    <s:checkboxlist name="list"list={'Java','Net','RoR','PHP'}" value="{'Java','Net'}"/>
    ' A8 B8 o3 n. {5 h9 f+ h3 K<input type="checkbox"name="list" value="Java"checked="checked"/><label>Java</label>! u# W, b2 q& Q3 f. c+ c
    <input type="checkbox"name="list" value="Net" checked="checked"/><label>Net</label>
    $ B% s4 j. O6 ?" l' e<input type="checkbox"name="list" value="RoR" ><label>Java</label>
    ( J2 ^0 C& j1 q7 _/ \+ n0 J<input type="checkbox"name="list" value="Java"/><label>Java</label>
    + x- G/ N2 u, i% ^) @! Z# K/ o如果集合为MAP5 k; ]8 j: f  Q' P1 h
    <s:checkboxlist name="map"list="#{1:'aa',2:'bb'}" listKey="key"listValue="value" value="{1}"/>
    * i! i6 B- `! ~0 X# g生成如下html代码:/ c9 K  J, h! s9 H: |( ~; U0 u, S8 Y3 x
    <input type="checkbox"name="map" value="1"checked="checked"/><label>aa</label>
      r% }) @8 z/ u/ I* X' b! K7 c- n$ H<input type="checkbox"name="map" value="2" /><label>bb</label>- O! h0 I5 C( \* G" ?& U0 X" a
    当然集合里面存放的也可以是对象类型8 ]1 t& r6 Y7 O( M' X- x! ]9 \3 `

    2 I  ?' ^) W1 ]' z3 B(6).单选框3 m7 v) l* e2 K( g9 x- G( Q
    <s:radio name="beans"list="#request_person" listKey="personid"listValue="name"/>8 S* w7 a- Y9 i

    & m# f& O8 W/ X6.struts2的处理流程与Action的管理方式; K3 w" T& n/ q# h. H; ^
    (1).用户请求->(查看web.xml文件)StrutsPrepareAndExecuteFilter->Interceptor(struts2内置的一些拦截器或用户自定义拦截器)->Action(用户编写的Action类,类似Struts1中的action,针对每一次请求,都创建一个Action)->Result(类似struts1中的forward)->Jsp/html(响应)
    , |! U5 G" L. m. O. |1 } 9 i4 U0 K2 b6 z$ ]0 v+ l
    (2).StrutsPrepareAndExecuteFilter是struts2框架的核心控制器,它负责拦截由<url-pattern>/"</url-pattern>指定的所有用户请求,当用户请求到达时,该fileter会过滤用户的请求,默认情况下,如果用户请求的路径不带后缀或者后缀以.action结尾,这时请求将被转入到struts2框架处理,否则struts2框架将略过该请求的处理,当请求转入struts2框架处理时会先经过一系列的拦截器,然后再到Action,与struts1不同,struts2对用户的每一次请求都会创建一个Action,所以struts2中的action是线程安全的.( P/ C2 V/ g: ~3 h  ?5 R

    6 a! z# t1 d: r7 G) q, F7.XML配置方式实现对action的所有方法进行校验
    " O8 U; N1 @( Z0 b" R8 Z(1).基于XML配置方式实现对action的所有方法进行输入校验:
    5 z/ N8 L! J7 d- o5 V使用基于XML配置方式实现输入校验时,Action也需要继承ActionSupport,并且提供校验文件,校验文件和action类放在同一个包下,文件的取名格式为:ActionClassName-validation.xml,其中,ActionClassName为action的简单类名,-validation为固定写法,如果Action类为cn.itcast.UserAction,那么该文件的取名应为:UserAction-validation.xml,下面是校验文件的模板:& e5 w# j3 c5 I
    <validators>. f6 i6 w' x' t) e& I  Y+ P1 I
    <field name="username">1 E: H4 F0 w9 R0 R) m
    <field-validatortype="requiredstring">
    & |4 j- O: J- {+ @" u3 B+ g$ r9 k4 u<paramname="trim">true</param>
    8 W9 c6 g4 i; ?, |5 k, N<message>用户名不能为空</message>- H8 j' m, M; q; b
    </field-validator>
    4 |( b3 M) r4 a7 {- X, T5 x</field>/ k6 H  }8 s, k1 O6 U
    </validators>, ?- k, e  O+ b9 A: b( O+ O; x
    <field>指定action中要校验的属性,<field-validator>指定校验器,上面指定的校验器requirestring是由系统提供的,系统提供了能满足大部分验证需求的校验器,这些校验器的定义可以在xwork-2.x.jar中的com.opensymphony.xwork2.validator.validators下的default.xml中找到,<message>为校验失败后的提示信息,如果需要国际化,可以为message指定key属性,key的值为资源文件中的key,在这个校验文件中,对action中字符串类型的username属性进行验证,首先要求调用trim()方法去掉空格,然后判断用户名是否为空.8 Q7 P" x# N* n+ F# d

    2 u4 f. s/ E$ D2 B: R(2).struts2提供的校验器列表:- l% ~/ {/ F7 C
    required(必填校验器,要求field的值不能为Null)
    , ^3 S* a$ G. H! Zrequiredstring(必填字符串校验器,要求field的值不能为null,并且长度大于0,默认情况下会对字符串取钱后空格)* `& Z& M3 O& X' ~: u  i6 _) B
    stringlength(字符串长度校验器,要求field的值必须在指定的范围内,否则校验失败,minLength参数指定最小长度,maxLength参数指定最大长度,trim参数指定校验field之前是否去除字符串前后的空格)2 N' r  p. N) b1 f5 K2 D
    regex(正则表达式校验器,检查被校验的field是否匹配一个正则表达式,expression参数指定正则表达式,caseSensitive参数指定进行正则表达式匹配时,是否区分大小写,默认值为true)
    # O8 G! L/ |0 h8 E5 Aint(整数校验器,要求field的整数值必须在指定范围内,min指定最小值,max指定最大值)
    0 @- d# Q  f% g7 P# ~$ K- mdouble(双精度浮点数校验器,要求field的双精度浮点数必须在指定范围内,min指定最小值,max指定最大值)
    5 b3 I6 |4 U$ lfieldexpression(字段OGNL表达式校验器,要求field满足一个OGNL表达式,expression参数指定OGNL表达式,该逻辑表达式基于ValueStack进行求值,返回true时校验通过,否则不通过)
    1 d+ ]8 A3 i* v" N- J! u0 Jemail(邮件地址校验器,要求如果field的值非空,则必须是合法的邮件地址)7 Y# ]  r' K2 f6 }2 i9 e
    URL(网址校验器,要求如果field的值非空,则必须是合法的URL地址)
    3 n8 W, f1 D# z! I+ p  jdate(日期校验器,要求field的日期值必须在指定范围内,min指定最小值,max指定最大值)
    6 Q4 [6 \1 |7 ^4 i0 k; d8 econversion(转换校验器,指定在类型转换失败时,提示的错误信息)
    * i! u6 q# S7 r4 ^1 Vvisitor(用于校验action中的符合属性,它指定一个校验文件用于校验符合属性中的属性)3 e' n& `7 q( U) ~
    expression(OGNL表达式校验器,expression参数指定ognl表达式,该逻辑表达式基于ValueStack进行求值,返回true时校验通过,否则不通过,该校验器不可用在字段校验器风格的配置中); p* k! _# g1 _" `* n$ ]- w" E
    " b. v$ j& A& I
    (3).![CDATA[文本内容]]:文本内容不会被解析,只会原封不动的当做文本处理
    1 |' |. ]% `* y - U! b. M- T* z1 a
    (4).编写校验文件时,不能出现帮助信息:2 Q% \6 |% ^& e: L# u" L
    在编写ActionClassName-validation.xml校验文件时,如果出现不了帮助信息,可以按照下面方式解决:( _$ d% e6 {. U5 S
    windows->preferences->myeclipse->filesand editors->xml->xmlcatalog:点击add,在出现的窗口中的location中选"file system"然后再xwork-2.1.2戒烟目录的src\java目录中选择xwork-validator-1.0.3.dtd,回到设置窗口的时候,不要急着关闭窗口,应把窗口中的Key Type改为URI,Key改为http://www.opensymphoney.com/xwork/xwork-validaor-1.0.3.dtd
    - n5 r8 Z$ h1 z" [. Z" B" q% R
    ; z4 b* Q" ]/ b. z/ g8.XML配置方式实现对action的指定方法校验
    4 O" x; {$ z1 X/ C9 p' j' \: o(1).基于XML配置方式对指定action方法实现输入校验:7 ?8 \7 r6 W" d8 k( c
    当校验文件的取名为ActionClassName-validation.xml时,会对action中的所有处理方法实施输入校验,如果你只需要对action中的某个action方法实施校验,那么校验文件的取名应为:ActionClassName-ActionName-validation.xml,其中ActionName为struts.xml中的action的名称,例如:在实际应用中,常有以下配置:
    8 ^% G* c* n* O# P5 V. D<action name="user_*"class="cn.itcast.action.UserAction" method="{1}">
    8 _" s1 C9 m5 b  D( W<resultname="success">/WEB-INF/page/message.jsp</result>
    % h2 l/ m- c6 Z: ?6 K) [<resultname="input">/WEB-INF/page/addUser.jsp</result>9 `( h2 [# g; d4 c& B8 ]4 t& u
    </action>3 s0 T6 T, t6 L. x( ~& X  M2 N
    UserAction中有以下两个处理方法:
    / Q# Y5 p( K2 d2 m, L2 t  |* s$ opublic String add() throws Exception{
    , q1 z, U! E% c% d  v  H; j + H# w$ }0 j- N: {7 V7 H  s( M7 E
    }. I6 m4 p! x1 [5 m  i# g( @  x
    public String update() throws Exception{/ D" v1 S( }" @' ], W) ]5 W; m9 S

    ) v0 P- q7 v# M  q# [}  V7 o. u  W  t. ^$ ~9 K- U% S
    要对add()方法实施验证,校验文件的取名为:UserAction-user_add-validation.xml
    9 Z  n8 s4 i  N. t' o8 n  W要对update()方法实施验证,校验文件的取名为:UserAction-user_update-validation.xml
    & h, e6 ?. y  _  h# t0 ^! }9 G+ x
    # W: {+ `+ B  p1 X(2).基于XML校验的一些特点:
    ' u3 h  m6 _' ^* }/ t7 X" B! F+ r当为某个action提供了ActionClassName-validation.xml和ActionClassName-ActionName-validation.xml两种规则的校验文件时,系统按下面顺序寻找校验文件:
    3 O* ^) Z3 v+ jActionClassName-validation.xml3 l7 K3 H$ e) z7 ]: Q: J+ n
    ActionClassName-ActionName-validation.xml
    7 w' d: T, Q% t系统寻找到第一个校验文件时还会继续搜索后面的校验文件,当搜索到所有校验文件时,会把校验文件里的所有校验规则汇总,然后全部应用于action方法的校验,如果两个校验文件中指定的校验规则冲突,则只使用后面文件中的规则。0 G7 I. l2 O5 [# i3 Y- A9 g
    当action继承了另一个action,父类action的校验文件会先被搜索到% |# O9 }3 I5 ^- O6 `' W
    假设UserAction继承BaseAction
    3 ]3 v  N/ l; m% }/ A<actionname="user" class="cn.itcast.action.UserAction"method="{1}">: S: A. M$ {* F; _% m  P$ p
    </action>8 B# Z4 Y# G3 A. t
    访问上面的action,系统先搜索父类的校验文件:BaseAction-validation.xml,BaseAction-user-validation.xml,接着搜索子类的校验文件:UserAction-validation.xml,UserAction-user-validation.xml,应用于上面action的校验规则为这四个文件的总和
    5 ]" i1 J" H* z* D$ L( x 3 g& U7 X! W! M3 l2 ^. Y: D
    9.动态方法调用和使用通配符定义action) A. ~2 M, i3 X! V& r+ [
    (1).在struts1中实现方法的动态调用:& ?5 T4 U! |5 g! h! t4 J1 \
    <actionpath="/control/employee/manage" type="....DispatchAction"parameter="method"/>" G% g; r: u& R3 y: g* I
    </action>
    % H% g8 O, F- O/ j6 ~9 h2 s/control/employee/manage?method=addUI0 t" x! T- H* l* n/ h
    但是Action必须继承DispatchAction4 K7 x' O4 h( O( q- O# y

    5 L/ q9 s, ~. U% S(2).在struts2中有两种方式:; F! l' u9 W/ V
    第一种:(struts2.1版本后就不建议使用了)是动态方法调用:如果Action中存在多个方法时,我们可以使用!+方法名调用指定方法,如下:7 b* u' P4 h  L- V: ^1 D& ]% ~  R8 R% Y
    public class HelloWorldAction{3 M" k& C5 K  {) [: O1 f/ O
    private String message;
    ) W9 G* }$ k$ y....
    . Q" W- Z0 ^2 L  F! Rpublic String execute()throws Exception{
    " G9 ~$ T1 B: \; r1 N9 |' g this.message="我的第一个struts2应用";
    : R# B0 v1 o9 Y* k' u}, F- S# M- j+ ]( K  A& ~4 |" x7 c: D, c
    public String other() throws Exception{8 k* e' J3 \2 `7 y1 p+ t
    this.message="第二个方法";$ H; r  j. t/ M2 r5 p$ I  X
    return "success";
    , q. j+ B2 L8 E) W) e* i1 j}
    4 B0 w4 \6 _, b7 b- w/ s% n0 b}
    7 |8 v; v/ ~! o: @& n* `+ O& T假设访问上面的action的URL路径为:/struts/test/helloworld.action,要访问action的other方法,我们就可以这样调用:/struts/test/helloworld!other.action,如果不想使用动态方法调用,我们可以通过常量struts.enable.DynamicMethodInvocation关闭动态方法调用:
    8 ]: Z1 b# a2 t7 K<constantname="struts.enable.DynamicMethodInvocation"value="false"/>
    ( k% y- ^4 d+ z. U; {( q第二种:使用通配符定义action(推荐使用的)
    4 G9 N8 U( A0 Z; J% m% ?4 t<package name="itcast"namespace="/test" extends="struts-default">- B: \( C: D) W9 I7 g
    <action name="helloworld_*"class="cn.itcast.action.HelloWorldAction" method="{1}>$ f4 c1 E1 }$ L8 Z" t6 X5 M% \/ F# }
    <resultname="success">/WEB-INF/page/hello.jsp</result>; |( O, _7 z2 H
    </action>  Z; M$ [7 O. h  P+ f
    </package>
    $ v1 I$ ~8 D# b# E% y( L) m" q+ t- Fpublic class HelloWorldAction{; i  _0 F; K0 p8 M* S. _
    private String message;" x9 a# `  ^; ]
    ....
    / h5 x# i9 O* @' p, `1 apublic String execute()throws Exception{
    5 v* y! [4 U( O' K this.message="我的第一个struts2应用";
    " b# l6 _2 b9 I% G2 A( }}
    " Z$ f( X/ B7 s* o* H" F( @' p, vpublic String other() throws Exception{
    5 a$ O1 x* C- z, y( g8 d& c! Bthis.message="第二个方法";9 h# T, }+ a# w1 w
    return "success";
    , }( I: `; s, C* ~1 X}3 j# A" @+ R! ]2 X
    }
    * x/ Q8 q7 u+ s  M$ o" _$ Y9 L要访问other()方法,可以通过这样的URL访问:/test/helloworld_other.action) {, ~* p7 k/ |1 Q. _  T7 S- e* ^6 J
    name="helloworld_*"后可根据多个*,method={1},'1'表示匹配*的位置5 Y) \2 K% E5 I2 V1 S) o6 W
    name="helloworld_*_*",method={2}:要访问other()方法,可以通过这样的URL访问:/test/helloworld_xxx_other.action
    ) V7 r1 \+ k- c4 z
    , q% F# P  ~1 R- |  x7 f3 N6 F) l10.对action指定的方法进行校验
    / i2 I1 n. W) y4 {4 s) k手工编写代码实现对action指定方法输入校验:' d3 Q+ K0 W5 {
    通过validateXxx()方法实现,validateXxx()只会校验action中方法名为Xxx的方法,其中Xxx的第一个字母要大写,当某个数据校验失败时,我们应该调用addFieldError()方法往系统的fieldErrors添加校验失败信息,(为了使用addFieldError()方法,action可以继承ActionSupport(),如果系统的fieldErrors包含失败信息,struts2会将请求转发到名为input的result,在input视图中可以通过<s:fielderror/>显示失败信息.
    $ j' U8 p1 F1 @0 E$ UvalidateXxx()方法使用例子:
    ! j+ J3 K) Q7 A: L% l  hpublic Stringadd() throws Exception{return "success";}% \. K# G4 e% `8 y* ?* [
    public voidvalidateAdd() {
    5 `  {7 _$ r* t6 \4 k- Yif(username==null&&"".equals(username.trim()))this.addFieldError("username","用户名不能为空");# a- N* g/ Q) ~. t
    }
    4 }- @# r* x1 B) ?5 q# D0 ~! v验证失败后,请求转发至input视图:<resultname="input">/WEB-INF/page/addUser.jsp</result>* b2 w  x+ o& _$ M  t) s+ ~
    在addUser.jsp页面中使用<s:fielderror/>显示失败信息5 K+ t: P; ?9 i9 _4 S1 j

    ! X5 L9 e$ R' q8 d/ P1 F11.对Action中所有方法进行输入校验
    0 q. Y5 V* Q) C! |! a+ ^! \5 ?(1).在struts2中,我们可以实现对action的所有方法进行校验或者对action的指定方法进行校验
    ' _" W) C  ^. I0 n2 j0 A   w2 ~3 {& I' K2 [# `0 g8 t2 j. H8 y
    (2).对于输入校验struts2提供了两种实现方法:一种是采用手工编写代码实现,另一种是基于XML配置方式实现
    % D, k) u- G0 g4 S$ w 7 l. Z+ J. H. I/ c) y
    (3).手工编写代码实现对action中所有方法输入校验:通过重写validate()方法实现,validate()方法会校验action中所有与execute方法签名相同的方法,当某个数据校验失败时,我们应该调用addFieldError()方法往系统的fieldErrors添加校验失败信息(为了使用addFieldError()方法,action可以继承ActionSupport(),如果系统的fieldErrors包含失败信息,struts2会将请求转发到名为input的result,在input视图中可以通过<s:fielderror/>显示失败信息.
    0 ~- P2 ?: `$ n. Tvalidate()使用例子:
    ' ~% p0 F. g% m7 H9 ]public voidvalidate(){
    % Z  F# v  S8 W  @if(this.mobile==null||"".equals(this.mobile.trim())){this.addFieldError("username","手机号不能为空")}else{if(Pattern.compile("^1[358]\\d{9}").matcher(this.mobile.trim()).matchers()){this.addFieldError("mobile","手机号的格式不正确");}}
    # [2 ?/ B, T0 f& x}* m1 C4 ~& r) {- E6 ~1 a, [% k% I
    验证失败后,请求转发至input视图:
    ! ^+ M- c; W' L9 P- z3 P0 H. Y. ^7 Q9 J<resultname="input">/WEB-INF/page/addUser.jsp</result>$ u& z0 G0 K2 @& w9 I; m; p! z9 \
    在addUser.jsp页面中使用<s:fielderror/>显示失败信息, Y) _; Q% W. q+ l
    : [# ~2 p3 v% x5 n( T  |
    12.多文件上传: w5 L$ R. _& Z
    (1).多文件上传,就是在一个文件上传的基础上,将属性File变成数组类型File[]类型即可,同时该字段的名称必须要和上传页面中属性name的名称一样.4 J. L3 S! F' x' |! o1 _
    然后进行一次迭代,就可以得到所有的文件
    & D0 e9 ?  [/ S# l( l4 {8 Y   T7 p9 F9 ]: w. J4 _% w
    13.防止表单重复提交% W  J( y+ `& L8 f1 ?3 x$ C
    <s:token>标签防止表单重复提交7 Z2 {' P+ j* t  ^9 H
    第一步:在表单中加入<s:token/>( c. B- u* i* k  M) ?, i  Z2 W
    <s:form action="helloworld_other"method="post" namespace="/test">  {0 E: h* a! z
    <s:textfieldname="person.name"/><s:token/><s:submit/>2 u) d2 @6 H% l- l
    </s:form>
    0 R! Y8 m3 }) X; i- R- g第二步:<action name="helloworld_*"class="cn.itcast.action.HelloWorldAction" method="{1}">
    # k7 [7 S& {; Z5 J<interceptor-refname="defaultStack"/>: B, |8 t2 \0 ^0 r4 a" _
    <interceptor-refname="token"/>
    7 ~3 w% R$ H% M# A7 M1 u<resultname="invalid.token">/WEB-INF/page/message.jsp</result>
    5 d7 a1 g  j0 J' g% ?" i<result>/WEB-INF/page/result.jsp</result>* }8 g3 G4 t  J, N! j3 Z# c3 a
    </action>8 G( Q/ h$ n) _' H) D& A
    以上配置加入了"token"拦截器和"invalid.token"结果,因为"token"拦截器在会话的tlent与请求的token不一致时,将会直接返回"invalid.token"结果8 N% m( i& x2 _( T7 u
    在debug状态,控制台出现下面信息,是因为Action中并没有struts.token和struts.token.name属性,我们不用关心这个错误
    4 O& {2 h) ~1 S' k1 S使用了<s:form/>标签可以不指定action的上下文标签路径,可以通过命名空间实现.和前面的原理是一样的,在路径后面添加上sessionid号,只是这步操作不需要我们自己得到sessionid号,struts帮我们操作.
      g2 \6 o3 A. e/ i在值栈中的对象,访问无需添加'#') f5 v+ M( ^6 m# A, f* @0 S9 i
    ; A6 M) o( @! u1 B$ e
    14.访问或添加几个属性3 s" Z1 e$ E7 M1 _9 {) |; O
    (1).访问或添加request/session/application属性,在struts2中的Action中的execute方法中没有Servlet api(没有响应的参数);
    2 s/ _& S; U; x: M& Z# E4 opublic String scope() throws Exception{
    9 `! J7 ~4 \* m! Y1 P: pActionContextctx=ActionContext.getContext();
    ) Y$ s/ R7 m# u3 a/ N/ J% Tctx.getApplication().put("app","应用范围");
    5 w: Y' u) L0 x( H, Zctx.getSession().put("ses","session范围");  A+ Y( D8 ^; L- _! E5 x
    ctx.put("request","request范围");2 h% p: |/ P! p' J3 R# y5 E  E6 `
    return "scope";9 ~1 X$ c$ x+ }7 Q) V
    }# E1 P$ Y1 a- @! B1 k
    0 q4 o+ M9 A! J
    <body># Y6 S- l  V% X) K' N
    ${applicationScope.app}
    ! l; |+ A% J3 M0 W( e${sessionScope.ses}
    , X8 ?5 b* G0 ^# a9 n0 z${requestScope.req}) H/ L2 k2 @, H. i2 F1 S
    </body>+ U2 i# b& Z9 q0 k
    6 t: c/ c) x" ~+ m! S0 ^0 V
    (2).获取HttpServletRequest/HttpSession/ServletContext/HttpServletResponse对象:
    % h4 M" ?4 y+ C1 ]方法一:通过ServletActionContext类直接获取
    2 R1 v6 H7 ^% e  N( p/ t1 cpublic String rsa() throws Exception{
    & [" R& {! }# C3 b) I% KHttpServletRequest request =ServletActionContext.getRequest();
    & P) U) H$ g5 bServletContextservletContext=ServletContext.getServletContext();0 h6 y9 f& N0 G# A. v( @
    request.getSession();
    # h- R/ i4 h  a4 P0 _# R. PHttpServletResponseresponse=ServletActionContext.getResponse();
    * g$ B7 ~$ E  I9 `7 c) c- h6 Qreturn "scope";: `1 V; N- o2 I7 j, g
    }. D( }$ m1 P7 `4 r& Z( v/ G5 r
    方法二:实现指定接口,由struts框架运行时注入:
    4 l# n: `, a$ }/ R  `9 F" c- A7 Kpublic class HelloWorldAction implementsServletRequestAware,ServletResponseAware,ServletContextAware{  _7 J$ D. y. d3 h; V# F2 m! @3 e
    private HttpServletRequest request;
    / V9 y: `' f6 o3 }/ d# h  S! E2 ?5 Sprivate ServletContext servletContext;
    4 A# ^/ e3 S, R& e$ b6 C  j7 @private HttpServletResponse response;
    ! q' x5 t7 p! R' Wpublic voidsetServletRequest(HttpServletRequest req){4 V, ^4 |8 B% }- K6 L; P$ b3 @  s' o
    this.request.req;6 K  E' D* J% F1 e# {: R  R
    }, Q. e, h; g% e: x6 U* _
    public voidsetServletResponse(HttpServletResponse res){: m# a8 g4 ]; T! g- h, J
    this.response=res;3 a8 F9 W. ?$ H) L5 I: t
    }
    4 F5 q6 z- D0 F2 s: npublic voidsetServletContext(ServletContext ser){
    1 d6 A0 h# {1 zthis.servletContext=ser;! ?9 p1 a$ E8 d/ D) x$ F$ y
    }
    / |4 z) O& X. p/ a9 E, e2 S注意1和2的不同,一个不需要得到对象,一个需要得到对象,所以要区分两个的应用场景
    , x. k% ^/ K3 ]# Y. o}
      g2 J! J( s( G6 u' s/ [8 l 2 j7 ~" T" `0 c8 _$ C: j/ `
    15.解决struts配置文件无提示问题/ v: ?6 l$ L% l3 X& z& h+ M$ ^
    找到struts2.0.dtd文件即可,windows->preferences->MyEclipse->XML->XMLCatalog,点击添加strut2.dtd& g3 H% R7 X) k
    ! e) D+ w5 ~  {0 Y
    16.介绍struts2及struts2开发环境的搭建: Z8 x, W' g) j8 u/ @
    (1).struts2是在webwork2基础发展而来的,和struts一样,struts2也属于MVC框架,不过有一点大家需要注意的是:尽管struts2和struts1在名字上的差别不是很大,但是struts2和struts1在代码编写分割上几乎是不一样的,那么既然有了struts1,为何还要推出struts2,主要是因为有一下有点:
    8 v$ m1 D  e8 P  Y第一:在软件设计上struts2没有像struts1那样跟Servlet api和struts api有着紧密的耦合,struts2的应用可以不依赖于servlet api 和struts api,struts2的这种设计属于无侵入式的设计,而struts1却属于侵入式设计,因为其的
    ; D+ T3 J) K6 qexecute()方法中的参数为ActionMapping,ActionForm,HttpServletRequest,HttpServletResponse/ a9 ^+ O6 R! D2 `. S: i& I$ p
    第二:struts2提供了拦截器,利用拦截器可以进行AOP编程,实现如权限拦截等功能( ]( o; _$ Z, I
    第三:struts2提供了类型转换器,我们可以把特殊的请求参数转换成需要的类型,在struts1中,如果我们要实现同样的功能,就必须向struts1的底层实现BeanUtils注册类型转换器才行! V) T# J: q# l6 a4 ?+ @8 y
    第四:struts2提供支持多种表现层技术,如:JSP,freeMarker,Velocity等
    , m5 e- @3 j8 G第五:struts2的输入校验可以对指定方法进行校验,解决了struts1长久之痛,struts1中的validate方法对所有的方法进行校验
    + z- e7 j: A- g6 v4 o: m第六:提供了全局范围、包范围、和Action范围的国际化资源文件管理实现.% b) L" O$ o9 j; \, ?: v- C
    ! `& P! v& c( a1 w2 k
    (2).搭建struts2的环境和struts1是相同的,第一步导入相关包,第二步建立struts2的配置文件,第三步在web.xml中注册struts2框架的配置$ m' Q0 F* v& R
    0 {% g) S3 d0 a- k
    (3).所需的包:struts2-core-2.x.x.jar,xwork-2.x.x.jar(webwork的核心架包),ognl-2.6.x.jar
    ! V0 b3 M6 J* [4 x4 P: o
    - n5 h+ u. V2 @: g! P8 E(4).struts2默认的配置文件为struts.xml,该文件需要放在/web-inf/classes目录下, z) C  {& b6 y( p( |1 a
    0 ?/ t( P! f! H: S0 k
    (5).在struts1中,struts框架是通过servlet启动的,在struts2中,struts框架是通过Filter启动的,它在web.xml中的配置如下所示:可以参照struts文件夹下的例子中拷贝,在strutsperpareExecuteFilter的init()方法中将会读取类路径下默认的配置文件struts.xml完成初始化操作,注意:struts2读取到struts.xml的内容后,以javabean形式存放在内存中,以后struts2对用户的每次请求处理将使用内存中的数据,而不是每次都读取struts.xml文件* @# W9 R2 Y" W. ]+ m9 x, O

    + G$ h, {- h  l3 l% D(6).自从struts2.1.3以后,下面的FilterDispatcher已经标注为过时了,struts2.1.3后期版本为StrutsPrepareAndExecuteFilter类: W* e& |, z9 Q% I! d0 K
    - _. Z9 M: h+ z$ N
    17.开发第一个应用
      }) A0 \' x* i(1).在struts.xml中的配置:
    5 L' N0 n( H0 o. \# o/ a4 l<package name="itcast"namespace="/test" extends="struts-default">
    . T$ |. d6 s8 j& }1 Q' F, i' t# _$ l <actionname="helloworld" class="cn.itcast.action.HelloWorldAction"method="execute">3 g+ m2 |9 O( t5 Y# S5 @. [
    <resultname="success">/WEB-INF/page/hello.jsp</result>! G' G1 c# m8 V( B5 D$ r. F8 z
    </action>
    , y. N" U2 N. V" m7 \* P  w</package>& q. {+ s3 Y7 n3 {1 G$ k  {+ l
    在struts2框架中使用包来管理Action,包的作用和Java中的类包是非常类似的,它主要用于管理一组业务功能相关的action,在实际应用中,我们应该吧一组业务功能相关的Action放在同一个包下
    $ p( E3 U2 b  n* z配置包时必须指定name属性,该name属性可以任意取名,但必须唯一,它不对应java的类包,如果其他包要继承该包,必须通过该属性进行引用,包的namespace属性用于定义该报的命名空间,命名空间作为访问该包下Action的路径的一部分,如访问上面例子的Action,访问路径为:/test/helloworld.action,namespace属性可以不配置,对本例而言,如果不指定该属性,默认的命名空间为" "(空字符串).当然配置可以减少重复的代码,struts1中的重复代码就可以使用命名空间来解决
    8 _+ _5 O3 f5 r0 E* ]通常每个包都应该继承struts-default包,因为struts2很多核心的功能都是拦截器来实现的,如:从请求中把请求参数封装转到action、文件上传和数据验证等都是通过拦截器实现的,struts-defaul定义了这些拦截器和Result类型,可以这么说:当包继承了struts-default才能使用struts2提供的核心功能,struts-default包是在struts2-core-2.x.x.jar文件中的struts-default.xml中定义,struts-default.xml也是struts2默认配置文件,struts2每次都会自动加载struts-default.xml文件,包还可以通过abstract="true"定义为抽象包,抽象包中不能包含action,可以查看struts-default.xml文件中,就可以看到定义了很多拦截器; O. l0 N% h( y' y* b
    <result></result>和struts1中的forward很相似,定义视图7 a0 E5 k; d9 D/ U% o8 X  B0 T

    ( m; z# K  E+ f0 I: {" n# E' t(2).public Stringexecute(){return 视图的名称;}注意到这个方法和struts1不同,没有参数,返回类型也不同,这就降低了耦合性,非侵入式的编程了.
    3 c4 g* N; }* i* T9 ^
    # z0 n3 K/ r: ^; W, \+ k- Y(3).在jsp中使用el表达式即可${message},message是Action中的一个方法getMessage()方法,而不是根据Action中的成员变量message
    " v  L  r1 n2 N6 F" g( n! H* a1 h / T7 c3 v) p1 c5 ^
    18.配置Action范围国际化资源文件
    9 |& w' C! ^: ^! F5 H3 ](1).我们也可以为某个action单独制定资源文件,方法如下:在Action类所在的路径,放置ActionClassName_language_country.properties资源文件,ActionClassName为Action类的简单名称当查找指定key的消息时,系统会先从ActionClassName_language_country.properties资源文件查找,如果没有找到对应的key,然后沿着当前包往上查找基本名为package的资源文件,一直找到最顶层包,乳沟还没有找到对应的key,最后会从常量struts.custom.i18n.resources指定的资源文件中查找
    * h0 ]1 g% T& v6 \ ( R- }0 N* U$ I/ V# a
    (2).JSP中直接访问某个资源文件" a- t' O& y. m, N# N
    struts2为我们提供了<s:i18n>标签,使用<s:i18n>标签我们可以在类路径下直接从某个资源文件中获取国际化数据,而无需任何配置:
    1 g6 I& }& q1 b+ A<s:i18n name="itcast">
    % b; ]/ G* s6 O<s:text name="welcome"/>
    : H1 O. W# s/ l0 p$ ?" D</s:i18n>
    ' a' c# d" H9 D3 P8 o' s) sitcast为类路径下资源文件的基本名
    ! w1 s. n. o9 R' D3 q  Y* Z: I% a如果要访问的资源文件在类路径的某个包下,可以这样访问:' l' ]) |" y( F+ P, l+ C
    <s:i18nname="cn/itcast/action/package">/ y! k+ O4 ~, {: T
    <s:text name="welcome">: b4 Z& S; v, g9 ?/ Z( @
    <s:param>小张</s:param>( {! I' I# `2 a! W' q
    </s:text>8 c. F5 _) j: j2 o4 b% V
    </s:i18n>$ A2 o5 q# k- m9 t3 R. @
    上面访问cn.itcast.action包下基本名为package的资源文件
    ; P2 B( H) v% _' O' Z1 h7 G
    - S4 T, {+ T  f19.配置包范围的国际化资源文件
    ) l( J7 H7 n' r/ O" W/ n(1).在一个大型应用中,整个应用有大量的内容需要实现国际化,如果我们把国际化的内容都放置在全局资源属性文件中,显然会导致资源文件变得过于庞大、臃肿,不便于维护,这个时候我们可以针对不同模块,使用包范围来组织国际化文件
    3 b5 Y) `0 m$ q* V, Q! P方法如下:在java的包下放置package_language_country.properties资源文件,package为固定写法,处于该包及子包下的action都可以访问该资源,当查找指定key的消息时,系统会先从package资源文件中查找,当找不到对应的key时,才会从常量struts.custom.i18n.resources指定的资源文件中寻找.% i; y6 e5 F* r

    % P. L9 ^  t. m. L* l5 C20.配置国际化全局资源文件、输出国际化信息# \8 ~/ T9 K- o, A9 ~
    (1).准备资源文件,资源文件的命名格式如下:
    7 [: `9 f" i$ c  @6 t9 xbaseName_language_country.properties$ z: T# y9 u/ o) x
    baseName_language.properties
    3 s- R3 ^# B! \/ V2 z# dbaseName.properties
    ! b4 N7 x/ L0 c" ?5 G3 G3 {' v其中baseName是资源文件的基本名,我们可以自定义,但是language和country必须是java支持的语言和国家。如:
    ! _- D& s6 Z; b+ `' S中国大陆:baseName_zh_CN.properties- w) n' }% S0 I& u" N1 X
    美国:baseName_en_US.properties
    ' v+ X' d8 I0 K# W% t7 t
      N  B# I" D1 i% u( _+ G(2).现在为应用添加两个资源文件:
    7 T$ z. e4 {  L9 p' b6 W第一个存放中文:itcast_zh_CN.properties
    , T) a2 ]3 q; c4 S内容为:welcom=欢迎来到传智播客
    & y1 X; @0 V+ M# I+ `5 I第二个存放英语(美国):itcast_en_US.properties( \" ?7 w6 v) C. S& A" T
    内容为:welcome=welcom to itcast8 v8 n5 p, M6 j1 J1 q6 m

    * q' e6 t( r% k$ x(3).对于中文的属性文件,我们编写好后,应该使用JDK提供的native2ascii命令把文件转换为unicode编码的文件,命令的使用方式如下:
    / X) r4 Q4 Z* h0 Gnative2ascii 源文件.properties 目标文件.properties,在MyEclipse6.6版本以及后面的版本会自动转换.
    / _5 s1 s) x  c! I7 f, O+ \4 a( e8 e : X3 n; A7 \4 b' L- _/ T* j. L
    (4).struts2有:全局范围,包范围,action范围的资源文件
    9 Y7 o" }; N5 w2 W# k2 n
    % r6 V/ A  x# S(5).配置全局资源与输出国际化信息:
    $ w, l, w4 t5 s当准备号资源文件之后,我们可以在struts.xml中通过:struts.custom.i18n.resources常量把资源文件定义为全局资源文件,如下:! B: S- h9 C* W1 l2 T+ X0 X8 v  n. g
    <constantname="struts.custom.i18n.resources" vlaue="itcast"/>
      z% b9 Z$ w* h8 D2 ?6 Jitcast为资源文件的基本名
    . z$ Z/ j) Z6 o' h. M4 ]  `后面我们就可以在页面或在action中访问国际化信息:
    ( y5 F, f4 Y0 _% Q在JSP页面中使用<s:text name=""/>标签输出国际化信息:
    9 e4 ~; |! A0 W( x# _) E<s:textname="user"/>,name为资源文件中的key
    & I8 l/ M6 r" Z在Action类中,可以继承ActionSupport,使用getText()方法得到国际化信息,该该方法的第一个参数用于指定资源文件中的key,! b% J1 E& a  _6 x0 I
    在表单标签中,通过key属性指定资源文件中的key,如:$ V) E6 r; L) M
    <s:textfieldname="realname" key="user"/>
    6 c3 x1 w) Z& h8 h" _. a$ [8 _
    7 P9 r# I& C* M" y" h! r21.请求参数接受  M  x9 w  [( W) q& X1 D  e& U
    (1).struts1中是使用ActionForm接受用户的请求参数
    9 H+ V# z1 i) I0 m- I
    * z6 I5 o; E7 O! ^(2).采用基本类型接受请求参数(get/post):- m! T- ]2 |9 w9 O; ^/ ?$ U1 t
    在Action类中定义与请求参数同名的属性,struts2便能自动接受请求参数并赋予给同名属性:请求路径:http://localhost:8080/test/view.action?id=78  A  C7 i; Z2 @- X- K
    public classProductAction{& G3 w$ F% c  l9 P
    private Integerid;* h+ g2 O5 N' i/ N6 l" i0 k
    public voidsetId(Integer id){//struts2通过反射技术调用与请求参数同名的属性的setter方法获取请求参数值
    % e2 |. N7 l9 Y2 vthis.id=id;- l0 v% ?; q5 l7 D* m. L' c* m7 n
    }
    5 {: Q/ s' T' y) i7 |+ @" zpublic IntegergetId(){return id;}
    ' l5 |; h, Z- ~4 s3 d4 F}, C3 k1 N, P2 }- B0 p9 e" Q4 J4 h
    1 }0 [: B; j/ X4 ]
    (3).采用复合类型接受请求参数+ X, ]/ M8 T. Y9 I1 G- A4 f
    请求路径:http://localhost:8080/test/view.action?product_id=78& @$ n2 t) i3 e5 u
    public class ProductAction{% l' Q: u' }6 {6 u5 p' ]
    private Product product;3 @' x+ d, _/ b9 V
    public void setProduct(Product product){htis.product=product;}
    , `/ S8 ~! o" k9 ]public Product getProduct(){returnproduct;}3 \& q$ E5 c  `- {7 M5 @
    }* k* N* [! N: D
    struts2首先通过反射技术调用Product的默认构造器创建product对象,然后再通过反射技术调用product中与请求参数同名的属性的setter方法来获取请求参数值0 g! q1 \: X: f+ u, D" E1 K- N  n

    9 |, }3 q: |4 l! z: o(4).关于struts2.1.6版本中存在一个Bug,及接受到的中文请求参数为乱码(以post方式提交),原因是struts2.1.6在获取并使用了请求参数后才调用HttpServletRequest的setCharacterEncoding()方法进行编码设置,导致应用使用的就是乱码请求参数,这个Bug在struts2.1.8中已经解决,如果你使用的是struts2.1.6,要解决这个问题,你可以这样做:新建一个Filter,把这个Filter放置在Struts2的Filter之前,然后再doFilter()方法中添加以下代码:+ d; S7 Y: G  ~. ]. X/ b
    public void doFilter(..){0 H( O* J& z6 b0 \! G& {' m5 _
    HttpServletRequest req=(HttpServletRequest)request;4 W; d# |/ D! r' I- c. s" W
    req.setCharacterEncoding("UTF-8");( I9 _% P: t/ X- q+ F7 s# `
    filterchain.doFilter(request,response);- W$ e' A9 y3 z: f% P
    }
    ; s' t. B8 l! y% s3 I" y6 B
    ; O, `1 O8 _+ q. y3 ~22.全局类型转换器
    $ w* m1 W- H& E& B* W, l# q& H- r自定义全局类型转换器:将上面的类型转换器注册为全局类型转换器:在WEB-INF/classes下放置xword-conversion.properties文件,在properties文件中的内容为:待转换的类型=类型转换器的全类名
    4 \) N0 P# f9 y7 c6 e, X3 b对于本例而言,xwork-conversion.properties文件中的内容为:$ J; C, [1 i0 j8 T
    java.util.Date=cn.itcast.conversion.DateConverter: Q, F9 s+ z0 v! T! E9 P8 e8 C

    * l% _5 u; _" K# k, a& u23.输出带有占位符的国际化信息2 c" [; }! t3 L9 J
    (1).资源文件中的内容如下:
    ) G# `7 ~6 T4 J8 `( fwelcom={0}欢迎来到传智播客{1}* e( ^$ i& [' q: S
    在jsp页面中输出带占位符的国际化信息( \& w4 x' R! b, y7 Y+ p/ O+ G
    <s:text name="welcom">
    ) x; |1 {. o( t2 W4 U1 L( z  v- Y<s:param><s:propertyvalue="realname"/></s:param>
    1 B; V2 }8 V3 K& D; g; h<s:param>学习</s:param>
    # A" F: E" N9 y: J% g! P  `/ z</s:text>
    9 i' E# [3 o0 l) m8 x% X5 R9 m在Action类中获取带占位符的国际化信息,可以使用getText(String key,String[] args)或getText(StringaTextName,List args)方法.
    : L/ o3 b4 P  O* N6 s 0 \7 S; l5 B/ ^) M0 M
    (2).占位符就当是一个变量参数,可以传递给定的参数值.
    5 }2 s8 i/ g% d3 `' u( F8 y
      K1 F7 {& S+ X" F24.为Action属性注入值
    2 G; V" n3 Y! m3 |struts2为Action中的属性提供了依赖注入功能,在struts2的配置文件中,我们可以很方便的为Action中的属性注入值,注意:属性必须提供setter方法,; _0 X, U5 w: Y  ?
    public class HelloWorldAction{
    + F! g9 y9 J4 d# [    private String savePah;& f. t$ Z3 J: a( Z8 {
        public String getSavePath(){
    & U9 j6 n2 z. P+ {% I                return savePath;' J. i+ m5 u4 A
              }
    0 a2 f. ^+ _2 p6 k3 w: M; G    public void setSavePath(String savePath){
    # v/ [5 U- G" [          this.savePath=savePath;3 W; T$ m3 h7 T. o/ Q3 }. o) U2 V( ^8 y
              }) f+ T7 ?! X% L+ Y/ T/ N
    }
    1 N+ ~# ~, y8 o% A, a: ^<package name="itcast"namespace="/test" extends="struts-default">5 R# }0 F4 x, o4 D& b! }/ E
    <action name="helloworld"class="cn.itcast.action.helloWorldAction">
    0 W8 l) }# a8 o4 @: `7 T! u/ |<paramname="savePath">/images</param>
    ' E. F0 R: u3 r. |, p4 H% S' v: F<resultname="success">/WEB-INF/page/hello.jsp</result>
    * }5 v0 Q6 @6 b9 b; K</action>7 [! ~9 z* h7 o& `7 h9 y* ?$ t' v  z
    </package>  \, \8 K1 Q3 N' [. N
    上面通过<param>节点为action的savePath属性注入"/images";Action的变量的值,不能写死,经常变换,需要通过配置来设置参数# k5 Z& m' k; w: |% ?

    8 K( e) z- t$ H) y25.为应用指定多个配置文件$ d# u7 Q) I6 F! ]( d
    (1).在大部分应用中,随着应用规模的增加,系统中的Action的数量也会大量增加,导致struts.xml配置文件变得非常臃肿,为了避免struts.xml文件过于庞大、臃肿,提高struts.xml文件的可读性,我们可以讲一个struts.xml配置文件分解成多个配置文件,然后再struts.xml文件中包含其他配置文件,下面的struts.xml通过<include>元素指定多个配置文件:
    % k  @. R4 F6 b/ ^9 ]) ]: U/ Z<struts>* p! ~8 Y* i# X4 ~! S
    <includefile="struts-user.xml"/>
    , C' D: Y/ Q" v: m, h. b<includefile="struts-order.xml"/>7 b9 l# B1 s" P- g7 s0 S) s: j: L6 x9 ?
    </struts>' O. y8 h' j* {- D9 m6 a
    通过这种方式,我们就可以将struts2的Action按模块添加在多个配置文件中
    3 Z' P5 y+ E8 I: h# j( R
    ' ?- q+ c7 i$ |; [26.文件上传2 I3 _& Z4 `( H; ~, |
    第一步:在WEB-INF/lib下加入commons-fileupload-1.2.1.jar、commons-io-1.3.2.jar,这两个文件可以从http://commons.apache.org下载,在struts2.1以前的版本需要添加,以后的版本就不需要添加
    . T0 s# H2 F2 ^% n2 t
    % |- W, e' ^3 N4 `7 V) {第二步:把form表的enctype设置为:"multipart/form.data",如下:; P* I9 H  Q6 D' L
    <form
    + h5 l  X8 d4 p: Q5 r  venctype="multipart/form-data"action="${pageContext.request.contextPath}/xxx.action"method="post">
    7 t+ `% b7 ~6 M/ N<inputtype="file" name="uploadImage">这个属性的name必须要和类中File名称一样& s3 C0 g. o) ?) j0 n
    </form>
    ; j) a0 k6 w) ~  {6 v2 o第三步:在Action类中添加以下属性) K5 ^2 M& ~( m6 `' i) D! K4 t/ n& O
    public class HelloWorldAction{
    ; N: @. C; q' n3 C- ?6 }private File uploadImage;//得到上传文件;+ V$ ^/ ^- L! ^# L* u* R: Z
    private String uploadImageContentType;//得到文件的类型
    - C+ m& k# n, d7 }. Cprivate String uploadImageFileName;//得到文件的名称" V& \  |( ^. R4 Y8 w1 G5 e
    //这里省略了属性的get/set方法(但是要注意get/set方法是必须的)0 x+ @  @7 j9 L2 U
    public String upload() throws Exception{
    $ Q3 V. U9 p3 [. s+ O% iString realpath =ServletActionContext.getServletContext().getRealPath("/images");  \* q% P- h& h
    File file=new File(realpath);/ b. `! Z" R% j% _
    if(file.getParentFile().exists())file.getParentFile().mkdirs();//目录是否存在,不存在就创建
    # K: K, T# k% W  u8 DFileUtils.copyFile(uploadImage,newFile(file,uploadImageFileName));8 p! Y% j- H4 ?$ L, I
    return "success";" K+ R# t4 A! [5 O
    }, X0 j4 v9 P* n9 |
    }# I' {& n) u  G6 e2 c0 w

    8 ^9 }2 v3 {+ U; S% g(1).如果文件不保存,struts2会把文件保存到自己的目录中,但是当这个Action执行完后,该文件就会被删除,所以我们要将上传的文件保存到硬盘上; D0 A+ _0 d' F0 F$ t# q) W! z' a+ k

    - `5 P5 c2 l/ l. l(2).最好还要判断以下,文件uploadImage是否为空
    1 `( T3 @0 ]  u1 `
    " T+ F+ V9 O5 n$ n9 ]  S7 _(3).如果上传大的文件,web都会失败,像一些门户网站上传视频,都是通过一个插件,可以把这个插件看成一个程序,只是这个程序是通过Socket变成的,针对一个端口进行传输数据
    . K2 \  e/ q" ?3 S2 C8 q1 b 9 {7 S/ Z3 D: R: Y9 T8 j5 N
    27.指定struts2处理的请求后缀
    2 {6 O1 l; t: `0 m  f(1).前面我们都是默认使用.action后缀访问Action,其实默认后缀是可以通过常量"struts.action.extension"进行修改的,例如:我们可以配置struts2只处理以.do为后缀的请求路径# e% G# x$ y+ a; ]& z
    <struts>
    ' F9 t' k3 a) n/ L5 |# P  s) p<constantname="struts.action.extendsion" value="do"/>/ c9 ?. Y4 b. c0 x- `; u
    </struts>
    , _' {/ q' @# e* [0 }如果用户需要制定多个请求后缀,则多个后缀之间以英文逗号","隔开,如:
    1 I% S' t  C1 C" u7 k, l3 b: F5 E<constantname="struts.action.extendsion" value="do,go"/>
    & N: |( Z9 Y. v * p5 ^# n8 `' a  v. l. }
    (2).常量可以在struts.xml或struts.properties中配置,建议在struts.xml中配置,两种配置方式如下:
    # G/ J# n2 L; q在struts.xml文件中配置常量:7 J6 H& c, G0 `" j
    <struts>
    / F9 O  _9 p* d% t+ O% A5 b<constantname="struts.action.extendsion" value="do"/>) D' l2 H* Y( C
    </struts>+ \6 g& F' f) b7 c* c
    在struts.properties中配置常量:
    , I: F) C# v) v1 Cstruts.action.extension=do0 }) l( K( o# E- z: t5 c" C  V! l5 z
    因为常量可以在下面多个配置文件中进行定义,所以我们需要了解struts2加载常量的搜索顺序:; f8 A" _4 D7 p: q' E
    struts-default.xml1 N% |# W! |' F3 H5 _/ R$ q
    struts-plugin.xml  W2 H, R6 i- \
    struts.xml# \; J% y% v3 |& K6 [2 g
    struts.properties
    ! W) p, G7 @( B, w& dweb.xml
    3 H1 ^$ Y3 b( ~4 {如果在多个文件中配置了同一个常量,则后一个文件中配置的常量值会覆盖前面文件中配置的常量值
    & k7 K4 T* Z) V5 e
    1 T! s2 p2 ^6 j- {6 E6 f(3).7 [+ s0 w) K( G9 t( x' H
    第一:默认编码集,作用于HttpServletRequest的setCharacterEncoding方法和freemarker、velocity的输出:
    ( u- G; p; E( h, P# |<constantname="struts.i18n.encoding" value="UTF-8"/>
    ! h9 |2 q8 z1 y3 G, G第二:该属性指定需要struts2处理的请求后缀,该属性的默认值是action,即所有匹配*.action的请求都由struts2处理,如果用户需要指定多个请求后缀,则多个后缀之间以英文逗号(,)隔开
    * b% @0 c. y( s& l  _' f<constant name="strtus.action.extension" value=do"/>  ^8 }1 g; @2 i
    第三:设置浏览器是否缓存静态内容默认值为true(生产环境下使用)开发阶段最好关闭,不然看不到修改后的数据
    - }. u( s: k% J6 v: u<constantname="struts.serve.static.browserCache" value="false"/>
    8 c) [+ v' O) C/ f) G6 ^8 W第四:当struts的配置文件修改后系统是否自动重新加载该文件默认值为false(生产环境下使用),开发阶段最好打开- z6 j  P: X" Q: r% S, ?/ \0 M
    <constantname="struts.configuration.xml.reload" value="true"/>2 L' N/ C& P0 B2 v" h& z# g; E
    第五:开发模式下使用,这样可以打印出更详细的错误信息+ r- S/ w* A- e) S/ f( V( @8 W  ~
    <constant name="struts.devMode"value="true"/>) G, r# V* {- z' D6 j! N
    第六:默认的视图主题
    8 H+ S3 |4 V' M7 u& }6 U<constantname="struts.ui.theme" value="simple"/>
    ( w# J/ _$ w8 v0 Y第七:与spring集成时,指定由spring负责action对象的创建8 E/ r- @; y) [, ]9 F5 M
    <constantname="struts.objectFactory" value="spring"/>1 O" g' m/ ], G
    第七:该属性设置struts2是否支持动态方法调用,该属性的默认值是true,如果需要关闭动态方法调用,则可设置该属性为false
    $ {3 R6 M% t4 Y) j) e+ F<constantname="struts.enable.DynamicMethodInvocation"value="false"/>1 x( K  t1 N- [( }( Q
    第八:上传所有文件的总大小限制
    # Z) F, q2 J: X+ Z% |" lconstantname="struts.mulitipart.maxSize" value="10701096"/>
    3 L2 Q5 {1 D/ ^+ a3 b8 S1 L! E
    ' z* q2 H  v+ q+ v& v- O28.自定义拦截器
    & {0 Q  g6 e: u" c(1).如果用户登录后可以访问action中的所有方法,如果用户没有登录不允许访问action中的方法,并且提示"你没有权限执行该操作"
    5 b. n; g2 v' [- z  O
    2 e0 e& B2 z, W(2).
    " h- ?2 u3 p/ d: B<interceptors>
    ( Q; w5 k1 v+ ^<interceptorname="permission"class="cn.itcast.interceptor.PermissionInterceptor"/>
    2 r( X2 O5 E! b5 p6 P( e</interceptors>
    2 K: o8 Y4 ~, c" A<actionname="list_*" class="cn.itcast.action.HelloWorldAction"method="{1}">' i& {2 b- M8 x) E
    <interceptor-refname="permission"/>
    1 G% w) e. B$ Q+ l3 X" j如果为某一个Action定义一个拦截器,struts2中对Action的默认的很多拦截器都失去功能,所以要想做到两全其美,需要定义一个拦截器栈:6 j9 W2 Y3 T: F0 P- G7 x
    <interceptors>. ]& L0 o2 D! I+ X2 J/ j8 `9 J
    <interceptorname="permission"class="cn.itcast.interceptor.PermissionInterceptor"/>
    3 S6 e* R2 S- Q3 d<interceptor-stackname="permissionStack">; Q$ @% ~$ T) K) f
    <interceptor-refname="defaultStack"/>
    / _* m" V, x' f, V( G<interceptor-refname="permission"/>
    - e! R* o9 O- H; |, n0 r</interceptor-stack>
    7 K% M2 D$ y2 l1 g9 f2 t% W2 Y2 s. n</interceptors>
    " \( h# H, s2 ~0 R: u# E$ s因为struts2中如文件上传,数据验证,封装请求参数到action等功能都是由系统默认的defaultStack中的拦截器实现的,所以我们定义的拦截器需要引用系统默认的defaultStack,这样应用才可以使用struts2框架提供的众多功能,如果希望包下的所有action都使用自定义的拦截器,可以通过<default-interceptor-refname="permissionStack"/>把拦截器定义为默认拦截器,注意:每个包只能指定一个默认拦截器,另外,一旦我们为该包中的某个action显示指定了某个拦截器,则默认拦截器不会起作用.$ L1 N/ x# t  K9 g, x& o+ W8 c& T
    (3).系统默认的拦截器可以到struts-default.xml中查看,很多功能.系统拦截器放在最前面,自定义的拦截器放在后面.
    7 L+ [" `0 m7 H* N) }/ B$ V4 I
    : }/ L+ \2 `& [+ ]29.自定义类型转换器
    3 N6 ^% n- U  d4 K5 x' r9 C(1).struts2中提供了两种类型转换器:局部类型转换器(只对某一个action起作用),全局类型转换器(所有的action起作用)
    9 U: L) }: H% V9 K3 Y
    % E/ t5 \% }) d2 t" ~(2).类型转换器必须继承DefaultTypeConverter最好用xwork2.jar中的,重写converValue(Map<String,Object>context,Objectvalue,Class toType){9 d7 p6 m" `6 N5 Q
    returnsuper.convertValue(context,value,toType);' h  o" B6 W8 ^
    }, a& `4 o& v* f2 m( r8 \
    其中第一个参数和ognl表达式,第二个参数是需要转换类型的内容(是String数组,因为可能有多个值),第三个参数是需要转换成什么类型,要实现双向转换
    , c. d, _7 |+ r) C; x & v# b  ~6 o" T  J, I
    (3).将上面的类型转换器注册为局部类型转换器:5 P% I/ |2 I7 b  a2 [1 A$ h, _$ `5 V
    在Action类所在的包下放置ActionClassName-conversion.properties文件,ActionClassName是Action的类名,后面的-conversion.properties是固定写法,对于本例而言,文件的名称应为HelloWorldAction-conversion.properties.在properties文件中的内容为:
    6 P; V) \( [/ |. {* l+ m需要转换的属性名称=类型转换器的全类名; c: y+ Y" w# q( ~9 u1 b& k
    对于本例而言,HelloWorldAction-conversion.properties文件中的内容为:0 N, i4 L4 A6 b
    createtime=cn.itcast.conversion.DateConverter
    红红火火恍恍惚惚
    回复

    使用道具 举报

    admin    

    1244

    主题

    544

    听众

    1万

    金钱

    管理员

  • TA的每日心情

    2021-2-2 11:21
  • 签到天数: 36 天

    [LV.5]常住居民I

    管理员

    10#
    发表于 2015-06-02 12:45:41 |只看该作者
    四、       Spring
    ! A7 p0 u! N2 Y. ]$ r4 K1.     @Autowired注解与自动装配$ ~' M$ N$ ?& y) h8 P9 M
    @Autowired
    8 z( n0 c( f' ]* m: O4 m; {private PersonDaopersonDao;' v2 `8 }* O4 M& E! {% a2 |4 c7 `/ X5 S
    拿PersonDao与<bean id=""..../>中的id值进行比较,相同就找到了,即进行类型注解,当然也可以过@Qualifier注解进行名称进行注解.
    ; X% \! {* W1 {* R+ m( v自动装配:- L1 r  Q. z# P+ x9 }% M  m) G
    对于自动装配,大家了解一下就可以了,实在不推荐大家使用,例子:<bean id="" class=""autowire="byType"/>% P7 s+ |2 ^) E. Y% B& `& d
    autowire属性取值如下:
    $ y3 P/ A" c6 e5 [1 s, _byType:按类型装配,可以根据属性的类型,在容器中寻找根该类型匹配的bean,如果发现多个,那么将会抛出异常,如果没有找到,即属性值为null1 l* b$ w0 G3 v: E8 a- [
    byName:按名称装配,可以根据属性的名称,在容器中寻找跟该属性名相同的bean,如果没有找到,即属性值为null
    + X) F1 R6 @0 ~- y# p1 Econstrutor与byType的方式类似,不同之处在于它应用于构造器参数,如果在容器中没有找到与构造器参数类型一致的bean,那么将会抛出异常.
    & r; S& q0 V0 Z8 i6 ]autodetect:通过bean类的自省机制,来决定是使用constructor还是byType方式进行自动装配,如果发现默认的构造器,那么将使用byType方式.
    + e* B4 e9 \& T+ o# s/ E( ~ 9 a" h( m* M: X* J1 s
    2.     @Resource注解完成属性装配
    8 I. j6 I9 `6 e( Y5 I, _+ a4 y(1).前面讲到了使用构造器注入,属性的setter方法注入,这里还可以使用注解的方式对Field进行注入
    " d8 }; `& b6 ]/ N3 P
    ( W+ ^/ j* H, D6 p# v4 l(2).注入依赖对象可以采用手工装配或自动装配,在实际应用中建议使用手工装配,因为自动转配会产生未知情况,开发人员无法预见最终的装配结果
    $ \& q6 s. Z* \5 k/ _& A- O 2 B/ E% S4 c1 o
    (3).手工装配依赖对象,在这种方式中又有两种编程方式; q- q5 i, y) x! \- ~
    方式一:在XML配置文件中,通过在Bean节点下配置,如:
    ' R7 m& S9 O+ N" a) }<bean id="orderService"class="cn.itcast.service.OrderServiceBean">
    . n6 ^2 R# w/ F& k<construtor-arg index="0"type="java.lang.String" value="xxx"/>构造器注入$ Z! u6 P$ ~1 @, {
    <property name="name"value="zhao"/>属性的setter方法注入
    8 _, H  U4 M3 R! v8 v  ]6 T* a</bean>
    / x; {4 y. H' K! h在XML中注入属性,会给XML文件变得很臃肿.特别是对集合类型进行注入时,变得很臃肿.
    ) [& c* K# j: R) o8 X方式二:# ~4 p" G/ W; U( G' I5 E- i
    在java代码中使用@Autowire或@Resoruce注解方式进行装配,但我们需要在XML配置文件中配置以下信息:
    - `  O0 R5 @4 Y" z<beansxmlns="http://www.springframe.....0 k7 q/ e6 i* I# s1 L. Y$ V
    ....( x0 b  V  d! Q8 e: I
    ....% ?* f( Q% a" U" j
    </beans>+ x; G8 k3 [3 W' f! E
    这些配置项隐式注册了多个对注释进行解析处理的处理器:AutowiredAnnotationBeanPostProcessor,CommonAnnotationBeanPostProcessor,PersistenceAnnotationBeanPostProcessor,RequireAnnotationBeanPostProcessor,每个注解都有一个注解处理器,注解本身不干活,是相对应的注解处理器在干活& Q" `& L# O( l% a
    注:@Resource注解在spring安装目录的lib\j2ee\common-annotations.jar# V& ]: D; t3 t8 V  x" M9 J: W

    , j, B: L7 M* f7 O6 Y3 |$ e3 \(4).在java代码中使用@Autowired或@Resource注解方式进行装配,这两个注解的区别是:
    4 ?$ s2 k6 T4 e; ]4 H@Autowired默认按类型装配,@Resource默认按名称装配,当找不到与名称匹配的bean才会按类型装配., d- x. N, y; ?/ D2 o
    @Autowired2 H" W  V8 V& Y  {; d+ Q
    private PersonDaopersonDao;//用于字段上. x% ]. X' x2 P9 o- `+ V
    @Autowired* J( c4 Z" i0 Y' p
    public voidsetOrderDao(OrderDao orderDao){//用于setter方法上3 n; w2 l% K' H/ C+ H- m
    this.orderDao=orerDao;: n* _% Y" ?" \
    }
    7 C8 g8 B5 @& G! B7 I1 {' F3 G@Autowired注解是按类型装配依赖对象,默认情况下,它要求依赖对象必须存在,如果允许null值,可以设置它required的属性为false,如果我们想使用按名称装配,可以结合@Qualifier注解一起使用,如下:
    4 g- p: e. R% t@Autowired@Qualifier("personDaoBean")  ~- F3 H- B% }3 W9 U/ M
    private PersonDaopersonDao;
    % g! Q7 O5 x. `5 k@Resource注解和@Autowired一样,也可以标注在字段或属性的setter方法上,但他默认按名称装配,名称可以通过@Resource的name属性执行,如果没有指定name属性,当注解标注在字段上,即默认取字段的名称作为bean名称寻找依赖对象,当注解标注在属性的setter方法上,即默认取属性名作为bean名称寻找依赖对象0 u0 h; k2 y  O9 F/ N( \0 P
    @Resource(name="personDaoBean")8 p9 s' I  |* D. X3 ^; `7 |* x
    private PersonDaopersonDao;//用于字段上5 K+ X: e/ l  u9 M! r# @
    注意:如果没有指定name属性,并且按照默认的名称仍然找不到依赖对象时,@Resoruce注解会回退到按类型装配,但一旦指定了name属性,就只能按名称装配了.拿personDao与<bean id=" ".../>中的id是否相同,相同就找到,属性的setter方法也不用写,既方便,又优雅.  r6 S5 o  R8 k* D$ K! r1 Z
    同时@Resource是j2ee提供的注解(建议使用),@Autowired是spring框架提供的.
    $ N! Y; _/ e8 ^4 W1 V3 l
    & y0 l0 K) z: M8 W& B3.     Spring的三种实例化Bean的方式0 _( A3 ^+ G( R! D# p
    (1).三种实例化bean的方式:/ H1 V8 O/ n' c+ _5 @  `
    第一种:<bean id="orderService"class="cn.itcast.OrderServiceBean/>
    : x- p1 w* E' N) `2 ?$ r第二种:使用静态工厂方法实例化:
    9 f+ j4 O( H+ Q<bean id="personService" class="cn.itcast.service.OrderFactory"factory-method="createOrder"/>+ p; N) }' {; {2 \! x
    public class OrderFactory{! ?$ [! B$ }/ R
    public static OrderServiceBeancreateOrder(){
    1 S; }" S$ L! j4 i+ Preturn new OrderServiceBean();/ A, j, e& ^# u
    }
    4 i; }4 s- w! }; o- G( Z: b}; [' u' q5 d& i$ r
    第三种:使用实例工厂方法实例化:% X' d; s& g; G5 D
    <beanid="personServiceFactory" class="cn.itcast.service.OrderFactory"/>
    , t) m+ Y, i( V5 w4 o<bean id="personService"factory-bean="personServiceFactory"factory-method="createOrder"/>
    % v0 [; ~% T+ j3 V+ |  _3 T" Zpublic class OrderFactory{% o6 Z. N% ?/ T7 M& x% C
    public OrderServiceBean createOrder(){
    0 c- l3 r  y7 S/ @return new OrderServiceBean();: D! g- J8 l2 r' }, l- W6 c
    }6 [' Q/ d  c7 ^; F. n
    }
    8 u9 C5 g: C( v7 \! f6 {2 k; i 7 g: g1 j9 L( T) D+ m  h
    4.     Spring管理的Bean的生命周期
    ! r6 |% q3 U2 V! i- v# U$ f(1).Bean实例化是在Spring容器实例化时进行的,但是这是Singleton作用域中,实例化的时机是可以更改的,lazy-init="true"延迟初始化,即更改为调用getBean()方法时进行初始化.同时也可以在配置文件中设置所有的bean延迟初始化,其实这个标签是不建议使用的.( C/ x9 i! X3 n% V* s5 s
    ; ^* H' j8 Z& d4 U, r6 a7 u/ R! Y
    (2).当把作用域改成Prototype时,Bean实例化是在调用getBean()方法进行的, J2 A: y! m/ m( v; O

    0 ^8 l4 z0 Q! y( q(3).可以指定一个初始化方法:init-method="";即在bean实例化后执行的初始化方法.如数据库的连接.容器通过反射技术调用的,同时还需要进行资源的释放.destory-method="";即在bean被销毁时执行的方法.. r$ h1 s% N6 o- w$ {

    . V* D; R6 e3 i( `6 ?(4).关闭Spring容器:ctx.close()方法,bean此时被销毁了/ r  E- Q6 Q# g3 d& }% _) e
    6 _+ i  w; c: |. C, m9 _- Z0 d
    5.Spring自动扫描和管理bean
    8 e+ p" ^  I- u' T) p通过在classpath自动扫描方式把组件纳入spring容器中管理,前面的例子我们都是使用XML的bean定义来配置组件,在一个稍大的项目中,通常会有上百个组件,如果这些组件采用xml的bean定义来配置,显然会增加配置文件的体积,查找及维护起来也不太方便,spring2.5为我们引入了组件自动扫描机制,它可以在类路径底下寻找标注了@Componet、@Service、@Controller、@Reponsitory注解的类,并把这些类纳入进spring容器中管理,它的作用和在XML文件中使用bean节点配置组件是一样的,要使用自动扫描机制,我们需要打开以下配置信息:' Y& C1 t. g" g
    <beansxmln="http://www.springframework.org/schema/beans"
    " m# ~7 H$ @4 a....  ?' N  a" g- \6 U* E
    ....
    & y/ W7 K9 K3 O# K: a( v% |</beans>
    ' o$ Z3 E7 v, M  W4 b其中base-package为需要扫描的包(含子包)# {/ Z. Q4 ?3 ^) }8 n
    @Service用于标注业务层组件、@Controller用于标注控制层组件(如struts中的action)、@Repository用于标注数据访问组件,即Dao组件,而@Component泛指组件,当组件不好归类的时候,我们可以使用这个注解进行标注.同时也可以通过注解@Scope("prototype")修改bean的作用域.@Service("personService")中的名称必须和bean的名称相同,只是开头字母变成小写了.当然这是默认设置,可以修改的.
    $ a5 Q) l% e6 _可以使用注解的方式指定初始化方法,在初始化方法init()上添加注解@PostConstruct,同样可以指定销毁方法destroy(),注解为:@PreDestroy  W4 i3 e6 T$ i7 x6 h
    这种扫描方式是很方便的,很多人都采用
    : |. E# U/ I" z" G6 K即前面所说的功能都使用注解进行操作! K6 @! J3 _3 i' f
    5 j0 g: v3 t9 i
    6.  SSH整合开发
    . F5 y* g( ?) j* K' Ihibernate核心安装包下的:, P; y" d5 ]8 h3 e& b3 b3 z# I
    hibernate3.jar
    % D, f4 i% a! ?% ?! S) m: F1 Zlib\required\*.jar- o% w3 T. q6 _! [3 N2 o
    lib\optional\ehcache-1.2.3.jar
    5 j. U1 k0 @% bhibernate注解安装包下的) ?9 m: \) r/ V9 t6 K' W9 m' [
    lib\test\slf4j-log4j12.jar! K5 t0 N; }( P/ Q1 W# q% r
    ; X( `# {8 C. q
    Spring安装包下的
    % D# h/ A, I6 f8 v7 R) t& Vdist\spring.jar) h& Z' y, u2 S0 T
    dist\modules\spring-webmvc-struts.jar. U9 i" S2 V8 ]
    lib\jakarta-commons\commons-logging.jar、commons-dbcp.jar、commons-pool.jar
    ! V7 O9 f( I) qlib\aspectj\aspectjweaver.jar、aspectjrt.jar
    * K" j) H2 e4 T8 G% R1 ]: xlib\cglib\cglib-nodep-2.1.3.jar
      |3 h7 w  H) ulib\j2ee\common-annotations.jar0 [+ w* @9 O# ?' e
    lib\log4j-1.2.14.jar& k/ W" u: m5 s" Y1 [/ g

    $ W5 I' [. K& W9 `3 Y: N2 g4 S& }& jStruts:下载struts-1.3.8-lib.zip,需要使用到解压目录下的所有jar,建议把jstl-1.0.2.jar和standard-1.0.2.jar更换为1.1版本,Spring中已经存在一个antlr-2.7.6.jar,所以把struts中的antlr-2.7.2.jar删除,避免jar冲突
    ( n. o" L9 U) p, Y& Z2 i  J数据库驱动jar1 d/ U1 p$ N0 E8 K! e* n' k1 e/ G2 U
    首先整合struts和hibernate然后再整合spring
    3 n* D* n' }+ c: }: I! p+ s
    , j# p: B6 }0 F& l$ C( X* M4 o, R7.编码解析@Resource注解的实现原理
    ( |% o8 i" ^9 A6 u(1).新建一个注解6 `+ z9 \5 i( w, D' u" \
    @Retention(RetentionPolicy.RUNTIME)+ q  |2 w' Q$ D# L
    @Target({ElementType.Field,ElementType.METHOD})
    4 @& L% U! M( \+ Vpublic @interface ItcastResource{; s/ E4 M1 Q, g" F1 a, p
    String name() default "";
    + {1 ^5 r" {* d: ?8 |/ B}" z3 T- w4 f/ j% f- O; @% K
    当然还要编写一个注解处理器., r0 C  w5 d' B( U, w
    private voidannotationInject(){) k! E. s7 ~! x& v
    首先循环所有的bean对象,判断其是否使用了注解
    ; _, n- a1 g- Q如果使用了注解,就进行属性注入.
    : V$ d; X3 {9 h* p* C8 q& u) r}
    6 H$ e5 w) \9 {2 ?0 X+ @9 N8 c1 p# v
    5 W! w: x! {: G8.编码解析Spring依赖注入的原理
    * ]* |7 c# J9 K; u' P(1).基本类型对象注入:
    % c  {3 K0 `/ v! T' M9 W<bean id="orderService"class="cn.itcast.service.OrderServiceBean">
    3 j+ _4 F- w+ ?( c% F4 S<constructor-arg index="0"type="java.lang.String" value="xxx"/>构造器注入4 l: Z6 F' d/ v, v% \
    <property name="name"value="zhao"/>//属性setter方法注入8 i* A7 l9 w) u3 q$ N
    </bean>. _. N' S3 A) x( o; K( E

    1 s1 L3 K: E; X8 D" S+ q$ H(2).注入其他bean:
    ) a" b& Y7 U+ s2 s! ~* [方式一:! K! m' S" y4 S& Y9 D# b9 N/ b
    <bean id="orderDao"class="cn.itcast.service.OrderDaoBean"/>0 J# x: ^8 `, R, a$ U. h
    <bean id="orderService"class="cn.itcast.service.OrderServiceBean">* P9 q* p. ]; }6 E0 j6 n3 m9 l
    <property name="orderDao"ref="orderDao"/>ref是指被注入的对象personDao
    " u3 r( n; B: Y! b$ |& P; N5 z( T</bean>
    4 I' F' t) T4 a  N方式二:(使用内部bean,但该bean不能被其他bean使用)
    $ B! F" t1 Q5 m: ^; j; W% U. x<bean id="orderService"class="cn.itcast.service.OrderServiceBean">
    3 A  p8 N; u3 ?/ D4 e, R. l<property name="orderDao">+ c4 \, a/ s" D. ?8 \! Z  {
    <beanclass="cn.itcast.service.OrderDaoBean"/>1 N  t8 ?  I5 x6 E2 P' ]( A
    </property>
    ( g# V3 [6 z$ b0 e( c</bean>& g* h* ?4 m2 p, c
    4 r& S. k" J! }; o  B# Y
    (3).依赖注入的内部原理9 w+ Y9 B  b# q7 W1 H3 b
    6 x5 [9 d* O6 E% m# a
    9.编码解析Spring装配基本属性原理
    ! V* Q7 V+ ?$ [7 v/ L  T( Z: p也可以对基本类型进行注入,类型转换.
    ( l/ o3 q& o# w% s! I 8 O0 T3 |' A* g" a  b8 T+ U+ P
    10.编码剖析Spring管理bean的原理0 {. R4 X& m: B* I( p6 T8 {/ k
    使用dom4j读取spring配置文件,使用反射技术即可
    1 c, y' {  H3 w# \$ C  _ 0 p8 F& j5 v  @5 S5 O- x, d; A
    11.搭建和配置Spring与JDBC整合的环境8 B( J1 M. {# p: o* u. X( s
    (1).配置数据源:
    . K) W0 g- s$ S4 n+ z. j& m  f<bean7 v3 {! P' |+ O  I, o: V- r5 w
    id="dataSource"
    " r& ]+ z7 w% _5 eclass="org.apache.commons.dbcp.BasicDataSource"destroy-method="close">
    0 S) Q) q1 \' g1 t) g4 H5 t' Z0 rapache的数据源:BasicDataSource7 n9 E( w9 N7 Q  o, }9 J. E
    <propertyname="driverClassName" value="org.gjt.mm.mysql.Driver"/>7 J$ \$ i0 O) }" q9 @
    <property
    6 P& x! _+ G3 pname="url" value="jdbc:mysql://localhost:3306/itcast?useUnicode=true&amp:characterEncoding=UTF-8"/>
    1 x0 C" m7 I3 z( e! H<property name="username"value="root"/>; n' V7 z7 S) {6 w+ v
    <property name="password"value="123456"/>
    " W9 l/ R* }7 l; C4 u) b<property name="initialSIze"value="1"/>连接池启动时的初始值, I7 W: c( C5 J3 J8 C
    <property name="maxActive"value="500"/>连接池的最大值
    * t' u6 I7 b5 E) v1 |; o  e<property name="maxIdle"value="2"/>最大空闲值
    # T  C- ^' H' e4 {) I  Y" ^$ F# z0 C<property name="minIdle"value="1"/>最小空闲值* L" k. }  b7 T
    </bean>
    + x% }6 H& Y- ]; e配置事务:; g- {8 N$ G6 ^* W  g6 P
    采用注解的方式:
    9 L. i' o+ W9 Z% u<bean
    6 d; U0 q! P& d" |* u7 rid="txManager"class="org.springframework.jdbc.datasoruce.DataSourceTransactionManager">
    ; F9 G( T/ E  v; j4 h" [<property name="dataSource"ref="dataSource"/>; n0 B5 w, m& B1 q
    </bean>
    + l  |8 H* d$ R8 O+ K+ v7 O
    + h1 u1 y7 n& o- R7 R6 |2 q<ts:annotation-driventransaction-manager="txManage"/>
      H" m+ t& W$ W) Q( Y( Y# Z: i
    ) w4 W( T8 d$ v* r12.搭建与测试spring的开发环境$ S3 ?/ |0 X% Q. q
    (1).dist\spring.jar;
    # |1 q8 a8 E# h2 elib\jakarta-commons\commons-logging.jar
    ! X( y" i/ m% v, h. q以上这两个包是必须的9 r' r, P2 o, n6 {9 _7 A- t
    lib\aspectj\aspectjweaver.jar和aspectjrt.jar+ _; P3 X2 A0 w; I
    lib\cglib\cglib-nodep-2.1.3.jar
    . h0 g9 K8 L: ~3 K, @以上这两个包是用于切面编程(AOP)& y8 t; n3 S, {. \# ]
    lib\j2ee\common-annotations.jar6 G  d0 b2 P: S1 g
    以上的这个包是JSR-250中的注解
    $ B# B4 W3 k, d% `+ b
    ' ]' b* [1 `% Z! g1 L(2).Spring项目既可以在j2se中也可以在j2ee中! ?7 Q5 {/ E& f# D

    " Y; V. ^4 J! O2 f  Q1 w(3).spring的配置文件模板可以从spring的参考手册或spring的例子中得到,配置文件的取名可以任意,文件可以存放在任何目录下,但考虑到通用性,一般放在类路径下
    ' a, y2 I/ H' Q , K4 s% r4 N1 S. i
    (4).实例化spring容器常用方式:
    ' ^/ ~! c' w4 a# |0 v- e. n9 V第一种方式:在类路径下寻找配置文件来实例化容器
    3 C% W# j4 |/ \9 T3 jApplicationContextctx=new ClassPathXmlApplicationContext(new String[]{"beans.xml"});; |4 H, M* ~( K+ f2 f
    第二种方式:在文件系统路径下寻找配置文件来实例化容器4 k' e# k2 {4 i/ e4 Z9 P
    ApplicationContextctx=new FileSystemXmlApplicationContext(newString[]{"d:\\beans.xml"});//将路径写死了,通用性不好,不建议使用.
    % }. E, s) m2 A3 M- xspring的配置文件可以指定多个,可以通过String数组传入+ Y" X2 r; u" ?

    ( L' Z8 E( }$ P3 i) Q(5).IOC:控制反转:3 x1 l( V! F* Z% q+ b0 g7 {
    public class PersonServiceBean{
    ( V# A9 b# m- Z# @; i. Wprivate PersonDao personDao = newPersonDaoBean();3 t& I: \; d$ ]6 n$ n  M
    public void save(Person person){* {9 Y* u7 }5 e' R" r2 `" e' y
    personDao.save(person);7 `% x2 P- q. Z) m/ c) ^
    }) s4 o( e& D! z% r4 U
    }
    . ^4 b0 T0 m) D- iPersonDaoBean是在应用内部创建及维护的,所谓控制反转就是应用本身不负责依赖对象的创建及维护,依赖对象的创建及维护是由外部容器负责的,这样控制权就由应用转移到外部容器,控制权的转移就是所谓反转.! M: U% r2 I8 C
    ; q5 m# N5 [- b  Y% `  V) a
    (6).建立一个业务bean为PersonServiceBean,放在cn.itcast.service.impl包中.面向接口编程,进行解耦,怎么将业务bean交给spring管理,需要在beans.xml配置文件中:<bean id="" name=""class=""></bean>,其中id,name都是bean的名称,但是id不能使用特殊字符,id本身就属于XML属性的,如:"/sfs/"就会出错,但是name不会出错,class属性是bean类的路径,需要操纵bean的时候,只需到spring容器中取出bean进行操作,而不需要实例化一个bean了:7 s9 t7 w  q' P, I- g
    ApplicationContext ctx=newClassPathXmlApplicationContext(new String[]{"beans.xml"});' |3 @! W& z) u8 K2 u$ A
    PersonServiceBeanpersonService=(PersonServiceBean)ctx.getBean("personService");5 Y  N3 h8 ?! k6 Q4 Y- e0 q% |
    personService.save();$ I0 G$ R  b3 e  e& L
    + v2 m6 j  i" {  D! m* l
    (7).当在配置文件中没有标签的提示信息,需要手动添加schema文件,方法如下:windows->preferences->myeclipse->filesand editors->xml->xmlcatalog,点击添加,在出现的窗口中的Key Type中选择URL,在location中选"File System",然后在spring解压目录的dist/resources目录中选择spring-beans-2.5.xsd,回到设置窗口的时候不要急着关闭窗口,应该把窗口的Key Type改为Schema location,Key改为http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
    , N" ]1 Y# y9 U( n; S0 ] " [' e+ D6 ~% x; x. D9 B. e, n$ Q. h
    13.配置Spring管理的bean的作用域
    ! x1 |- J! Y. q1 @(1).Singleton:默认情况下,bean是单例模式的,在每个springIoc容器中一个bean定义只有一个对象实例,默认情况下会在容器启动时初始化bean,但是我们可以指定Bean节点的lazy-init="true"来延迟初始化bean,这时候,只有第一次获取bean会才初始化bean,如:<bean id="xxx"class="cn.itcast.OrderServiceBean" lazy-init="true"/>1 f: G& f5 Z1 h3 n8 e! K  p; Y: ]. j
    如果想对所有bean都应用延迟初始化,可以在根节点beans设置default-lazy-init="true",如下:<beans default-lazy-init="true">
    ; N" o3 x. G( _Prototype:每次从容器获取bean都是新的对象6 {9 y3 I! e* ?9 n. l
    Request:在request的域中: R  Z0 l! S1 S# P4 k# M& E; y
    Session:在Session的域中" Z6 Y6 C% v2 n6 w( k5 t0 o/ F4 _! a0 K
    Global Session:在全局的Session的域中, V# u/ C0 X; J

    " ~3 c& }$ o6 J: h! O4 C! ]6 X4 r14.全面阐释Spring及其各项功能
    8 |) e/ l7 I+ j; |- R4 Q+ q(1).Spring是一个开源的控制反转(Inversion of control,IoC)和面向切面(AOP)的容器框架,它的主要目的是简化企业开发
    - e. l, l- N7 n
    1 n& E+ e  C8 c(2).IOC:控制反转:
    " h& i5 P' _, {1 ypublic class PersonServiceBean{
    1 L9 P  M. a% d( \% S) _private PersonDao personDao = newPersonDaoBean();: E8 ^+ P3 o. E
    public void save(Person person){
    ( t: s4 H8 S0 {4 O5 S: IpersonDao.save(person);' V6 I  }9 H1 x& y! }  Z" Q
    }' g9 f% |* q6 d' Z& f, C% e
    }2 [( P" ~: V- h0 F% x/ t
    PersonDaoBean是在应用内部创建及维护的,所谓控制反转就是应用本身不负责依赖对象的创建及维护,依赖对象的创建及维护是由外部容器负责的,这样控制权就由应用转移到外部容器,控制权的转移就是所谓反转.PersonServiceBean是业务逻辑层类,PersonDaoBean是Dao层类,在业务逻辑层类中控制和创建PersonDaoBean,这就是应用本身创建和维护了,但是有了spring容器后,PersonDaoBean的创建和维护是在容器中进行的,不需要PersonServiceBean进行管理了,控制权进行的反转7 V" u' x* A# {7 E/ W
      E- D/ X" K' }5 x! J
    (3).依赖注入:当我们把依赖对象交给外部容器负责创建,那么PersonServiceBean类可以改成如下:. q6 k7 Q; d9 M/ y0 x1 U
    public class PersonServiceBean{
    & S/ y) \9 ]. L5 N- l0 J, E; hprivate PersonDao personDao;- ^7 r& R1 @3 e
    //通过构造器参数,让容器把创建好的依赖对象注入进PersonServiceBean,当然也可以使用setter方法进行注入
    ) `/ D5 G; Y0 g( S" i/ Wpublic PersonServiceBean(PersonDaopersonDao){
    - t5 h. i0 N% G2 {5 o0 B% y& S3 h9 ythis.personDao=personDao;
    " [" m% A  d- h7 y( X5 i}
    3 p  I, j! t1 R/ ~, rpublic void save(Person person){
    5 C3 d+ I0 s# r& @2 HpersonDao.save(person);7 x. ^+ b( B1 H" r
    }3 p( V/ O- K9 ^5 U0 f/ K4 o
    }+ |( L& u: ~4 V6 T, E
    所谓依赖注入就是指:在运行期,由外部容器动态的将依赖对象注入到组件中.0 P! ]' H2 y4 s' S
    4 r! `" y9 E1 E) T4 [
    (4).为何要使用spring:0 c9 ~; P3 Z$ s( t( S* R
    第一:降低组件之间的耦合度,实现软件各层之间的解耦:
    : g, C/ J: W7 w1 ?' qController->Service->Dao
    . |5 d9 G- l3 E7 E  \) f第二:可以使用容器提供的众多服务,如:事务管理服务,消息服务等,当我们使用容器管理事务时,开发人员就不再需要手工控制事务,也不需要处理复杂的事务传播
    * `9 d6 Z$ d9 F第三:容器提供单例模式支持,开发人员不在需要自己编写实现代码. Y0 w' \2 c9 [& w1 p9 ~) i' D
    第四:容器提供了AOP技术,利用它很容易实现如权限拦截、运行期监控等功能& x7 L$ }0 X! m: B) f/ A
    第五:容器提供的众多辅助类,使用这些类能够加快应用的开发,如:JdbcTemplate,HibernateTemplate% D: w5 s; q6 @
    第六:Spring对于主流的应用框架提供了集成技术,如集成Hibernate、JPA、Struts等,这样更便于应用的开发.
    : m) h. g) d, E" i1 z0 b
    % O) P% {) r! @% a6 G$ A3 u1 {(5).轻量级和重量级概念的划分,其实划分一个应用是否属于轻量级还是重量级,主要看他使用了多少服务,使用的服务越多,容器要为普通的java对象的工作就越多,必然会影响到应用的发布时间或者是运行性能,对于spring容器,它提供了很多服务,但是这些服务并不是默认为应用打开的,应用需要某种服务,还需要指明使用该服务,如果应用使用的服务很少,如:只是用了spring的核心服务,那么我们就可以认为此时应用属于轻量级的,如果应用使用了spring提供的大部分服务,这时应用就属于重量级,目前EJB容器就因为他默认为应用提供了EJB规范中所有的功能,所以他属于重量级/ a5 i* D3 ~+ v! T9 Y

    # t: m& s+ _3 E; y3 y' S15.使用CGLIB实现AOP功能与AOP概念详解% f% M/ ^* _  }) l& |3 a3 D
    (1).使用cglib架包,构建代理,不需要被代理的对象需要实现接口( J: D- l; B3 E
    public class CGlibProxyFactory implementsMethodInterceptor{+ y" n2 F/ }$ A8 L, G
    private Object targetObject;0 _. |& d0 d, I: T: O
    public Object createProxyIntance(ObjecttargetObject){( m) f9 u* N1 Y! f, A7 t6 A7 r" ^
    this.targetObject=targetObject;& G2 z! P- g. Z
    Enhancer enhancer = new Enhancer();
    % r: ?: p* X4 |6 _* `) qenhancer.setSuperclass(this.targetObject.getClass());
    # \0 l8 F% v3 N9 f1 e, `- ?3 tenhancer.setCallback(this);% G. Q& p" L. E2 d* {
    return enhancer.create();7 M) x/ D& t& _5 _) q& l, M$ M
    }
    $ J& g" m' w+ M2 \public Object intercep(Object proxy,Method method,Object[]args,MethodProxy methodProxy)throws Throwable{
    3 c, |* Z; W1 ]2 jreturnmethodProxy.invoke(this.targetObject,args);
    , W5 h1 `4 o% Y# a7 @}* o) G" T; w+ G; M' W9 _: y' R
    }
    0 q& @9 E. b8 p; NCGLIB可以生成目标类的子类,并重写父类非final修饰符的方法' n$ O& P; I5 x# U
    # B- }# h; n4 g4 a/ |" H. {! P' n
    16.使用JDK中的Proxy技术实现AOP功能
    6 [3 l& j7 V, @" c2 X(1).使用在权限拦截上.) |+ }9 @  M! t  L/ P% Q- @

    ; B7 N9 L8 O+ f0 a* c(2).被代理对象必须实现接口
    0 d' T6 U; n- apublic class JDKProxyFactory implementsInvocationHandler{
    0 F+ _& A: x6 r3 Jprivate Object targetObject;
    . M% L" s$ Y, [( |# [8 [public Object createProxyIntance(ObjecttargetObject){
    2 H  l1 |9 }5 MreturnProxy.newProxyInstance(this.targetObject.getClass().getClassLoader(),this.targetObject.getClass().getInterfaces(),this);
    $ ?% }0 s" C( Q" C7 z5 d# x}
    0 X$ _% o( U  Tpublic Object invoke(Object proxy,Methodmethod,Object[]args) throws Throwable{//环绕通知
    ! K+ p4 I5 i# I# H( _PersonServiceBean bean=(PersonServiceBean)this.targetObject;
    0 p9 \/ `2 C/ [1 f: f, h  j9 W8 sObject result=null;
    * o. ]% [! j; d! I, H* Rif(bean.getUser()!=null){; f, p- S) j, \, Y- n: U1 ]9 p
    //....advice(),调用方法前处理,叫做前置通知# C7 `* q: e! ]! ~' N) Y( z( K
    try{
    , `9 |, e  P/ N, @result=method.invoke(targetObject,args);9 `. w8 X6 b0 G% v2 g
    //....afteradvice();调用方法后处理,叫做后置通知
      l9 |% L" `. v# l}catch(RuntimeException e){
    2 e- }3 y# ~' X//....exceptionadvice();调用方法出现例外后处理,叫做例外通知
    / L! l, b/ x) `+ H4 y& H}finally{
    . ^, G  e* }& K  {: ?5 Y+ z5 p5 {//....finallyadvice();调用方法最终都会处理,叫做最终通知
    $ q. i/ d+ r/ X7 k7 A- G}0 @# g5 Y. F" D- \9 X
    }
    ; F+ l4 L, _. V: Y0 A; F$ ~! ]* t/ ^& F$ Zreturn result;& p0 Q& R4 s1 d, G
    }
    5 h! h- ~8 E+ }. x* ?2 {) a}
    / [) t& Z! o1 `1 ?8 |整个invoke方法叫做环绕通知.
    , v  ?9 Y) F1 b2 [ 7 d8 d1 e4 i/ A% u( F
    (3).AOP中的概念:
    ( B. M, X1 @* L6 K. JAspect(切面):指横切性关注点的抽象即为切面,它与类相似,只是两者的关注点不一样,类是对物体特征的抽象,而切面是横切性关注点的抽象。
    ( ]% t5 U4 c( _* e2 n' v. cJoinPoint(连接点):所谓连接点是指那些被拦截到的点,在spring中,这些点指的是方法,因为spring只支持方法类型的连接点,实际上joinpoint还可以是field或类构造器
    & d5 [+ q+ S  Y9 v8 Y9 a& K5 iPointcut:切入点:所谓切入点是指我们要对那些joinpoint进行拦截的定义; `* B4 {/ @- h; u* C! Z
    Advice(通知):所谓通知是指拦截到joinpoint之后所要做的事情就是通知,通知分为前置通知,后置通知,异常通知,最终通知,环绕通知; B4 L$ B6 I- M
    Target(目标对象):代理的目标对象4 y+ ]/ F6 v8 }' {( T7 T! O: L. m
    Weave(织入):指将aspect应用到target对象并导致proxy对象创建的过程称之为织入
    / c" Y3 [' R' r3 D# n% XIntroduction(引入):在不修改类代码的前提下,Introduction可以在运行期为类动态的添加一些方法或Field
    9 S" l& V' @( h0 y( Q + y' j! m7 Q# y- W& M+ \
    17.使用Spring的注解方式实现AOP
    1 Y+ s: r1 {( s8 U  x+ H7 O9 ](1).要进行AOP编程,首先我们要在Spring的配置文件中引入AOP命名空间:: c; j8 i1 {3 @  f! R( K
    <beansxmlns="http://www.springframework.org/schema/beans": x3 l- k; S0 W* @! A3 J2 j/ w0 @
    </beans>2 f2 t6 }& t9 Q6 a* \; k+ Q) ^
    Spring提供了两种切面使用方式,实际工作中我们可以选用其中一种:一种是基于XML配置方式进行AOP开发,另外一种是基于注解方式进行AOP开发
    9 Q# t% J. U& x. _
    0 X0 _1 u5 v7 |' U(2).基于注解方式声明切面:首先启动对@AspectJ注解的支持:
    # Q- ?9 O3 c5 U& m8 ?<aop:aspectj-autoproxy/>4 U. f7 {; o+ J1 i
    <bean id="orderservice"class="cn.itcast.service.OrderServiceBean"/>
    6 H: l' d/ G* D: U$ Q- G0 F3 T<bean id="log"class="cn.itcast.service.LogPrint"/>% R2 l* k7 ^7 b2 Y
    </beans>
    - c. b6 V" [: M定义一个切面类(MyInterceptor):
    , [2 W, V: ~! j( K& ?; \@Aspect
    8 `8 `' N3 T  Jpublic class MyInterceptor{$ J8 @1 t! S2 X/ x0 Q. p# k
    @Pointcut("execution(*cn.itcast.service..*.*(..))) c' D& g' R- J5 ^
    //定义切入点,通配符:*指的是任何返回类型,..是指子包下也进行拦截,*:指的是拦截所有类,*:指定的是所有方法,..是指任意参数
    0 G" H% t4 Q2 W) ~private void anyMethod(){}//声明一个切入点3 }& d# A( c' v* V# H) T+ Y: Q: `3 y
    @Before("anyMethod()")//定义一个前置通知,名称为切入点名称
    % Z  A  g/ V: Z: U+ x* \  Tpublic void doAccessCheck(){
    ( [6 w4 B5 H# I. H" g' Q6 K. gSystem.out.println("前置通知");: G' |3 g6 g2 N- J6 x& s5 {8 ?2 x
    }8 ?5 m. o8 ~8 T5 h4 N% R" s
    @AfterReturning("anyMethod()")//后置通知% n/ F* @( k- `* q
    public void doAfterReturning(){5 X* G/ g& b# ^' z
    System.out.println("后置通知");. k& n4 H* F! @3 h
    }
    7 }% |9 V; v" b6 x9 n! F6 \6 a@After("anyMethod()")//最终通知; T9 g, I2 f  d) u# ]5 Y1 S& C7 F
    public void doAfter(){
    / p. O. f1 L. K  _3 l. \( U$ _System.out.println("最终通知");
    & o- h# w( _/ o6 S& c# {: `}
    ! S5 ?, m; z% S: Q, g) K9 a@AfterThrowing("anyMethod()");//例外通知# E) A- _. i# s$ D% g4 M4 L( \
    public void doAfterThrowing(){7 z  M. ~9 [. w' l& }
    System.out.println("例外通知");
    & E% T* p1 ~8 b) ~/ R}6 ~9 B, {4 }# h/ J3 `0 m* H
    @Around("anyMethod()")//环绕通知
    , v3 m/ V0 r1 v# K- xpublic ObjectdoBasicProfiling(ProceedingJoinPoint pjp)throws Throwable{
    - _" }( r  c- C2 ^System.out.println("进入方法");# z: b' a2 y$ N% B4 U; F
    if(){//判断用户是否有权限,有权限就执行该方法.
    . g4 o  t- l! LObject result = pjp.proceed();
    8 W3 o, c9 c1 x. [- f7 lSystem.out.println("退出方法");
    0 [' S5 T& o. T7 V3 D& J}else{
    / _* X0 |3 R" P% l& B" V}
    " ~. Z' C, r' R: xreturn result;
    ; x$ I% G5 I( u8 a9 O! s$ t}
    & N+ k' |1 e( ~" I ' Z* \) u* `" O0 c% g, ]( W
    }" k' G- ]) d+ ?4 d
    所以当出现例外通知时,后置通知是不执行的,即它们两者肯定有一个不执行,一个执行.9 C2 d$ k8 {; P: ^. ~/ o6 O' g+ X
    需要将切面类交给spring管理:基于XML配置管理或者自动扫描管理% V6 E9 a. @, @+ D) M
    ' n* w  |/ y+ Y/ ]; a) k
    (3).想得到参数:9 Z( e3 t: u- P4 `" k% F! J
    @Before(anyMethod() &&args(userName)")//添加参数,只会拦截到对应的方法,即一个参数的方法.
    : J4 S& o$ K. _7 _5 C) U+ Dpublic void doAccessCheck(String name){- |0 I+ b1 }, H( \( Z+ i1 F
    System.out.println("前置通知");! v# f3 ^! f$ R# D5 V( [
    }
    3 `9 V4 a" w% w. }  j4 b% N% w . e! B2 v8 S) c  s8 o/ {% p1 C9 S
    (4).想得到返回结果:
    # O8 a* ?" z4 T3 v+ |% m" C@AfterReturning("anyMethod()",returning="result")//后置通知  `. z3 K9 t1 S7 B# ]8 y& c
    public void doAfterReturning(String result){
    8 l/ D  ~7 b0 q8 oSystem.out.println("后置通知");
    ) n% V. m; _, d' ~- I* e. gSystem.out.println(result);//打印返回结果* d3 w7 L- l) Z1 v2 h; ~
    }* q4 J7 G8 ?6 n% O. K) _3 D
    + O6 o+ P  o7 h" ]
    (5).得到异常(例外):( r8 Z+ W+ w6 m: }
    @AfterThrowing("anyMethod()",throwing="e");//例外通知
    & k0 `; A$ i8 W* tpublic void doAfterThrowing(Exception e){, i5 X5 g" t9 i8 t: {! E4 a) r
    System.out.println("例外通知");
    ! g# e9 [4 M1 ~+ _; F! GSystem.out.println(e);//打印例外
    & a& i  F6 U' l/ X}
    $ {0 n( C% Z% Y ; D& X( x: ?: M  w4 K* j) ?- E
    18.使用Spring配置文件实现AOP* E2 J3 c- _. b' D) P8 s
    (1).使用配置文件实现AOP,切面类只是个普通的类,其内部没有任何注解7 H$ j! W7 L$ z+ l2 F3 P
    public class MyInteceptor{
    2 P& B0 B( _4 u" U6 Q1 vpublic void doAccessCheck(){
    5 Z" f# x9 j" U% n/ U, RSystem.out.println("前置通知");
    1 ]9 i; D; z* \1 N- x) X3 M% r( v}
    # V% D% j& {7 t9 ^( o  Upublic void doAfterReturning(){0 R4 b& B5 `, k0 Z
    System.out.println("后置通知");' M: Q. W1 U% h3 b
    }3 g" |7 K5 d8 E" ~
    public void doAfter(){
    6 U$ O. w6 z( G' o3 S5 ]6 rSystem.out.println("最终通知");% `! j5 C1 L4 j4 F
    }
    * N0 ]) F3 p- D* S- c& ?public void doAfterThrowing(){/ l- O0 _# s" S0 W- A
    System.out.println("例外通知");- }: c0 h( i1 ]& `# z
    }" P- t! m8 H* D# O: @! N
    public ObjectdoBasicProfiling(ProceedingJoinPoint pjp)throws Throwable{
    ' _. ]$ s. D6 h% jSystem.out.println("进入方法");2 E, z4 v$ E4 g+ X: V9 ]+ V' R, `
    Object result=pjp.proceed();
    4 K! `4 L2 p( L$ w2 FSystem.out.println("退出方法");
    ' J6 V1 y  [7 U" [3 R+ a* s  I9 `return result;5 K8 v, @# Z, v- r1 i$ Q
    }# `! q# Z8 c5 \; V8 z+ [% W
    }
    3 U2 p& t! Y+ }" k5 n6 h& g( N
    7 x; w3 h: p; C5 R. L(2).基于XML配置方式声明切面
    5 |  w' s0 C9 o% Q  M9 _8 t: Q) j<bean id="orderservice"class="cn.itcast.service.OrderServiceBean"/>2 d: G! g$ z4 a& v# W
    <bean id="log"class="cn.itcast.service.LogPrint"/>
    ( P. L( ~! V- x# i8 u<aop:config>& h) d# z2 r* }
    <aop:aspect id="myaop"ref="log">
    * h0 L5 \! D0 b5 p! ]8 h, Q+ u# {<aop:pointcut id="mycut"expression="execution(* cn.itcast.service..*.*(..))"/>2 b. y) l3 z4 g. Y: s" H
    <aop:beforepointcut-ref="mycut" method="doAccessCheck"/>3 ^' g7 }8 l% j3 Z1 T- I
    <aop:after-returningpointcut-ref="mycut" method="doReturnCheck"/>
    : N5 L  r4 s- P  S<aop:after-throwingpointcut-ref="mycut" method="doExceptionAction"/>
    + q+ w5 ~4 m6 R6 U0 L6 S, `<aop:afterpointcut-ref="mycut" method="doReleaseAction"/>6 Q0 M0 e$ M. p; O) \, f
    <aop:aroundpointcut-ref="mycut" method="doBasicProfiling"/>
    ( P3 w% E/ B* q7 T: [! I</aop:aspect>
    # Q' G3 J, x, u) g* N</aop:config>
    0 O2 p9 Z6 z, L  a* c% `' K4 H ; D, |" _9 i7 E
    (3).对于表达式expression的细节:
    - F! O. Z$ G# g$ x5 k" t& L- Z返回值类型为String:execution(java.lang.Stringcn.itcast.service..*.*(..))
    , r" b0 \1 j7 i9 M第一个参数为String:execution(java.lang.Stringcn.itcast.service..*.*(java.lang.String..))
    9 d# _0 Q6 O8 a* ?# m返回值类型为非void:execution(!void cn.itcast.service..*.*(..))0 I# C( I6 W1 t1 d1 B8 Z- O1 w0 p
    9 F' n. a8 t; Z6 O" P1 B( r
    19.使用Spring配置文件实现事务管理. w. X) Q" d3 P. h& z$ ?9 @/ w5 K
    (1).* a: e* x- E7 i0 U2 e- \1 p
    <aop:config>- D1 x, H/ m, ^+ C6 a3 j; }
    <aop:pointcutid="transactionPointcut" expression="execution(*cn.itcast.service.*.*(..))"/>对指定的方法进行拦截
    7 M& Q1 J) H  E' y. X/ r<aop:advisoradvice-ref="txAdvice"pointcut-ref="transactionPointcut"/>/ D  f: Y1 M) @
    </aop:config>2 O0 F3 {" V- D' ?
    <tx:adviceid="txAdvice" transaction-manager="txManager">  c9 m9 P0 S; j5 Z9 Q2 A. p* _! A
    <tx:attributes>
    ( M1 t0 }6 {  o3 b/ r2 F, z<tx:method9 u: K+ z3 q/ k/ G* {/ ~3 p( U
    name="get*"read-only="true" 所有已get方法都不开启事务propagation="NOT_SUPPORTED"/>$ \, o3 `/ F: F4 W- D6 J/ n
    <tx:methodname="*"/>/ P& Q6 k$ H! l" Z, y: R
    </tx:attributes>' z. y' S7 O3 f$ w& h- q/ M% w
    </tx:advice>! F' U0 @5 e6 l" z$ j1 \+ l

    - ^$ ?- J2 a0 l* z4 y, Z1 n& l20.使用Spring注解方式管理事务与传播行为详解( y( i0 w7 i" U% D2 z0 G
    (1).只有当遇到RuntimeException时,事务进行回滚.Spring开启的事务管理,当遇到运行期例外(unchecked),而(checked异常)是不进行事务的回滚的., f$ k4 J2 Q3 N: d) l- s
    ' |' k/ o' ^. g# h, }6 F- `, L
    (2).当然也可修改这种情况.把unckecked异常改成不会进行回滚了:@Transactional(noRollbackFor=RuntimeException.class),0 j& g2 g  W. K. O7 Y

    ; Z3 G$ q. T) R% G+ o(3).@Transactional(propagation=Propagation.NOT_SUPPORTED);关闭事务,不开启事务的.spring容器默认是打开事务的.当然还有其他一些值:(事务的传播行为)
    , k# @$ H% s9 x7 P0 y) s9 YREQUIRED:业务方法需要在一个事务中运行,如果方法运行时,已经处在一个事务中,那么加入到该事务,否则为自己创建一个新的事务(容器的默认值)
    2 }8 B3 b  x, f& _5 _$ R# wNOT_SUPPORTED:声明方法不需要事务,如果方法没有关联到一个事务,容器不会为他开启事务,如果方法再一个事务中被调用,该事务会被挂起,在方法调用结束后,原先的事务便会恢复执行7 {& e" r2 r0 H
    REQUIRESNEW:属性声明不管是否存在事务,业务方法总会为自己发起一个新的事务,如果方法已经运行在一个事务中,则原有事务会被挂起,新的事务会被创建,直到方法执行结束,新事务才算结束,原先的事务才会恢复执行
    ; D2 K" {+ D% U3 e/ x7 @# @MANDATORY:该属性指定业务方法只能在一个已经存在的事务中执行,业务方法不能发起自己的事务,如果业务方法再没有事务的环境下调用,容器就会抛出异常* n3 _: g1 R! u  @  B. [, h  j
    SUPPORTS:这一事务属性声明,如果业务方法再某个事务范围内被调用,则方法成为该事务的一部分,如果业务方法再事务范围外被调用,则方法再没有事务的环境下执行* R( d( C5 _- d9 m5 t
    Never:指定业务方法绝对不能在事务范围内执行,如果业务方法再某个事务中执行,容器会抛出异常,只有业务方法没有关联到任何事务,才能正常执行.8 j' t6 D* V2 u
    NESTED:如果一个活动的事务存在,则运行在一个嵌套的事务中,如果没有活动事务,则按REQUIRED属性执行,它使用了一个单独的事务,这个事务拥有多个可以回滚的保存点,内部事务的回滚不会对外部事务造成影响,它只对DataSourceTransactionManager事务管理器起效.Savepoint savepoint=conn.setSavepoint();conn.rollback(savepoint);
    8 O& f! w$ a& D6 [% ` 2 H3 f. M9 H% c2 p" L/ t
    (4).readOnly值为事务不能修改了.timeout是事务的超时时间,isolation数据库中的隔离级别.: ?5 o1 S" ~* p6 e

    " Y) P# m& |3 O, r(5).数据库系统提供了四种事务隔离级别供用户选择,不同的隔离级别采用不同的锁类型来实现,在四种隔离级别中,Serializable的隔离级别最高,Read Uncommited的隔离级别最低,大多数据库默认的隔离级别为Read Commited,如SQLServer,当然也有少部分数据库默认的隔离级别为Repeatable Read,如MySql
    5 r# n- ]8 h1 U1 D  lRead Uncommited:读未提交数据(会出现脏读,不可重复读和幻读)
    9 e- Q7 T: B% ?6 }" e5 lRead Commited:读已提交数据(会出现不可重复读和幻读)
    5 I3 i& q1 ?( \: g# G; LRepeatable Read:可重复读(会出现幻读)! E& o9 O4 ^9 S+ k. E
    Serializable:串行化
    $ R7 q% u" d" N6 @脏读:一个事务读取到另一个事务未提交的更新数据
    ) B5 L0 C% C$ e0 K不可重复读:在同一事务中,多次读取同一数据返回的结果有所不同,换句话说就是,后续读取可以读到另一事务已经提交的更新数据,相反,“可重复读”在同一事务中多次读取数据时,能够保证所读数据一样,也就是,后续读取不能读到另一事务已提交的更新数据.; Y  Z7 o, u: u' ]4 d: _' q4 H
    幻读:一个事务读取到另一个事务已提交的insert数据.
    红红火火恍恍惚惚
    回复

    使用道具 举报

    快速回复
    您需要登录后才可以回帖 登录 | 立即注册

       

    关闭

    站长推荐上一条 /1 下一条

    发布主题 快速回复 返回列表 联系我们 官方QQ群 科帮网手机客户端
    快速回复 返回顶部 返回列表