Crm系统使用struts2架构实现web层,web层实现的功能是控制逻辑和视图。
整体调用流程如下:
1、用户请求至struts2
2、struts2调用service
3、service调用dao
4、dao持久化数据
Struts2是一个基于MVC设计模式的WEB层框架。
Struts2和Struts1没有任何联系.Struts2内核是webwork的内核.
* apps :Struts2提供一些案例
* docs :Struts2开发文档.
* lib :Struts2的开发的jar包
* src :Struts2的源码
D:\struts2\struts-2.3.24\apps\struts2-blank\WEB-INF\lib\*.jar
<h1>Struts2的入门案例h1>
<a href="${pageContext.request.contextPath }/strutsDemo1.action">访问Struts2的Action.a>
public class StrutsDemo1 {
/**
* 提供一个默认的执行的方法:execute
*/
public String execute(){
System.out.println("StrutsDemo1中的execute执行了...");
return null;
}
}
在src下引入一个struts.xml
<struts>
<package name="demo1" extends="struts-default" namespace="/">
<action name="strutsDemo1" class="cn.it.struts2.action.StrutsDemo1">
action>
package>
struts>
<filter>
<filter-name>struts2filter-name>
<filter-class>org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilterfilter-class>
filter>
<filter-mapping>
<filter-name>struts2filter-name>
<url-pattern>/*url-pattern>
filter-mapping>
public class StrutsDemo1 {
/**
* 提供一个默认的执行的方法:execute
*/
public String execute(){
System.out.println("StrutsDemo1中的execute执行了...");
return "success";
}
}
<struts>
<package name="demo1" extends="struts-default" namespace="/">
<action name="strutsDemo1" class="cn.it.struts2.action.StrutsDemo1">
action>
package>
struts>
从客户端发送请求过来 先经过前端控制器(核心过滤器StrutsPrepareAndExecuteFilter)过滤器中执行一组拦截器(一组拦截器 就会完成部分功能代码)执行目标Action,在Action中返回一个结果视图,根据Result的配置进行页面的跳转.
DTD存放的路径:src\core\src\main\resources\struts-2.3.dtd
Window--->Preferences--->在左侧搜索xml catalog--->add添加.
* 选择URI
* 文件系统 选择dtd路径
查看StrutsPrepareAndExecuteFilter:(核心过滤器)两个功能 :预处理 和 执行
在预处理功能中 init 方法中会有加载配置文件的代码:
dispatcher.init();
init_DefaultProperties(); // [1] ---- 加载org.apache.struts.default.properties.配置的是struts2的所有常量.
init_TraditionalXmlConfigurations(); // [2] ---- 加载struts-default.xml、struts-plugin.xml、struts.xml
init_LegacyStrutsProperties(); // [3] ---- 加载用户自定义struts.properties
init_CustomConfigurationProviders(); // [5] ---- 加载Struts2定义Bean.
init_FilterInitParameters() ; // [6] ---- 加载web.xml
init_AliasStandardObjects() ; // [7] ---- 用户自定义Bean
结论:
* default.properties
* struts-default.xml
* struts-plugin.xml
* struts.xml ---- 配置Action以及常量.(******)
* struts.properties ---- 配置常量
* web.xml ---- 配置核心过滤器及常量.
***** 后配置的常量 会 覆盖先配置的常量.
* package:包. 不是java中说那个包. Struts2中的包 管理
* 属性:
* name :包名.包名是唯一的不能重复的.
* extends :继承.继承struts-default.(struts-default包中定义结果类型和拦截器.)
* namespace :名称空间.与
* 写法:
* namespace有名称: namespace=”/aa”
* namespace只是一个/: namespance=”/”
* namespace默认的: namespace没写.
* abstract :抽象的.用于使其他的包可以继承的.
*
* action:配置Action类的访问路径.
* 属性:
* name :名称.与
* class :类的全路径.要执行的Action类的全路径.
* method :方法.用来指定Action中执行那个方法的方法名.(默认的值execute)
* result:配置Action执行后的页面跳转.
* 属性:
* name :逻辑视图名称.(不是真实的视图,为真实的视图起了一个别名,在Action中返回这个字符串的别名,从而找到具体页面.)
* type :跳转的类型.
默认的Action:
<default-action-ref name="strutsDemo1"/>
Action的默认处理类:
<default-class-ref class="com.opensymphony.xwork2.ActionSupport"/>
修改常量:
* struts.xml中修改常量(推荐使用)
<constant name="struts.action.extension" value="abc"/>
* struts.properties修改常量
struts.action.extension=xxx
* web.xml中修改常量
<filter>
<filter-name>struts2filter-name>
<filter-class>org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilterfilter-class>
filter>
【Action的是一个POJO的类】
Action是简单的Java对象没有实现任何借口 和 继承任何类.
public class ActionDemo1 {
public String execute(){
System.out.println("执行Demo1");
return null;
}
}
【Action类实现一个Action的接口】
public class ActionDemo2 implements Action{
@Override
public String execute() throws Exception {
System.out.println("执行ActionDemo2");
return null;
}
}
Action接口中提供了5个已经定义好的视图名称:
* SUCCESS :success,代表成功.
* NONE :none,代表页面不跳转
* ERROR :error,代表跳转到错误页面.
* INPUT :input,数据校验的时候跳转的路径.
* LOGIN :login,用来跳转到登录页面.
【Action类继承ActionSupport类】
public class ActionDemo3 extends ActionSupport{
@Override
public String execute() throws Exception {
System.out.println("执行ActionDemo3");
return NONE;
}
}
ActionSupport中提供了一些功能,比如数据校验,比如国际化… 如果Action继承了ActionSupport,那么Action就会有这些功能.
Action的访问不是难题,因为之前已经访问过了,但是出现一个问题一次请求现在对应一个Action,那么如果请求很多对应很多个Action.现在要处理的问题就是要让一个模块的操作提交到一个Action中。
【解决Action的访问的问题的方式一:通过配置method属性完成】
页面:
<h3>客户的管理h3>
<a href="${ pageContext.request.contextPath }/saveCustomerAction.action">添加客户a> <br/>
<a href="${ pageContext.request.contextPath }/updateCustomerAction.action">修改客户a> <br/>
<a href="${ pageContext.request.contextPath }/deleteCustomerAction.action">删除客户a> <br/>
<a href="${ pageContext.request.contextPath }/findCustomerAction.action">查询客户a> <br/>
编写Action:
public class CustomerAction extends ActionSupport{
public String save(){
System.out.println("执行CustomerAction中save方法);
return NONE;
}
public String update(){
System.out.println("执行CustomerAction中update方法");
return NONE;
}
public String delete(){
System.out.println("执行CustomerAction中delete方法");
return NONE;
}
public String find(){
System.out.println("执行CustomerAction中find方法");
return NONE;
}
}
配置Action:
<package name="demo3" extends="struts-default" namespace="/">
<action name="saveCustomerAction" class="cn.it.struts2.demo3.CustomerAction" method="save">action>
<action name="updateCustomerAction" class="cn.it.struts2.demo3.CustomerAction" method="update">action>
<action name="deleteCustomerAction" class="cn.it.struts2.demo3.CustomerAction" method="delete">action>
<action name="findCustomerAction" class="cn.it.struts2.demo3.CustomerAction" method="find">action>
package>
【解决Action的访问的问题的方式二:通过通配符的配置完成】
第一种解决方案不是很优秀,因为在Action的配置中配置多条.能不能一个Class类只对应一个配置?
页面:
<h3>联系人的管理h3>
<a href="${ pageContext.request.contextPath }/linkman_save.action">添加联系人a> <br/>
<a href="${ pageContext.request.contextPath }/linkman_update.action">修改联系人a> <br/>
<a href="${ pageContext.request.contextPath }/linkman_delete.action">删除联系人a> <br/>
<a href="${ pageContext.request.contextPath }/linkman_find.action">查询联系人a> <br/>
编写Action:
public class LinkManAction extends ActionSupport{
public String save(){
System.out.println("保存");
return NONE;
}
public String update(){
System.out.println("修改.");
return NONE;
}
public String delete(){
System.out.println("删除");
return NONE;
}
public String find(){
System.out.println("查询");
return NONE;
}
}
配置Action:
<action name="linkman_*" class="cn.it.struts2.demo3.LinkManAction" method="{1}">action>
【解决Action的访问的问题的方式三:动态方法访问】
开启一个常量:动态方法访问.
编写Action:
public class UserAction extends ActionSupport{
public String save(){
System.out.println("保存");
return NONE;
}
public String update(){
System.out.println("修改");
return NONE;
}
public String delete(){
System.out.println("删除");
return NONE;
}
public String find(){
System.out.println("查询用户");
return NONE;
}
}
配置Action:
<action name="userAction" class="cn.it.struts2.demo3.UserAction">action>
页面路径写法:
<h3>用户的管理h3>
<a href="${ pageContext.request.contextPath }/userAction!save.action">添加用户a> <br/>
<a href="${ pageContext.request.contextPath }/userAction!update.action">修改用户a> <br/>
<a href="${ pageContext.request.contextPath }/userAction!delete.action">删除用户a> <br/>
<a href="${ pageContext.request.contextPath }/userAction!find.action">查询用户a> <br/>
public class RequestActionDemo1 extends ActionSupport{
@Override
public String execute() throws Exception {
// 接收表单的参数:
// 使用的是Struts2中的一个对象ActionContext对象.
ActionContext actionContext = ActionContext.getContext();
// 接收参数:
Map
for (String key : paramsMap.keySet()) {
String[] value = (String[]) paramsMap.get(key);
System.out.println(key+" "+value[0]);
}
// 向request中存入数据 request.setAttribute(String name,Object value);
actionContext.put("requestName", "张三");
// 向session中存入数据 request.getSession().setAttribute(String name,Object value);
actionContext.getSession().put("sessionName", "李四");
// 向application中存入数据 this.getServletContext().setAttribute(String name,Object value);
actionContext.getApplication().put("applicationName", "王五");
return SUCCESS;
}
}
public class RequestActionDemo2 extends ActionSupport{
@Override
public String execute() throws Exception {
// 接收参数:
HttpServletRequest req = ServletActionContext.getRequest();
Map
for (String key : map.keySet()) {
String[] value = map.get(key);
System.out.println(key+" "+value[0]);
}
// 向域中存入数据:
req.setAttribute("requestName", "张三");
// 向session中存入数据:
req.getSession().setAttribute("sessionName", "李四");
// 向application中保存:
ServletActionContext.getServletContext().setAttribute("applicationName", "王五");
return SUCCESS;
}
}
<global-results>
global-results>
<action name="requestActionDemo2" class="cn.it.struts2.demo4.RequestActionDemo2">
action>
* name :返回一个逻辑视图名称.
* type : 跳转的采用的方式.
* dispatcher :默认值,转发. 转发到一个JSP页面
* redirect :重定向. 重定向到一个JSP页面
* chain :转发,转发到一个Action.
* redirectAction :重定向到另一个Action
实际开发的场景:页面提交参数,在Action中接收参数,需要进行数据的封装,封装到JavaBean中,将JavaBean传递给业务层.
页面:
<h1>Struts2的属性驱动中:set方法的方式h1>
<form action="${ pageContext.request.contextPath }/strutsDemo1.action" method="post">
名称:<input type="text" name="name"><br/>
年龄:<input type="text" name="age"><br/>
生日:<input type="text" name="birthday"><br/>
<input type="submit" value="提交">
form>
Action:
public class StrutsDemo1 extends ActionSupport{
// 接收参数:
private String name;
private Integer age;
private Date birthday;
public void setName(String name) {
this.name = name;
}
public void setAge(Integer age) {
this.age = age;
}
public void setBirthday(Date birthday) {
this.birthday = birthday;
}
…
}
***** 这种方式还需要手动封装对象。
页面:
<h1>Struts2的属性驱动中:OGNL表达式的方式h1>
<form action="${ pageContext.request.contextPath }/strutsDemo2.action" method="post">
名称:"text" name="user.name">
年龄:"text" name="user.age">
生日:"text" name="user.birthday">
<input type="submit" value="提交">
form>
Action:
public class StrutsDemo2 extends ActionSupport{
private User user;
// 必须提供get方法.
public User getUser() {
return user;
}
public void setUser(User user) {
this.user = user;
}
…
}
页面:
<h1>Struts2的模型驱动驱动中:模型驱动的方式h1>
<form action="${ pageContext.request.contextPath }/strutsDemo3.action" method="post">
名称:<input type="text" name="name"><br/>
年龄:<input type="text" name="age"><br/>
生日:<input type="text" name="birthday"><br/>
<input type="submit" value="提交">
form>
Action:
public class StrutsDemo3 extends ActionSupport implements ModelDriven
// 模型驱动使用的对象.
private User user = new User();// 必须手动new
@Override
public User getModel() {
return user;
}
…
}
页面:
<form action="${ pageContext.request.contextPath }/strutsDemo4.action" method="post">
名称:<input type="text" name="list[0].name"><br/>
年龄:<input type="text" name="list[0].age"><br/>
生日:<input type="text" name="list[0].birthday"><br/>
名称:<input type="text" name="list[1].name"><br/>
年龄:<input type="text" name="list[1].age"><br/>
生日:<input type="text" name="list[1].birthday"><br/>
<input type="submit" value="提交">
form>
Action:
public class StrutsDemo4 extends ActionSupport{
private List
public List
return list;
}
public void setList(List
this.list = list;
}
@Override
public String execute() throws Exception {
for (User user : list) {
System.out.println(user);
}
return NONE;
}
}
页面:
<h1>批量插入用户:封装到Map集合h1>
<form action="${ pageContext.request.contextPath }/strutsDemo5.action" method="post">
名称:<input type="text" name="map['one'].name"><br/>
年龄:<input type="text" name="map['one'].age"><br/>
生日:<input type="text" name="map['one'].birthday"><br/>
名称:<input type="text" name="map['two'].name"><br/>
年龄:<input type="text" name="map['two'].age"><br/>
生日:<input type="text" name="map['two'].birthday"><br/>
<input type="submit" value="提交">
form>
Action:
public class StrutsDemo5 extends ActionSupport {
private Map
public Map
return map;
}
public void setMap(Map
this.map = map;
}
@Override
public String execute() throws Exception {
for (String key : map.keySet()) {
User user = map.get(key);
System.out.println(key+" "+user);
}
return NONE;
}
}
拦截器,在AOP(Aspect-Oriented Programming)中用于在某个方法或字段被访问之前,进行拦截然后在之前或之后加入某些操作。拦截是AOP的一种实现策略。
在Webwork的中文文档的解释为——拦截器是动态拦截Action调用的对象。它提供了一种机制可以使开发者可以定义在一个action执行的前后执行的代码,也可以在一个action执行前阻止其执行。同时也是提供了一种可以提取action中可重用的部分的方式。
谈到拦截器,还有一个词大家应该知道——拦截器链 (Interceptor Chain,在Struts 2中称为拦截器栈Interceptor Stack)。拦截器链就是将拦截器按一定的顺序联结成一条链。在访问被拦截的方法或字段时,拦截器链中的拦截器就会按其之前定义的顺序被调用。
大部分时候,拦截器方法都是通过代理的方式来调用的。Struts 2的拦截器实现相对简单。当请求到达Struts 2的ServletDispatcher时,Struts 2会查找配置文件,并根据其配置实例化相对的拦截器对象,然后串成一个列表,最后一个一个地调用列表中的拦截器。
Struts2拦截器是可插拔的,拦截器是AOP的一种实现。Struts2拦截器栈就是将拦截器按一定的顺序联结成一条链。在访问被拦截的方法或字段时,Struts2拦截器链中的拦截器就会按其之前定义的顺序被调用。
第一步: 自定义一个实现Interceptor接口(或者继承自AbstractInterceptor)的类。
第二步:在strutx.xml中注册上一步中定义的拦截器。
第三步:在需要使用的Action中引用上述定义的拦截器,为了方便也可将拦截器定义为默认的拦截器,这样在不加特殊声明的情况下所有的Action都被这个拦截器拦截。
1、支持对象方法调用,如xxx.doSomeSpecial();
2、支持类静态的方法调用和值访问,表达式的格式:
@[类全名(包括包路径)]@[方法名 | 值名],例如:
@java.lang.String@format('foo %s', 'bar')
或@tutorial.MyConstant@APP_NAME;
设置 struts.ognl.allowStaticMethodAccess=true
3、访问OGNL上下文(OGNL context)和ActionContext;访问值栈
4、支持赋值操作和表达式串联,如price=100, discount=0.8,
calculatePrice(),这个表达式会返回80;
5、操作集合对象。
@Test
// OGNL调用对象的方法:
public void demo1() throws OgnlException{
OgnlContext context = new OgnlContext();
Object obj = Ognl.getValue("'helloworld'.length()", context, context.getRoot());
System.out.println(obj);
}
@Test
// OGNL获取数据:
public void demo3() throws OgnlException{
OgnlContext context = new OgnlContext();
// 获取OgnlContext中的数据:
/*context.put("name", "张三");
String name = (String) Ognl.getValue("#name", context, context.getRoot());
System.out.println(name);*/
// 获得Root中的数据
User user = new User();
user.setName("李四");
context.setRoot(user);
String name = (String) Ognl.getValue("name", context, context.getRoot());
System.out.println(name);
}
ValueStack是Struts的一个接口,字面意义为值栈,OgnlValueStack是ValueStack的实现类,客户端发起一个请求struts2架构会创建一个action实例同时创建一个OgnlValueStack值栈实例,OgnlValueStack贯穿整个 Action 的生命周期,struts2中使用OGNL将请求Action的参数封装为对象存储到值栈中,并通过OGNL表达式读取值栈中的对象属性值。
在 OnglValueStack 中包括两部分,值栈和map(即ognl上下文)
OgnlContext中的一些引用:
parameters: 该 Map 中包含当前请求的请求参数
request: 该 Map 中包含当前 request 对象中的所有属性
session: 该 Map 中包含当前 session 对象中的所有属性
application:该 Map 中包含当前 application 对象中的所有属性
attr: 该 Map 按如下顺序来检索某个属性: request, session, application
CompoundRoot 继承ArrayList实现压栈和出栈功能,拥有栈的特点,先进后出,后进先出,最后压进栈的数据在栈顶。我们把它称为对象栈。
struts2对原OGNL作出的改进就是Root使用CompoundRoot(自定义栈),使用OnglValueStack的findValue方法可以在CompoundRoot中从栈顶向栈底找查找的对象的属性值。
CompoundRoot作为OgnlContext 的Root对象,并且在CompoundRoot中action实例位于栈顶,当读取action的属性值时会先从栈顶对象中找对应的属性,如果找不到则继续找栈中的其它对象,如果找到则停止查找。
通过源码查询:
public ActionContext createActionContext(HttpServletRequest request, HttpServletResponse response) {
ActionContext ctx;
Integer counter = 1;
Integer oldCounter = (Integer) request.getAttribute(CLEANUP_RECURSION_COUNTER);
if (oldCounter != null) {
counter = oldCounter + 1;
}
ActionContext oldContext = ActionContext.getContext();
if (oldContext != null) {
// detected existing context, so we are probably in a forward
ctx = new ActionContext(new HashMap
} else {
ValueStack stack = dispatcher.getContainer().getInstance(ValueStackFactory.class).createValueStack();
stack.getContext().putAll(dispatcher.createContextMap(request, response, null));
ctx = new ActionContext(stack.getContext());
}
request.setAttribute(CLEANUP_RECURSION_COUNTER, counter);
ActionContext.setContext(ctx);
return ctx;
}
* 在创建ActionContext的时候 创建ValueStack的对象,将ValueStack对象给ActionContext.
* ActionContext中有一个ValueStack的引用. ValueStack中也有一个ActionContext的引用.
* ActionContext获取ServletAPI的时候,依赖值栈了.
【通过ActionContext对象获取值栈.】
ValueStack stack1 =ActionContext.getContext().getValueStack();
【通过request域获取值栈.】
ValueStack stack2 = (ValueStack) ServletActionContext.getRequest().getAttribute(ServletActionContext.STRUTS_VALUESTACK_KEY);
【对Action中的属性提供get方法的方式】
因为Action本身在值栈中,Action中的属性也就默认在值栈中了,所以我们可以通过对Action的属性提供get方法的方式来操作值栈。
【手动操作值栈】
调用值栈的push和set方法对值栈进行操作
底层对Request对象的getAttribute方法进行增强:
public class StrutsRequestWrapper extends HttpServletRequestWrapper {
private static final String REQUEST_WRAPPER_GET_ATTRIBUTE = "__requestWrapper.getAttribute";
private final boolean disableRequestAttributeValueStackLookup;
/**
* The constructor
* @param req The request
*/
public StrutsRequestWrapper(HttpServletRequest req) {
this(req, false);
}
/**
* The constructor
* @param req The request
* @param disableRequestAttributeValueStackLookup flag for disabling request attribute value stack lookup (JSTL accessibility)
*/
public StrutsRequestWrapper(HttpServletRequest req, boolean disableRequestAttributeValueStackLookup) {
super(req);
this.disableRequestAttributeValueStackLookup = disableRequestAttributeValueStackLookup;
}
/**
* Gets the object, looking in the value stack if not found
*
* @param key The attribute key
*/
public Object getAttribute(String key) {
if (key == null) {
throw new NullPointerException("You must specify a key value");
}
if (disableRequestAttributeValueStackLookup || key.startsWith("javax.servlet")) {
// don't bother with the standard javax.servlet attributes, we can short-circuit this
// see WW-953 and the forums post linked in that issue for more info
return super.getAttribute(key);
}
ActionContext ctx = ActionContext.getContext();
Object attribute = super.getAttribute(key);
if (ctx != null && attribute == null) {
boolean alreadyIn = isTrue((Boolean) ctx.get(REQUEST_WRAPPER_GET_ATTRIBUTE));
// note: we don't let # come through or else a request for
// #attr.foo or #request.foo could cause an endless loop
if (!alreadyIn && !key.contains("#")) {
try {
// If not found, then try the ValueStack
ctx.put(REQUEST_WRAPPER_GET_ATTRIBUTE, Boolean.TRUE);
ValueStack stack = ctx.getValueStack();
if (stack != null) {
attribute = stack.findValue(key);
}
} finally {
ctx.put(REQUEST_WRAPPER_GET_ATTRIBUTE, Boolean.FALSE);
}
}
}
return attribute;
}
}
【获取context的数据】
【用于构建一个Map集合】:使用struts的UI标签的时候.
<s:iterator value="#{'aaa':'111','bbb':'222','ccc':'333' }" var="entry">
<s:property value="key"/>---<s:property value="value"/><br/>
<s:property value="#entry.key"/>---<s:property value="#entry.value"/><br/>
s:iterator>
【%强制解析OGNL表达式】
【%强制不解析OGNL表达式】
【在配置文件中使用OGNL表达式】
在struts的配置文件中使用.XML文件 或者 是属性文件.
说明:由于个人能力有限,所以有些地方借鉴了他人的经验,如有错误,或者涉及侵权等问题,请及时告知,我会立即修改或者删除。谢谢各位。