CRM系统中有客户的显示的功能,效果如图:
我们实际的开发中会使用Struts2作为Web的架构
Struts是一种基于MVC模式的轻量级Web框架,它自问世以来,就受到了广大Web开发者的关注,并广泛应用于各种企业系统的开发中。目前掌握Struts框架几乎成为Web开发者的必备技能之一。接下来将针对Struts2的特点、安装以及执行流程等内容进行详细的说明。
在介绍Struts2之前,先来认识下Struts1。Struts1是最早的基于MVC模式的轻量级Web框架,它能够合理的划分代码结构,并包含验证框架、国际化框架等多种实用工具框架。但是随着技术的进步,Struts1的局限性也越来越多的暴露出来。为了符合更加灵活、高效的开发需求,Struts2框架应运而生。
Struts2是Struts1的下一代产品,是在Struts1和WebWork技术的基础上进行合并后的全新框架(WebWork是由OpenSymphony组织开发的,致力于组件化和代码重用的J2EE Web框架,它也是一个MVC框架)。虽然Struts2的名字与Struts1相似,但其设计思想却有很大不同。实质上,Struts2是以WebWork为核心的,它采用拦截器的机制来处理用户的请求。这样的设计也使得业务逻辑控制器能够与ServletAPI完全脱离开,所以Struts2可以理解为WebWork的更新产品。
Struts2拥有优良的设计和功能,其优势具体如下:
Struts2是一个基于MVC设计模式的WEB层框架。
Struts2的内核相对于Struts1来讲已经发生巨大变化。
上面列举的就是Struts2的一系列技术优势,只需对它们简单了解即可,在学习了后面的知识后,会慢慢对这些技术优势有更好的理解和体会。
那么除了Struts2之外,还有那些优秀的Web层框架呢?
Struts2
Struts1
Webwork
SpringMVC
Web层框架都会有一个特点,就是基于前端控制器模式实现的。
什么是前端控制器模式呢?我们来看下图,在图中传统方式的开发,有一次请求就会对应一个Servlet。这样会导致出现很多Servlet。而Struts2将所有的请求都先经过一个前端控制器,在前段控制器中实现框架的部分功能,剩下具体操作要提交到具体的Action中。那么所有的请求都会经过前段控制器,那用什么来实现前端控制器呢?过滤器就是最好的一个实现方式,因为需要所有的请求都可以被过滤器拦截,然后在过滤器中实现部分的功能。所以Struts2的前端控制器也是有过滤器来实现的。
http://struts.apache.org/
apps :Struts2提供的应用,war文件:web项目打成war包。直接放入到tomcat可以运行。
docs :Struts2的开发文档和API
lib :Strtus2框架的开发的jar包
src :Struts2的源码
首先,需要我们创建一个Web工程,引入相关的jar包文件。引入哪些jar包呢?将struts-2.3.24框架目录中的lib文件夹打开,得到Struts2开发中可能用到的所有JAR包(此版本有107个JAR包)。实际的开发中,我们根本不用引入这么多的jar包。
要进行struts2的基本的开发,可以struts-2.3.24中的apps下的一些示例代码,其中struts2-blank.war是一个struts的空工程。我们只需要将struts2-blank.war解压后进入到WEB-INF下的lib中查看。
struts-blank项目下找jar包
上面这些包就是struts2的基本的开发包了,那么这些包都是什么含义呢?
文件名 | 说明 |
---|---|
asm-3.3.jar | 操作Java字节码的类库 |
asm-commons-3.3.jar | 提供了基本事件的表现形式 |
asm-tree-3.3.jar | 提供了基于对象的表现形式 |
struts-core-2.3.24.jar | Struts2框架的核心类库 |
xwork-core-2.3.24.jar | WebWork核心库,Struts2的构建基础 |
freemarker-2.3.22.jar | Struts2标签模板使用的类库 |
javassist-3.11.0.GA.jar | javaScript字节码解释器 |
commons-fileupload-1.3.1.jar | Struts2文件上传组件依赖包 |
commons-io-2.2.jar | Struts2的输入输出,传文件依赖的jar |
commons-lang-2.4.jar | 包含一些数据类型工具,是对java.lang包的增强 |
log4j-api-2.2.jar | Struts2的日志管理组件依赖包的api |
log4j-core-2.2.jar | Struts2的日志管理组件依赖包 |
从表可以看出,此版本的Struts2项目所依赖的基础JAR包共13个。Struts2根据版本的不同所依赖的基础JAR包可能不完全相同,不过基本上变化不大,读者可以视情况而定。
需要注意的是,通常使用Struts2的Web项目并不需要利用到Struts2的全部JAR包,因此没有必要一次将Struts2的lib目录下的全部JAR包复制到Web项目的WEB-INF/lib路径下,而是根据需要,再添加相应的JAR包。
那么Struts2的基本jar包已经引入完成了,我们使用Struts2都是从页面发起请求到服务器,再有服务器处理请求,响应到页面的这个过程。接下来我们就从页面开发进行Struts的开发吧。
首先需要在WebContent下创建一个目录demo1,在demo1下创建一个新的jsp。在jsp中编写一个Action的访问路径。
点击该连接,需要提交请求到服务器的Action。那么接下来需要编写Action去处理请求。
在src下创建一个包com.itheima.action,在该包下新建一个HelloAction的类。在这个类中编写一个公有的、返回值为String类型的方法,这个方法叫做execute,且方法没有任何参数。(因为这个方法最终要被反射执行)
Action类编写好了以后,Struts2框架如何识别它就是一个Action呢,那么我们需要对Action类进行配置。
这个时候,我们还需要观察apps中的示例代码,WEB-INF的classes中,有一个名称为struts.xml的文件,这个文件就是struts2的配置文件。
我们在开发中需要将struts.xml文件引入到工程的src下,因为src下内容发布到web服务器中就是WEB-INF下的classes中。将struts.xml中原有的内容删除掉,然后配置上自己编写的Action类就可以了。
在src下创建(提供)名称叫做struts.xml的配置文件
Action类已经配置好了,配置好了以后大家考虑下,现在是否可以执行呢?其实现在还不行,因为之前我们介绍过,Web层的框架都有一个特点就是基于前端控制器的模式,这个前端控制器是由过滤器实现的,所以我们需要配置Struts的核心过滤器。这个过滤器的名称是StrutsPrepareAndExecuteFilter。
Struts框架要想执行,所有的请求都需要经过这个前端控制器(核心过滤器),所以需要配置这个核心过滤器。因为这个过滤器完成了框架的部分的功能。那么我们接下来对过滤器进行配置。
web.xml中配置核心过滤器
那么到这,我们程序就可以执行了,但是到了Action以后,页面并没有跳转,只是会在控制台输出Action中的内容,往往我们在实际的开发中,处理了请求以后,还需要进行页面的跳转,如何完成完成Struts2的页面的跳转呢?
这个时候我们需要修改Action类中的execute方法的返回值了,这个方法返回了一个String类型,这个String类型的值就是一个逻辑视图(逻辑视图:相当于对一个真实的页面,取了一个别名。)那么我们来修改一个Action类。
修改Action中的execute方法的返回值,我们先任意给其返回一个字符串,比如返回一个success的字符串。这个字符串就作为一个逻辑视图名称。
返回一个success的字符串了,这个success的字符串又怎么能代表一个页面呢?这个时候我们又要对struts2进行配置了。这个时候需要修改struts.xml,对Action的配置进行完善。
打开struts.xml文件,对
到这儿,我们整个程序就执行完毕了。我们可以启动服务器并且测试项目。
从客户端发送请求过来,先经过前端控制器(核心过滤器 StrutsPrepareAndExecuteFilter)过滤器中执行一组拦截器(一组拦截器 就会完成部分功能代码),到底哪些拦截器执行了呢,在Struts2中定义很多拦截器,在其默认栈中的拦截器会得到执行,这个我们可以通过断点调试的方式测试,拦截器执行完成后,就会执行目标Action,在Action中返回一个结果视图,根据Restult的配置进行页面的跳转。
当用户访问某一个Action的时候,先经过核心过滤器,在核心过滤器中执行一组拦截器(这组拦截器实现部分功能),执行目标Action,根据Action的返回值,进行页面跳转。
以上内容是Struts2的执行流程,但是可能大家在编写测试的时候会遇到一些问题,比如在编写XML配置文件的时候没有提示。
开发过程中如果可以上网,struts.xml 会自动缓存 dtd,提供提示功能。如果不能上网,则需要我们手动配置本地dtd,这样才能够使struts.xml产生提示。具体配置方法如下:
1.首先,在eclipse中,依次点击工具栏中的window和下方的preferences弹出对话框。然后在左侧的搜索框中输入xml,显示出所有与xml有关的选项后,点击XML Catalog。
2.接下来在已下载的Struts2解压包的lib包中找到其核心包struts2-core-2.3.24.jar,使用解压工具将其解压成文件夹形式。解压后,我们会看到文件夹中有几个以.dtd结尾的文件。我们所使用的是struts-2.3.dtd。
3.将此dtd使用notepad++等文本打开后,找到图中选中内容,将其http地址复制。
4.点击Eclipse中弹出对话框中右侧的Add按钮,此时会弹出Add XML Catalog Element界面。点击File System按钮,找到本地刚才解压文件夹中的struts-2.3.dtd,然后将界面中的Key type改为URI,并将刚才复制的地址黏贴到Key中。如图所示。
在图中点击OK后,关闭已打开的struts.xml,然后再重新打开struts.xml,此时再编写struts.xml内容的时候,就会有提示了。
每次从客户端发送请求到服务器都要先经过Struts2的核心过滤器StrutsPrepareAndExecuteFilter,这个过滤器有两个功能:预处理和执行。在预处理中主要就是来加载配置文件的。对应的就是过滤器中的init方法,而执行是用来执行一组拦截器完成部分功能的,对应的是过滤器的doFilter方法。所以我们如果要去了解Struts2的配置文件的加载顺序,那么我们需要查询过滤器的init方法。
在init方法中,调用了init的initDispatcher的方法来加载配置文件,进入到该代码中:
我们发现这个方法又调用了dispatcher的init方法。进入init方法内部:
注意:后配置的常量的值会覆盖先配置的常量的值。
Struts2框架的核心配置文件是struts.xml文件,该文件主要用来配置Action和请求的对应关系。
Struts2框架的核心组件是Action和拦截器,它使用包来管理Action和拦截器。每个包就是多个Action、多个拦截器、多个拦截器引用的集合。在struts.xml文件中,package元素用于定义包配置,每个package元素定义了一个包配置。package元素的常用属性,如表所示。
表中就是package元素的常用属性,其中,在配置包时,必须指定name属性,就是包的标识。除此之外,还可以指定一个可选的extends属性,extends属性值必须是另一个包的name属性值,但该属性值通常都设置为struts-default,这样包中的Action就具有了struts2框架默认的拦截器等功能了。除此之外,struts2还提供了一种所谓的抽象包,抽象包不能包含Action定义。为了显示指定一个包是抽象包,可以为该package元素增加abstract="true"属性。
在package中还有namespace的配置,namespaces属性与action标签的name属性共同决定了访问路径。
name :包的名称,只有在一个项目中不重名即可。
extends :继承哪个包,通常值为struts-default。
namespace :名称空间,与
名称空间有三种写法:
带名称的名称空间 :namespace=”/aaa”
根名称空间 :namespance=”/”
默认名称空间 :namespace=””
abstract :抽象的,用于其他包的继承。
action相关配置
Action映射是框架中的基本“工作单元”。Action映射就是将一个请求的URL映射到一个Action类,当一个请求匹配某个Action名称时,框架就使用这个映射来确定如何处理请求。在struts.xml文件中,通过
name :与namespace共同决定访问路径
class :Action类的全路径
method :执行Action中的哪个方法的方法名,默认值execute
converter :用于设置类型转换器
Struts2的这些常量大多在默认的配置文件中已经配置好,但根据用户需求的不同,开发的要求也不同,可能需要修改这些常量,修改的方法就是在配置文件对常量进行重新配置。
在Struts2的框架中,提供了非常多的常量:(在default.properties)
在Struts2中修改一些常量的值:
struts.xml中进行修改
struts.properties中进行修改
web.xml中进行修改
在实际开发中,我们通常很多人都需要修改同一个配置文件就是struts.xml。因为这个文件是Struts2的核心配置文件。而且这个文件一旦改错了一点,那么会导致整个项目都会出现问题,所以Struts2提供了
为了让大家更直观地理解如何在struts.xml文件中进行包含配置,接下来通过一段示例来说明,具体如下:
在struts2的应用开发中,Action作为框架的核心类,实现对用户请求的处理,Action类被称为业务逻辑控制器。一个Action类代表一次请求或调用,每个请求的动作都对应于一个相应的Action类,一个Action类是一个独立的工作单元。也就是说,用户每次请求,都会转到一个相应的Action类里面,由这个Action类进行处理。简而言之,Action就是用来处理一次用户请求的对象。
实现Action控制类共有3种方式,接下来,分别对它们进行讲解,具体如下。
Action类是POJO的类
在Struts2中,Action可以不继承特殊的类或不实现任何特殊的接口,仅仅是一个POJO。POJO全称Plain Ordinary Java Object(简单的Java对象),只要具有一部分getter/setter方法的那种类,就可以称作POJO。一般在这个POJO类中,要有一个公共的无参构造方法(采用默认的构造方法就可以)和一个execute()方法。定义格式如下:
execute()方法的要求如下:
也就是说,满足上述要求的POJO都可算作是Struts2的Action实现。
通常会让开发者自己编写Action类或者实现Action接口或者继承ActionSupport类。
Action类实现一个Action的接口
为了让用户开发的Action类更规范,Struts2提供了一个Action接口,用户在实现Action控制类时,可以实现Struts2提供的这个Action接口。
Action接口定义了Struts的Action处理类应该实现的规范,Action接口中的具体代码如下所示。
Action接口位于com.opensymphony.xwork2包中,这个接口里只定义了一个execute()方法,该接口的规范规定了Action处理类应该包含一个execute()方法,该方法返回一个字符串。除此之外,该接口还定义了5个字符串常量,它们的作用是统一execute()方法的返回值。
Action接口中提供了5个已经定义的常量如下:
由于Xwork的Action接口简单,为开发者提供的帮助较小,所以在实际开发过程中,Action类很少直接实现Action接口,通常都是从ActionSupport类继承。
Action类继承ActionSupport类(推荐)
ActionSupport类本身实现了Action接口,是Struts2中默认的Action接口的实现类,所以继承ActionSupport就相当于实现了Action接口。ActionSupport类还实现了Validateable、ValidationAware、TextProvider、LocaleProvider和Serializable等接口,来为用户提供更多的功能。
ActionSupport类中提供了许多默认方法,这些默认方法包括获取国际化信息的方法、数据校验的方法、默认的处理用户请求的方法等。实际上,ActionSupport类,则会大大简化Action开发。
Action的类已经会编写了,如果要执行这个Action,可以通过前面的配置来完成,但是之前的方式有一个缺点,就是多次请求不能对应同一个Action,因为实际开发中,一个模块的请求通常由一个Action类处理就好了 ,否则会造成Action类过多。
Action的访问不是难题,因为之前已经访问过了,但是出现一个问题一次请求现在对应一个Action,那么如果请求很多对应很多个Action 现在要处理的问题就是要让一个模块的操作提交到一个Action中。
其实我们学过在
解决Action的访问的问题的方式一:通过配置method属性完成
但是这种方式我们会发现,同一个Action类就被配置了很多次,只是修改了后面的method的值。那么能不能配置简单化呢?也就是一个Action类,只配置一次就好了?这个时候我们就需要使用通配符的配置方式了。
解决Action的访问的问题的方式二:通过通配符的方式进行配置(*****)
编写Action:
配置Action:
在
这个时候我们就只配置一个就可以了,在上述代码中,当客户端发送/linkman_save.action 这样的请求时,action元素的name属性就被设置成linkman_save,method属性就被设置成save。当客户端发送/linkman_update.action这样的请求时,action元素的name属性就被设置为linkman_update,method属性也被设置成update。
当然使用通配符是开发中常用的方式,Struts2还有一种解决这类问题的办法,这种大家可以作为扩展内容来学习。
解决Action的访问的问题的方式三:动态方法访问
开启动态方法访问
动态方法访问主要在Struts2中默认是不开启的,如果想要使用需要先去开启一个常量。
动态方法访问主要的控制是在页面端,所以编写Action和配置Action都很简单,关键是访问路径的编写。
编写访问路径
Struts2
Hibernate
Struts2
struts.xml
web.xml
struts2_crm
index.html
index.htm
index.jsp
default.html
default.htm
default.jsp
struts2
org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter
struts2
/*
Hibernate
核心配置 hibernate.cfg.xml
com.mysql.jdbc.Driver
jdbc:mysql:///struts2_crm
root
abc
org.hibernate.dialect.MySQLDialect
true
true
update
org.hibernate.connection.C3P0ConnectionProvider
5
20
120
3000
4
thread
映射文件
日志文件 log4j.properties
### direct log messages to stdout ###
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.Target=System.err
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%d{ABSOLUTE} %5p %c{1}:%L - %m%n
### direct messages to file mylog.log ###
log4j.appender.file=org.apache.log4j.FileAppender
log4j.appender.file.File=c\:mylog.log
log4j.appender.file.layout=org.apache.log4j.PatternLayout
log4j.appender.file.layout.ConversionPattern=%d{ABSOLUTE} %5p %c{1}:%L - %m%n
### set log levels - for more verbose logging change 'info' to 'debug' ###
# error warn info debug trace
log4j.rootLogger= info, stdout
CREATE TABLE `cst_customer` (
`cust_id` bigint(32) NOT NULL AUTO_INCREMENT COMMENT '客户编号(主键)',
`cust_name` varchar(32) NOT NULL COMMENT '客户名称(公司名称)',
`cust_source` varchar(32) DEFAULT NULL COMMENT '客户信息来源',
`cust_industry` varchar(32) DEFAULT NULL COMMENT '客户所属行业',
`cust_level` varchar(32) DEFAULT NULL COMMENT '客户级别',
`cust_phone` varchar(64) DEFAULT NULL COMMENT '固定电话',
`cust_mobile` varchar(16) DEFAULT NULL COMMENT '移动电话',
PRIMARY KEY (`cust_id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;
HibernateUtils.java
package com.itheima.utils;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.cfg.Configuration;
/**
* Hibernate的工具类
*
*/
public class HibernateUtils {
public static final Configuration cfg;
public static final SessionFactory sf;
static{
cfg = new Configuration().configure();
sf = cfg.buildSessionFactory();
}
public static Session openSession(){
return sf.openSession();
}
public static Session getCurrentSession(){
return sf.getCurrentSession();
}
}