导图社区 Java EE
Java EE知识思维导图,包括:Java FrameWork;Struts2 + Spring Framework + Hibernate整合;Java Web;Spring MVC+Sping framework + MyBatis整合。
编辑于2022-06-28 21:17:29Java EE
Java FrameWork
Persistence Framework
Hibernate3
初始Hibernate
简介
一个实现了JPA标准的ORM(Object-Relationship-Map)持久化框架,常见的其它ORM框架还有Ibatis(MyBatis)
好处
简化了JDBC代码的写法,扩展性更好。
配置步骤
编写一个Hibernate程序的步骤 (1)读取并解析配置文件 Configuration conf = newConfiguration().configure(); (2)读取并解析映射信息,创建SessionFactory SessionFactory sessionFactory = conf.buildSessionFactory(); (3)打开Session Session session = sessionFactory.openSession(); (4)开始一个事务(增删改操作必须,查询操作可选) Transaction tx = session.beginTransaction(); (5)数据库操作 session.save(user);//或其它操作 (6)提交事务(若发生意外则回滚事务) tx.commit();(tx.rollback();) (7)关闭session session.close();
实现第一个例子
1.导入相应的jar包  NOTE:如果是Hibernate3.6-x的版本,必须要去掉hibernate-annotations.jar和hibernate-commons-annotations.jar包,因为在hibernate.jar包中已经包含了以上两个jar包的功能,否则会抛出异常:java.lang.NoSuchFieldError: INSTANCE;并且要导入hibernate-jpa-2.0-api-1.0.x.Final.jar包,否则会抛出java.lang.ClassNotFoundException: javax.persistence.EntityListene 2.在hibernate.cfg.xml文件中数据库连接的相关信息  3.生成hibernate工具类(可用ide工具自动生成)     4.创建模型    5.创建模型对应的映射文件User.hbm.xml  NOTE:DTD文档中的hibernate-mapping一定是PUBLIC的,不能是SYSTEM,以及文档路径是http://hibernate.sourceforge.net,不能是http://www.hibernate.org.否则在不联网的情况下会报错 6.把映射文件的信息添加到hibernate.cfg.xml文件中  7.用Junit4测试:保存用户到数据库中    附完整的hibernate.cfg.xml文件:   
Hibernate对象三种状态的关系
 1. 瞬时(Transient):由 new 操作符创建,且尚未与Hibernate Session 关联的对象被认定为瞬时(Transient)的。瞬时(Transient)对象不会被持久化到数据库中,也不会被赋予持久化标识( identifier)。 如果瞬时(Transient)对象在程序中没有被引用,它会被垃圾回收器(garbage collector)销毁。 使用 Hibernate Session可以将其变为持久(Persistent)状态。(Hibernate会自 动执行必要的SQL语句) 2. 持久(Persistent):持久(Persistent)的实例在数据库中有对应的记录,并拥有一个持久化标识(identifier)。 持久(Persistent)的实例可能是刚被保存的,或刚被加载的,无论哪一种,按定义,它存在于相关联的Session作用范围内。 Hibernate会检测到处于持久(Persistent)状态的对象的任何改动,在当前操作单元(unit of work)执行完毕时将对象数据(state)与数据库同步(synchronize)。 开发者不需要手动执行UPDATE。将对象从持 久(Persistent)状态变成瞬时(Transient)状态同样也不需要手动执行 DELETE 语句。 3. 脱管(Detached): 与持久(Persistent)对象关联的Session被关闭后,对象就变为脱管(Detached)的。对脱管(Detached)对象的引用依然有效,对象可继续被修改。脱管(Detached)对象如果重新关联到某个新的 Session 上, 会再次转变为持久(Persistent)的(在Detached其间的改动将被持久化到数据库)。 这个功能使得一种编程模型,即中间会给用 户思考时间(user think-time)的长时间运行的操作单元(unit of work)的编程模型成为可 能。我们称之为应用程序事务,即从用户观点看是一个操作单元(unit of work)。
Hibernate的延迟加载策略
Hibernate懒加载/延迟加载(Lazy)机制总结 1. Lazy的有效期:只有在session打开的时候才有效;session关闭后lazy就没效了。即持久化对象才有效 2. lazy策略可以用在: (1) 标签上:可以取值true/false (2) 标签上,可以取值true/false,这个特性需要类增强 (3)/等集合上,可以取值为true/false/extra (4)/等标签上,可以取值false/proxy/no-proxy 3.1 get和load的区别: (1) get不支持延迟加载,而load支持。 (2) 当查询特定的数据库中不存在的数据时,get会返回null,而load则抛出异常。 (3) get是获取真实对象,而load是获取的代理对象。 3.2 类(Class)的延迟加载: (1) 设置标签中的lazy="true",或是保持默认(即不配置lazy属性) (2) 如果lazy的属性值为true,那么在使用load方法加载数据时,只有确实用到数据的时候才会发出sql语句;这样有可能减少系统的开销。 (3) //不会发出查询sql System.out.println("group id=" + group.getId()); 这里有一个问题,为什么加载主键的时候不需要发出sql语句。(因为在将detached的对象转变为persistent对象的时候,调用Session对象的get/load方法的时候,已经传入了id) 3.3 集合(collection)的延迟加载:可以取值true,false,extra (1) 保持集合上的lazy的默认值,此时的效果和lazy="extra"是基本一样的。 (2) 设置集合上的lazy=extra,此时的效果和lazy属性的默认值是基本一样的。但是推荐使用这个属性值,因为在统计时这种情况显得比较智能。当然延迟是有效果的。 (3) 设置集合上的lazy=false true:默认取值,它的意思是只有在调用这个集合获取里面的元素对象时,才发出查询语句,加载其集合元素的数据 false:取消懒加载特性,即在加载对象的同时,就发出第二条查询语句加载其关联集合的数据 extra:一种比较聪明的懒加载策略,即调用集合的size/contains等方法的时候,hibernate并不会去加载整个集合的数据,而是发出一条聪明的SQL语句,以便获得需要的值,只有在真正需要用到这些集合元素对象数据的时候,才去发出查询语句加载所有对象的数据 3.4 Hibernate单端关联懒加载策略:即在/标签上可以配置 懒加载策略。可以取值为:false/proxy/no-proxy false:取消懒加载策略,即在加载对象的同时,发出查询语句,加载其关联对象 proxy:这是hibernate对单端关联的默认懒加载策略,即只有在调用到其关联对象的方法的时候才真正发出查询语句查询其对象数据,其关联对象是代理类 no-proxy:这种懒加载特性需要对类进行增强,使用no-proxy,其关联对象不是代理类 注意:在class标签上配置的lazy属性不会影响到关联对象!!!
Hibernate的ID生成策略
1、assigned 主键由外部程序负责生成,在 save() 之前必须指定一个。Hibernate不负责维护主键生成。与Hibernate和底层数据库都无关,可以跨数据库。在存储对象前,必须要使用主键的setter方法给主键赋值,至于这个值怎么生成,完全由自己决定,这种方法应该尽量避免。  注释方式  “ud”是自定义的策略名,人为起的名字,后面均用“ud”表示。 特点:可以跨数据库,人为控制主键生成,应尽量避免。 2、increment 由Hibernate从数据库中取出主键的最大值(每个session只取1次),以该值为基础,每次增量为1,在内存中生成主键,不依赖于底层的数据库,因此可以跨数据库。  Hibernate调用org.hibernate.id.IncrementGenerator类里面的generate()方法,使用select max(idColumnName) from tableName语句获取主键最大值。该方法被声明成了synchronized,所以在一个独立的Java虚拟机内部是没有问题的,然而,在多个JVM同时并发访问数据库select max时就可能取出相同的值,再insert就会发生Dumplicate entry的错误。所以只能有一个Hibernate应用进程访问数据库,否则就可能产生主键冲突,所以不适合多进程并发更新数据库,适合单一进程访问数据库,不能用于群集环境。 官方文档:只有在没有其他进程往同一张表中插入数据时才能使用,在集群下不要使用。 注释方式  特点:跨数据库,不适合多进程并发更新数据库,适合单一进程访问数据库,不能用于群集环境。 3、hilo hilo(高低位方式high low)是hibernate中最常用的一种生成方式,需要一张额外的表保存hi的值。保存hi值的表至少有一条记录(只与第一条记录有关),否则会出现错误。可以跨数据库。  hibernate_hilo 指定保存hi值的表名 next_hi 指定保存hi值的列名 100 指定低位的最大值 也可以省略table和column配置,其默认的表为hibernate_unique_key,列为next_hi  hilo生成器生成主键的过程(以hibernate_unique_key表,next_hi列为例): 1. 获得hi值:读取并记录数据库的hibernate_unique_key表中next_hi字段的值,数据库中此字段值加1保存。 2. 获得lo值:从0到max_lo循环取值,差值为1,当值为max_lo值时,重新获取hi值,然后lo值继续从0到max_lo循环。 3. 根据公式 hi * (max_lo + 1) + lo计算生成主键值。 注意:当hi值是0的时候,那么第一个值不是0*(max_lo+1)+0=0,而是lo跳过0从1开始,直接是1、2、3…… 那max_lo配置多大合适呢? 这要根据具体情况而定,如果系统一般不重启,而且需要用此表建立大量的主键,可以吧max_lo配置大一点,这样可以减少读取数据表的次数,提高效率;反之,如果服务器经常重启,可以吧max_lo配置小一点,可以避免每次重启主键之间的间隔太大,造成主键值主键不连贯。 注释方式  特点:跨数据库,hilo算法生成的标志只能在一个数据库中保证唯一。 4、seqhilo 与hilo类似,通过hi/lo算法实现的主键生成机制,只是将hilo中的数据表换成了序列sequence,需要数据库中先创建sequence,适用于支持sequence的数据库,如oracle。  注释方式  特点:与hilo类似,只能在支持序列的数据库中使用。 5、sequence 采用数据库提供的sequence机制生成主键,需要数据库支持sequence。如oralce、DB、SAP DB、PostgerSQL、McKoi中的sequence。MySQL这种不支持sequence的数据库则不行(可以使用identity)。  hibernate_id 指定sequence的名称 Hibernate生成主键时,查找sequence并赋给主键值,主键值由数据库生成,Hibernate不负责维护,使用时必须先创建一个sequence,如果不指定sequence名称,则使用Hibernate默认的sequence,名称为hibernate_sequence,前提要在数据库中创建该sequence。 注释方式  特点:只能在支持序列的数据库中使用,如Oracle。 6、identity identity由底层数据库生成标识符。identity是由数据库自己生成的,但这个主键必须设置为自增长,使用identity的前提条件是底层数据库支持自动增长字段类型,如DB2、SQL Server、MySQL、Sybase和HypersonicSQL等,Oracle这类没有自增字段的则不支持。  例:如果使用MySQL数据库,则主键字段必须设置成auto_increment。 id int(11) primary key auto_increment 注释方式  特点:只能用在支持自动增长的字段数据库中使用,如MySQL。 7、native native由hibernate根据使用的数据库自行判断采用identity、hilo、sequence其中一种作为主键生成方式,灵活性很强。如果能支持identity则使用identity,如果支持sequence则使用sequence。  例如MySQL使用identity,Oracle使用sequence 注意:如果Hibernate自动选择sequence或者hilo,则所有的表的主键都会从Hibernate默认的sequence或hilo表中取。并且,有的数据库对于默认情况主键生成测试的支持,效率并不是很高。 使用sequence或hilo时,可以加入参数,指定sequence名称或hi值表名称等,如 hibernate_id 注释方式  特点:根据数据库自动选择,项目中如果用到多个数据库时,可以使用这种方式,使用时需要设置表的自增字段或建立序列,建立表等。 8、uuid UUID:Universally Unique Identifier,是指在一台机器上生成的数字,它保证对在同一时空中的所有机器都是唯一的。按照开放软件基金会(OSF)制定的标准计算,用到了以太网卡地址、纳秒级时间、芯片ID码和许多可能的数字,标准的UUID格式为: xxxxxxxx-xxxx-xxxx-xxxxxx-xxxxxxxxxx (8-4-4-4-12) 其中每个 x 是 0-9 或 a-f 范围内的一个十六进制的数字。  Hibernate在保存对象时,生成一个UUID字符串作为主键,保证了唯一性,但其并无任何业务逻辑意义,只能作为主键,唯一缺点长度较大,32位(Hibernate将UUID中间的“-”删除了)的字符串,占用存储空间大,但是有两个很重要的优点,Hibernate在维护主键时,不用去数据库查询,从而提高效率,而且它是跨数据库的,以后切换数据库极其方便。 注释方式  特点:uuid长度大,占用空间大,跨数据库,不用访问数据库就生成主键值,所以效率高且能保证唯一性,移植非常方便,推荐使用。 9、guid GUID:Globally Unique Identifier全球唯一标识符,也称作 UUID,是一个128位长的数字,用16进制表示。算法的核心思想是结合机器的网卡、当地时间、一个随即数来生成GUID。从理论上讲,如果一台机器每秒产生10000000个GUID,则可以保证(概率意义上)3240年不重复。  Hibernate在维护主键时,先查询数据库,获得一个uuid字符串,该字符串就是主键值,该值唯一,缺点长度较大,支持数据库有限,优点同uuid,跨数据库,但是仍然需要访问数据库。 注意:长度因数据库不同而不同 MySQL中使用select uuid()语句获得的为36位(包含标准格式的“-”) Oracle中,使用select rawtohex(sys_guid()) from dual语句获得的为32位(不包含“-”) 注释方式  特点:需要数据库支持查询uuid,生成时需要查询数据库,效率没有uuid高,推荐使用uuid。 10、foreign 使用另外一个相关联的对象的主键作为该对象主键。主要用于一对一关系中。  该例使用domain.User的主键作为本类映射的主键。 注释方式  特点:很少使用,大多用在一对一关系中。 11、select 使用触发器生成主键,主要用于早期的数据库主键生成机制,能用到的地方非常少。 12、其他注释方式配置 注释方式与配置文件底层实现方式相同,只是配置的方式换成了注释方式 自动增长,适用于支持自增字段的数据库  根据底层数据库自动选择方式,需要底层数据库的设置 如MySQL,会使用自增字段,需要将主键设置成auto_increment。  使用表存储生成的主键,可以跨数据库。 每次需要主键值时,查询名为"hibernate_table"的表,查找主键列"gen_pk"值为"2"记录,得到这条记录的"gen_val"值,根据这个值,和allocationSize的值生成主键值。  使用序列存储主键值  13、小结 1、为了保证对象标识符的唯一性与不可变性,应该让Hibernate来为主键赋值,而不是程序。 2、正常使用Hibernate维护主键,最好将主键的setter方法设置成private,从而避免人为或程序修改主键,而使用assigned方式,就不能用private,否则无法给主键赋值。 2、Hibernate中唯一一种最简单通用的主键生成器就是uuid。虽然是个32位难读的长字符串,但是它没有跨数据库的问题,将来切换数据库极其简单方便,推荐使用! 3、自动增长字段类型与序列  4、关于hilo机制注意: hilo算法生成的标志只能在一个数据库中保证唯一。 当用户为Hibernate自行提供连接,或者Hibernate通过JTA,从应用服务器的数据源获取数据库连接时,无法使用hilo,因为这不能保证hilo单独在新的数据库连接的事务中访问hi值表,这种情况,如果数据库支持序列,可以使用seqhilo。 5、使用identity、native、GenerationType.AUTO等方式生成主键时,只要用到自增字段,数据库表的字段必须设置成自动增加的,否则出错。 6、还有一些方法未列出来,例如uuid.hex,sequence-identity等,这些方法不是很常用,且已被其他方法代替,如uuid.hex,官方文档里建议不使用,而直接使用uuid方法。 7、Hibernate的各版本主键生成策略配置有略微差别,但实现基本相同。如,有的版本默认sequence不指定序列名,则使用名为hibernate_sequence的序列,有的版本则必须指定序列名。 8、还可以自定义主键生成策略,这里暂时不讨论,只讨论官方自带生成策略。
hibernate查询
HQL查询
HQL查询: Criteria查询对查询条件进行了面向对象封装,符合编程人员的思维方式,不过HQL(Hibernate Query Lanaguage)查询提供了更加丰富的和灵活的查询特性,因此 Hibernate将HQL查询方式立为官方推荐的标准查询方式,HQL查询在涵盖Criteria查询的所有功能的前提下,提供了类似标准SQL语句的查询方式,同时也提供了更 加面向对象的封装。完整的HQL语句形势如下: Select/update/delete…… from …… where …… group by …… having …… order by …… asc/desc 其中的update/delete为Hibernate3中所新添加的功能,可见HQL查询非常类似于标准SQL查询。由于HQL查询在整个Hibernate实体操作体系中的核心地位,这一节我 将专门围绕HQL操作的具体技术细节进行讲解。 1、 实体查询: 有关实体查询技术,其实我们在先前已经有多次涉及,比如下面的例子: String hql=”from User user ”; List list=session.CreateQuery(hql).list(); 上面的代码执行结果是,查询出User实体对象所对应的所有数据,而且将数据封装成User实体对象,并且放入List中返回。这里需要注意的是,Hibernate的实体查 询存在着对继承关系的判定,比如我们前面讨论映射实体继承关系中的Employee实体对象,它有两个子类分别是HourlyEmployee,SalariedEmployee,如果有这样的 HQL语句:“from Employee”,当执行检索时Hibernate会检索出所有Employee类型实体对象所对应的数据(包括它的子类HourlyEmployee,SalariedEmployee对应 的数据)。 因为HQL语句与标准SQL语句相似,所以我们也可以在HQL语句中使用where字句,并且可以在where字句中使用各种表达式,比较操作符以及使用“and”,”or”连接 不同的查询条件的组合。看下面的一些简单的例子: from User user where user.age=20; from User user where user.age between 20 and 30; from User user where user.age in(20,30); from User user where user.name is null; from User user where user.name like ‘%zx%’; from User user where (user.age%2)=1; from User user where user.age=20 and user.name like ‘%zx%’; 2、 实体的更新和删除: 在继续讲解HQL其他更为强大的查询功能前,我们先来讲解以下利用HQL进行实体更新和删除的技术。这项技术功能是Hibernate3的新加入的功能,在Hibernate2 中是不具备的。比如在Hibernate2中,如果我们想将数据库中所有18岁的用户的年龄全部改为20岁,那么我们要首先将年龄在18岁的用户检索出来,然后将他们的 年龄修改为20岁,最后调用Session.update()语句进行更新。在Hibernate3中对这个问题提供了更加灵活和更具效率的解决办法,如下面的代码: Transaction trans=session.beginTransaction(); String hql=”update User user set user.age=20 where user.age=18”; Query queryupdate=session.createQuery(hql); int ret=queryupdate.executeUpdate(); trans.commit(); 通过这种方式我们可以在Hibernate3中,一次性完成批量数据的更新,对性能的提高是相当的可观。同样也可以通过类似的方式来完成delete操作,如下面的代码 : Transaction trans=session.beginTransaction(); String hql=”delete from User user where user.age=18”; Query queryupdate=session.createQuery(hql); int ret=queryupdate.executeUpdate(); trans.commit(); 如果你是逐个章节阅读的化,那么你一定会记起我在第二部分中有关批量数据操作的相关论述中,讨论过这种操作方式,这种操作方式在Hibernate3中称为bulk delete/update,这种方式能够在很大程度上提高操作的灵活性和运行效率,但是采用这种方式极有可能引起缓存同步上的问题(请参考相关论述)。 3、 属性查询: 很多时候我们在检索数据时,并不需要获得实体对象所对应的全部数据,而只需要检索实体对象的部分属性所对应的数据。这时候就可以利用HQL属性查询技术 ,如下面程序示例: List list=session.createQuery(“select user.name from User user ”).list(); for(int i=0;i System.out.println(list.get(i)); } 我们只检索了User实体的name属性对应的数据,此时返回的包含结果集的list中每个条目都是String类型的name属性对应的数据。我们也可以一次检索多个属性, 如下面程序: List list=session.createQuery(“select user.name,user.age from User user ”).list(); for(int i=0;i Object[] obj=(Object[])list.get(i); System.out.println(obj[0]); System.out.println(obj[1]); } 此时返回的结果集list中,所包含的每个条目都是一个Object[]类型,其中包含对应的属性数据值。作为当今我们这一代深受面向对象思想影响的开发人员,可能 会觉得上面返回Object[]不够符合面向对象风格,这时我们可以利用HQL提供的动态构造实例的功能对这些平面数据进行封装,如下面的程序代码: List list=session.createQuery(“select new User(user.name,user.age) from User user ”).list(); for(int i=0;i User user=(User)list.get(i); System.out.println(user.getName()); System.out.println(user.getAge()); } 这里我们通过动态构造实例对象,对返回结果进行了封装,使我们的程序更加符合面向对象风格,但是这里有一个问题必须注意,那就是这时所返回的User对象, 仅仅只是一个普通的Java对象而以,除了查询结果值之外,其它的属性值都为null(包括主键值id),也就是说不能通过Session对象对此对象执行持久化的更新操 作。如下面的代码: List list=session.createQuery(“select new User(user.name,user.age) from User user ”).list(); for(int i=0;i User user=(User)list.get(i); user.setName(“gam”); session.saveOrUpdate(user);//这里将会实际执行一个save操作,而不会执行update操作,因为这个User对象的id属性为null,Hibernate会把它作为一个自由对 象(请参考持久化对象状态部分的论述),因此会对它执行save操作。 } 4、 分组与排序 A、Order by子句: 与SQL语句相似,HQL查询也可以通过order by子句对查询结果集进行排序,并且可以通过asc或者desc关键字指定排序方式,如下面的代码: from User user order by user.name asc,user.age desc; 上面HQL查询语句,会以name属性进行升序排序,以age属性进行降序排序,而且与SQL语句一样,默认的排序方式为asc,即升序排序。 B、Group by子句与统计查询: 在HQL语句中同样支持使用group by子句分组查询,还支持group by子句结合聚集函数的分组统计查询,大部分标准的SQL聚集函数都可以在HQL语句中使用,比如: count(),sum(),max(),min(),avg()等。如下面的程序代码: String hql=”select count(user),user.age from User user group by user.age having count(user)>10 ”; List list=session.createQuery(hql).list(); C、优化统计查询: 假设我们现在有两张数据库表,分别是customer表和order表,它们的结构如下: customer ID varchar2(14) age number(10) name varchar2(20) order ID varchar2(14) order_number number(10) customer_ID varchar2(14) 现在有两条HQL查询语句,分别如下: from Customer c inner join c.orders o group by c.age;(1) select c.ID,c.name,c.age,o.ID,o.order_number,o.customer_ID from Customer c inner join c.orders c group by c.age;(2) 这两条语句使用了HQL语句的内连接查询(我们将在HQL语句的连接查询部分专门讨论),现在我们可以看出这两条查询语句最后所返回的结果是一样的,但是它们 其实是有明显区别的,语句(1)检索的结果会返回Customer与Order持久化对象,而且它们会被置于Hibernate的Session缓存之中,并且Session会负责它们在缓存 中的唯一性以及与后台数据库数据的同步,只有事务提交后它们才会从缓存中被清除;而语句(2)返回的是关系数据而并非是持久化对象,因此它们不会占用 Hibernate的Session缓存,只要在检索之后应用程序不在访问它们,它们所占用的内存就有可能被JVM的垃圾回收器回收,而且Hibernate不会同步对它们的修改。 在我们的系统开发中,尤其是Mis系统,不可避免的要进行统计查询的开发,这类功能有两个特点:第一数据量大;第二一般情况下都是只读操作而不会涉及到对统 计数据进行修改,那么如果采用第一种查询方式,必然会导致大量持久化对象位于Hibernate的Session缓存中,而且Hibernate的Session缓存还要负责它们与数据 库数据的同步。而如果采用第二种查询方式,显然就会提高查询性能,因为不需要Hibernate的Session缓存的管理开销,而且只要应用程序不在使用这些数据,它 们所占用的内存空间就会被回收释放。 因此在开发统计查询系统时,尽量使用通过select语句写出需要查询的属性的方式来返回关系数据,而避免使用第一种查询方式返回持久化对象(这种方式是在有 修改需求时使用比较适合),这样可以提高运行效率并且减少内存消耗。㊣真正的高手并不是精通一切,而是精通在合适的场合使用合适的手段。 5、 参数绑定: Hibernate中对动态查询参数绑定提供了丰富的支持,那么什么是查询参数动态绑定呢?其实如果我们熟悉传统JDBC编程的话,我们就不难理解查询参数动态绑定, 如下代码传统JDBC的参数绑定: PrepareStatement pre=connection.prepare(“select * from User where user.name=?”); pre.setString(1,”zhaoxin”); ResultSet rs=pre.executeQuery(); 在Hibernate中也提供了类似这种的查询参数绑定功能,而且在Hibernate中对这个功能还提供了比传统JDBC操作丰富的多的特性,在Hibernate中共存在4种参数绑 定的方式,下面我们将分别介绍: A、按参数名称绑定: 在HQL语句中定义命名参数要用”:”开头,形式如下: Query query=session.createQuery(“from User user where user.name=:customername and user.customerage=:age ”); query.setString(“customername”,name); query.setInteger(“customerage”,age); 上面代码中用:customername和:customerage分别定义了命名参数customername和customerage,然后用Query接口的setXXX()方法设定名参数值,setXXX()方法包 含两个参数,分别是命名参数名称和命名参数实际值。 B、按参数位置邦定: 在HQL查询语句中用”?”来定义参数位置,形式如下: Query query=session.createQuery(“from User user where user.name=? and user.age =? ”); query.setString(0,name); query.setInteger(1,age); 同样使用setXXX()方法设定绑定参数,只不过这时setXXX()方法的第一个参数代表邦定参数在HQL语句中出现的位置编号(由0开始编号),第二个参数仍然代表参 数实际值。 注:在实际开发中,提倡使用按名称邦定命名参数,因为这不但可以提供非常好的程序可读性,而且也提高了程序的易维护性,因为当查询参数的位置发生改变时 ,按名称邦定名参数的方式中是不需要调整程序代码的。 C、setParameter()方法: 在Hibernate的HQL查询中可以通过setParameter()方法邦定任意类型的参数,如下代码: String hql=”from User user where user.name=:customername ”; Query query=session.createQuery(hql); query.setParameter(“customername”,name,Hibernate.STRING); 如上面代码所示,setParameter()方法包含三个参数,分别是命名参数名称,命名参数实际值,以及命名参数映射类型。对于某些参数类型setParameter()方法可 以更具参数值的Java类型,猜测出对应的映射类型,因此这时不需要显示写出映射类型,像上面的例子,可以直接这样写: query.setParameter(“customername”,name);但是对于一些类型就必须写明映射类型,比如java.util.Date类型,因为它会对应Hibernate的多种映射类型,比如 Hibernate.DATA或者Hibernate.TIMESTAMP。 D、setProperties()方法: 在Hibernate中可以使用setProperties()方法,将命名参数与一个对象的属性值绑定在一起,如下程序代码: Customer customer=new Customer(); customer.setName(“pansl”); customer.setAge(80); Query query=session.createQuery(“from Customer c where c.name=:name and c.age=:age ”); query.setProperties(customer); setProperties()方法会自动将customer对象实例的属性值匹配到命名参数上,但是要求命名参数名称必须要与实体对象相应的属性同名。 这里还有一个特殊的setEntity()方法,它会把命名参数与一个持久化对象相关联,如下面代码所示: Customer customer=(Customer)session.load(Customer.class,”1”); Query query=session.createQuery(“from Order order where order.customer=:customer ”); query. setProperties(“customer”,customer); List list=query.list(); 上面的代码会生成类似如下的SQL语句: Select * from order where customer_ID=’1’; E、使用绑定参数的优势: 我们为什么要使用绑定命名参数?任何一个事物的存在都是有其价值的,具体到绑定参数对于HQL查询来说,主要有以下两个主要优势: ①、可以利用数据库实施性能优化,因为对Hibernate来说在底层使用的是PrepareStatement来完成查询,因此对于语法相同参数不同的SQL语句,可 以充分利用预编译SQL语句缓存,从而提升查询效率。 ②、可以防止SQL Injection安全漏洞的产生: SQL Injection是一种专门针对SQL语句拼装的攻击方式,比如对于我们常见的用户登录,在登录界面上,用户输入用户名和口令,这时登录验证程序可能会生成如 下的HQL语句: “from User user where user.name=’”+name+”’ and user.password=’”+password+”’ ” 这个HQL语句从逻辑上来说是没有任何问题的,这个登录验证功能在一般情况下也是会正确完成的,但是如果在登录时在用户名中输入”zhaoxin or ‘x’=’x”, 这时如果使用简单的HQL语句的字符串拼装,就会生成如下的HQL语句: “from User user where user.name=’zhaoxin’ or ‘x’=’x’ and user.password=’admin’ ”; 显然这条HQL语句的where字句将会永远为真,而使用户口令的作用失去意义,这就是SQL Injection攻击的基本原理。 而使用绑定参数方式,就可以妥善处理这问题,当使用绑定参数时,会得到下面的HQL语句: from User user where user.name=’’zhaoxin’’ or ‘’x=’’x’’ ‘ and user.password=’admin’;由此可见使用绑定参数会将用户名中输入的单引号解 析成字符串(如果想在字符串中包含单引号,应使用重复单引号形式),所以参数绑定能够有效防止SQL Injection安全漏洞。
Criteria查询
Criteria 基本查询 要对资料库管理系统进行操作,最基本的就是使用SQL(Standard Query Language)语句,大部份的资料库都支援标准的SQL语句,然而也有一些特定于资料库的SQL语句,应用程式配合SQL语句进行资料库查询时,若使用到特定于资料库的SQL语句,程式本身会有相依于特定资料库的问题。 使用Hibernate时,即使您不了解SQL的使用与撰写,也可以使用它所提供的API来进行SQL语句查询, org.hibernate.Criteria对SQL进行封装,您可以从Java物件的观点来组合各种查询条件,由Hibernate自动为您产生 SQL语句,而不用特别管理SQL与资料库相依的问题,就某个程度的意涵来看,这就像是在编译时期也可以得到对SQL语法的检查与验证。 以最基本的查询来说,如果您想要查询某个物件所对应的资料表中所有的内容,您可以如下进行查询: Criteria criteria = session.createCriteria(User.class); List users = criteria.list(); for(Iterator it = users.iterator(); it.hasNext(); ) { User user = (User) it.next(); System.out.println(user.getId() + " \t " + user.getName() + "/" + user.getAge()); } Criteria建立后,若不给予任何的条件,预设是查询物件所对应表格之所有资料,如果您执行以上的程式片段,并于设定档中设定了了Hibernate的”show_sql”属性,则可以在主控下看到以下的SQL语句之产生: Hibernate: select this_.id as id0_0_, this_.name as name0_0_, this_.age as age0_0_ from T_USER this_ org.hibernate.Criteria实际上是个条件附加的容器,如果想要设定查询条件,则要使用 org.hibernate.criterion.Restrictions的各种静态方法传回 org.hibernate.criterion.Criteria实例,传回的每个org.hibernate.criterion.Criteria 实例代表着一个条件,您要使用org.hibernate.Criteria的add()方法加入这些条件实例,例如查询” age”大于20且小于40的资料: Criteria criteria = session.createCriteria(User.class); criteria.add(Restrictions.gt("age", new Integer(20))); criteria.add(Restrictions.lt("age", new Integer(40))); List users = criteria.list(); for(Iterator it = users.iterator(); it.hasNext(); ) { User user = (User) it.next(); System.out.println(user.getId() + " \t " + user.getName() + "/" + user.getAge()); } Restrictions的gt()方法表示大于(great than)的条件,而lt表示小于(less than)的条件,执行以上程式片段,观察所产生的SQL语句,将使用where与and子句产来完成SQL的条件查询: Hibernate: select this_.id as id0_0_, this_.name as name0_0_, this_.age as age0_0_ from T_USER this_ where this_.age>? and this_.age 使用add()方法加入条件时,预设是使用and来组合条件,如果要用or的方式来组合条件,则可以使用Restrictions.or()方法,例如结合age等于(eq)20或(or)age为空(isNull)的条件: Criteria criteria = session.createCriteria(User.class); criteria.add(Restrictions.or( Restrictions.eq("age", new Integer(20)), Restrictions.isNull("age") )); List users = criteria.list(); 观察所产生的SQL语句,将使用where与or子句完成SQL的条件查询: Hibernate: select this_.id as id0_0_, this_.name as name0_0_, this_.age as age0_0_ from T_USER this_ where (this_.age=? or this_.age is null) 您也可以使用Restrictions.like()方法来进行SQL中like子句的功能,例如查询”name”中名称为”just”开头的资料: Criteria criteria = session.createCriteria(User.class); criteria.add(Restrictions.like("name", "just%")); List users = criteria.list(); 观察所产生的SQL语句如下: Hibernate: select this_.id as id0_0_, this_.name as name0_0_, this_.age as age0_0_ from T_USER this_ where this_.name like ? Restrictions的几个常用限定查询方法如下表所示: 
命名查询
命名查询示例一: User.java public class User { private int id; private String name; private int age; public int getId() { return id; } public void setId(int id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } } User.hbm.xml "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd"> hibernate.cfg.xml "-//Hibernate/Hibernate Configuration DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd"> com.mysql.jdbc.Driver jdbc:mysql://localhost:8080/hibernate3?createDatabaseIfNotExist=treu root sunhuaying org.hibernate.dialect.MySQLDialect true true update NamedQuery.java public class NamedQuery { public static void main(String[] args) { List users = getUsers(); for (User user : users) { System.out.println(user.getName()); } } public static List getUsers() { Session session = null; try { session = HibernateUtil.getSession(); Query query = session.getNamedQuery("getUserByName"); return query.setString("name", "alan").list(); } catch (HibernateException e) { e.printStackTrace(); throw e; } finally { if (session != null) { session.close(); } } } } 命名查询示例二: User.java public class User { private int id; private String name; private int age; public int getId() { return id; } public void setId(int id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } } User.hbm.xml "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd"> hibernate.cfg.xml "-//Hibernate/Hibernate Configuration DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd"> com.mysql.jdbc.Driver jdbc:mysql://localhost:8080/hibernate3?createDatabaseIfNotExist=treu root sunhuaying org.hibernate.dialect.MySQLDialect true true update NamedQuery.java public class NamedQuery { public static void main(String[] args) { List users = getUsers(); for (User user : users) { System.out.println(user.getName()); } } public static List getUsers() { Session session = null; try { session = HibernateUtil.getSession(); Query query = session.getNamedQuery("getUserByName"); return query.setString("name", "alan").list(); } catch (HibernateException e) { e.printStackTrace(); throw e; } finally { if (session != null) { session.close(); } } } } 本地SQL示例: public class Base { public static void main(String[] args) { List users = getUsers(); for (User user : users) { System.out.println(user.getName()); } } public static List getUsers() { Configuration cfg = new Configuration(); cfg.configure(); SessionFactory sessionFactory = cfg.buildSessionFactory(); Session session = null; try { session = sessionFactory.openSession(); Query query = session.createSQLQuery("select * from t_user") .addEntity(User.class); return query.list(); } catch (HibernateException e) { throw e; } finally { if (session != null) { session.close(); } } } }
Hibernate关联映射
1.在我们以前的数据库设计中,设计表就不是一件轻松的事情。多种事物之间往往都是有这样那样的关系的。那怎样设计表格,才能既将事情描述明白,又能使数据库设计的比较合理呢?那里我们提供了好多规范,好多约束来满足这些事情。在hibernate中,通过对象来创建表,当然也需要有一些东西来维护各个对象之间的关系,以创建出合适的表。这个东西就是映射。通过映射,可以轻松的将对象间的关系表述的非常清楚明白。对象间关系搞明白了,数据库表自然也就出来了。那我们先看一下关联映射。关联映射就是将关联关系映射到数据库中,所谓的关联关系在对象模型中就是一个或多个引用。 2.通过,这么多映射的比较,他们之间确实是有几点共性的地方。 假设有关系A,B两端。若A与B有单向关联,即A—>B,则A的实体类中会有B作为他的属性。若B端为一(非多),则A中多一个B类型的属性,若B端为多,则A中多一个Set类型的集合,而它存储的类型当然为B。这跟A端是多或者一是没有关系的。那映射文件呢?若one-to-one或者many-to-one,one端name为另一端实体类中实例化的变量的名字。而若是*-to-many,则many一端name为多的一端的对象类所在的位置。哪个包下边哪个类。若实体中出现了Set类型的属性,那映射文件中对应的也会有Set标签。
一对多关联映射
多对一的单项关联映射
举个例子来说用户和组。一个组中有多个用户,一个用户只能属于一组。用户和组之间就是一个多对一的关系的。 如下图  这个关系我们要怎样维护呢?我们想象一下,假如在一的一端维护关系,即在group一端加一个字段userId来标识学生。那设计出来的表格存储数据是这个样子的  不解释,直接看在多的一端维护关系  不用说,大家就知道在多的一端维护数据冗余要少的多。怎么来解释这个问题呢?大家想一下是多的记少的容易记,还是少的记多的容易记呢?举个例子员工和老板。你说是老板记员工比较容易还是员工记老板比较容易呢?很明显记少的比较容易啊,能维护二者的关系也能减少工作量。hibernate当然也是这么做的。看一下实体和配置文件。这里只显示部分代码。  对应的关系映射文件 
一对多的单项关联映射
一对多关联映射和多对一关联映射的原理确实是一致的,都是在多的一端加入外键,指向一的一端。 但是他们也是有区别的。我们先看一下他的实现。我们仍然采用一对多的学生用户举例。  看一下对应的配置文件又是怎么实现的呢?  这里面并没有体现出任何与一的一端有关联的字段。一对多的关联最后生成的表格与多对一是一样的。但是他们到底有什么区别呢?多对一的维护关系是多指向一的关系,有了此关系,在加载多的时候可以将一加载上来。即我们查询用户的时候,组也被查询出来了。而一对多的关系,是指在加载一的时候可以将多加载进来。即查询组的时候,用户也被查出来了。他们适用于不同的需求。 刚开始我们说过,不管是一对多还是多对一,都是在多的一端维护关系。从程序的执行状况来解释一下这样做的原因。若在一的一端维护关系,多的一端User并不知道Group的存在。所以在保存User的时候,关系字段groupId是为null的。如果将该关系字段设置为非空,则将无法保存数据。另外因为User不维护关系,Group维护关系,则在对Group进行操作时,Group就会发出多余的update语句,去维持Group与User的关系,这样加载Group的时候才会把该Group对应的学生加载进来。可见一对多关联映射是存在这很大的问题的。那怎么解决这些问题呢?我们可以尝试用一对多双向关联去实现。
一对多的双向关联映射
一对多的双向关联实际上可以认为是一对多的单向关联和多对一的单向关联的组合 双向一对多实体类实现:  接下来看一下映射文件  通过一对多双向关联映射,我们将关系交给多的一端维护,而且从一的一端也能够看到多的一端。这样就很好的解决了一对多单向关联的缺陷,优化之后的它查询数据,不管是一的一端还是多的一端,只需要一个sql语句就搞定了。要知道他不是由于需求驱动而设计的。 NOTE:在User.hbm.xml文件的many-to-one元素中的属性name值应该为User对象模型中引用的Group的引用名group,而不是groupId.
一对一映射
一对一共享主键关联映射
一对一主键关联映射:我们来拿人和身份证举例。采用一对一主键关联映射,他要表达的意思是人的id来自于身份证的id。要想在人的表中存数据,在身份证表中必须有对应的id的身份证记录。如图所示  表关系为:  我们看一下具体的实现: 实体类实现:  对应的映射文件设置为:  采用一对一主键关联映射,两个实体的主键一样,这样就不需要加入多余的字段了。我们在加载维护关系的一端如例子中提到的Person时,从他的实体类实现也可以看出,要给他的IdCard属性赋值。但是赋值时,是不需要必须显示的用session的save将其变为Persistent状态的。因为一对一的主键关联映射已经为我们执行了此操作。也可以说他默认了cascade级联特性。说了一对一主键关联映射,自然也少不了双向关联映射。一对一主键双向关联映射和单向关联映射在存储方式上是没有什么不同的。但在查询上会稍有不同。看一下实现: 实体实现:  从实体类上来说,每端都能看到另一端的对象。查询的时候,我们自然可以从任意一段进行查询了。这是一点不同。再来看映射文件  可以看出Person的映射文件与单向关联是完全一样的。在单向关联中,我们只能从Person一端查询的时候才可以查询到IdCard端的值。如果我们只查询IdCard的属性,Person是不能被查询到的。而在双向关联中,我们则可以通过任意一端查询到另外一端的属性。单项和双项在这一区别上得到了验证。
一对一外键关联映射
参考多对一的用户与组的例子。我们在多的一端维护关系,最后生成的表格中,多的一端User中多出来一个字段groupId用来存储组的主键。这里,多个用户可以属于同一组。即在用户这个表中,groupId字段的值是可以重复的。但有的时候可能这个外键值是不可以重复的。比如仍然举身份证与人的例子。我们可以采用上面的一对一主键关联映射。当然,我们也可以在人的表中添加一个字段身份证id来存储信息。但只是这样还不够,不同的人拥有相同的身份证号码这是不允许的。那怎么办呢?一对一唯一外键映射来给我们解决这个问题。先来看一下图。 人与身份证的实体关系图  最后生成表关系图  下面来看具体代码的实现。 实体类实现:  看一下映射文件  普通的实体类映射。从上面可以看出,一对一唯一外键映射的重点就是Person一端的many-to-one标签的设定。同一对一主键关联映射一样,看完单向之后,我们来看一下一对一唯一外键映射的双向关联。同样拿人与身份证进行举例。 实体类实现:  对应的映射文件:  一对一唯一外键关联映射双向采用标签映射,而且是必须指定。这也是在映射文件的实现上他们唯一的区别。
多对多映射
多对多的单项关联映射
举个例子来说,就是用户与角色。一个用户可以属于多个角色,而一个角色又可以拥有多个用户。文字表述就是这样了,看一下用图的直观表示。 实体关系图  表格关系图  当关系两方有一端为一时,我们让多的一端维护关系,可以减少数据的冗余,提高效率。那要是多对多的话,如果还在某一方维护数据,那缺点就一点都没有避免。那怎么解决这个问题呢?我们呢采用第三张表格来维护这个关系。即上图中间用户角色表。将关系两方的主键抽离出来放到第三张表中。最大程度的减少冗余性。看一下具体实现。 实体类实现:  对应的映射文件实现 
多对多的双项关联映射
看完单向,与前面一样,我们看一下双向。 实体类代码实现:  对应的配置文件的实现:  多对多双向关联的实现原理也是新增一张表,使两端的主键联系起来。所以,在映射文件的配置时,生成的中间表名称必须是一样的,比如这里的set标签中定义的table字段t_user_role。除了表名称以外,表字段名称也必须是一样的如此处set标签中的column标签的值。
组合映射
组合映射: 组件映射实体关系图为:  生成的表格为:  具体的代码实现为:  相应的配置文件 
集合映射
集合映射生成表之间的关系  
继承映射
继承映射: 在对象中,是有继承的概念的。那既然Hibernate实现表采用的是一种面向对象的方式,则当然也少不了继承的概念。举个例子来说,小猪和小鸟都是动物,他们都有一些共同的属性。如他们都有一个id,有姓名,有性别。但是呢,小猪有重量,小鸟游高度。这就是一个继承关系了,在Hibernate中我们怎么实现它呢?先把这个情况用图来表示一下  实现后表的形式为:    这三种方式所生成的表格想必大家不难理解。那hibernate中具体是怎么实现的呢? 这三种方式的实体类构造完全是一样的。实现形式如下:  关联映射的映射文件都是与实体相对应的,每个实体对应一个映射文件。继承映射就省去了这些麻烦。他们只需要一个映射文件就搞定了。我们看一下不同方式的继承映射文件是如何实现的。 要注意,继承映射中映射文件的名称为Extends.hbm.xml    继承映射,对于鉴别值的存储时hibernate自动完成的,不需要我们手动去存储,其他的存储与关联映射没有区别。但是,对于查找来说,他就发挥出他的优势了。多态查找的功能,你不必确切的知道他属于哪个类,只要知道父类就能完成查找功能。get和hql方式是完全支持多态查询的,但是load有些特殊。他在lazy设置为false的情况下支持多态查询,当为true时,则不支持。我们看一个例子体会一下多态查询。  通过instanceof可以自动鉴别出具体是哪个对象的实例。对它的三种实现形式,采用表格比照的方式,将他们的实现形式以及实现的结果进行了展示。三种方式适合与不同的场景。具体什么时候应用根据他们的实现结果,根据需求我们在判断吧!
hibernate连接查询
连接查询: 关系型数据库之所以强大,其中一个原因就是可以统一使用表来管理同类数据信息,并且可以在相关数据之间建立关系。作为支持关系型数据库的SQL语句来说,自然要对全面发挥这种强大功能提供支持,这个支持就是连接查询。同样作为一种关系型数据库的持久层框架,Hibernate也对连接查询提供了丰富的支持,在Hibernate中通过HQL与QBC两种查询方式都可以支持连接查询。下面这一部分我们将通过这两种查询技术,来详细讨论有关Hibernate对连接查询支持的各个细节。在讲解连接查询之前,我们先来回忆一下在有关实体关联关系的映射,在实体的配置文件中可以通过配置集合元素来指定对关联实体的映射以及检索策略。因此我们可以在实体映射配置文件中,指定关联实体检索策略,对关联实体的检索策略可以指定为“延迟检索”,“立即检索”,“迫切左外连接检索”,如下所示对与School实体关联的Student实体设置延迟加载:,这种在实体映射配置文件中设定的检索策略,称为默认检索策略,但是这种默认检索策略是可以被覆盖的,那就是在程序代码当中可以动态指定各种迫切检索策略来覆盖默认检索策略。
隐式(implicit)连接查询
hibernate中的隐式连接即:使用英文符号(.)来隐式的连接关联实体,隐式连接的底层将会被转换成为SQL中的交叉连接 例1:语句内连接  生成的交叉连接的SQL语句如下:  例2:条件内连接  生成的SQL语句如下:  NOTE:尽量避免使用隐式连接查询
显示(explicit)连接
hibernate中显示连接的底层将会被转换成为inner join left join right join等连接
内连接查询
 生成的SQL语句如下: 
左连接查询
查询语句:  查询结果: 
右连接查询
查询语句:  查询结果: 
不相关子查询
查询语句:  查询结果: 
相关子查询
查询代码:  查询结果: 
hibernate抓取策略fetch详解
多对一的抓取策略
单端抓取策略
hibernate抓取策略(单端代理的批量抓取fetch=select(默认)/join) 测试用例:  1)保持默认,同fetch="select",如:  fetch="select",另外发送一条select语句抓取当前对象关联实体或集合 执行结果:2条语句  2)设置fetch="join",如:  fetch="join",hibernate会通过select语句使用外连接来加载其关联实体或集合 此时lazy会失效 执行结果:一条join语句 
批量抓取策略
对于多对一的批量抓取策略,批量抓取属性batch-size,可以批量加载实体类,把该属性添加到一方映射文件的class属性里即可
一对多的抓取策略
单端抓取策略
测试用例:  (1)保持默认,同fetch="select",如:   (2)设置fetch="join",如:  fetch="join",hibernate会通过select语句使用外连接来加载其关联实体或集合 此时lazy会失效 测试结果:1条独立的join查询语句  (3)设置fetch="subselect",如:用在查询语句中  fetch="subselect",另外发送一条select语句抓取在前面查询到的所有实体对象的关联集合 测试用例:  当不设fetch="subselect" ,即:,结果如下: 执行了3条查询语句  当设fetch="subselect" ,即:,结果如下: 执行了1条查询语句(嵌套子查询) 
批量抓取策略
对于一对多的批量抓取策略,批量抓取属性batch-size,可以批量加载实体类,把该属性添加到多方映射文件的Set属性里即可
抓取策略总结
总结: 1.hibernate抓取策略(单端代理的批量抓取) 保持默认,同fetch="select",如: fetch="select",另外发送一条select语句抓取当前对象关联实体或集合 2.hibernate抓取策略(单端代理的批量抓取) 设置fetch="join",如: fetch="join",hibernate会通过select语句使用外连接来加载其关联实体或集合 此时lazy会失效 3.hibernate抓取策略(集合代理的批量抓取) 保持默认,同fetch="select",如: fetch="select",另外发送一条select语句抓取当前对象关联实体或集合 4.hibernate抓取策略(集合代理的批量抓取) 设置fetch="join",如: fetch="join",hibernate会通过select语句使用外连接来加载其关联实体或集合 此时lazy会失效 5.hibernate抓取策略(集合代理的批量抓取) 设置fetch="subselect",如: fetch="subselect",另外发送一条select语句抓取在前面查询到的所有实体对象的关联集合 6.hibernate抓取策略,,batch-szie在上的应用 batch-size属性,可以批量加载实体类,参见:Classes.hbm.xml 当查classes对象时发出9条hql语句配置过后batch-size=3后会之发9/3=3条hql语句,提高性能 7.hibernate抓取策略,batch-szie在集合上的应用 batch-size属性,可以批量加载实体类,参见:Classes.hbm.xml 当查students对象时发出10条hql语句配置过后batch-size=5后会之发10/5=2条hql语句,提高性能 总体上分析:默认是fetch="select" 当配置fetch="join"时直接查询包含的对象或者集合lazy失效。
hibernate缓存
Hibernate的缓存包括Session缓存和SessionFactory缓存。 SessionFactory缓存又可以分为两类:内置缓存和外置缓存。 Session缓存和SessionFactory内置缓存都是内置的,不能被卸载。它们也被称为Hibernate的一级缓存。 SessionFactory的内置缓存和Session缓存在实现方式上比较相似,前者是SessionFactory对象的一些集合属性包含的数据,后者是Session的一些集合属性包含的数据。 SessionFactory内置缓存中存放了映射元数据和预定义SQL语句,映射元数据是映射文件中数据的拷贝,而预定义SQL语句在Hibernate初始化阶段根据映射元数据推倒出来,SessionFactory的内置缓存是只读的,应用程序不能修改缓存中的映射元数据和预定义SQL语句,因此SessionFactory不需要进行内置缓存与映射文件的同步。 SessionFactory的外置缓存是一个可配置的插件,在默认情况下,SessionFactory不会启用这个插件。 外置缓存的数据是数据库数据的拷贝,外置缓存的介质可以是内存或者硬盘。SessionFactory的外置缓存也被称为Hibernate的第二级缓存。
内置(一级)缓存
Session缓存和SessionFactory内置缓存都是内置的,不能被卸载。它们也被称为Hibernate的一级缓存。 SessionFactory的内置缓存和Session缓存在实现方式上比较相似,前者是SessionFactory对象的一些集合属性包含的数据,后者是Session的一些集合属性包含的数据。 SessionFactory内置缓存中存放了映射元数据和预定义SQL语句,映射元数据是映射文件中数据的拷贝,而预定义SQL语句在Hibernate初始化阶段根据映射元数据推到出来,SessionFactory的内置缓存是只读的,应用程序不能修改缓存中的映射元数据和预定义SQL语句,因此SessionFactory不需要进行内置缓存与映射文件的同步。
外置缓存
SessionFactory的外置缓存是一个可配置的插件,在默认情况下,SessionFactory不会启用这个插件。 外置缓存的数据是数据库数据的拷贝,外置缓存的介质可以是内存或者硬盘。SessionFactory的外置缓存也被称为Hibernate的第二级缓存。
二级缓存
使用背景
(一)Hibernate的二级缓存策略的一般过程如下: 1) 条件查询的时候,总是发出一条select * from table_name where …. (选择所有字段)这样的SQL语句查询数据库,一次获得所有的数据对象。 2) 把获得的所有数据对象根据ID放入到第二级缓存中。 3) 当Hibernate根据ID访问数据对象的时候,首先从Session一级缓存中查;查不到,如果配置了二级缓存,那么从二级缓存中查;查不到,再查询数据库,把结果按照ID放入到缓存。 4) 删除、更新、增加数据的时候,同时更新缓存。 Hibernate的二级缓存策略,是针对于ID查询的缓存策略,对于条件查询则毫无作用。为此,Hibernate提供了针对条件查询的Query Cache。 (二)什么样的数据适合存放到第二级缓存中? 1 很少被修改的数据 2 不是很重要的数据,允许出现偶尔并发的数据 3 不会被并发访问的数据 4 参考数据,指的是供应用参考的常量数据,它的实例数目有限,它的实例会被许多其他类的实例引用,实例极少或者从来不会被修改。 (三)不适合存放到第二级缓存的数据? 1 经常被修改的数据 2 财务数据,绝对不允许出现并发 3 与其他应用共享的数据。
使用示例
使用EhCache配置二级缓存: 配置准备: 1)把ehcache-1.2.3.jar加入到当前应用的classpath中。 2)在hibernate.cfg.xml文件中加入EhCache缓存插件的提供类  3)挎贝ehcache.xml文件到类路径(项目工程的src目录下),这个文件在Hibernate安装目录的etc下。 配置步骤: Hibernate允许在类和集合的粒度上设置第二级缓存。在映射文件中,和元素都有一个子元素,这个子元素用来配置二级缓存。 示例:以category(产品类别)和product(产品)的映射为例: 1) 修改要配置缓存的那个持久化类的对象关系映射文件: Category.hbm.xml  Product.hbm.xml  2)编辑ehcache.xml文件:  打开二级缓存就不会出现N+1问题,因为二级缓存会缓存对象 NOTE:查询缓存一定要在查询语句固定的时候打开,而且如果要使用查询缓存,就必须开启二级缓存,否则会出现N+1问题 在Spring托管的Hibernate中使用二级缓存 1.在spring的配置文件中,hibernate部分加入 xml 代码 org.hibernate.cache.EhCacheProvider true 2.为HBM表设置cache策略 xml 代码 3.在DAO中,调用find方法查询之前,设置使用缓存 Java代码 getHibernateTemplate().setCacheQueries(true); 补充: 如果不设置“查询缓存”,那么hibernate只会缓存使用load()方法获得的单个持久化对象,如果想缓存使用findall()、list()、Iterator()、createCriteria()、createQuery()等方法获得的数据结果集的话,就需要设置 hibernate.cache.use_query_cache true 才行。
查询缓存
hibernate查询缓存(hibernate默认是关闭的) 查询缓存是针对普通属性结果集的缓存 对实体对象的结果集只缓存id 查询缓存的生命周期,当前关联的表发生修改,那么查询缓存生命周期结束 查询缓存的配置和使用: 1. 启用查询缓存:在hibernate.cfg.xml中加入: true 2. 在程序中必须手动启用查询缓存,如:query.setCacheable(true); 测试查询缓存: 一. 开启查询缓存,关闭二级缓存,开启一个session,分别调用query.list (查询属性) Query query = session.createQuery(“select s.name from Student s”); //启用查询缓存 query.setCacheable(true); List names = query.list(); for(Iterator iter = names.terator();iter.hasNext();) { String name = (String)iter.next(); System.out.println(name); } System.out.println(“------------------------------------------”); query = session.createQuery(“select s.name from Student s”); //启用查询缓存 query.setCacheable(true); names = query.list(); for(Iterator iter = names.terator();iter.hasNext();) { String name = (String)iter.next(); System.out.println(name); } 第二次没有去查询数据库,因为启用了查询缓存 二.开启查询缓存,关闭二级缓存,开启两个session,分别调用query.list()(查询属性) Query query = session.createQuery(“select s.name from Student s”); //启用查询缓存 query.setCacheable(true); List names = query.list(); for(Iterator iter = names.terator();iter.hasNext();) { String name = (String)iter.next(); System.out.println(name); } session.close(); System.out.println(“------------------------------------------”); ……… Query query = session.createQuery(“select s.name from Student s”); //启用查询缓存 query.setCacheable(true); List names = query.list(); for(Iterator iter = names.terator();iter.hasNext();) { String name = (String)iter.next(); System.out.println(name); } 第二次没有去查询数据库,因为查询缓存生命周期与session生命周期无关 三. 开启查询缓存,关闭二级缓存,开启两个session,分别调用query.iterate() (查询属性) Query query = session.createQuery(“select s.name from Student s”); //启用查询缓存 query.setCacheable(true); for(Iterator iter =query.iterate();iter.hasNext();) { String name = (String)iter.next(); System.out.println(name); } session.close(); System.out.println(“------------------------------------------”); ……… Query query = session.createQuery(“select s.name from Student s”); //启用查询缓存 query.setCacheable(true); for(Iterator iter = query.iterate();iter.hasNext();) { String name = (String)iter.next(); System.out.println(name); } 第二去查询数据库,因为查询缓存只对query.list()起作用,对query.iterate()不起作用,也就是说query.iterate()不使用查询缓存 四. 关闭查询缓存,关闭二级缓存,开启两个session,分别调用query.list (查询实体对象) Query query = session.createQuery(“ from Student s”); //query.setCacheable(true); List students = query.list(); for(Iterator iter = students.iterate();iter.hasNext();){ Student stu = (Student)iter.next(); System.out.println(stu.getName()); } session.close(); System.out.println(“------------------------------------------”); ……… Query query = session.createQuery(“ from Student s”); //query.setCacheable(true); List students = query.list(); for(Iterator iter = students.iterate();iter.hasNext();){ Student stu = (Student)iter.next(); System.out.println(stu.getName()); } 第二去查询数据库,因为list默认每次都会发出查询sql 五. 开启查询缓存,关闭二级缓存,开启两个session,分别调用query.list() (查询实体对象) Query query = session.createQuery(“ from Student s”); query.setCacheable(true); List students = query.list(); for(Iterator iter = students.iterate();iter.hasNext();){ Student stu = (Student)iter.next(); System.out.println(stu.getName()); } session.close(); System.out.println(“------------------------------------------”); ……… Query query = session.createQuery(“ from Student s”); query.setCacheable(true); List students = query.list(); for(Iterator iter = students.iterate();iter.hasNext();){ Student stu = (Student)iter.next(); System.out.println(stu.getName()); } 第二去查询数据库时,会发出N条sql语句,因为开启了查询缓存,关闭了二级缓存,那么查询缓存会缓存实体对象的id,所以hibernate会根据实体对象的id去查询相应的实体,如果缓存中不存在相应的实体,那么将发出根据实体id查询的sql语句,否则不会发出sql,使用缓存中的数据 六. 开启查询缓存,开启二级缓存,开启两个session,分别调用query.list() (查询实体对象) Query query = session.createQuery(“ from Student s”); query.setCacheable(true); List students = query.list(); for(Iterator iter = students.iterate();iter.hasNext();) { Student stu = (Student)iter.next(); System.out.println(stu.getName()); } session.close(); System.out.println(“------------------------------------------”); ……… Query query = session.createQuery(“ from Student s”); query.setCacheable(true); List students = query.list(); for(Iterator iter = students.iterate();iter.hasNext();) { Student stu = (Student)iter.next(); System.out.println(stu.getName()); } 第二不会发出sql,因为开启了二级缓存和查询缓存,查询缓存缓存了实体对象的id列表,hibernate会根据实体对象的id列表到二级缓存中取得相应的数据
hibernate并发访问的锁策略
Hibernate支持两种锁机制: 即通常所说的“悲观锁(Pessimistic Locking)”和 “乐观锁(OptimisticLocking)”。
悲观锁
悲观锁的实现,往往依靠数据库提供的锁机制(也只有数据库层提供的锁机制才能真正保证数据访问的排他性,否则,即使在本系统中实现了加锁机制,也无法保证外部系统不会修改数据)。 Hibernate的加锁模式有: Ø LockMode.NONE : 无锁机制。 Ø LockMode.WRITE :Hibernate在Insert和Update记录的时候会自动 获取。 Ø LockMode.READ : Hibernate在读取记录的时候会自动获取。 以上这三种锁机制一般由Hibernate内部使用,如Hibernate为了保证Update 过程中对象不会被外界修改,会在save方法实现中自动为目标对象加上WRITE锁。 Ø LockMode.UPGRADE :利用数据库的for update子句加锁。 Ø LockMode. UPGRADE_NOWAIT :Oracle的特定实现,利用Oracle的for update nowait子句实现加锁。 Hibernate的悲观锁,也是基于数据库的锁机制实现。 下面的代码实现了对查询记录的加锁:  获取数据 query.setLockMode 对查询语句中特定别名所对应的记录进行加锁(我们为 TUser类指定了一个别名“user”),这里也就是对返回的所有user记录进行加锁。 观察运行期Hibernate生成的SQL语句:   这里Hibernate通过使用数据库的for update子句实现了悲观锁机制。 上面这两种锁机制是我们在应用层较为常用的,加锁一般通过以下方法实现: Criteria.setLockMode Query.setLockMode Session.lock 注意,只有在查询开始之前(也就是Hiberate 生成SQL 之前)设定加锁,才会 真正通过数据库的锁机制进行加锁处理,否则,数据已经通过不包含for update 子句的Select SQL加载进来,所谓数据库加锁也就无从谈起。
乐观锁
乐观锁,大多是基于数据版本(Version)记录机制实现。何谓数据版本?即为数据增加一个版本标识,在基于数据库表的版本解决方案中,一般是通过为数据库表增加一个“version”字段来实现。读取出数据时,将此版本号一同读出,之后更新时,对此版本号加一。此时,将提交数据的版本数据与数据库表对应记录的当前版本信息进行比对,如果提交的数据版本号大于数据库表当前版本号,则予以更新,否则认为是过期数据。 乐观锁例子: 1.实体类:  2.映射文件  3.测试类:  
MyBatis3
MVC框架
Struts2
简介
Struts2介绍
1. Struts2是基于MVC的轻量级的web应用框架。 所谓框架:就是能完成一定功能的半成品软件。在没有框架的时候,所有的工作都要乖乖的从零做起;有了框架,它为我们提供了一定的功能,就可以在框架的基础上做起,大大提高开发的效率和质量。 web应用框架,这说明Struts2的应用范围是Web应用而不是其它地方。Struts2更注重将Web应用领域的日常工作和常见问题抽象化,提供一个平台让我们能快速的完成Web应用开发。 轻量级:是相对于重量级而言,指的是Struts2在运行的时候,对Web服务器的资源消耗较少,比如CPU、内存等,但是运行速度相对较快。 基于MVC,说明基于Struts2开发的Web应用自然就能实现MVC,也说明Struts2着力于在MVC的各个部分为我们的开发提供相应帮助。 2. Struts2基本组成 WebWork与Struts合并之后,根据功能的细分和设计,拆分出一个叫xwork的部分,用来处理与Web无关的部分,也就是与Servlet无关的部分,比如:用户数据的类型转换、动作调用之前的数据验证、动作的调用等等。其余与Web相关的部分,也就是与servlet相关的部分,被称为struts2部分。因此请注意,此处的“Struts2”可以理解为一个模块,是Struts2框架的一部分,如:如何接收用户请求的数据,如何跳转到下一个页面等等。其中struts2部分调用了xwork部分,但是xwork部分是不依赖于Struts2部分的,xwork是完全独立的、纯Java的应用。因此,可以用下图来表示struts2部分和xwork部分的关系。  3. Struts2能干什么 了解了Struts2是什么和有什么后,看看Struts2可以做什么? Struts2通过简单、集中的配置来调度动作类,使得我们配置和修改都非常容易。 Struts2提供简单、统一的表达式语言来访问所有可供访问的数据。 Struts2提供内存式的数据中心,所有可供访问的数据都集中存放在内存中,在调用中不需要将数据传来传去,都去这个内存数据中心访问即可。 Struts2提供在动作类执行的前或后附加执行一定功能的能力,能实现AOP。 Struts2提供标准的、强大的验证框架和国际化框架,且与Struts2的其他特性紧密结合。 类似的功能很多,不胜枚举。 4. Struts2和MVC的关系 Struts2是一种基于MVC的Web应用框架,下面看看Struts2和MVC的关系。这里只是先讲一下Struts2是如何跟MVC对应的,其中一些名词所代表的具体功能,比如前端控制器(FilterDispatcher)、动作(Action)、结果(Result)等。在之后的学习中会不断深入具体的细节。  控制器——FilterDispatcher 用户请求首先到达前端控制器FilterDispatcher。FilterDispatcher负责根据用户提交的URL和struts.xml中的配置,来选择合适的动作(Action),让这个Action来处理用户的请求。FilterDispatcher其实是一个过滤器(Filter,servlet规范中的一种web组件),它是Struts2核心包里已经做好的类,不需要我们去开发,只是要在项目的web.xml中配置一下即可。FilterDispatcher体现了J2EE核心设计模式中的前端控制器模式。 动作——Action 在用户请求经过FilterDispatcher之后,被分发到了合适的动作Action对象。Action负责把用户请求中的参数组装成合适的数据模型,并调用相应的业务逻辑进行真正的功能处理,获取下一个视图展示所需要的数据。Struts2 的Action,相比于别的web框架的动作处理,它实现了与Servlet API的解耦,使得Action里面不需要再直接去引用和使用HttpServletRequest与HttpServletResponse等接口。 因而使得Action的单元测试更加简单,而且强大的类型转换也使得我们少做了很多重复的工作。 视图——Result 视图结果用来把动作中获取到的数据展现给用户。在Struts2中有多种优秀的结果展示方式,常规的jsp,模板 freemarker、velocity,还有各种其它专业的展示方式,如图表jfreechart、报表JasperReports、将XML转化为 HTML的XSLT等等。而且各种视图结果在同一个工程里面可以混合出现。 看到这里,大家应该大致知道了Struts2是什么,能干什么,粗略的了解到Struts2里面有什么了,接下来就是究竟如何使用Struts2来开发基于MVC的Web应用了。
struts2的配置及第一个例子
1. 添加struts2的支持  2. 在部署描述符(web.xml)中添加struts2过滤器:  NOTE:此处添加的是struts2.1以上的版本,如果是Struts2.0的版本,则需要添加的过滤器为FilterDispatcher 3. 在src下生成struts.xml配置文件:  4.写模型文件Action类,一般情况Action类要继承ActionSupport类  5. 将Action配置到struts.xml文件中  6.生成相应的映射结果页面helloWorldResult.jsp  7. 把该项目部署到Tomcat容器中,打开浏览器访问http://localhost:8080/struts2.3/helloWorld即看到结果: 
Struts2的原理图
 Configuration manager是xwork配置管理的核心类,包含了门面模式,读取配置信息交给ActionProxy ActionProxy实现了xwork和struts的融合,两个不同的层之间合并成为了可能,远程代理 ActionInvocation是一个表示action的执行状态,它拥有拦截器和action的实例,
struts2框架教程
网址:http://www.dzone.com/tutorials/java/struts-2/struts-2-tutorial/struts-2-framework-tutorial-1.html Struts 2 Framework Tutorial A framework tries to automate the common tasks and provides a platform for the users to build applications quickly. Struts 2 is based on the OpenSymphony Web Works Framework. Struts 2 framework implements the Model-View-Controller (MVC) design pattern. In Struts 2 the model, view and controller are implemented by the action, result and FilterDispatcher respectively. The controller's job is to map the user request to appropriate action. In Struts 2 FilterDispatcher does the job of Controller. Model contains the data and the business logic. In Struts 2 the model is implemented by the Action component. View is the presentation component of the MVC Pattern. In Struts 2 the view is commonly implemented using JSP, Velocity Template, Freemaker or some other presentation-layer technology.  The controller receives the user request and determine which Struts 2 action to invoke. The framework creates an instance of this action and associate it with the newly created instance of the ActionInvocation. In Struts 2 the invocation of action should pass through a series of interceptors as defined in the application's XML file. The framework calls the ActionInvocations invoke() method to start the execution of the action. Each time the invoke() method is called, ActionInvocation consults its state and executes whichever interceptor comes next. ActionInvocation hands control over to the interceptor in the stack by calling the interceptors intercept() method. The intercept() method of the interceptor inturn calls the invoke() method of the ActionInvocation till all the interceptors are invoked, in the end the action itself will be called and the corresponding result will be returned back to the user. Some interceptor do work before the action is executed and some do work after the action is executed. It's not necessary that it should do something each time it is invoked. These interceptors are invoke both before and after the action. First all the interceptors are executed in the order they are defined in the stack. Then the action is invoked and the result is generated. Again all the interceptors present in the stack are invoked in the reverse order. The other important features of Struts 2 are OGNL and ValueStack. Object-Graph Navigation Language (OGNL) is a powerful expression language that is used to reference and manipulate data on the ValueStack. OGNL help in data transfer and type conversion. OGNL expression language provides simplified stytax to reference java objects. OGNL is used to bind the java-side data properties to the string-based view layer.  In Struts 2 the action resides on the ValueStack which is a part of the ActionContext. ActionContext is a global storage area that holds all the data associated with the processing of a request. When a request comes the params interceptor helps in moving the request data to the ValueStack. Now the OGNL does the job of converting the string based form data to their corresponding java types. OGNL does this by using the set of available built-in type converters. Again when the results are generated the OGNL converts the java types of the property on the ValueStack to the string-based HTML output. ActionContext is thread local which means that the values stored in the ActionContext are unique per thread, this makes the Struts 2 actions thread safe.
struts2的配置文件
struts-default——struts-Plugin。xml
驱动方式
属性驱动
属性驱动:属性要有getter和setter方法,然后表单提交来的值被填充到相应的setter方法, 获取该属性值本质上是调用getter方法。对于显示结果页面,可以直接使用EL语言或struts2的property标签直接获取该属性值。 struts2的标签本质上是封装的OGNL语言,对于Struts2的标签而言,它有一个OGNL的根对象叫ValueStack,所有的属性都是放在ValueStack中,放在ValueStack中的数据都可以在结果页面上直接获取其值 NOTE:属性驱动的名称一定要和表单提交过来的名称保持一致 属性驱动示例: 提交到属性驱动测试类PropertyDrivenAction的表单: pagelanguage="java"import="java.util.*"pageEncoding="UTF-8"%> taglibprefix="s"uri="/struts-tags"%> DOCTYPEHTMLPUBLIC"-//W3C//DTD HTML 4.01 Transitional//EN"> html> head> title>My JSP 'propertyDriven.jsp' starting pagetitle> metahttp-equiv="pragma"content="no-cache"> metahttp-equiv="cache-control"content="no-cache"> metahttp-equiv="expires"content="0"> metahttp-equiv="keywords"content="keyword1,keyword2,keyword3"> metahttp-equiv="description"content="This is my page"> head> body> s:formaction="propertyDriven"> s:textfieldname="username"label="用户名"/> br> s:textfieldname="age"label="年 龄"/> br> s:submitvalue="提交"/> s:form> body> html> NOTE:Struts2的标签库引入可以从项目中导入的struts2-core-2.x.jar的包中的struts-tags.tld中获取,如下图:  打开struts-tags.tld文件引入下图中的名称和uri即可:  属性驱动测试Action类: import com.opensymphony.xwork2.ActionSupport; @SuppressWarnings("serial") publicclassPropertyDrivenActionextends ActionSupport { private String username; privateintage; public String getUsername() { returnusername; } publicvoid setUsername(String username) { this.username = username; } publicint getAge() { returnage; } publicvoid setAge(int age) { this.age = age; } public String execute() throws Exception { returnSUCCESS; } } 把该类配置到struts.xml中的文件中 actionname="propertyDriven"class="org.pb.struts2.action.driven_type.property_basic.PropertyDrivenAction"> resultname="success"type="dispatcher">/propertyDrivenResult.jspresult> action> 映射到的结果页面propertyDrivenResult.jsp pagelanguage="java"import="java.util.*"pageEncoding="UTF-8"%> taglibprefix="s"uri="/struts-tags"%> DOCTYPEHTMLPUBLIC"-//W3C//DTD HTML 4.01 Transitional//EN"> html> head> title>My JSP 'propertyDrivenResult.jsp' starting pagetitle> metahttp-equiv="pragma"content="no-cache"> metahttp-equiv="cache-control"content="no-cache"> metahttp-equiv="expires"content="0"> metahttp-equiv="keywords"content="keyword1,keyword2,keyword3"> metahttp-equiv="description"content="This is my page"> head> body> 属性驱动中的${username} 和${requestScope.age}访问成功!br> 用户名:s:propertyvalue="username"/>和 年龄:s:propertyvalue="age"/>br> 用户名:s:propertyvalue="%{username}"/>和 年龄:s:propertyvalue="%{age}"/>br> body> html>
模型驱动
实现ModelDriven接口
模型驱动方式一: 通过实现ModelDriven接口方式 对于提交表单和显示结果页面的标记而言,直接写模型相对应的属性名即可 实现ModelDriven方式的模型驱动示例: 模型驱动方式提交表单页面 pagelanguage="java"import="java.util.*"pageEncoding="UTF-8"%> taglibprefix="s"uri="/struts-tags"%> DOCTYPEHTMLPUBLIC"-//W3C//DTD HTML 4.01 Transitional//EN"> html> head> title>My JSP 'modelDriven.jsp' starting pagetitle> metahttp-equiv="pragma"content="no-cache"> metahttp-equiv="cache-control"content="no-cache"> metahttp-equiv="expires"content="0"> metahttp-equiv="keywords"content="keyword1,keyword2,keyword3"> metahttp-equiv="description"content="This is my page"> head> body> s:formaction="modelDriven"> s:textfieldname="username"label="用户名"/>br> s:textfieldname="age"label="年 龄"/>br> s:submitvalue="提交"/> s:form> body> html> 模型驱动所使用的模型User publicclass User { private String username; privateintage; public String getUsername() { returnusername; } publicvoid setUsername(String username) { this.username = username; } publicint getAge() { returnage; } publicvoid setAge(int age) { this.age = age; } } 模型驱动方式表单提交到的ModelDrivenAction类 publicclassModelDrivenActionextends ActionSupport implements ModelDriven { private User user = new User(); public User getUser() { returnuser; } publicvoid setUser(User user) { this.user = user; } public String execute() throws Exception { returnSUCCESS; } @Override public User getModel() { returnuser; } } 把该类配置到struts.xml中的文件中 actionname="modelDriven"class="org.pb.struts2.action.driven_type.model_basic.model_driven_impl.ModelDrivenAction"> resultname="success"type="dispatcher">/modelDrivenResult.jspresult> action> 映射到的结果页面modelDrivenResult.jsp pagelanguage="java"import="java.util.*"pageEncoding="UTF-8"%> taglibprefix="s"uri="/struts-tags"%> DOCTYPEHTMLPUBLIC"-//W3C//DTD HTML 4.01 Transitional//EN"> html> head> title>My JSP 'modelDrivenResult.jsp' starting pagetitle> metahttp-equiv="pragma"content="no-cache"> metahttp-equiv="cache-control"content="no-cache"> metahttp-equiv="expires"content="0"> metahttp-equiv="keywords"content="keyword1,keyword2,keyword3"> metahttp-equiv="description"content="This is my page"> head> body> 用户名:s:propertyvalue="username"/>和 年龄:s:propertyvalue="age"/>br> 用户名:s:propertyvalue="%{username}"/>和 年龄:s:propertyvalue="%{age}"/>br> body> html>
领域对象作为Java Bean
模型驱动之通过领域对象模型的方式实现模型驱动 领域对象模型的方式实现模型驱动示例: 领域对象模型的方式提交表单页面: pagelanguage="java"import="java.util.*"pageEncoding="UTF-8"%> taglibprefix="s"uri="/struts-tags"%> DOCTYPEHTMLPUBLIC"-//W3C//DTD HTML 4.01 Transitional//EN"> html> head> title>My JSP 'domainObjectDriven.jsp' starting pagetitle> metahttp-equiv="pragma"content="no-cache"> metahttp-equiv="cache-control"content="no-cache"> metahttp-equiv="expires"content="0"> metahttp-equiv="keywords"content="keyword1,keyword2,keyword3"> metahttp-equiv="description"content="This is my page"> head> body> s:form action="domainObjectDriven"> s:textfieldname="user.username"label="用户名"/>br> s:textfieldname="user.age"label="年 龄"/>br> s:submitvalue="提交"/> s:form> body> html> NOTE:领域对象模型提交的表单标记名称前一定要加上模型属性的名字,即模型属性名一定要与Action中的属性名称保持一致 领域对象模型所使用的模型User publicclass User { private String username; privateintage; public String getUsername() { returnusername; } publicvoid setUsername(String username) { this.username = username; } publicint getAge() { returnage; } publicvoid setAge(int age) { this.age = age; } } 领域对象模型方式表单提交到的DoMainObjectDrivenAction类 publicclassDoMainObjectDrivenActionextends ActionSupport { private User user; public User getUser() { returnuser; } publicvoid setUser(User user) { this.user = user; } public String execute() throws Exception { returnSUCCESS; } } 把该类配置到struts.xml中的文件中 actionname="domainObjectDriven"class="org.pb.struts2.action.driven_type.model_basic.domain_object_basic.DoMainObjectDrivenAction"> resultname="success"type="dispatcher">/domainObjectDrivenResult.jsp result> action> 映射到的结果页面domainObjectDrivenResult.jsp pagelanguage="java"import="java.util.*"pageEncoding="UTF-8"%> taglibprefix="s"uri="/struts-tags"%> DOCTYPEHTMLPUBLIC"-//W3C//DTD HTML 4.01 Transitional//EN"> html> head> title>My JSP 'domainObjectDrivenResult.jsp' starting pagetitle> metahttp-equiv="pragma"content="no-cache"> metahttp-equiv="cache-control"content="no-cache"> metahttp-equiv="expires"content="0"> metahttp-equiv="keywords"content="keyword1,keyword2,keyword3"> metahttp-equiv="description"content="This is my page"> head> body> 用户名:s:propertyvalue="user.username"/>和 年龄:s:propertyvalue="user.age"/>br> 用户名:s:propertyvalue="%{user.username}"/>和 年龄:s:propertyvalue="%{user.age}"/>br> body> html>
获取范围值的三种方式
获取范围之前,先看一张图:  所有的属性值都放在ValueStack中,但如果是在Class-Action中的execute方法中设置属性值,需要根据获取的范围取值。 1.如果把数据保存到Reques范围内,在获取该值时就要用%{#request.attribute_name}获取该数据。 2.如果把数据保存到Session范围内,在获取该值时就要用%{#session.attribute_name}获取该数据。 3.如果把数据保存到Application范围内,在获取该值时就要用%{#application.attribute_name}获取该数据。 4.如果在获取值时要用%{#attr.attribute_name}获取该数据,则其会按照request--session--application的范围顺序查找,直到找到该范围的值才停止查找。 5.parameters基本不用。
获取Request范围的三种方式
ActionContext获取Request范围值
通过ActionContext获取Request范围并获取值的示例如下: Action类所用模型对象:   Action类示例代码:  Action类在struts.xml中的配置:  Action类所映射的结果页面:  
ServletActionContext获取Request范围值
通过ServletActionContext获取Request范围并获取值的示例如下: Action类所用模型对象:   Action类示例代码:  Action类在struts.xml中的配置:  Action类所映射的结果页面:  
RequestAware获取Request范围值
通过ActionContext获取Request范围并获取值的示例如下: Action类所用模型对象:   Action类示例代码:  Action类在struts.xml中的配置:  Action类所映射的结果页面:  
获取Session范围的三种方式
ActionContext获取Session范围值
通过ActionContext获取Session范围并获取值的示例如下: Action类所用模型对象:   Action类示例代码:  Action类在struts.xml中的配置:  Action类所映射的结果页面:  
ServletActionContext获取Session范围值
通过ActionContext获取Session范围并获取值的示例如下: Action类所用模型对象:   Action类示例代码:  Action类在struts.xml中的配置:  Action类所映射的结果页面:  
SessiontAware获取Session范围值
通过ActionContext获取Session范围并获取值的示例如下: Action类所用模型对象:   Action类示例代码:   Action类在struts.xml中的配置:  Action类所映射的结果页面:  
获取Application范围的三种方式
ActionContext获取Application范围值
通过ActionContext获取Application范围并获取值的示例如下: Action类所用模型对象:   Action类示例代码:   Action类在struts.xml中的配置:  Action类所映射的结果页面:  
ServletActionContext获取Application范围值
通过ActionContext获取Application范围并获取值的示例如下: Action类所用模型对象:   Action类示例代码:   Action类在struts.xml中的配置:  Action类所映射的结果页面:  
ApplicationAware获取Application范围值
通过ActionContext获取Application范围并获取值的示例如下: Action类所用模型对象:   Action类示例代码:   Action类在struts.xml中的配置:  Action类所映射的结果页面:  
校验
代码校验
课程来源:http://www.dzone.com/tutorials/java/struts-2/struts-2-example/struts-2-validation-example-1.html Struts 2 Validation Tutorial In this example we will see how we can validate a login page using Struts 2. Let's first create the login page. We use Struts UI tags to create the login page. The tag should be placed in the head section of the HTML page. The s:head tag automatically generates links to the css and javascript libraries that are necessary to render the form elements. The s:form tag contains all the form elements. The action attribute contains the action name to wich the form should be submitted. This action name should be same as the one specified in the XML declarative architecture. In this example we use struts.xml file to do the configuration. The textfield tag is used create a text box. The label attribute of the textfield tag contains the name to be displayed on the page and the name attribute contains the name of the property in the action class to be mapped. The password tag is same as the textfield tag except that the input value is masked. The submit tag is used to create a submit button, the value "Login" represents the label of the button. Note that the code is simple without any HTML tables, this is because Struts 2 will automatically create the necessary tables for the page based on the theme selected. By default the XHTML theme is selected.  When the user clicks the Login button the request will be forwarded to the Login action. We do the action mapping using the struts.xml file. First we need to create a package for our action.  Here our "default" package extends "struts-default" package. By extending the "struts-default" package the action will by default inherit the set of interceptors defined in the defaultstack. The "struts-default" package is defined in the struts-default.xml file. All the common tasks done by the Actions are seperated and placed in different interceptors. You can define an interceptor stack for each action. Most commonly used interceptors are grouped in defaultstack of the struts-default package. The defaultstack will be sufficient in most cases. The inteceptors will be fired in the order in which they are declared in the stack both before and after the action is executed. Here the "Login" action is mapped to the "Login" class in the "vaannila" package. The results are defined using the "" element. If any validation errors occur the user will be forwarded to the login.jsp page. If the login is successfull then the user will be forwarded to the success.jsp page. The defaultstack contains the following interceptors.  Our Login Action class extends ActionSupport. It is good to extend ActionSupport class as it provides default implementation for most common tasks.  The ActionSupport class implements Action interface which exposes the execute() method. The following constants are declared in the Action interface which can be used as return values in the execute() method. public static final String ERROR = "error" public static final String INPUT = "input" public static final String LOGIN = "login" public static final String NONE = "none" public static final String SUCCESS = "success" ERROR is returned when the action execution fails. INPUT is returned when the action requires more input from the user. LOGIN is returned when the user is not logged into the system. NONE is returned when the action execution is successfull and there are no views to display. SUCCESS is returned when the action executed successfully and the corresponding result is displayed to the user. Now lets see the roles played by the different interceptors. The params interceptor helps in transfering the request data onto the action object. The workflow interceptor controls the flow of cotrol. The workflow interceptor checks whether the action implements the Validateable interface , if it does, the workflow interceptor will invoke the validate() method of the Action class. In the validate() method we validate the user name and the password. If the validation fails an error is added using the addFiledError() method. The validate() method doesn't return any errors, instead it stores all the errors with the help of the ValidationAware interface. Now the workflow interceptor will check any validation errors has occured. If any error has occured the workflow interceptor will stop the request processing and transfer the control to the input page with the appropriate error messages. On executing the sample example the following page will be displayed to the user.  For each field the error messages can be added using the addFieldError() method. The error messages can either be added directly or it can be specified in a seperate properties file. The properties files should have the same name as the Action class. In our case the properties file name is "Login.properties" and the Action name is "Login.java". The Login.properties file contains the following entry.  The getText() method provided by the TextProvider interface can be used to retrive the error messages.  You can also do the business validations in the validate() method.  If there are no errors, then the execute() method will be invoked by the workflow interceptor. In our execute() method we simply return "success". The user will be forwarded to the success page. 
单个execute方法校验
单个execute方法校验示例: 校验提交表单如下:   校验Action代码:    struts.xml配置文件:  校验通过显示成功结果页面singleExecuteMethodForCodeValidateResult.jsp  
多个execute方法校验
多个execute方法的校验示例(了解): 多个校验方法的Action类:     struts.xml配置文件(部分):  
xml校验
教程来源:http://www.dzone.com/tutorials/java/struts-2/struts-2-example/struts-2-validation-using-xml-example-1.html Struts 2 Validation Using XML File Tutorial In this example we will see how we can perform validations using XML validation file. The naming convention of the XML validation file should be ActionClass-Validation.xml. Here our Action Class name is "Login.java" and the XML validation file name is "Login-Validation.xml". NOTE:检验文件的名字是错误的:ActionClass-Validation.xml应该为ActionClass-validation.xml.v是小写,不是大写。 The Login-Validation.xml file contains the following code.  The field element contains the name of the form property that needs to be validated. The filed-validator element inside the field element contains the type of validation that needs to be performed. Here you can either specify the error message directly using the message element or you can use the properties file to define all the errror messages and use the key attribute to specify the error key. Note the properties file should also have the same name as the Action class. The Login Action class contains the following code.  The login.jsp page contains the following code.  The tag is used to include the required css and js file for the selected theme. By default the xhtml theme is used. Execute the example and click the Login button without entering the user name and password the following page will be displayed.  Enter a user name and password and click the Login button the following success page will be displayed. 
字段校验
字段校验示例: 字段校验提交表单:   字段校验Action类:   字段校验FieldValidateAction-validation.xml配置文件:   NOTE:Action类和字段校验配置文件必须放到同一个路径(文件夹)下 struts.xml配置文件:  字段校验成功显示结果页面: 
非字段校验
非字段校验示例: 非字段校验提交表单:   非字段校验Action类:   非字段校验FieldValidateAction-validation.xml配置文件:   NOTE:Action类和非字段校验配置文件必须放到同一个路径(文件夹)下 struts.xml配置文件:  非字段校验成功显示结果页面: 
对象校验
对象校验示例: 对象校验提交表单:   字段校验模型类:   字段校验模型的校验文件:   NOTE:模型校验文件一定要和模型类放在同一个路径(文件夹)下 字段校验Action类:  字段校验FieldValidateAction-validation.xml配置文件:  NOTE:Action类和字段校验配置文件必须放到同一个路径(文件夹)下 struts.xml配置文件:  字段校验成功显示结果页面: 
国际化
教程网址:http://struts.apache.org/release/2.3.x/docs/localization.html    NOTE:国际化的优先顺序是:类级别-->包级别-->全局级别
类级别的国际化
基于代码校验的国际化
基于代码校验国际化示例: 代码校验国际化提交页面:   代码校验国际化Action类:    国际化资源文件I18nForCodeValidateAction.properties  NOTE:国际化资源文件名字必须和类名相同,并且必须和Action类放在同一目录(文件夹)下. struts.xml配置文件中代码:  国际化值代码国际化的显示结果页面: 
基于XML文件校验的国际化
国际化之XML文件校验国际化示例: 国际化之XML文件校验国际化提交页面:   国际化之XML文件校验Action类:   国际化之xml校验文件国际化的校验文件I18nForFieldValidateAction-validation.xml:   国际化资源文件I18nForFieldValidateAction.properties  NOTE:国际化资源文件名字必须和类名相同,并且必须和Action类、Action相对应的XML字段校验文件放在同一目录(文件夹)下. struts.xml配置文件中代码:  国际化之XML文件校验国际化的显示结果页面: 
包级别的国际化
国际化之XML文件校验国际化示例: 国际化之XML文件校验国际化提交页面:   国际化之XML文件校验Action类:   国际化之xml校验文件国际化的校验文件I18nForFieldValidateAction-validation.xml:   国际化资源文件package.properties  NOTE:国际化资源文件名字必须叫package.properties,并且该包及其自爆中所有的Action类均可使用此国际化文件 struts.xml配置文件中代码:  国际化之XML文件校验国际化的显示结果页面: 
全局级别的国际化
国际化之XML文件校验国际化示例: 国际化之XML文件校验国际化提交页面:   国际化之XML文件校验Action类:   国际化之xml校验文件国际化的校验文件I18nForFieldValidateAction-validation.xml:   国际化资源文件global.properties  NOTE:国际化资源文件必须放在src目录下,全局国际化资源文件名称一定要与struts.xml中应用的名称保持一致 struts.xml配置文件中代码:   国际化之XML文件校验国际化的显示结果页面: 
类型转换
教程网址:http://struts.apache.org/release/2.3.x/docs/type-conversion.html           
原生类型转换错误处理
原生类型转换错误处理示例: 原生类型转换错误处理提交页面:   原生类型转换错误处理Action类:   原生类型转换错误处理Action类的XML字段校验文件:   原生类型转换错误处理的资源文件,与Action同名:  NOTE:开发时可以把转换值置为空,这样转换错误的结果就不会和校验错误的结果重复出现,如下所示:  struts.xml配置文件:  原生类型转换错误的结果显示页面: 
自定义类型转换
对象的类型转换
对象类型转换处理示例: 对象类型转换处理提交页面:  对象类型转换处理自定义对象:   对象类型转换处理Action类:  对象类型转换处理工具类: 1.基于xwork的工具类(转换原理):   2.基于struts2的工具类(建议使用基于struts2的转换):   对象类型转换处理的资源文件,与Action同名,名字为ActionName-conversion.proerties:  struts.xml配置文件:  原生类型转换错误的结果显示页面: 
List类型转换
List类型转换处理示例: List类型转换处理提交页面:   List类型转换处理自定义对象:   对象类型转换处理Action类:  对象类型转换处理工具类:     对象类型转换处理的资源文件,与Action同名,名字为ActionName-conversion.proerties: struts.xml配置文件:  对象类型转换的结果显示页面:  
拦截器
拦截器官网教程网址:http://struts.apache.org/release/2.3.x/docs/interceptors.html                       
默认拦截器defaultStack
默认拦截器就是defaultStack拦截器,详情请参考struts-default.xml文件。在struts.xml文件里所有配置的Action映射中,在action和result元素中间默认都会加上该默认拦截器栈的引用。如果有自定义的拦截器,则该拦截器栈会被覆盖掉,此时必须要显示引用该默认拦截器栈。
自定义拦截器
自定义拦截器教程
教程网址:http://struts.apache.org/release/2.3.x/docs/action-chaining.html   
自定义拦截器示例
自定义拦截器提交页面:  自定义拦截器:  自定义拦截器在struts.xml文件中的配置:  自定义拦截器Action类:   action类在struts.xml中的配置:  自定义拦截器结果显示页面: 
自定义方法拦截器
自定义方法拦截器示例: 自定义方法拦截器提交页面:  自定义方法拦截器:  自定义方法拦截器在struts.xml中的配置:  自定义方法拦截器的Action类:    自定义方法拦截器Action类在struts.xml中的配置:  自定义方法拦截器显示结果页面: 
文件上传与下载
文件上传
文件上传教程网址:http://www.dzone.com/tutorials/java/struts-2/struts-2-example/struts-2-file-upload-example-1.html        
单个文件的上传
单个文件上传例子: 当个文件上传表单提交页面:  单个文件上传Action类:      struts.xml中的配置文件:   单个文件上传结果显示页面: 
多文件上传
多个文件上传例子: 多个文件上传表单提交页面:  多个文件上传Action类:      struts.xml中的配置文件:   单个文件上传结果显示页面: 
文件下载
一个简单的利用struts2做文件下载的demo…… 首先配好struts: web.xml xmlns="http://java.sun.com/xml/ns/j2ee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd"> index.jsp struts2 org.apache.struts2.dispatcher.FilterDispatcher struts2 /* struts.xml——这里是重点 "-//Apache Software Foundation//DTD Struts Configuration 2.0//EN" "http://struts.apache.org/dtds/struts-2.0.dtd"> application/octet-stream inputStream attachment;filename="${fileName}" 4096 当result为stream类型时,struts2会自动根据你配置好的参数下载文件。 其中主要使用的参数是: contentType 指定下载文件的文件类型 —— application/octet-stream 表示无限制 inputName 流对象名 —— 比如这里写inputStream,它就会自动去找Action中的getInputStream方法。 contentDisposition 使用经过转码的文件名作为下载文件名 —— 默认格式是attachment;filename="${fileName}",将调用该Action中的getFileName方法。 bufferSize 下载文件的缓冲大小 之后写个DownloadAction: package action; import java.io.InputStream; import org.apache.struts2.ServletActionContext; public class DownloadAction { private String fileName; public void setFileName(String fileName) { this.fileName = fileName; } public InputStream getInputStream() { return ServletActionContext.getServletContext().getResourceAsStream("/" + fileName); } public String execute(){ return "success"; } } * 注意使用getResourceAsStream方法时,文件路径必须是以“/”开头,且是相对路径。这个路径是相对于项目根目录的。 * 可以用return new FileInputStream(fileName)的方法来得到绝对路径的文件。 在WEB-INF下随意丢一个test.txt,部署好后进入浏览器,输入tomcat地址/项目路径/download.action?fileName=test.txt即可下载到该文件。
ajax整合struts2
ajax读取xml文件
ajax读取xml文件示例: ajax读取xml文件提交页面: pagelanguage="java"import="java.util.*"pageEncoding="UTF-8"%> taglibprefix="s"uri="/struts-tags"%> DOCTYPEHTMLPUBLIC"-//W3C//DTD HTML 4.01 Transitional//EN"> html> head> title>My JSP 'getUserInfoByXml.jsp' starting pagetitle> metahttp-equiv="pragma"content="no-cache"> metahttp-equiv="cache-control"content="no-cache"> metahttp-equiv="expires"content="0"> metahttp-equiv="keywords"content="keyword1,keyword2,keyword3"> metahttp-equiv="description"content="This is my page"> scriptsrc="jQuery/jquery-1.8.3.js">script> scripttype="text/javascript"> function getUserInfoByXml() { $ .post( "getUserInfoByXml.action", { name : $("#name").val() }, function(returnData, status) { if ("success" == status) { var id = $(returnData).find("id").text(); var name = $(returnData).find("name").text(); var age = $(returnData).find("age").text(); var address = $(returnData).find("address") .text(); var birthday = $(returnData).find("date") .text(); var result = " 编号 姓名 年龄 地址 生日 " + id + " " + name + " " + age + " " + address + " " + birthday + "" $("body table:eq(0)").remove(); $("#myBody").append(result); } }, "xml"); } script> head> bodyid="myBody"> s:selectlist="#{'zhangsan' : 'zhangsan', 'lisi' : 'lisi'}"id="name">s:select> inputtype="button"value="提交"onclick="getUserInfoByXml();"/> body> html> Action类中模拟从数据库中取出模型数据的对象: publicclass User { privateintid; private String name; privateintage; private String address; private String birthday; public User() { } publicint getId() { returnid; } publicvoid setId(int id) { this.id = id; } public String getName() { returnname; } publicvoid setName(String name) { this.name = name; } publicint getAge() { returnage; } publicvoid setAge(int age) { this.age = age; } public String getAddress() { returnaddress; } publicvoid setAddress(String address) { this.address = address; } public String getBirthday() { returnbirthday; } publicvoid setBirthday(String birthday) { this.birthday = birthday; } } ajax读取xml文件所用Action类: publicclass GetUserInfoByXmlAction extends ActionSupport { private String name; public String getName() { returnname; } publicvoid setName(String name) { this.name = name; } @Override public String execute() throws Exception { SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd"); User user = new User(); user.setId(1); user.setName("zhangsan"); user.setAge(25); user.setAddress("北京"); user.setBirthday(sdf.format(new Date())); User user2 = new User(); user2.setId(2); user2.setName("lisi"); user2.setAge(30); user2.setAddress("上海"); user2.setBirthday(sdf.format(new Date())); // 创建XML文档对象 Document document = DocumentHelper.createDocument(); // 通过文档对象创建元素对象 Element usersElement = document.addElement("users"); usersElement.addComment("通过文档对象创建元素对象"); Element userElement = usersElement.addElement("user"); Element idElement = userElement.addElement("id"); Element nameElement = userElement.addElement("name"); Element ageElement = userElement.addElement("age"); Element addressElement = userElement.addElement("address"); Element dateElement = userElement.addElement("date"); if ("zhangsan".equals(this.name)) { idElement.setText(String.valueOf(user.getId())); nameElement.setText(user.getName()); ageElement.setText(user.getAge() + ""); addressElement.setText(user.getAddress()); dateElement.setText(user.getBirthday().toString()); } else if ("lisi".equals(this.name)) { idElement.setText(String.valueOf(user2.getId())); nameElement.setText(user2.getName()); ageElement.setText(user2.getAge() + ""); addressElement.setText(user2.getAddress()); dateElement.setText(user2.getBirthday().toString()); } HttpServletResponse response = ServletActionContext.getResponse(); response.setCharacterEncoding("utf-8"); response.setContentType("text/html"); response.setHeader("cache-control", "no-cache"); PrintWriter out = response.getWriter(); OutputFormat format = OutputFormat.createPrettyPrint(); XMLWriter writer = new XMLWriter(out, format); writer.write(document); writer.flush(); writer.close(); returnnull; } } Action类在struts.xml文件中的配置: actionname="getUserInfoByXml" class="org.accp.ay225.struts2.action.ajax.ajax_xml.GetUserInfoByXmlAction" />
ajax读取json文件
ajax读取json文件示例: ajax读取json文件提交页面: pagelanguage="java"import="java.util.*"pageEncoding="UTF-8"%> taglibprefix="s"uri="/struts-tags"%> DOCTYPEHTMLPUBLIC"-//W3C//DTD HTML 4.01 Transitional//EN"> html> head> title>My JSP 'getUserInfoByJson.jsp' starting pagetitle> metahttp-equiv="pragma"content="no-cache"> metahttp-equiv="cache-control"content="no-cache"> metahttp-equiv="expires"content="0"> metahttp-equiv="keywords"content="keyword1,keyword2,keyword3"> metahttp-equiv="description"content="This is my page"> scripttype="text/javascript"src="jQuery/jquery-1.8.3.js">script> scripttype="text/javascript"> function getUserInfoByJson() { $ .post( "getUserInfoByJson.action", { name : $("#name").val() }, function(returnData, status) { if ("success" == status) { var id = returnData.id; var name = returnData.name; var age = returnData.age; var address = returnData.address; var birthday = returnData.birthday; var strTable = " ID 姓名 年龄 地址 生日 " + id + " " + name + " " + age + " " + address + " " + birthday + "" $("body table:eq(0)").remove(); $("#myBody").append(strTable); } }, "json"); } script> head> bodyid="myBody"> s:selectlist="#{'zhangsan' : 'zhangsan', 'lisi' : 'lisi'}"id="name">s:select> inputtype="button"value="提交"onclick="getUserInfoByJson();"> body> html> Action类中模拟从数据库中取出模型数据的对象: publicclass User { privateintid; private String name; privateintage; private String address; private String birthday; public User() { } publicint getId() { returnid; } publicvoid setId(int id) { this.id = id; } public String getName() { returnname; } publicvoid setName(String name) { this.name = name; } publicint getAge() { returnage; } publicvoid setAge(int age) { this.age = age; } public String getAddress() { returnaddress; } publicvoid setAddress(String address) { this.address = address; } public String getBirthday() { returnbirthday; } publicvoid setBirthday(String birthday) { this.birthday = birthday; } } ajax读取json文件所用Action类: publicclass GetUserInfoByJsonAction extends ActionSupport { private String name; privateintid; privateintage; private String address; private String birthday; public String getName() { returnname; } publicvoid setName(String name) { this.name = name; } publicint getId() { returnid; } publicvoid setId(int id) { this.id = id; } publicint getAge() { returnage; } publicvoid setAge(int age) { this.age = age; } public String getAddress() { returnaddress; } publicvoid setAddress(String address) { this.address = address; } public String getBirthday() { returnbirthday; } publicvoid setBirthday(String birthday) { this.birthday = birthday; } @Override public String execute() throws Exception { SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd"); User user = new User(); user.setId(1); user.setName("zhangsan"); user.setAge(25); user.setAddress("北京"); user.setBirthday(sdf.format(new Date())); User user2 = new User(); user2.setId(2); user2.setName("lisi"); user2.setAge(30); user2.setAddress("上海"); user2.setBirthday(sdf.format(new Date())); if("zhangsan".equals(this.name)) { this.id = user.getId(); this.name = user.getName(); this.age = user.getAge(); this.address = user.getAddress(); this.birthday = user.getBirthday(); } else if("lisi".equals(this.name)) { this.id = user2.getId(); this.name = user2.getName(); this.age = user2.getAge(); this.address = user2.getAddress(); this.birthday = user2.getBirthday(); } returnSUCCESS; } } Action类在struts.xml文件中的配置: actionname="getUserInfoByJson" class="org.accp.ay225.struts2.action.ajax.ajax_json.GetUserInfoByJsonAction"> resultname="success"type="json">result> action> NOTE:此例子需要添加struts-json-plugin.jar包
ajax读取gson文件
ajax读取Gson文件示例: ajax读取Gson文件提交页面: pagelanguage="java"import="java.util.*"pageEncoding="UTF-8"%> taglibprefix="s"uri="/struts-tags"%> DOCTYPEHTMLPUBLIC"-//W3C//DTD HTML 4.01 Transitional//EN"> html> head> title>My JSP 'getUserInfoByXml.jsp' starting pagetitle> metahttp-equiv="pragma"content="no-cache"> metahttp-equiv="cache-control"content="no-cache"> metahttp-equiv="expires"content="0"> metahttp-equiv="keywords"content="keyword1,keyword2,keyword3"> metahttp-equiv="description"content="This is my page"> scripttype="text/javascript"src="jQuery/jquery-1.8.3.js">script> scripttype="text/javascript"> function getAllUserInfo() { $.post("getUserInfoByGson.action", function(returnData, status) { if("success" == status) { alert(returnData[0].name); //var result = eval("(" + returnData + ")"); //alert(returnData[0].name); } }); } script> head> body> inputtype="button"value="获取用户信息"onclick="getAllUserInfo();"/>body> html> Action类中模拟从数据库中取出模型数据的对象: Address模型对象: publicclass Address { private String homeAddress; private String schoolAddress; public String getHomeAddress() { returnhomeAddress; } publicvoid setHomeAddress(String homeAddress) { this.homeAddress = homeAddress; } public String getSchoolAddress() { returnschoolAddress; } publicvoid setSchoolAddress(String schoolAddress) { this.schoolAddress = schoolAddress; } } User模型对象: publicclass User { privateintid; private String name; privateintage; private Address address; private List friends = new ArrayList(); publicint getId() { returnid; } publicvoid setId(int id) { this.id = id; } public String getName() { returnname; } publicvoid setName(String name) { this.name = name; } publicint getAge() { returnage; } publicvoid setAge(int age) { this.age = age; } public Address getAddress() { returnaddress; } publicvoid setAddress(Address address) { this.address = address; } public List getFriends() { returnfriends; } publicvoid setFriends(List friends) { this.friends = friends; } } ajax读取Gson文件所用Action类: publicclass GetUserInfoByGsonAction extends ActionSupport { public String execute() throws Exception { Address address = new Address(); address.setHomeAddress("beijing"); address.setSchoolAddress("shanghai"); User friend11 = new User(); friend11.setId(1111); friend11.setName("xige"); friend11.setAge(23); friend11.setAddress(address); User friend12 = new User(); friend12.setId(2222); friend12.setName("qiangge"); friend12.setAge(23); friend12.setAddress(address); User user = new User(); user.setId(1); user.setName("zhangsan"); user.setAge(32); user.setAddress(address); user.getFriends().add(friend11); user.getFriends().add(friend12); Address address2 = new Address(); address2.setHomeAddress("tianjin"); address2.setSchoolAddress("chongqing"); User friend21 = new User(); friend21.setId(1111); friend21.setName("xige"); friend21.setAge(23); friend21.setAddress(address2); User friend22 = new User(); friend22.setId(2222); friend22.setName("qiangge"); friend22.setAge(23); friend22.setAddress(address2); User user2 = new User(); user2.setId(2); user2.setName("lisi"); user2.setAge(32); user2.setAddress(address2); user2.getFriends().add(friend21); user2.getFriends().add(friend22); List users = new ArrayList(); users.add(user); users.add(user2); Gson gson = new Gson(); String result = gson.toJson(users); // System.out.println(result); HttpServletResponse response = ServletActionContext.getResponse(); response.setContentType("application/json;charset=utf-8"); response.setHeader("cache-control", "no-cache"); PrintWriter out = response.getWriter(); out.print(result); out.flush(); out.close(); returnnull; } } Action类在struts.xml文件中的配置: actionname="getUserInfoByGson" class="org.accp.ay225.struts2.action.ajax.ajax_gson.GetUserInfoByGsonAction"> action> NOTE:此例子需要添加gson的jar包
Spring MVC
Spring FrameWork
IOC
容器综述
Spring IOC容器产生对象可以通过一个工厂方法来理解: 示例如下所示: 1.首先写一个Person接口:  2.分别写一个American类和Chinese类来实现这个接口:   3.写一个产生对象的静态工厂方法:  4.写一个工厂方法测试类:  对比静态工厂方法,看Spring的实现方式如下: 1.首先写一个Person接口:  2.分别写一个American类和Chinese类来实现这个接口:   3.Spring的配置文件applicationContext.xml:  4.Spring测试类:  通过工厂方法和Spring实例的对比可以看出:spring容器是生成对象的地方。
容器bean
命名bean
命名bean示例: 1.首先写一个Person接口:  2.写一个Chinese类来实现这个接口:  3.写一个applicationContext.xml文件:  4.写一个Spring的测试类: 
实例化bean
构造器实例化bean
构造方法实例化bean示例: 1.实例化类:  2.配置actionContext.xml文件:  3.Spring 测试类: 
静态工厂方法实例化bean
实例化静态工厂方法bean示例: 1.实例化类:  2.静态工厂类:  3.配置actionContext.xml文件:  4.Spring静态工厂bean 测试类: 
实例工厂方法实例化bean
实例化工厂方法bean示例: 1.实例化类:   2.实例化工厂类:  3.配置actionContext.xml文件:  4.Spring实例化工厂bean 测试类: 
Dependencies(依赖)
Dependency Injection(依赖注入)
Constructor Injection(构造器注入)
name-value 构造器注入
name-value构造器注入示例: 1.Tools接口  2.Tools接口的实现类Chopstricks  3.spring的actionContext.xml配置文件:  4.Spring的测试类: 
name-ref 构造器注入
name-ref构造器注入示例: 1.Tools和Person接口   2.Tools接口的实现类Chopstricks和Person接口的实现类Chinese:   3.spring的actionContext.xml配置文件:  4.Spring的测试类: 
index-value 构造器注入
index-value构造器注入示例: 1.Person接口  2.Person接口的实现类Chinese:  3.spring的actionContext.xml配置文件:  4.Spring的测试类: 
type-value 构造器注入
index-value构造器注入示例: 1.Person接口  2.Person接口的实现类Chinese:  3.spring的actionContext.xml配置文件:  4.Spring的测试类: 
c-namespace 构造器注入
c-namespace构造器注入示例: 1.Tools和Person接口   2.Tools接口的实现类Chopstricks和Person接口的实现类Chinese:   3.spring的actionContext.xml配置文件:  4.Spring的测试类: 
index-c-namespace 构造器注入
c-namespace构造器注入示例: 1.Tools和Person接口   2.Tools接口的实现类Chopstricks和Person接口的实现类Chinese:   3.spring的actionContext.xml配置文件:  4.Spring的测试类: 
Setter Injection(Setter注入)
property-value 的setter注入
name-value构造器注入示例: 1.Tools接口  2.Tools接口的实现类Chopstricks  3.spring的actionContext.xml配置文件:  4.Spring的测试类: 
property-ref 的setter注入
name-ref构造器注入示例: 1.Tools和Person接口   2.Tools接口的实现类Chopstricks和Person接口的实现类Chinese:   3.spring的actionContext.xml配置文件:  4.Spring的测试类: 
property-inner-class 的setter注入
property-bean(inner_class)的setter注入示例: 1.Tools和Person接口   2.Tools接口的实现类Chopstricks和Person接口的实现类Chinese:   3.spring的actionContext.xml配置文件:  4.Spring的测试类: 
property-inherited 的setter注入
property-inherited的setter注入示例: 1.property-inherited的父类Person:   2.property-inherited的子类Student:  3.property-inherited的actionContext.xml配置文件:  4.property-inherited的测试类: 
property-abstract-inherited 的setter注入
property-abstract-inherited的setter注入示例: 1.property-abstract-inherited的父类Person:   2.property-abstract-inherited的子类Student:  3.property-abstract-inherited的actionContext.xml配置文件:  4.property-abstract-inherited的测试类: 
p-namespace 的setter注入
name-ref构造器注入示例: 1.Tools和Person接口   2.Tools接口的实现类Chopstricks和Person接口的实现类Chinese:   3.spring的actionContext.xml配置文件:  4.Spring的测试类: 
Annotation Injection(注解注入)
Spring注解讲解 1. 使用Spring注解来注入属性 1.1. 使用注解以前我们是怎样注入属性的 类的实现: Java代码 复制代码 public class UserManagerImpl implements UserManager { private UserDao userDao; public void setUserDao(UserDao userDao) { this.userDao = userDao; } ... } public class UserManagerImpl implements UserManager { private UserDao userDao; public void setUserDao(UserDao userDao) { this.userDao = userDao; } ... } 配置文件: Java代码 复制代码 1.2. 引入@Autowired注解(不推荐使用,建议使用@Resource) 类的实现(对成员变量进行标注) Java代码 复制代码 public class UserManagerImpl implements UserManager { @Autowired private UserDao userDao; ... } public class UserManagerImpl implements UserManager { @Autowired private UserDao userDao; ... } 或者(对方法进行标注) Java代码 复制代码 public class UserManagerImpl implements UserManager { private UserDao userDao; @Autowired public void setUserDao(UserDao userDao) { this.userDao = userDao; } ... } public class UserManagerImpl implements UserManager { private UserDao userDao; @Autowired public void setUserDao(UserDao userDao) { this.userDao = userDao; } ... } 配置文件 Java代码 复制代码 @Autowired可以对成员变量、方法和构造函数进行标注,来完成自动装配的工作。以上两种不同实现方式中,@Autowired的标注位置不同,它们都会在Spring在初始化userManagerImpl这个bean时,自动装配userDao这个属性,区别是:第一种实现中,Spring会直接将UserDao类型的唯一一个bean赋值给userDao这个成员变量;第二种实现中,Spring会调用setUserDao方法来将UserDao类型的唯一一个bean装配到userDao这个属性。 1.3. 让@Autowired工作起来 要使@Autowired能够工作,还需要在配置文件中加入以下代码 Java代码 复制代码 1.4. @Qualifier @Autowired是根据类型进行自动装配的。在上面的例子中,如果当Spring上下文中存在不止一个UserDao类型的bean时,就会抛出BeanCreationException异常;如果Spring上下文中不存在UserDao类型的bean,也会抛出BeanCreationException异常。我们可以使用@Qualifier配合@Autowired来解决这些问题。 1. 可能存在多个UserDao实例 Java代码 复制代码 @Autowired public void setUserDao(@Qualifier("userDao") UserDao userDao) { this.userDao = userDao; } @Autowired public void setUserDao(@Qualifier("userDao") UserDao userDao) { this.userDao = userDao; } 这样,Spring会找到id为userDao的bean进行装配。 2. 可能不存在UserDao实例 Java代码 复制代码 @Autowired(required = false) public void setUserDao(UserDao userDao) { this.userDao = userDao; } @Autowired(required = false) public void setUserDao(UserDao userDao) { this.userDao = userDao; } 1.5. @Resource(JSR-250标准注解,推荐使用它来代替Spring专有的@Autowired注解) Spring 不但支持自己定义的@Autowired注解,还支持几个由JSR-250规范定义的注解,它们分别是@Resource、@PostConstruct以及@PreDestroy。 @Resource的作用相当于@Autowired,只不过@Autowired按byType自动注入,而@Resource默认按byName自动注入罢了。@Resource有两个属性是比较重要的,分别是name和type,Spring将@Resource注解的name属性解析为bean的名字,而type属性则解析为bean的类型。所以如果使用name属性,则使用byName的自动注入策略,而使用type属性时则使用byType自动注入策略。如果既不指定name也不指定type属性,这时将通过反射机制使用byName自动注入策略。 @Resource装配顺序 如果同时指定了name和type,则从Spring上下文中找到唯一匹配的bean进行装配,找不到则抛出异常 如果指定了name,则从上下文中查找名称(id)匹配的bean进行装配,找不到则抛出异常 如果指定了type,则从上下文中找到类型匹配的唯一bean进行装配,找不到或者找到多个,都会抛出异常 如果既没有指定name,又没有指定type,则自动按照byName方式进行装配(见2);如果没有匹配,则回退为一个原始类型(UserDao)进行匹配,如果匹配则自动装配; 1.6. @PostConstruct(JSR-250) 在方法上加上注解@PostConstruct,这个方法就会在Bean初始化之后被Spring容器执行(注:Bean初始化包括,实例化Bean,并装配Bean的属性(依赖注入))。 它的一个典型的应用场景是,当你需要往Bean里注入一个其父类中定义的属性,而你又无法复写父类的属性或属性的setter方法时,如: Java代码 复制代码 public class UserDaoImpl extends HibernateDaoSupport implements UserDao { private SessionFactory mySessionFacotry; @Resource public void setMySessionFacotry(SessionFactory sessionFacotry) { this.mySessionFacotry = sessionFacotry; } @PostConstruct public void injectSessionFactory() { super.setSessionFactory(mySessionFacotry); } ... } public class UserDaoImpl extends HibernateDaoSupport implements UserDao { private SessionFactory mySessionFacotry; @Resource public void setMySessionFacotry(SessionFactory sessionFacotry) { this.mySessionFacotry = sessionFacotry; } @PostConstruct public void injectSessionFactory() { super.setSessionFactory(mySessionFacotry); } ... } 这里通过@PostConstruct,为UserDaoImpl的父类里定义的一个sessionFactory私有属性,注入了我们自己定义的sessionFactory(父类的setSessionFactory方法为final,不可复写),之后我们就可以通过调用super.getSessionFactory()来访问该属性了。 1.7. @PreDestroy(JSR-250) 在方法上加上注解@PreDestroy,这个方法就会在Bean初始化之后被Spring容器执行。由于我们当前还没有需要用到它的场景,这里不不去演示。其用法同@PostConstruct。 1.8. 使用简化配置 Spring2.1添加了一个新的context的Schema命名空间,该命名空间对注释驱动、属性文件引入、加载期织入等功能提供了便捷的配置。我们知道注释本身是不会做任何事情的,它仅提供元数据信息。要使元数据信息真正起作用,必须让负责处理这些元数据的处理器工作起来。 AutowiredAnnotationBeanPostProcessor和CommonAnnotationBeanPostProcessor就是处理这些注释元数据的处理器。但是直接在Spring配置文件中定义这些Bean显得比较笨拙。Spring为我们提供了一种方便的注册这些BeanPostProcessor的方式,这就是: Java代码 复制代码 xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd"> xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd"> 将隐式地向Spring容器注册AutowiredAnnotationBeanPostProcessor、CommonAnnotationBeanPostProcessor、 PersistenceAnnotationBeanPostProcessor以及RequiredAnnotationBeanPostProcessor这4个BeanPostProcessor。 2. 使用Spring注解完成Bean的定义 以上我们介绍了通过@Autowired或@Resource来实现在Bean中自动注入的功能,下面我们将介绍如何注解Bean,从而从XML配置文件中完全移除Bean定义的配置。 2.1. @Component(不推荐使用)、@Repository、@Service、@Controller 只需要在对应的类上加上一个@Component注解,就将该类定义为一个Bean了: Java代码 复制代码 @Component public class UserDaoImpl extends HibernateDaoSupport implements UserDao { ... } @Component public class UserDaoImpl extends HibernateDaoSupport implements UserDao { ... } 使用@Component注解定义的Bean,默认的名称(id)是小写开头的非限定类名。如这里定义的Bean名称就是userDaoImpl。你也可以指定Bean的名称: @Component("userDao") @Component是所有受Spring管理组件的通用形式,Spring还提供了更加细化的注解形式:@Repository、@Service、@Controller,它们分别对应存储层Bean,业务层Bean,和展示层Bean。目前版本(2.5)中,这些注解与@Component的语义是一样的,完全通用,在Spring以后的版本中可能会给它们追加更多的语义。所以,我们推荐使用@Repository、@Service、@Controller来替代@Component。 2.2. 使用让Bean定义注解工作起来 Java代码 复制代码 xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd"> xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd"> 这里,所有通过元素定义Bean的配置内容已经被移除,仅需要添加一行配置就解决所有问题了——Spring XML配置文件得到了极致的简化(当然配置元数据还是需要的,只不过以注释形式存在罢了)。的base-package属性指定了需要扫描的类包,类包及其递归子包中所有的类都会被处理。 还允许定义过滤器将基包下的某些类纳入或排除。Spring支持以下4种类型的过滤方式: 过滤器类型 表达式范例 说明 注解 org.example.SomeAnnotation 将所有使用SomeAnnotation注解的类过滤出来 类名指定 org.example.SomeClass 过滤指定的类 正则表达式 com\.kedacom\.spring\.annotation\.web\..* 通过正则表达式过滤一些类 AspectJ表达式 org.example..*Service+ 通过AspectJ表达式过滤一些类 以正则表达式为例,我列举一个应用实例: Java代码 复制代码 值得注意的是配置项不但启用了对类包进行扫描以实施注释驱动Bean定义的功能,同时还启用了注释驱动自动注入的功能(即还隐式地在内部注册了AutowiredAnnotationBeanPostProcessor和CommonAnnotationBeanPostProcessor),因此当使用后,就可以将移除了。 2.3. 使用@Scope来定义Bean的作用范围 在使用XML定义Bean时,我们可能还需要通过bean的scope属性来定义一个Bean的作用范围,我们同样可以通过@Scope注解来完成这项工作: Java代码 复制代码 @Scope("session") @Component() public class UserSessionBean implements Serializable { ... } @Scope("session") @Component() public class UserSessionBean implements Serializable { ... } 3. 参考 http://kingtai168.iteye.com/blog/244002 http://www.iteye.com/topic/244153 http://static.springframework.org/spring/docs/2.5.x/reference/beans.html#beans-annotation-config http://static.springframework.org/spring/docs/2.5.x/reference/beans.html#beans-classpath-scanning
Dependencies and Configuration in detail(依赖和配置详情)
Straight values(直接值)
原生类型和String都是直接依赖
References to other beans (collaborators)
property-bean(inner_class)的setter注入示例: 1.Tools和Person接口   2.Tools接口的实现类Chopstricks和Person接口的实现类Chinese:   3.spring的actionContext.xml配置文件:  4.Spring的测试类: 
Inner Class
Collection
List、Set、Map及Properties的注入
List、Set、Map及Properties的注入示例: 1.List、Set、Map及Properties的注入的模型类Person:  2.List、Set、Map及Properties的注入类CollectionType:    3.List、Set、Map及Properties的注入类CollectionType的配置文件applicationContext.xml:     4.List、Set、Map及Properties的注入类CollectionType的测试类: 
Collection Merging
List、Set、Map及Properties的注入示例: 1.List、Set、Map及Properties的注入的模型类Person:  2.List、Set、Map及Properties的注入类CollectionType及其父类AbstractCollectionType:   3.List、Set、Map及Properties的注入类CollectionType的配置文件applicationContext.xml:   4.List、Set、Map及Properties的注入类CollectionType的测试类: 
Strongly-typed collection (Java 5+ only)
List、Set、Map及Properties的注入示例: 1.List、Set、Map及Properties的注入的模型类Person:  2.List、Set、Map及Properties的注入类CollectionType:  3.List、Set、Map及Properties的注入类CollectionType的配置文件applicationContext.xml:  4.List、Set、Map及Properties的注入类CollectionType的测试类: 
Null and empty string values
Null和empty string value示例: 1. Null和empty string value的Address类:  2.Null和empty string value在applicationContext.xml中的配置文件:  3.Null和empty string value的测试类: 
XML shortcut with the p-namespace
XML shortcut with the c-namespace
Compound property names(复合属性名)
depends-on(依赖)
depends-on构造器注入示例: 1.Person接口  2.ConcreteTool和Person接口的实现类Chinese:   3.spring的actionContext.xml配置文件:  4.Spring的测试类: 
Lazy-initialized beans
depends-on构造器注入示例: 1.Tool接口和Person接口   2.实现Tool接口的ConcreteTool和Person接口的实现类Chinese:   3.spring的actionContext.xml配置文件:  4.Spring的测试类: 
Method injection
Lookup method injection
Lookup method injection示例: 1.定义一个抽象类AbstractIRandomNumber  2.定义一个产生随机值的类RandomNumber  3.applicationContextion.xml配置文件:  4.SpringTest测试类:  
Arbitrary method replacement(任意方法替换)
depends-on构造器注入示例: 1.Tool接口和Person接口   2.实现Tool接口的ConcreteTool和Person接口的实现类Chinese:   3.spring的actionContext.xml配置文件:  4.Spring的测试类: 
Bean Scope
The singleton scope
the sintleon scope: 1. singleton scope类Person:  2.Singleton scope的配置文件applicationContext.xml  3.Singleton Scope的测试类: 
The prototype scope
the prototype scope: 1. prototype scope类Employee:  2.prototype scope的配置文件applicationContext.xml  3.prototype Scope的测试类: 
Singleton beans with prototype-bean dependencies
Bean definition inheritance
AOP
JDK动态代理
运行时
Cglib代理
Struts2 + Spring Framework + Hibernate整合
Java Web
应用服务器
常用的Java应用服务器
1、Tomcat:tomcat.apache.org 2、JBoss 3、WebSphere 4、WebLogic 5、Resin 6、Jetty
Tomcat应用服务器
Tomcat的下载
Tomcat 我们主要通过Apache的Tomcat来进行讲解,首先去www.apache.org网站上面下载Tomcat 
Tomcat的安装及环境变量的配置
1、下载Tomcat(www.apache.org) 2、在环境变量中设置 CATALINA_HOME=Tomcat的路径 JAVA_HOME=Java 路径 3、将Tomcat的bin路径添加到环境变量中 4、启动Tomcat,在命令提示符中输入catalina.bat start 5、启动之后在浏览器中输入http://localhost:8080如果显示下图就表示安装成功! 
Tomcat的目录结构
 1. bin:Tomcat的可运行文件的存储目录 2. conf:Tomcat配置文件所在的目录 conf目录下的文件:  (1)server.xml: Tomcat的系统配置文件。在server.xml中可以完成 对站点访问的配置 (2)web.xml:Tomcat基于Web的配置文件。在web.xml中可以完成 一些web的基本访问的配置 3. lib:Tomcat自带的一些jar包(功能包) 4. webapps: Tomcat的项目所在的目录。只要将相应的项目复制到webapps中就可以直接访问该项目 5. work: 编译后文件所在的目录
server.xml中的重要配置
 Connector用来配置相应的访问属性,port表示访问端口为8888,URIEncoding表示访问的字符编码 -----------------------------------------------------------------------------  Host中存储了所有访问的主机,对于任意一台计算机而言均有一个默认的访问地址localhost,这个文件是在drivers/etc/host这个文件中配置的 -----------------------------------------------------------------------------  Context表示访问的虚拟路径,path表示要访问的网页上下文路径,docBase表示要访问的文件夹所在路径,reloadable表示是否在修改之后进行重新启动,如果使用eclipse开发建议设置为false,因为在eclipse中会通过debug进行启动
web.xml中的重要配置
 listings表示是否在显示页面时打开文件列表,建议在调试时打开,方便查询一些特殊的网页文件 -----------------------------------------------------------------------------  表示默认访问的文件
WEB容器、WEB服务器和应用服务器的区别与联系
对于一个不了解 WEB 开发的人来说,下面的概念是为了免于被别人鄙视和忽悠的~~ 【web 容器】 何为容器: 容器是一种服务调用规范框架,Java EE大量运用了容器和组件技术来构建分层的企业级应用。在 Java EE规范中,相应的有 WEB Container 和 EJB Container 等。 WEB 容器给处于其中的应用程序组件(JSP,SERVLET)提供一个环境,使 JSP,SERVLET 直接跟容器中的环境变量交互,不必关注其它系统问题(从这个角度来说,WEB 容器应该属于架构上的概念)。WEB容器主要由 WEB 服务器来实现。例如:TOMCAT,WEBLOGIC,WEBSPHERE, JBoss 等。 若容器提供的接口严格遵守 Java EE 规范中的 WEB APPLICATION 标准,我们把该容器叫做 Java EE中的 WEB 容器。 WEB 容器更多的是跟基于 HTTP 的请求打交道。而 EJB 容器不是。它是更多的跟数据库、其它服务打交道。 容器的行为是将其内部的应用程序组件与外界的通信协议交互进行了隔离,从而减轻内部应用程序组件的负担(实现方面的负担)。 例如,SERVLET 不用关心 HTTP 的细节,而是直接引用环境变量 session、request、response 就行、EJB 不用关心数据库连接速度、各种事务控制,直接由容器来完成。 【Web服务器】 Web 服务器(Web Server)可以处理 HTTP 协议。当 Web 服务器接收到一个 HTTP 请求,会返回一个 HTTP 响应,例如送回一个 HTML 页面。 Web 服务器可以响应针对静态页面或图片的请求, 进行页面跳转(redirect),或者把动态响应(dynamic response)的产生委托(delegate)给一些其它的程序,例如 CGI 脚本,JSP(JavaServer Pages)脚本,servlets,ASP(Active Server Pages)脚本,服务器端 JavaScript,或者一些其它的服务器端技术。 Web 服务器仅仅提供一个可以执行服务器端程序和返回(程序所产生的)响应的环境,而不会超出职能范围。 Web 服务器主要是处理需要向浏览器发送 HTML 的请求以供浏览。 【应用程序服务器(The Application Server)】 根据定义,作为应用程序服务器,要求可以通过各种协议(包括 HTTP 协议)把商业逻辑暴露给(expose)客户端应用程序。应用程序使用此商业逻辑就像你调用对象的一个方法或过程(语言中的一个函数)一样。 【serverlet】 Servlet(Server Applet),全称 Java Servlet,未有中文译文。是用 Java 编写的服务器端程序。其主要功能在于交互式地浏览和修改数据,生成动态 Web 内容。狭义的 Servlet 是指 Java 语言实现的一个接口,广义的 Servlet 是指任何实现了这个 Servlet 接口的类,一般情况下,人们将 Servlet 理解为后者。 Servlet 运行于支持 Java 的应用服务器中。从实现上讲,Servlet 可以响应任何类型的请求,但绝大多数情况下 Servlet 只用来扩展基于 HTTP 协议的 Web 服务器。 【Tomcat】 Tomcat 服务器是一个免费的开放源代码的 Web 应用服务器,属于轻量级应用服务器,在中小型系统和并发访问用户不是很多的场合下被普遍使用,是开发和调试 JSP 程序的首选。对于一个初学者来说,可以这样认为,当在一台机器上配置好 Apache 服务器,可利用它响应对 HTML 页面的访问请求。实际上 Tomcat 部分是Apache 服务器的扩展,但它是独立运行的,所以当你运行 tomcat 时,它实际上作为一个与 Apache 独立的进程单独运行的。 Apache Tomcat is an open source software implementation of the Java Servlet and JavaServer Pages technologies. 【Tomcat与Web服务器、应用服务器的关系】 Tomcat 服务器是一个免费的开放源代码的 Web 应用服务器。因为 Tomcat 技术先进、性能稳定且免费,所以深受 Java 爱好者的喜爱并得到了部分软件开发商的认可,成为目前比较流行的 Web 应用服务器。 一、Tomcat 与应用服务器 到目前为止,Tomcat 一直被认为是 Servlet/JSP API 的执行器,也就所谓的 Servlet 容器。然而,Tomcat并不仅仅如此,它还提供了 JNDI 和 JMX API 的实现机制。尽管如此,Tomcat 仍然还不能算是应用服务器,因为它不提供大多数 Java EE API 的支持。 很有意思的是,目前许多的应用服务器通常把 Tomcat 作为它们 Servlet 和 JSP API 的容器。由于 Tomcat允许开发者只需通过加入一行致谢,就可以把 Tomcat 嵌入到它们的应用中。遗憾的是,许多商业应用服务器并没有遵守此规则。 对于开发者来说,如果是为了寻找利用 Servlet、JSP、JNDI 和 JMX 技术来生成 Java Web 应用的话,选择Tomcat 是一个优秀的解决方案;但是为了寻找支持其他的 Java EE API,那么寻找一个应用服务器或者把 Tomcat作为应用服务器的辅助,将是一个不错的解决方案;第三种方式是找到独立的 Java EE API 实现,然后把它们跟Tomcat 结合起来使用。虽然整合会带来相关的问题,但是这种方式是最为有效的。 二、Tomcat 与 Web 服务器 Tomcat 是提供一个支持 Servlet 和 JSP 运行的容器。Servlet 和 JSP 能根据实时需要,产生动态网页内容。而对于 Web 服务器来说, Apache 仅仅支持静态网页,对于支持动态网页就会显得无能为力;Tomcat 则既能为动态网页服务,同时也能为静态网页提供支持。尽管它没有通常的 Web 服务器快、功能也不如 Web 服务器丰富,但是 Tomcat 逐渐为支持静态内容不断扩充。大多数的 Web 服务器都是用底层语言编写如 C,利用了相应平台的特征,因此用纯 Java 编写的 Tomcat 执行速度不可能与它们相提并论。 一般来说,大的站点都是将 Tomcat 与 Apache 的结合,Apache 负责接受所有来自客户端的 HTTP 请求,然后将 Servlets 和 JSP 的请求转发给 Tomcat 来处理。Tomcat 完成处理后,将响应传回给 Apache,最后 Apache 将响应返回给客户端。
Java Web项目的创建
web项目的目录结构

web项目的发布
两种发布方式: 1、将相应的项目目录拷贝到Tomcat目录的webapps目录中,之后直接在浏览器中输入访问的地址:localhost:8080/项目名称 2、打开Tomcat->conf->server.xml,在这个主机中增加一个Context即可 即可以完成项目发布,之后通过localhost:8080/myProject就可以访问该目录
myeclipse中发布项目
在myeclipse中配置Tomcat
1. 首先我们打开Myeclipse,进入偏好设置window-perference:如下图所示:  2. 进入偏好设置(perference),在偏好设置的搜索栏那里输入tomcat查找tomcat.如下图所示:  3. 我们可以看到搜索到的有四个tomcat项: 第一个是Myeclipse的自带tomcat,然后是自己下载使用的tomcat版本,有5.x,6.x,4.x,我们最常用的就是tomcat6.0,在这里我们以6.0作为说明:  4. 在这里我们点击tomcat6.0进入,然后将自己安装目录添加进去。  5. 下面来看看我的电脑上的tomcat的解压缩目录吧!这里强烈推荐使用解压缩版本,比安装版好很多。  6. 然后我们找到Myeclipse自带的tomcat项,将自带的tomcat设为禁用(disable)。如图所示:  7. 然后我们点击tomcat6.x,也就是我们自己添加的tomcat,我们将tomcat6.x的jdk设置为自己安装的jdk,为了统一。  8. 设置完这些之后,我们点击apply,然后点击ok就行了。 
创建项目
第一步:新建Web Project,如下图。  第二步,在弹出的窗口填写下面内容。Project Name填写的内容是项目名称;JavaEE Specification Level选择Java EE 5.0,点击“Fininsh”按钮,进入下一步。  第三步,新建的项目结构如下图。项目包含下面几个包,一个src,一个Webroot,还有JRE...和Java EE...这个两个是项目依赖的类(暂时不管)。我们主要看WebRoot,Jsp页面我们是放在这个目录下的。  第四步,建立Servlet类。按下图操作。  第五步,在Name中填写Servlet类的名称:TestServlet。,下面的多选项,我们去除其他的,只是选择doPost()。  第六步,填写完上述步骤,点击“Next”按钮,来到下面这部。在这里,我们什么也不用填写。我们解析一下这里面的内容。这说明我们将建立一个TestServlet类,也同时在web.xml文件里面修改相关的映射路径。映射路径初始为"servlet/TestServlet"。点击“Finish”按钮,完成操作  第七步,我们看到如下页面,src中多了一个TestServlet.java文件。我们准备修改这块页面。  至此,一个Java Web项目创建完成,并且创建了一个servlet
HTTP协议详解
引言
HTTP是一个属于应用层的面向对象的协议,由于其简捷、快速的方式,适用于分布式超媒体信息系统。它于1990年提出,经过几年的使用与发展,得到不断地完善和扩展。目前在WWW中使用的是HTTP/1.0的第六版,HTTP/1.1的规范化工作正在进行之中,而且HTTP-NG(Next Generation of HTTP)的建议已经提出。 HTTP协议的主要特点可概括如下: 1.支持客户端/服务器模式。 2.简单快速:客户向服务器请求服务时,只需传送请求方法和路径。请求方法常用的有GET、HEAD、POST。每种方法规定了客户与服务器联系的类型不同。由于HTTP协议简单,使得HTTP服务器的程序规模小,因而通信速度很快。 3.灵活:HTTP允许传输任意类型的数据对象。正在传输的类型由Content-Type加以标记。 4.无连接:无连接的含义是限制每次连接只处理一个请求。服务器处理完客户的请求,并收到客户的应答后,即断开连接。采用这种方式可以节省传输时间。 5.无状态:HTTP协议是无状态协议。无状态是指协议对于事务处理没有记忆能力。缺少状态意味着如果后续处理需要前面的信息,则它必须重传,这样可能导致每次连接传送的数据量增大。另一方面,在服务器不需要先前信息时它的应答就较快。
HTTP协议详解之URL篇
http(超文本传输协议)是一个基于请求与响应模式的、无状态的、应用层的协议,常基于TCP的连接方式,HTTP1.1版本中给出一种持续连接的机制,绝大多数的Web开发,都是构建在HTTP协议之上的Web应用。 HTTP URL (URL是一种特殊类型的URI,包含了用于查找某个资源的足够的信息)的格式如下: http://host[":"port][abs_path] http表示要通过HTTP协议来定位网络资源;host表示合法的Internet主机域名或者IP地址;port指定一个端口号,为空则使用缺省端口80;abs_path指定请求资源的URI;如果URL中没有给出abs_path,那么当它作为请求URI时,必须以“/”的形式给出,通常这个工作浏览器自动帮我们完成。 eg: 1、输入:www.google.com 浏览器自动转换成:http://www.google.com/ 2、http:192.168.0.116:8080/index.jsp
HTTP协议详解之请求篇
http请求由三部分组成,分别是:请求行、消息报头、请求正文 1、请求行以一个方法符号开头,以空格分开,后面跟着请求的URI和协议的版本,格式如下:Method Request-URI HTTP-Version CRLF 其中 Method表示请求方法;Request-URI是一个统一资源标识符;HTTP-Version表示请求的HTTP协议版本;CRLF表示回车和换行(除了作为结尾的CRLF外,不允许出现单独的CR或LF字符)。 请求方法(所有方法全为大写)有多种,各个方法的解释如下: GET 请求获取Request-URI所标识的资源 POST 在Request-URI所标识的资源后附加新的数据 HEAD 请求获取由Request-URI所标识的资源的响应消息报头 PUT 请求服务器存储一个资源,并用Request-URI作为其标识 DELETE 请求服务器删除Request-URI所标识的资源 TRACE 请求服务器回送收到的请求信息,主要用于测试或诊断 CONNECT 保留将来使用 OPTIONS 请求查询服务器的性能,或者查询与资源相关的选项和需求 应用举例: GET方法:在浏览器的地址栏中输入网址的方式访问网页时,浏览器采用GET方法向服务器获取资源,eg:GET /form.html HTTP/1.1 (CRLF) POST方法要求被请求服务器接受附在请求后面的数据,常用于提交表单。 eg:POST /reg.jsp HTTP/ (CRLF) Accept:image/gif,image/x-xbit,... (CRLF) ... HOST:www.guet.edu.cn (CRLF) Content-Length:22 (CRLF) Connection:Keep-Alive (CRLF) Cache-Control:no-cache (CRLF) (CRLF) //该CRLF表示消息报头已经结束,在此之前为消息报头 user=zhangsan&pwd=1234 //此行以下为提交的数据 HEAD方法与GET方法几乎是一样的,对于HEAD请求的回应部分来说,它的HTTP头部中包含的信息与通过GET请求所得到的信息是相同的。利用这个方法,不必传输整个资源内容,就可以得到Request-URI所标识的资源的信息。该方法常用于测试超链接的有效性,是否可以访问,以及最近是否更新。 2、请求报头后述 3、请求正文(略)
HTTP协议详解之响应篇
在接收和解释请求消息后,服务器返回一个HTTP响应消息。 HTTP响应也是由三个部分组成,分别是:状态行、消息报头、响应正文 1、状态行格式如下: HTTP-Version Status-Code Reason-Phrase CRLF 其中,HTTP-Version表示服务器HTTP协议的版本;Status-Code表示服务器发回的响应状态代码;Reason-Phrase表示状态代码的文本描述。 状态代码有三位数字组成,第一个数字定义了响应的类别,且有五种可能取值: 1xx:指示信息--表示请求已接收,继续处理 2xx:成功--表示请求已被成功接收、理解、接受 3xx:重定向--要完成请求必须进行更进一步的操作 4xx:客户端错误--请求有语法错误或请求无法实现 5xx:服务器端错误--服务器未能实现合法的请求 常见状态代码、状态描述、说明: 200 OK //客户端请求成功 400 Bad Request //客户端请求有语法错误,不能被服务器所理解 401 Unauthorized //请求未经授权,这个状态代码必须和WWW-Authenticate报 //头域一起使用 403 Forbidden //服务器收到请求,但是拒绝提供服务 404 Not Found //请求资源不存在,eg:输入了错误的URL 500 Internal Server Error //服务器发生不可预期的错误 503 Server Unavailable //服务器当前不能处理客户端的请求,一段时间后, //可能恢复正常 eg:HTTP/1.1 200 OK (CRLF) 2、响应报头后述 3、响应正文就是服务器返回的资源的内容
HTTP协议详解之消息报头篇
HTTP消息由客户端到服务器的请求和服务器到客户端的响应组成。请求消息和响应消息都是由开始行(对于请求消息,开始行就是请求行,对于响应消息,开始行就是状态行),消息报头(可选),空行(只有CRLF的行),消息正文(可选)组成。 HTTP消息报头包括普通报头、请求报头、响应报头、实体报头。 每一个报头域都是由名字+“:”+空格+值 组成,消息报头域的名字是大小写无关的。 1、普通报头 在普通报头中,有少数报头域用于所有的请求和响应消息,但并不用于被传输的实体,只用于传输的消息。 eg: Cache-Control 用于指定缓存指令,缓存指令是单向的(响应中出现的缓存指令在请求中未必会出现),且是独立的(一个消息的缓存指令不会影响另一个消息处理的缓存机制),HTTP1.0使用的类似的报头域为Pragma。 请求时的缓存指令包括:no-cache(用于指示请求或响应消息不能缓存)、no-store、max-age、max-stale、min-fresh、only-if-cached; 响应时的缓存指令包括:public、private、no-cache、no-store、no-transform、must-revalidate、proxy-revalidate、max-age、s-maxage. eg:为了指示IE浏览器(客户端)不要缓存页面,服务器端的JSP程序可以编写如下:response.sehHeader("Cache-Control","no-cache"); //response.setHeader("Pragma","no-cache");作用相当于上述代码,通常两者//合用 这句代码将在发送的响应消息中设置普通报头域:Cache-Control:no-cache Date普通报头域表示消息产生的日期和时间 Connection普通报头域允许发送指定连接的选项。例如指定连接是连续,或者指定“close”选项,通知服务器,在响应完成后,关闭连接 2、请求报头 请求报头允许客户端向服务器端传递请求的附加信息以及客户端自身的信息。 常用的请求报头 Accept Accept请求报头域用于指定客户端接受哪些类型的信息。eg:Accept:image/gif,表明客户端希望接受GIF图象格式的资源;Accept:text/html,表明客户端希望接受html文本。 Accept-Charset Accept-Charset请求报头域用于指定客户端接受的字符集。eg:Accept-Charset:iso-8859-1,gb2312.如果在请求消息中没有设置这个域,缺省是任何字符集都可以接受。 Accept-Encoding Accept-Encoding请求报头域类似于Accept,但是它是用于指定可接受的内容编码。eg:Accept-Encoding:gzip.deflate.如果请求消息中没有设置这个域服务器假定客户端对各种内容编码都可以接受。 Accept-Language Accept-Language请求报头域类似于Accept,但是它是用于指定一种自然语言。eg:Accept-Language:zh-cn.如果请求消息中没有设置这个报头域,服务器假定客户端对各种语言都可以接受。 Authorization Authorization请求报头域主要用于证明客户端有权查看某个资源。当浏览器访问一个页面时,如果收到服务器的响应代码为401(未授权),可以发送一个包含Authorization请求报头域的请求,要求服务器对其进行验证。 Host(发送请求时,该报头域是必需的) Host请求报头域主要用于指定被请求资源的Internet主机和端口号,它通常从HTTP URL中提取出来的,eg: 我们在浏览器中输入:http://www.guet.edu.cn/index.html 浏览器发送的请求消息中,就会包含Host请求报头域,如下: Host:www.guet.edu.cn 此处使用缺省端口号80,若指定了端口号,则变成:Host:www.guet.edu.cn:指定端口号 User-Agent 我们上网登陆论坛的时候,往往会看到一些欢迎信息,其中列出了你的操作系统的名称和版本,你所使用的浏览器的名称和版本,这往往让很多人感到很神奇,实际上,服务器应用程序就是从User-Agent这个请求报头域中获取到这些信息。User-Agent请求报头域允许客户端将它的操作系统、浏览器和其它属性告诉服务器。不过,这个报头域不是必需的,如果我们自己编写一个浏览器,不使用User-Agent请求报头域,那么服务器端就无法得知我们的信息了。 请求报头举例: GET /form.html HTTP/1.1 (CRLF) Accept:image/gif,image/x-xbitmap,image/jpeg,application/x-shockwave-flash,application/vnd.ms-excel,application/vnd.ms-powerpoint,application/msword,*/* (CRLF) Accept-Language:zh-cn (CRLF) Accept-Encoding:gzip,deflate (CRLF) If-Modified-Since:Wed,05 Jan 2007 11:21:25 GMT (CRLF) If-None-Match:W/"80b1a4c018f3c41:8317" (CRLF) User-Agent:Mozilla/4.0(compatible;MSIE6.0;Windows NT 5.0) (CRLF) Host:www.guet.edu.cn (CRLF) Connection:Keep-Alive (CRLF) (CRLF) 3、响应报头 响应报头允许服务器传递不能放在状态行中的附加响应信息,以及关于服务器的信息和对Request-URI所标识的资源进行下一步访问的信息。 常用的响应报头 Location Location响应报头域用于重定向接受者到一个新的位置。Location响应报头域常用在更换域名的时候。 Server Server响应报头域包含了服务器用来处理请求的软件信息。与User-Agent请求报头域是相对应的。下面是 Server响应报头域的一个例子: Server:Apache-Coyote/1.1 WWW-Authenticate WWW-Authenticate响应报头域必须被包含在401(未授权的)响应消息中,客户端收到401响应消息时候,并发送Authorization报头域请求服务器对其进行验证时,服务端响应报头就包含该报头域。 eg:WWW-Authenticate:Basic realm="Basic Auth Test!" //可以看出服务器对请求资源采用的是基本验证机制。 4、实体报头 请求和响应消息都可以传送一个实体。一个实体由实体报头域和实体正文组成,但并不是说实体报头域和实体正文要在一起发送,可以只发送实体报头域。实体报头定义了关于实体正文(eg:有无实体正文)和请求所标识的资源的元信息。 常用的实体报头 Content-Encoding Content-Encoding实体报头域被用作媒体类型的修饰符,它的值指示了已经被应用到实体正文的附加内容的编码,因而要获得Content-Type报头域中所引用的媒体类型,必须采用相应的解码机制。Content-Encoding这样用于记录文档的压缩方法,eg:Content-Encoding:gzip Content-Language Content-Language实体报头域描述了资源所用的自然语言。没有设置该域则认为实体内容将提供给所有的语言阅读 者。eg:Content-Language:da Content-Length Content-Length实体报头域用于指明实体正文的长度,以字节方式存储的十进制数字来表示。 Content-Type Content-Type实体报头域用语指明发送给接收者的实体正文的媒体类型。eg: Content-Type:text/html;charset=ISO-8859-1 Content-Type:text/html;charset=GB2312 Last-Modified Last-Modified实体报头域用于指示资源的最后修改日期和时间。 Expires Expires实体报头域给出响应过期的日期和时间。为了让代理服务器或浏览器在一段时间以后更新缓存中(再次访问曾访问过的页面时,直接从缓存中加载,缩短响应时间和降低服务器负载)的页面,我们可以使用Expires实体报头域指定页面过期的时间。eg:Expires:Thu,15 Sep 2006 16:23:12 GMT HTTP1.1的客户端和缓存必须将其他非法的日期格式(包括0)看作已经过期。eg:为了让浏览器不要缓存页面,我们也可以利用Expires实体报头域,设置为0,jsp中程序如下:response.setDateHeader("Expires","0");
利用telnet观察http协议的通讯过程
实验目的及原理: 利用MS的telnet工具,通过手动输入http请求信息的方式,向服务器发出请求,服务器接收、解释和接受请求后,会返回一个响应,该响应会在telnet窗口上显示出来,从而从感性上加深对http协议的通讯过程的认识。 实验步骤: 1、打开telnet 1.1 打开telnet 运行-->cmd-->telnet 1.2 打开telnet回显功能 set localecho 2、连接服务器并发送请求 2.1 open www.guet.edu.cn 80 //注意端口号不能省略 HEAD /index.asp HTTP/1.0 Host:www.guet.edu.cn /*我们可以变换请求方法,请求桂林电子主页内容,输入消息如下*/ open www.guet.edu.cn 80 GET /index.asp HTTP/1.0 //请求资源的内容 Host:www.guet.edu.cn 2.2 open www.sina.com.cn 80 //在命令提示符号下直接输入telnet www.sina.com.cn 80 HEAD /index.asp HTTP/1.0 Host:www.sina.com.cn 3 实验结果: 3.1 请求信息2.1得到的响应是: HTTP/1.1 200 OK //请求成功 Server: Microsoft-IIS/5.0 //web服务器 Date: Thu,08 Mar 200707:17:51 GMT Connection: Keep-Alive Content-Length: 23330 Content-Type: text/html Expries: Thu,08 Mar 2007 07:16:51 GMT Set-Cookie: ASPSESSIONIDQAQBQQQB=BEJCDGKADEDJKLKKAJEOIMMH; path=/ Cache-control: private //资源内容省略 3.2 请求信息2.2得到的响应是: HTTP/1.0 404 Not Found //请求失败 Date: Thu, 08 Mar 2007 07:50:50 GMT Server: Apache/2.0.54 Last-Modified: Thu, 30 Nov 2006 11:35:41 GMT ETag: "6277a-415-e7c76980" Accept-Ranges: bytes X-Powered-By: mod_xlayout_jh/0.0.1vhs.markII.remix Vary: Accept-Encoding Content-Type: text/html X-Cache: MISS from zjm152-78.sina.com.cn Via: 1.0 zjm152-78.sina.com.cn:80 X-Cache: MISS from th-143.sina.com.cn Connection: close 失去了跟主机的连接 按任意键继续... 4 .注意事项:1、出现输入错误,则请求不会成功。 2、报头域不分大小写。 3、更深一步了解HTTP协议,可以查看RFC2616,在http://www.letf.org/rfc上找到该文件。 4、开发后台程序必须掌握http协议
HTTP协议的状态码

Servlet
Servlet的概念
Servlet是基于HTTP协议的请求--响应模式的服务器端的java编程语言
Servlet的生命周期
在Servlet被部署到容器之后,其生命周期是由容器来控制。当一个请求映射给一个Servlet,容器执行以下步骤: 1.如果这个Servlet的实例不存在,web容器 a.加载Servlet类 b.创建Servlet类对象 c.调用init方法初始化Servlet实例 2.调用service方法,传递请求和响应对象。 3.如果需要移除Servlet,则容器会调用servlet的destroy方法将其销毁 Servlet生命周期时序图: 
Servlet的创建
1、写一个类让其继承HttpServlet。 2、覆盖doGet方法,在doGet方法中加入一些测试程序 3、配置web.xml让该servlet可以访问 示例:  
Servlet的配置
1.创建 xxxx ---->为该servlet设定一个名称 xxx.xxx.xxx---->servlet的类,要加入包路径 2.创建 xxxx------>说明servlet的name /xxx------->声明在浏览器中需要访问的地址 具体的使用说明看下图 Servlet调用过程时序图: 
JSP
JSP简介
概念
JSP全称Java Server Pages,是一种动态网页开发技术。它使用JSP标签在HTML网页中插入Java代码。标签通常以结束。 JSP是一种Java servlet,主要用于实现Java web应用程序的用户界面部分。网页开发者们通过结合HTML代码、XHTML代码、XML元素以及嵌入JSP操作和命令来编写JSP。 JSP通过网页表单获取用户输入数据、访问数据库及其他数据源,然后动态地创建网页。 JSP标签有多种功能,比如访问数据库、记录用户选择信息、访问JavaBeans组件等,还可以在不同的网页中传递控制信息和共享信息。
优势
JSP程序与CGI程序有着相似的功能,但和CGI程序相比,JSP程序有如下优势: 性能更加优越,因为JSP可以直接在HTML网页中动态嵌入元素而不需要单独引用CGI文件。 服务器调用的是已经编译好的JSP文件,而不像CGI/Perl那样必须先载入解释器和目标脚本。 JSP基于Java Servlets API,因此,JSP拥有各种强大的企业级Java API,包括JDBC,JNDI,EJB,JAXP等等。 JSP页面可以与处理业务逻辑的servlets一起使用,这种模式被Java servlet 模板引擎所支持。 最后,JSP是Java EE不可或缺的一部分,是一个完整的企业级应用平台。这意味着JSP可以用最简单的方式来实现最复杂的应用。
JSP的生命周期
理解JSP底层功能的关键就是去理解它们所遵守的生命周期。 JSP生命周期就是从创建到销毁的整个过程,类似于servlet生命周期,区别在于JSP生命周期还包括将JSP文件转译成servlet。 以下是JSP生命周期中所走过的几个阶段: 编译阶段: Servelet容器编译Servelet源文件,生成servelet类 初始化阶段: 加载与JSP对应的Servelet类,创建其实例,并调用它的初始化方法 执行阶段: 调用与JSP对应的Servelet实例的服务方法 销毁阶段: 调用与JSP对应的Servelet实例的销毁方法,然后销毁Servelet实例 很明显,JSP生命周期的四个主要阶段和servlet生命周期非常相似,下面给出图示:  JSP编译 当浏览器请求JSP页面时,JSP引擎会首先去检查是否需要编译这个文件。如果这个文件没有被编译过,或者在上次编译后被更改过,则编译这个JSP文件。 编译的过程包括三个步骤: 解析JSP文件。 将JSP文件转为servlet。 编译servlet。 JSP初始化 容器载入JSP文件后,它会在为请求提供任何服务前调用jspInit()方法。如果您需要执行自定义的JSP初始化任务,复写jspInit()方法就行了,就像下面这样: public void jspInit(){ // 初始化代码 } 一般来讲程序只初始化一次,servlet也是如此。通常情况下您可以在jspInit()方法中初始化数据库连接、打开文件和创建查询表。 JSP执行 这一阶段描述了JSP生命周期中一切与请求相关的交互行为,直到被销毁。 当JSP网页完成初始化后,JSP引擎将会调用_jspService()方法。 _jspService()方法需要一个HttpServletRequest对象和一个HttpServletResponse对象作为它的参数,就像下面这样: void _jspService(HttpServletRequest request, HttpServletResponse response) { // 服务端处理代码 } _jspService()方法在每个request中被调用一次并且负责产生与之相对应的response,并且它还负责产生所有7个HTTP方法的回应,比如GET、POST、DELETE等等。 JSP清理 JSP生命周期的销毁阶段描述了当一个JSP网页从容器中被移除时所发生的一切。 jspDestroy()方法在JSP中等价于servlet中的销毁方法。当您需要执行任何清理工作时复写jspDestroy()方法,比如释放数据库连接或者关闭文件夹等等。 jspDestroy()方法的格式如下: public void jspDestroy() { // 清理代码 } 实例 JSP生命周期代码实例如下所示: private int initVar=0; private int serviceVar=0; private int destroyVar=0; %> public void jspInit(){ initVar++; System.out.println("jspInit(): JSP被初始化了"+initVar+"次"); } public void jspDestroy(){ destroyVar++; System.out.println("jspDestroy(): JSP被销毁了"+destroyVar+"次"); } %> serviceVar++; System.out.println("_jspService(): JSP共响应了"+serviceVar+"次请求"); String content1="初始化次数 : "+initVar; String content2="响应客户请求次数 : "+serviceVar; String content3="销毁次数 : "+destroyVar; %>
JSP的语法
脚本程序 脚本程序可以包含任意量的Java语句、变量、方法或表达式,只要它们在脚本语言中是有效的。 脚本程序的语法格式: 或者,您也可以编写与其等价的XML语句,就像下面这样: 代码片段 任何文本、HTML标签、JSP元素必须写在脚本程序的外面。 下面给出一个示例,同时也是第一个JSP示例: Hello World! out.println("Your IP address is " + request.getRemoteAddr()); %> 注意:请确保Apache Tomcat已经安装在C:\apache-tomcat-7.0.47目录下并且运行环境已经正确设置。 将以上代码保存在hello.jsp中,然后将它放置在 C:\apache-tomcat-7.0.47\webapps\ROOT目录下,打开浏览器并在地址栏中输入http://localhost:8080/hello.jsp。运行后得到以下结果: JSP声明 一个声明语句可以声明一个或多个变量、方法,供后面的Java代码使用。在JSP文件中,您必须先声明这些变量和方法然后才能使用它们。 JSP声明的语法格式: 或者,您也可以编写与其等价的XML语句,就像下面这样: 代码片段 程序示例: JSP表达式 一个JSP表达式中包含的脚本语言表达式,先被转化成String,然后插入到表达式出现的地方。 由于表达式的值会被转化成String,所以您可以在一个文本行中使用表达式而不用去管它是否是HTML标签。 表达式元素中可以包含任何符合Java语言规范的表达式,但是不能使用分号来结束表达式。 JSP表达式的语法格式: 同样,您也可以编写与之等价的XML语句: 表达式 程序示例: Today's date: 运行后得到以下结果: Today's date: 11-Sep-2013 21:24:25 JSP注释 JSP注释主要有两个作用:为代码作注释以及将某段代码注释掉。 JSP注释的语法格式: 程序示例: A Test of Comments 运行后得到以下结果: A Test of Comments 不同情况下使用注释的语法规则: 语法 描述 JSP注释,注释内容不会被发送至浏览器甚至不会被编译 HTML注释,通过浏览器查看网页源代码时可以看见注释内容 %\> 代表静态 %> 常量 \' 在属性中使用的单引号 \" 在属性中使用的双引号 JSP指令 JSP指令用来设置与整个JSP页面相关的属性。 JSP指令语法格式: 这里有三种指令标签: 指令 描述 定义页面的依赖属性,比如脚本语言、error页面、缓存需求等等 包含其他文件 引入标签库的定义,可以是自定义标签 JSP行为 JSP行为标签使用XML语法结构来控制servlet引擎。它能够动态插入一个文件,重用JavaBean组件,引导用户去另一个页面,为Java插件产生相关的HTML等等。 行为标签只有一种语法格式,它严格遵守XML标准: 行为标签基本上是一些预先就定义好的函数,下表罗列出了一些可用的JSP行为标签:: 语法 描述 jsp:include 用于在当前页面中包含静态或动态资源 jsp:useBean 寻找和初始化一个JavaBean组件 jsp:setProperty 设置 JavaBean组件的值 jsp:getProperty 将 JavaBean组件的值插入到 output中 jsp:forward 从一个JSP文件向另一个文件传递一个包含用户请求的request对象 jsp:plugin 用于在生成的HTML页面中包含Applet和JavaBean对象 jsp:element 动态创建一个XML元素 jsp:attribute 定义动态创建的XML元素的属性 jsp:body 定义动态创建的XML元素的主体 jsp:text 用于封装模板数据 JSP隐含对象 JSP支持九个自动定义的变量,江湖人称隐含对象。这九个隐含对象的简介见下表: 对象 描述 requestHttpServletRequest类的实例 response HttpServletResponse类的实例 out PrintWriter类的实例,用于把结果输出至网页上 sessionHttpSession类的实例 application ServletContext类的实例,与应用上下文有关 config ServletConfig类的实例 pageContext PageContext类的实例,提供对JSP页面所有对象以及命名空间的访问 page 类似于Java类中的this关键字 ExceptionException类的对象,代表发生错误的JSP页面中对应的异常对象 控制流语句 JSP提供对Java语言的全面支持。您可以在JSP程序中使用Java API甚至建立Java代码块,包括判断语句和循环语句等等。 判断语句 If…else块,请看下面这个例子: Today is weekend Today is not weekend 运行后得到以下结果: Today is not weekend 现在来看看switch…case块,与if…else块有很大的不同,它使用out.println(),并且整个都装在脚本程序的标签中,就像下面这样: switch(day) { case 0: out.println("It\'s Sunday."); break; case 1: out.println("It\'s Monday."); break; case 2: out.println("It\'s Tuesday."); break; case 3: out.println("It\'s Wednesday."); break; case 4: out.println("It\'s Thursday."); break; case 5: out.println("It\'s Friday."); break; default: out.println("It's Saturday."); } %> 运行后得出以下结果: It's Wednesday. 循环语句 在JSP程序中可以使用Java的三个基本循环类型:for,while,和 do…while。 让我们来看看for循环的例子: JSP Tutorial 运行后得到以下结果: JSP Tutorial JSP Tutorial JSP Tutorial 将上例改用while循环来写: JSP Tutorial 运行后得到同样的结果: JSP Tutorial JSP Tutorial JSP Tutorial JSP运算符 JSP支持所有Java逻辑和算术运算符。 下表罗列出了JSP常见运算符,优先级从高到底: 类别 操作符 结合性 后缀 () [] . (点运算符) 左到右 一元 ++ - - ! ~ 右到左 可乘性 * / % 左到右 可加性 + - 左到右 移位 >> >>> 关系 > >= 相等/不等 == != 左到右 位与 & 左到右 位异或 ^ 左到右 位或 | 左到右 逻辑与 && 左到右 逻辑或 || 左到右 条件判断 ?: 右到左 赋值 = += -= *= /= %= >>= 逗号 , 左到右 JSP常量 JSP语言定义了以下几个常量: Boolean:true and false Integer:与Java中的一样 Floating point:与Java中的一样 String:以单引号或双引号开始和结束。 " 被转义成 \",'被转义成 \', \ 被转义成\\ Null:null
JSP的指令
JSP指令用来设置整个JSP页面相关的属性,如网页的编码方式和脚本语言。 语法格式如下: 指令可以有很多个属性,它们以键值对的形式存在,并用逗号隔开。 JSP中的三种指令标签: 指令 描述 定义网页依赖属性,比如脚本语言、error页面、缓存需求等等 包含其他文件 引入标签库的定义 Page指令 Page指令为容器提供当前页面的使用说明。一个JSP页面可以包含多个page指令。 Page指令的语法格式: 等价的XML格式: 属性 下表列出与Page指令相关的属性: 属性 描述 buffer 指定out对象使用缓冲区的大小 autoFlush 控制out对象的 缓存区 contentType 指定当前JSP页面的MIME类型和字符编码 errorPage 指定当JSP页面发生异常时需要转向的错误处理页面 isErrorPage 指定当前页面是否可以作为另一个JSP页面的错误处理页面 extends 指定servlet从哪一个类继承 import 导入要使用的Java类 info 定义JSP页面的描述信息 isThreadSafe 指定对JSP页面的访问是否为线程安全 language 定义JSP页面所用的脚本语言,默认是Java session 指定JSP页面是否使用session isELIgnored 指定是否执行EL表达式 isScriptingEnabled 确定脚本元素能否被使用 Include指令 JSP可以通过include指令来包含其他文件。被包含的文件可以是JSP文件、HTML文件或文本文件。包含的文件就好像是该JSP文件的一部分,会被同时编译执行。 Include指令的语法格式如下: Include指令中的文件名实际上是一个相对的URL。如果您没有给文件关联一个路径,JSP编译器默认在当前路径下寻找。 等价的XML语法: Taglib指令 JSP API允许用户自定义标签,一个自定义标签库就是自定义标签的集合。 Taglib指令引入一个自定义标签集合的定义,包括库路径、自定义标签。 Taglib指令的语法: uri属性确定标签库的位置,prefix属性指定标签库的前缀。 等价的XML语法:
JSP的动作
JSP动作元素 与JSP指令元素不同的是,JSP动作元素在请求处理阶段起作用。JSP动作元素是用XML语法写成的。 利用JSP动作可以动态地插入文件、重用JavaBean组件、把用户重定向到另外的页面、为Java插件生成HTML代码。 动作元素只有一种语法,它符合XML标准: 动作元素基本上都是预定义的函数,JSP规范定义了一系列的标准动作,它用JSP作为前缀,可用的标准动作元素如下: 语法 描述 jsp:include 在页面被请求的时候引入一个文件。 jsp:useBean 寻找或者实例化一个JavaBean。 jsp:setProperty 设置JavaBean的属性。 jsp:getProperty 输出某个JavaBean的属性。 jsp:forward 把请求转到一个新的页面。 jsp:plugin 根据浏览器类型为Java插件生成OBJECT或EMBED标记。 jsp:element 定义动态XML元素 jsp:attribute 设置动态定义的XML元素属性。 jsp:body 设置动态定义的XML元素内容。 jsp:text 在JSP页面和文档中使用写入文本的模板 常见的属性 所有的动作要素都有两个属性:id属性和scope属性。 id属性: id属性是动作元素的唯一标识,可以在JSP页面中引用。动作元素创建的id值可以通过PageContext来调用。 scope属性: 该属性用于识别动作元素的生命周期。 id属性和scope属性有直接关系,scope属性定义了相关联id对象的寿命。 scope属性有四个可能的值: (a) page, (b)request, (c)session, 和 (d) application。 动作元素 动作元素用来包含静态和动态的文件。该动作把指定文件插入正在生成的页面。语法格式如下: 前面已经介绍过include指令,它是在JSP文件被转换成Servlet的时候引入文件,而这里的jsp:include动作不同,插入文件的时间是在页面被请求的时候。 以下是include动作相关的属性列表。 属性 描述 page包含在页面中的相对URL地址。 flush 布尔属性,定义在包含资源前是否刷新缓存区。 实例 以下我们定义了两个文件date.jsp和main.jsp,代码如下所示: date.jsp文件代码: Today's date: main.jsp文件代码: The include action Example 现在将以上两个文件放在服务器的根目录下,访问main.jsp文件。显示结果如下: The include action Example Today's date: 12-Sep-2013 14:54:22 动作元素 jsp:useBean动作用来装载一个将在JSP页面中使用的JavaBean。 这个功能非常有用,因为它使得我们既可以发挥Java组件重用的优势,同时也避免了损失JSP区别于Servlet的方便性。 jsp:useBean动作最简单的语法为: 在类载入后,我们既可以通过 jsp:setProperty 和 jsp:getProperty 动作来修改和检索bean的属性。 以下是useBean动作相关的属性列表。 属性 描述 class 指定Bean的完整包名。 type指定将引用该对象变量的类型。 beanName 通过 java.beans.Beans 的 instantiate() 方法指定Bean的名字。 在给出具体实例前,让我们先来看下 jsp:setProperty 和 jsp:getProperty 动作元素: 动作元素 jsp:setProperty用来设置已经实例化的Bean对象的属性,有两种用法。首先,你可以在jsp:useBean元素的外面(后面)使用jsp:setProperty,如下所示: ... 此时,不管jsp:useBean是找到了一个现有的Bean,还是新创建了一个Bean实例,jsp:setProperty都会执行。第二种用法是把jsp:setProperty放入jsp:useBean元素的内部,如下所示: ... 此时,jsp:setProperty只有在新建Bean实例时才会执行,如果是使用现有实例则不执行jsp:setProperty。 属性 描述 namename属性是必需的。它表示要设置属性的是哪个Bean。 property property属性是必需的。它表示要设置哪个属性。有一个特殊用法:如果property的值是"*",表示所有名字和Bean属性名字匹配的请求参数都将被传递给相应的属性set方法。 value value 属性是可选的。该属性用来指定Bean属性的值。字符串数据会在目标类中通过标准的valueOf方法自动转换成数字、boolean、Boolean、 byte、Byte、char、Character。例如,boolean和Boolean类型的属性值(比如"true")通过 Boolean.valueOf转换,int和Integer类型的属性值(比如"42")通过Integer.valueOf转换。 value和param不能同时使用,但可以使用其中任意一个。 param param 是可选的。它指定用哪个请求参数作为Bean属性的值。如果当前请求没有参数,则什么事情也不做,系统不会把null传递给Bean属性的set方法。因此,你可以让Bean自己提供默认属性值,只有当请求参数明确指定了新值时才修改默认属性值。 动作元素 jsp:getProperty动作提取指定Bean属性的值,转换成字符串,然后输出。语法格式如下: ... 下表是与getProperty相关联的属性: 属性 描述 name要检索的Bean属性名称。Bean必须已定义。 property 表示要提取Bean属性的值 实例 以下实例我们使用了Bean: /* 文件: TestBean.java */ package action; public class TestBean { private String message = "No message specified"; public String getMessage() { return(message); } public void setMessage(String message) { this.message = message; } } 编译以上实例并生成 TestBean.class 文件,将该文件拷贝至服务器正式存放Java类的目录下,而不是保留给修改后能够自动装载的类的目录( 如:C:\apache-tomcat-7.0.2\webapps\WEB-INF\classes\action目录中,CLASSPATH 变量必须包含该路径。 )。例如,对于Java Web Server来说,Bean和所有Bean用到的类都应该放入classes目录,或者封装进jar文件后放入lib目录,但不应该放到servlets 下。 下面是一个很简单的例子,它的功能是装载一个Bean,然后设置/读取它的message属性。 现在让我们在main.jsp文件中调用该Bean: Using JavaBeans in JSP property="message" value="Hello JSP..." /> Got message.... 执行以上文件,输出如下所示: Using JavaBeans in JSP Got message.... Hello JSP... 动作元素 jsp:forward动作把请求转到另外的页面。jsp:forward标记只有一个属性page。语法格式如下所示: 以下是forward相关联的属性: 属性 描述 pagepage属性包含的是一个相对URL。page的值既可以直接给出,也可以在请求的时候动态计算,可以是一个JSP页面或者一个 Java Servlet. 实例 以下实例我们使用了两个文件,分别是: date.jps 和 main.jsp。 date.js文件代码如下: Today's date: main.jsp文件代码: The include action Example 现在将以上两个文件放在服务器的根目录下,访问main.jsp文件。显示结果如下: Today's date: 12-Sep-2010 14:54:22 动作元素 jsp:plugin动作用来根据浏览器的类型,插入通过Java插件 运行Java Applet所必需的OBJECT或EMBED元素。 如果需要的插件不存在,它会下载插件,然后执行Java组件。 Java组件可以是一个applet或一个JavaBean。 plugin动作有多个对应HTML元素的属性用于格式化Java 组件。param元素可用于向Applet 或 Bean 传递参数。 以下是使用plugin 动作元素的典型实例: width="60" height="80"> Unable to initialize Java Plugin 如果你有兴趣可以尝试使用applet来测试jsp:plugin动作元素,元素是一个新元素,在组件出现故障的错误是发送给用户错误信息。 、 、 动作元素 、 、 动作元素动态定义XML元素。动态是非常重要的,这就意味着XML元素在编译时是动态生成的而非静态。 以下实例动态定义了XML元素: xmlns:jsp="http://java.sun.com/JSP/Page"> Value for the attribute Body for XML element 执行时生成HTML代码如下: xmlns:jsp="http://java.sun.com/JSP/Page"> Body for XML element 动作元素 动作元素允许在JSP页面和文档中使用写入文本的模板,语法格式如下: Template data 以上文本模板不能包含其他元素,只能只能包含文本和EL表达式(注:EL表达式将在后续章节中介绍)。请注意,在XML文件中,您不能使用表达式如 ${whatever > 0},因为>符号是非法的。 你可以使用 ${whatever gt 0}表达式或者嵌入在一个CDATA部分的值。 ]]> 如果你需要在 XHTML 中声明 DOCTYPE,必须使用到动作元素,实例如下: PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "DTD/xhtml1-strict.dtd">]]> Welcome to JSP Programming 你可以对以上实例尝试使用及不使用该动作元素执行结果的区别。
JSP的范围
作用域范围 1、在当前页面attribute的值存在 2、客户端跳转attribute作用域的值丢失 3、服务器端跳转值会丢失 request作用域范围 1、当前页面可以存储数据 2、客户端跳转无法存储数据 3、服务器端跳转可以存储数据 总体来说使用request传递数据仅仅支持在服务器端跳转 Session作用域范围 1、当前页面有数据 2、客服端跳转有数据 3、服务器端跳转有数据 只有在重新启动浏览器之后数据才会丢失,可以使用session很方便的实现购物车和用户登录检测 对于session的数据而言可以使用session.invalidate()来结束session session.removeAttribute(),来移除session中的数据 Application作用域范围 只有在重启服务器数据才会丢失,计数器
JSP的内置对象
JSP隐含对象是JSP容器为每个页面提供的Java对象,开发者可以直接使用它们而不用显式声明。JSP隐含对象也被称为预定义变量。 JSP所支持的九大隐含对象: 对象 描述 request HttpServletRequest类的实例 response HttpServletResponse类的实例 out PrintWriter类的实例,用于把结果输出至网页上 session HttpSession类的实例 application ServletContext类的实例,与应用上下文有关 config ServletConfig类的实例 pageContext PageContext类的实例,提供对JSP页面所有对象以及 命名空间的访问 page 类似于Java类中的this关键字 Exception Exception类的对象,代表发生错误的JSP页面中对应的 异常对象 request对象 request对象是javax.servlet.http.HttpServletRequest 类的实例。每当客户端请求一个JSP页面时,JSP引擎就会制造一个新的request对象来代表这个请求。 request对象提供了一系列方法来获取HTTP头信息,cookies,HTTP方法等等。 response对象 response对象是javax.servlet.http.HttpServletResponse类的实例。当服务器创建request对象时会同时创建用于响应这个客户端的response对象。 response对象也定义了处理HTTP头模块的接口。通过这个对象,开发者们可以添加新的cookies,时间戳,HTTP状态码等等。 out对象 out对象是 javax.servlet.jsp.JspWriter 类的实例,用来在response对象中写入内容。 最初的JspWriter类对象根据页面是否有缓存来进行不同的实例化操作。可以在page指令中使用buffered='false'属性来轻松关闭缓存。 JspWriter类包含了大部分java.io.PrintWriter类中的方法。不过,JspWriter新增了一些专为处理缓存而设计的方法。还有就是,JspWriter类会抛出IOExceptions异常,而PrintWriter不会。 下表列出了我们将会用来输出boolean,char,int,double,Srtring,object等类型数据的重要方法: 方法 描述 out.print(dataType dt) 输出Type类型的值 out.println(dataType dt) 输出Type类型的值然后换行 out.flush() 刷新输出流 session对象 session对象是 javax.servlet.http.HttpSession 类的实例。和Java Servlets中的session对象有一样的行为。 session对象用来跟踪在各个客户端请求间的会话。 application对象 application对象直接包装了servlet的ServletContext类的对象,是javax.servlet.ServletContext 类的实例。 这个对象在JSP页面的整个生命周期中都代表着这个JSP页面。这个对象在JSP页面初始化时被创建,随着jspDestroy()方法的调用而被移除。 通过向application中添加属性,则所有组成您web应用的JSP文件都能访问到这些属性。 config对象 config对象是 javax.servlet.ServletConfig 类的实例,直接包装了servlet的ServletConfig类的对象。 这个对象允许开发者访问Servlet或者JSP引擎的初始化参数,比如文件路径等。 以下是config对象的使用方法,不是很重要,所以不常用: config.getServletName(); 它返回包含在元素中的servlet名字,注意,元素在 WEB-INF\web.xml 文件中定义。 pageContext 对象 pageContext对象是javax.servlet.jsp.PageContext 类的实例,用来代表整个JSP页面。 这个对象主要用来访问页面信息,同时过滤掉大部分实现细节。 这个对象存储了request对象和response对象的引用。application对象,config对象,session对象,out对象可以通过访问这个对象的属性来导出。 pageContext对象也包含了传给JSP页面的指令信息,包括缓存信息,ErrorPage URL,页面scope等。 PageContext类定义了一些字段,包括PAGE_SCOPE,REQUEST_SCOPE,SESSION_SCOPE, APPLICATION_SCOPE。它也提供了40余种方法,有一半继承自javax.servlet.jsp.JspContext 类。 其中一个重要的方法就是removeArribute(),它可接受一个或两个参数。比如,pageContext.removeArribute("attrName")移除四个scope中相关属性,但是下面这种方法只移除特定scope中的相关属性: pageContext.removeAttribute("attrName", PAGE_SCOPE); page 对象 这个对象就是页面实例的引用。它可以被看做是整个JSP页面的代表。 page 对象就是this对象的同义词。 exception 对象 exception 对象包装了从先前页面中抛出的异常信息。它通常被用来产生对出错条件的适当响应。
JSP的表单处理
我们在浏览网页的时候,经常需要向服务器提交信息,并让后台程序处理。浏览器中使用 GET 和 POST 方法向服务器提交数据。 GET 方法 GET方法将请求的编码信息添加在网址后面,网址与编码信息通过"?"号分隔。如下所示: http://www.w3cschool.cc/hello?key1=value1&key2=value2 GET方法是浏览器默认传递参数的方法,一些敏感信息,如密码等建议不使用GET方法。 用get时,传输数据的大小有限制 (注意不是参数的个数有限制),最大为1024字节。 POST 方法 一些敏感信息,如密码等我们可以同过POST方法传递,post提交数据是隐式的。 POST提交数据是不可见的,GET是通过在url里面传递的(可以看一下你浏览器的地址栏)。 JSP使用getParameter()来获得传递的参数,getInputStream()方法用来处理客户端的二进制数据流的请求。 JSP 读取表单数据 getParameter(): 使用 request.getParameter() 方法来获取表单参数的值。 getParameterValues(): 获得如checkbox类(名字相同,但值有多个)的数据。 接收数组变量 ,如checkobx类型 getParameterNames():该方法可以取得所有变量的名称,该方法返回一个Emumeration。 getInputStream():调用此方法来读取来自客户端的二进制数据流。 使用URL的 GET 方法实例 以下是一个简单的URL,并使用GET方法来传递URL中的参数: http://localhost:8080/main.jsp?first_name=ZARA&last_name=ALI 以下是main.jsp文件的JSP程序用于处理客户端提交的表单数据,我们使用getParameter()方法来获取提交的数据: Using GET Method to Read Form Data First Name: Last Name: 接下来我们通过浏览器访问http://localhost:8080/main.jsp?first_name=ZARA&last_name=ALI 输出结果如下所示: Using GET Method to Read Form Data First Name: ZARA Last Name: ALI 使用表单的 GET 方法实例 以下是一个简单的HTML表单,该表单通过GET方法将客户端数据提交 到main.jsp文件中: First Name: Last Name: 将以上HTML代码保存到Hello.htm文件中。 将该文件放置于/webapps/ROOT 目录下。 通过访问 http://localhost:8080/Hello.htm,输出界面如下所示: form-1 在"First Name" 与 "Last Name"两个表单中填入信息,并点击"Submit"按钮,它将输出结果。 使用表单的 POST 方法实例 接下来让我们使用POST方法来传递表单数据,修改main.jsp与Hello.htm文件代码,如下所示: main.jsp文件代码: Using GET Method to Read Form Data First Name: Last Name: 以下是Hello.htm修改后的代码: First Name: Last Name: 通过浏览器访问 http://localhost:8080/Hello.htm,输出如下: form-1 在"First Name" 与 "Last Name"两个表单中填入信息,并点击"Submit"按钮,它将输出结果。 传递 Checkbox 数据到JSP程序 复选框 checkbox 可以传递一个甚至多个数据。 以下是一个简单的HTML代码,并将代码保存在CheckBox.htm文件中: Maths Physics Chemistry 以上代码在浏览器访问如下所示: 以下为main.jsp文件代码,用于处理复选框数据: Reading Checkbox Data Maths Flag: Physics Flag: Chemistry Flag: 以上实例输出结果为: form-2 读取所有表单参数 以下我们将使用 HttpServletRequest 的getParameterNames()来读取所有可用的表单参数,该方法可以取得所有变量的名称,该方法返回一个Emumeration。 一旦我们有了一个Enumeration(枚举),我们就可以调用hasMoreElements()方法来确定何时停止使用和nextElement()方法来获得每个参数的名称。 HTTP Header Request Example Param Name Param Value(s) Enumeration paramNames = request.getParameterNames(); while(paramNames.hasMoreElements()) { String paramName = (String)paramNames.nextElement(); out.print(" " + paramName + " \n"); String paramValue = request.getHeader(paramName); out.println(" " + paramValue + " \n"); } %> 以下是Hello.htm文件的内容: Maths Physics Chem 现在我们通过浏览器访问 Hello.htm 文件并提交数据,输出结果如下: form-3 你可以尝试使用以上的JSP代码读取其它对象,如文本框,单选按钮或下拉框等等其他形式的数据。
JSP的表达式语言
JSP 表达式语言 JSP表达式语言(EL)使得访问存储在JavaBean中的数据变得非常简单。JSP EL既可以用来创建算术表达式也可以用来创建逻辑表达式。在JSP EL表达式内可以使用整型数,浮点数,字符串,常量true、false,还有null。 一个简单的语法 典型的,当您需要在JSP标签中指定一个属性值时,只需要简单地使用字符串即可: JSP EL允许您指定一个表达式来表示属性值。一个简单的表达式语法如下: ${expr} 其中,expr指的是表达式。在JSP EL中通用的操作符是"."和"[]"。这两个操作符允许您通过内嵌的JSP对象访问各种各样的JavaBean属性。 举例来说,上面的标签可以使用表达式语言改写成如下形式: value="${2*box.width+2*box.height}"/> 当JSP编译器在属性中见到"${}"格式后,它会产生代码来计算这个表达式,并且产生一个替代品来代替表达式的值。 您也可以在标签的模板文本中使用表达式语言。比如标签简单地将其主体中的文本插入到JSP输出中: Hello JSP! 现在,在标签主体中使用表达式,就像这样: Box Perimeter is: ${2*box.width + 2*box.height} 在EL表达式中可以使用圆括号来组织子表达式。比如${(1 + 2) * 3}等于9,但是${1 + (2 * 3)} 等于7。 想要停用对EL表达式的评估的话,需要使用page指令将isELIgnored属性值设为true: 这样,EL表达式就会被忽略。若设为false,则容器将会计算EL表达式。 EL中的基础操作符 EL表达式支持大部分Java所提供的算术和逻辑操作符: 操作符 描述 . 访问一个Bean属性或者一个映射条目 [] 访问一个数组或者链表的元素 ( ) 组织一个子表达式以改变优先级 + 加 - 减或负 * 乘 / or div 除 % or mod 取模 == or eq 测试是否相等 != or ne 测试是否不等 > or gt 测试是否大于 >= or gt 测试是否大于等于 && or and 测试逻辑与 || or or 测试逻辑或 ! or not 测试取反 empty 测试是否空值 JSP EL中的函数 JSP EL允许您在表达式中使用函数。这些函数必须被定义在自定义标签库中。函数的使用语法如下: ${ns:func(param1, param2, ...)} ns指的是命名空间(namespace),func指的是函数的名称,param1指的是第一个参数,param2指的是第二个参数,以此类推。比如,有函数fn:length,在JSTL库中定义,可以像下面这样来获取一个字符串的长度: ${fn:length("Get my length")} 要使用任何标签库中的函数,您需要将这些库安装在服务器中,然后使用标签在JSP文件中包含这些库。 JSP EL隐含对象 JSP EL支持下表列出的隐含对象: 隐含对象 描述 pageScope page 作用域 requestScope request 作用域 sessionScope session 作用域 applicationScope application 作用域 param Request 对象的参数,字符串 paramValues Request对象的参数,字符串集合 header HTTP 信息头,字符串 headerValues HTTP 信息头,字符串集合 initParam 上下文初始化参数 cookie Cookie值 pageContext 当前页面的pageContext 您可以在表达式中使用这些对象,就像使用变量一样。接下来会给出几个例子来更好的理解这个概念。 pageContext对象 pageContext对象是JSP中pageContext对象的引用。通过pageContext对象,您可以访问request对象。比如,访问request对象传入的查询字符串,就像这样: ${pageContext.request.queryString} Scope对象 pageScope,requestScope,sessionScope,applicationScope变量用来访问存储在各个作用域层次的变量。 举例来说,如果您需要显式访问在applicationScope层的box变量,可以这样来访问:applicationScope.box。 param和paramValues对象 param和paramValues对象用来访问参数值,通过使用request.getParameter方法和request.getParameterValues方法。 举例来说,访问一个名为order的参数,可以这样使用表达式:${param.order},或者${param["order"]}。 接下来的例子表明了如何访问request中的username参数: String title = "Accessing Request Param"; %> ${param["username"]} param对象返回单一的字符串,而paramValues对象则返回一个字符串数组。 header和headerValues对象 header和headerValues对象用来访问信息头,通过使用 request.getHeader方法和request.getHeaders方法。 举例来说,要访问一个名为user-agent的信息头,可以这样使用表达式:${header.user-agent},或者${header["user-agent"]}。 接下来的例子表明了如何访问user-agent信息头: String title = "User Agent Example"; %> ${header["user-agent"]} 运行结果如下: jsp-expression-language header对象返回单一值,而headerValues则返回一个字符串数组。
JSP标准标签库
JSP标准标签库(JSTL)是一个JSP标签集合,它封装了JSP应用的通用核心功能。 JSTL支持通用的、结构化的任务,比如迭代,条件判断,XML文档操作,国际化标签,SQL标签。 除了这些,它还提供了一个框架来使用集成JSTL的自定义标签。 根据JSTL标签所提供的功能,可以将其分为5个类别。 核心标签 格式化标签 SQL 标签 XML 标签 JSTL 函数 使用任何库,你必须在每个JSP文件中的头部包含标签。 核心标签 核心标签是最常用的JSTL标签。引用核心标签库的语法如下: uri="http://java.sun.com/jsp/jstl/core" %> 标签 描述 用于在JSP中显示数据,就像 用于保存数据 用于删除数据 用来处理产生错误的异常状况,并且将错误信息储存起来 与我们在一般程序中用的if一样 本身只当做和的父标签 的子标签,用来判断条件是否成立 的子标签,接在标签后,当标签判断为false时被执行 检索一个绝对或相对 URL,然后将其内容暴露给页面 基础迭代标签,接受多种集合类型 根据指定的分隔符来分隔内容并迭代输出 用来给包含或重定向的页面传递参数 重定向至一个新的URL. 使用可选的查询参数来创造一个URL 格式化标签 JSTL格式化标签用来格式化并输出文本、日期、时间、数字。引用格式化标签库的语法如下: uri="http://java.sun.com/jsp/jstl/fmt" %> 标签 描述 使用指定的格式或精度格式化数字 解析一个代表着数字,货币或百分比的字符串 使用指定的风格或模式格式化日期和时间 解析一个代表着日期或时间的字符串 绑定资源 指定地区 绑定资源 指定时区 指定时区 显示资源配置文件信息 设置request的字符编码 SQL标签 JSTL SQL标签库提供了与关系型数据库(Oracle,MySQL,SQL Server等等)进行交互的标签。引用SQL标签库的语法如下: uri="http://java.sun.com/jsp/jstl/sql" %> 标签 描述 指定数据源 运行SQL查询语句 运行SQL更新语句 将SQL语句中的参数设为指定值 将SQL语句中的日期参数设为指定的java.util.Date 对象值 在共享数据库连接中提供嵌套的数据库行为元素,将所有语句以一个事务的形式来运行 XML 标签 JSTL XML标签库提供了创建和操作XML文档的标签。引用XML标签库的语法如下: uri="http://java.sun.com/jsp/jstl/xml" %> 在使用xml标签前,你必须将XML 和 XPath 的相关包拷贝至你的\lib下: XercesImpl.jar: 下载地址: http://www.apache.org/dist/xerces/j/ xalan.jar: 下载地址: http://xml.apache.org/xalan-j/index.html 标签 描述 与,类似,不过只用于XPath表达式 解析 XML 数据 设置XPath表达式 判断XPath表达式,若为真,则执行本体中的内容,否则跳过本体 迭代XML文档中的节点 和的父标签 的子标签,用来进行条件判断 的子标签,当判断为false时被执行 将XSL转换应用在XML文档中 与共同使用,用于设置XSL样式表 JSTL函数 JSTL包含一系列标准函数,大部分是通用的字符串处理函数。引用JSTL函数库的语法如下: uri="http://java.sun.com/jsp/jstl/functions" %> 函数 描述 fn:contains() 测试输入的字符串是否包含指定的子串 fn:containsIgnoreCase() 测试输入的字符串是否包含指定的子串,大小写不敏感 fn:endsWith() 测试输入的字符串是否以指定的后缀结尾 fn:escapeXml() 跳过可以作为XML标记的字符 fn:indexOf() 返回指定字符串在输入字符串中出现的位置 fn:join() 将数组中的元素合成一个字符串然后输出 fn:length() 返回字符串长度 fn:replace() 将输入字符串中指定的位置替换为指定的字符串然后返回 fn:split() 将字符串用指定的分隔符分隔然后组成一个子字符串数组并返回 fn:startsWith() 测试输入字符串是否以指定的前缀开始 fn:substring() 返回字符串的子集 fn:substringAfter() 返回字符串在指定子串之后的子集 fn:substringBefore() 返回字符串在指定子串之前的子集 fn:toLowerCase() 将字符串中的字符转为小写 fn:toUpperCase() 将字符串中的字符转为大写 fn:trim() 移除首位的空白符
JSP自定义标签库
自定义标签是用户定义的JSP语言元素。当JSP页面包含一个自定义标签时将被转化为servlet,标签转化为对被 称为tag handler的对象的操作,即当servlet执行时Web container调用那些操作。 JSP标签扩展可以让你创建新的标签并且可以直接插入到一个JSP页面。 JSP 2.0规范中引入Simple Tag Handlers来编写这些自定义标记。 你可以继承SimpleTagSupport类并重写的doTag()方法来开发一个最简单的自定义标签。 创建"Hello"标签 接下来,我们想创建一个自定义标签叫作,标签格式为: 要创建自定义的JSP标签,你首先必须创建处理标签的Java类。所以,让我们创建一个HelloTag类,如下所示: package com.tutorialspoint; import javax.servlet.jsp.tagext.*; import javax.servlet.jsp.*; import java.io.*; public class HelloTag extends SimpleTagSupport { public void doTag() throws JspException, IOException { JspWriter out = getJspContext().getOut(); out.println("Hello Custom Tag!"); } } 以下代码重写了doTag()方法,方法中使用了getJspContext()方法来获取当前的JspContext对象,并将"Hello Custom Tag!"传递给JspWriter对象。 编译以上类,并将其复制到环境变量CLASSPATH目录中。最后创建如下标签库:webapps\ROOT\WEB-INF\custom.tld。 1.0 2.0 Example TLD Hello com.tutorialspoint.HelloTag empty 接下来,我们就可以在JSP文件中使用Hello标签: 以上程序输出结果为: Hello Custom Tag! 访问标签体 你可以像标准标签库一样在标签中包含消息内容。如我们要在我们自定义的Hello中包含内容,格式如下: This is message body 我们可以修改标签处理类文件,代码如下: package com.tutorialspoint; import javax.servlet.jsp.tagext.*; import javax.servlet.jsp.*; import java.io.*; public class HelloTag extends SimpleTagSupport { StringWriter sw = new StringWriter(); public void doTag() throws JspException, IOException { getJspBody().invoke(sw); getJspContext().getOut().println(sw.toString()); } } 接下来我们需要修改TLD文件,如下所示: 1.0 2.0 Example TLD with Body Hello com.tutorialspoint.HelloTag scriptless 现在我们可以在JSP使用修改后的标签,如下所示: This is message body 以上程序输出结果如下所示: This is message body 自定义标签属性 你可以在自定义标准中设置各种属性,要接收属性,值自定义标签类必须实现setter方法, JavaBean 中的setter方法如下所示: package com.tutorialspoint; import javax.servlet.jsp.tagext.*; import javax.servlet.jsp.*; import java.io.*; public class HelloTag extends SimpleTagSupport { private String message; public void setMessage(String msg) { this.message = msg; } StringWriter sw = new StringWriter(); public void doTag() throws JspException, IOException { if (message != null) { /* 从属性中使用消息 */ JspWriter out = getJspContext().getOut(); out.println( message ); } else { /* 从内容体中使用消息 */ getJspBody().invoke(sw); getJspContext().getOut().println(sw.toString()); } } } 属性的名称是"message",所以setter方法是的setMessage()。现在让我们在TLD文件中使用的元素添加此属性: 1.0 2.0 Example TLD with Body Hello com.tutorialspoint.HelloTag scriptless message 现在我们就可以在JSP文件中使用message属性了,如下所示: 以上实例数据输出结果为: This is custom tag 你还可以包含以下属性: 属性 描述 name 定义属性的名称。每个标签的是属性名称必须是唯一的。 required指定属性是否是必须的或者可选的,如果设置为false为可选。 rtexprvalue 声明在运行表达式时,标签属性是否有效。 type 定义该属性的Java类类型 。默认指定为 String description 描述信息 fragment如果声明了该属性,属性值将被视为一个 JspFragment。 以下是指定相关的属性实例: ..... attribute_name false java.util.Date false ..... 如果你使用了两个属性,修改TLD文件,如下所示: ..... attribute_name1 false java.util.Boolean false attribute_name2 true java.util.Date .....
Spring MVC+Sping framework + MyBatis整合