原文链接:http://blog.csdn.net/qq_22329521/article/details/74547658
第一步导包,不多说了
第二步配置struts2文件
<?xml version="1.0" encoding="utf-8" ?>
<!DOCTYPE struts PUBLIC
"-//Apache Software Foundation//DTD Struts Configuration 2.3//EN"
"http://struts.apache.org/dtds/struts-2.3.dtd">
<struts>
<!--
package:将Action配置封装,就是可以在Package中配置很多action
name:给包起名字,起到标识作用,随便起,不能其包名重复
namespace属性:给action的访问路径定义一个命名空间
extends属性:继承一个指定包(struts-default在struts2-core jar包下有struts-default.xml中有package struts-default)
abstract属性:包是否为抽象的;标识性属性。标识该包不能独立运行。专门被继承
-->
<package name="hello" namespace="/hello" extends="struts-default">
<!-- action 元素:配置action类
name属性:决定了返回Action访问资源名
class属性:action的完整类名
method属性:指定调用action中的那个方法来处理请求-->
<action name="HelloAction" class="com.fmt.struct.HelloAction" method="hello">
<!--result 元素:结果配置
name:标识结果处理的名称:与action的返回值对应
type属性:指定调用那个result类来处理结果默认使用转发(在继承的struts-default包中有result的配置已经设置好了)(type提供重定向,转发,重定向action,转发action)
标签体:填写页面的相对路径-->
<result name="success" type="dispatcher">/hello.jsp</result>
</action>
</package>
</struts>
web.xml中配置struts2核心过滤器
<!-- struts2 核心过滤器-->
<filter>
<filter-name>struts2</filter-name>
<filter-class>org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>struts2</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
public class HelloAction {
public String hello(){
System.out.println("hello");
return "success";
}
}
访问http://localhost:8080/structs2/hello/HelloAction,就访问成功了
默认配置
如果 一些必要参数没写,默认加载的是struts-default下的配置com.opensymphony.xwork2.ActionSupport 这个类
<struts>
<package name="default" namespace="/default" extends="struts-default" >
<!-- 找不到包下的action,会使用Demo2Action作为默认action处理请求 -->
<default-action-ref name="Demo2Action"></default-action-ref>
<!-- method属性:execute -->
<!-- result的name属性:success -->
<!-- result的type属性:dispatcher 转发 -->
<!-- class属性:com.opensymphony.xwork2.ActionSupport -->
<action name="Demo2Action" >
<result >/hello.jsp</result>
</action>
</package>
</struts>
struts常量配置
struts的基本常量是在struts2-corejar中的org.apache.struts2中的default.properties中有基本的常量
外部修改常量的三种方式
- 在之前struts.xml的<struts>标签下填写
<!--i18:国际化,解决post提交乱码-->
<constant name="struts.i18n.encoding" value="UTF-8"></constant>
<!--指定访问action时的后缀名
如果value改为do:http://....../HelloAction.do
-->
<constant name="struts.action.extension" value="action,,"></constant>
<!--是否是开发模式(更多的好处可以在default.properties文件下看)
1,可以热加载配置(不需要重启服务)
2.提供了更多的错误信息,方便开发时的调试
-->
<constant name="struts.devMode" value="true"></constant>
- 在src目录下创建strtus.properties,然后填写键值对如
struts.i18n.encoding=UTF-8
- 在web.xml中填写
<context-param>
<param-name>struts.i18n.encoding</param-name>
<param-value>UTF-8</param-value>
</context-param>
加载的顺序是依次加载的
如果要引入其他配置
在当前struct标签下加入
<include file="com/fmt/struct/struts.xml"/>
动态访问一个类
public class Demo1Action {
public String add(){
System.out.println("add");
return "success";
}
public String delete(){
System.out.println("delete");
return "success";
}
public String change(){
System.out.println("change");
return "success";
}
public String find(){
System.out.println("find");
return "success";
}
}
根据url访问的话配置
配置一
<package name="dynamic" namespace="/dynamic" extends="struts-default">
<action name="Demo1Action_add" class="com.fmt.b_dynamic.Demo1Action"
method="add">
<result name="success" type="dispatcher">/hello.jsp</result>
</action>
<action name="Demo1Action_delete" class="com.fmt.b_dynamic.Demo1Action"
method="delete">
<result name="success" type="dispatcher">/hello.jsp</result>
</action>
....
</package>
//上诉的配置显然比较累赘
配置二
<!--配置动态方法调用常量
默认是关闭的,需要重启
-->
<constant name="struts.enable.DynamicMethodInvocation" value="true"></constant>
<package name="dynamic" namespace="/dynamic" extends="struts-default">
<action name="Demo1Action" class="com.fmt.b_dynamic.Demo1Action"
>
<result name="success" type="dispatcher">/hello.jsp</result>
</action>
</package>
访问的url 为http://......//Demo1Action!delete
http://......//Demo1Action!add
...
//上面注意点是带!感叹号的 这种也是不怎么用的的,搜索引擎在搜索的时候可能因为你这路径的特点,不会收录下啦
配置3:通配符
<package name="dynamic" namespace="/dynamic" extends="struts-default">
<!--动态调用方式,使用通配符的样子-->
<action name="Demo1Action_*" class="com.fmt.b_dynamic.Demo1Action"
method="{1}">
<result name="success" type="dispatcher">/hello.jsp</result>
</action>
</package>
为http://......//Demo1Action_delete http://......//Demo1Action_add
ServetApi
获取request 这些的方式
public class DemoAction extends ActionSupport {
@Override
public String execute() throws Exception {
//session域
Map<String, Object> session = ActionContext.getContext().getSession();
session.put("name","session");
//application
Map<String, Object> application = ActionContext.getContext().getApplication();
application.put("name","application");
/**
* //request域(struct2并不推荐使用原生的request域中)
Map<String, Object> request = (Map<String, Object>) ActionContext.getContext().get("request");
*/
//直接使用,推荐
ActionContext.getContext().put("name","request域");
return super.execute();
}
public String execute1() throws Exception {
//原生的request(这些内部实际还是从ActionContext中获取)
javax.servlet.http.HttpServletRequest request = ServletActionContext.getRequest();
//原生的Response
HttpServletResponse response = ServletActionContext.getResponse();
//原生的ServletContext
ServletContext servletContext = ServletActionContext.getServletContext();
//获取session对象
HttpSession session = request.getSession();
return super.execute();
}
}
如果不希望每个方法都手动调用获取request可以采用下面方式
public class DemoAction extends ActionSupport implements ServletRequestAware{
HttpServletRequest request;
@Override
public String execute() throws Exception {
}
@Override
public void setServletRequest(HttpServletRequest httpServletRequest) {
this.request=httpServletRequest;
}
}
直接实现接口接可以了
原因在于 我们配置了的action 继承与struts-default
然后在struts-default
其中interceptor-stack是个拦截栈,我们查看servletConfig 所对应的对象他的class是 org.apache.struts2.interceptor.ServletConfigInterceptor
public String intercept(ActionInvocation invocation) throws Exception {
Object action = invocation.getAction();
ActionContext context = invocation.getInvocationContext();
HttpServletRequest request;
//这里就是拦截器做的操作,如果我们当前这个action实现了这个接口,默认调用
if(action instanceof ServletRequestAware) {
request = (HttpServletRequest)context.get("com.opensymphony.xwork2.dispatcher.HttpServletRequest");
((ServletRequestAware)action).setServletRequest(request);
}
if(action instanceof ServletResponseAware) {
HttpServletResponse response = (HttpServletResponse)context.get("com.opensymphony.xwork2.dispatcher.HttpServletResponse");
((ServletResponseAware)action).setServletResponse(response);
}
.....
获取参数
- 通过成员变量获取
//http://localhost:8080/structs2/api/xxxx?name=tom
public class DemoAction extends ActionSupport{
@Override
public String execute() throws Exception {
System.out.println(name);
return super.execute();
}
public String name;
public String getName(){
return name;
}
public void setName(String name) {
this.name = name;
}
//每次访问一个action的时候会new一个action对象出来
public DemoAction() {
System.out.println("init");
}
}
- 封装一个对象来获取
//http://localhost:8080/structs2/xxx?mUser.name=tom&mUser.age=123
public class User {
public String name;
public int age;
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;
}
@Override
public String toString() {
return "User{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
public class DemoAction extends ActionSupport{
public User mUser
@Override
public String execute() throws Exception {
System.out.println(mUser.toString());
return super.execute();
}
public User getmUser() {
return mUser;
}
public void setmUser(User mUser) {
this.mUser = mUser;
}
}
但是带mUser.这种参数提交方式很奇怪 。
- 模型驱动
//http://localhost:8080/structs2/api/xxx?name=tom&age=123
public class DemoAction extends ActionSupport implements ModelDriven<User>{
public User mUser=new User();
public String execute() throws Exception {
System.out.println(mUser.toString());
return super.execute();
}
@Override
public User getModel() {
return mUser;
}
}
OGNL语法
OGNL是Object-Graph Navigation Language的缩写,全称为对象图导航语言,是一种功能强大的表达式语言,它通过简单一致的语法,可以任意存取对象的属性或者调用对象的方法,能够遍历整个对象的结构图,实现对象属性类型的转换等功能。
OGNL表达式的计算是围绕OGNL上下文进行的。
@Test
public void fun1() throws OgnlException {
User rootUser=new User("root",12);
HashMap<String, User> context = new HashMap<>();
context.put("user1",new User("root1",13));
context.put("user2",new User("root2",14));
OgnlContext ognlContext = new OgnlContext();
ognlContext.setRoot(rootUser);
ognlContext.setValues(context);
//取出root中user对象的name属性
String name= (String) Ognl.getValue("name",context,ognlContext.getRoot());
Integer age= (Integer) Ognl.getValue("age",context,ognlContext.getRoot());
System.out.println(name+"age="+age);
//#是从context中取值user1位键
String name1= (String) Ognl.getValue("#user1.name",context,ognlContext.getRoot());
Integer age1= (Integer) Ognl.getValue("#user1.age",context,ognlContext.getRoot());
System.out.println(name1+"age="+age1);
//修改Root中的值
String name2= (String) Ognl.getValue("name='jack'",context,ognlContext.getRoot());
Integer age2= (Integer) Ognl.getValue("age",context,ognlContext.getRoot());
System.out.println(name2+"age="+age2);
//修改Root中的值
String name3= (String) Ognl.getValue("#user1.name='tom'",context,ognlContext.getRoot());
Integer age3= (Integer) Ognl.getValue("#user1.age",context,ognlContext.getRoot());
System.out.println(name3+"age="+age3);
//调用root中的对象方法
Ognl.getValue("setName('lilei')",context,ognlContext.getRoot());
String name4= (String) Ognl.getValue("getName()",context,ognlContext.getRoot());
System.out.println(name4);
//调用map中的对象方法
String name5= (String) Ognl.getValue("#user1.setName('11'),#user1.getName()",context,ognlContext.getRoot());
System.out.println(name5);
//调用静态方法
String name6=(String)Ognl.getValue("@com.fmt.Utils@echo('hello world')",context,ognlContext.getRoot());
System.out.println(name6);
//调用静态方法
Double pi=(Double)Ognl.getValue("@@PI",context,ognlContext.getRoot());
System.out.println(pi);
//创建对象
Integer size= (Integer) Ognl.getValue("{'1','2','3','4'}.size()",context,ognlContext.getRoot());
System.out.println(size);
Character number= (Character) Ognl.getValue("{'1','2','3','4'}[2]",context,ognlContext.getRoot());
System.out.println(number);
Character number1= (Character) Ognl.getValue("{'1','2','3','4'}.get(2)",context,ognlContext.getRoot());
System.out.println(number1);
//创建map
Integer map_size= (Integer) Ognl.getValue("#{'name':'tom','age':18}.size()",context,ognlContext.getRoot());
System.out.println(map_size);
}
ValueStack
ValueStack是一个接口,在struts2中使用OGNL(Object-Graph Navigation Language)表达式实际上是使用,实现了ValueStack接口的类OgnlValueStack.它是ValueStack的默认实现类.
public interface ValueStack {
....
Map<String, Object> getContext();
...
CompoundRoot getRoot();
...
}
public class CompoundRoot extends ArrayList {
public CompoundRoot() {
}
public CompoundRoot(List list) {
super(list);
}
public CompoundRoot cutStack(int index) {
return new CompoundRoot(this.subList(index, this.size()));
}
public Object peek() {
return this.get(0);
}
public Object pop() {
return this.remove(0);
}
public void push(Object o) {
this.add(0, o);
}
}
- ValueStack贯穿整个action的生命周期,每一个action实例都拥有一个ValueStack对象,其中保存了当前action对象和其他相关对象.
- struts2把ValueStack对象保存在名为:struts.valueStack的request域中.即ValueStack作用域为request.当action创建的时候,ValueStack就创建了,action被销毁的时候,ValueStack就销毁了
- ValueStack中的数据分两部分存放:root(栈结构,CompoundRoot)和context(map形式,OgnlContext)
分析流程
首先是web.xml 中配置了拦截器,拦截了请求到StrutsPrepareAndExecuteFilter
<!-- struts2 核心过滤器-->
<filter>
<filter-name>struts2</filter-name>
<filter-class>org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>struts2</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
//StrutsPrepareAndExecuteFilter
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest)req;
HttpServletResponse response = (HttpServletResponse)res;
try {
if(this.excludedPatterns != null && this.prepare.isUrlExcluded(request, this.excludedPatterns)) {
chain.doFilter(request, response);
} else {
//设置编码和国际化
this.prepare.setEncodingAndLocale(request, response);
//创建actionContext上下文(ActionContext,其实通过ThreadlLocal线程内部维护所以不会出现不同action,共享的问题)
this.prepare.createActionContext(request, response);
this.prepare.assignDispatcherToThread();
//这是request的包装类StrutsRequestWrapper,重写了getAttribute的方法(先从request取,如果没有再次value stack的栈取,最后从actioncntext取)
request = this.prepare.wrapRequest(request);
//创建ActionMaping对象(是当前请求信息映射为一个对象)(ActionMaping的创建是通过ActionMapper的getMapping方法,而ActionMapper的默认实现是DefaultActionMapper可以从这个类的getMaping方法中查看,答题是解析url构建mappig对象)
ActionMapping mapping = this.prepare.findActionMapping(request, response, true);
//如果解析出来是null 则不会调用action,直接放行
if(mapping == null) {
boolean handled = this.execute.executeStaticResourceRequest(request, response);
if(!handled) {
chain.doFilter(request, response);
}
} else {
//执行action
this.execute.executeAction(request, response, mapping);
}
}
} finally {
this.prepare.cleanupRequest(request);
}
}
public ActionContext createActionContext(HttpServletRequest request, HttpServletResponse response) {
Integer counter = Integer.valueOf(1);
Integer oldCounter = (Integer)request.getAttribute("__cleanup_recursion_counter");
if(oldCounter != null) {
counter = Integer.valueOf(oldCounter.intValue() + 1);
}
//从本地的ThreadLocal中获取
ActionContext oldContext = ActionContext.getContext();
ActionContext ctx;
if(oldContext != null) {
ctx = new ActionContext(new HashMap(oldContext.getContextMap()));
} else {
// 创建了ValueStack
ValueStack stack = ((ValueStackFactory)this.dispatcher.getContainer().getInstance(ValueStackFactory.class)).createValueStack();
//ValueStack 中context放置了一地参数 进入createContextMap内部一对map,有application,params,session等
stack.getContext().putAll(this.dispatcher.createContextMap(request, response, (ActionMapping)null));
//返回ActionContext实际还是ValuesStack中的context的
ctx = new ActionContext(stack.getContext());
}
request.setAttribute("__cleanup_recursion_counter", counter);
ActionContext.setContext(ctx);
return ctx;
}
//执行 this.execute.executeAction(request, response, mapping); 最终进入
public void serviceAction(HttpServletRequest request, HttpServletResponse response, ActionMapping mapping) throws ServletException {
Map<String, Object> extraContext = this.createContextMap(request, response, mapping);
ValueStack stack = (ValueStack)request.getAttribute("struts.valueStack");
boolean nullStack = stack == null;
if(nullStack) {
ActionContext ctx = ActionContext.getContext();
if(ctx != null) {
stack = ctx.getValueStack();
}
}
if(stack != null) {
extraContext.put("com.opensymphony.xwork2.util.ValueStack.ValueStack", this.valueStackFactory.createValueStack(stack));
}
String timerKey = "Handling request from Dispatcher";
try {
UtilTimerStack.push(timerKey);
String namespace = mapping.getNamespace();
String name = mapping.getName();
String method = mapping.getMethod();
//此次根据actinMapping的action信息构建action的代理
ActionProxy proxy = ((ActionProxyFactory)this.getContainer().getInstance(ActionProxyFactory.class)).createActionProxy(namespace, name, method, extraContext, true, false);
request.setAttribute("struts.valueStack", proxy.getInvocation().getStack());
if(mapping.getResult() != null) {
Result result = mapping.getResult();
result.execute(proxy.getInvocation());
} else {
//代理执行(ActionProxy实现类StrutsActionProxy)
proxy.execute();
}
if(!nullStack) {
request.setAttribute("struts.valueStack", stack);
}
} catch (ConfigurationException var17) {
this.logConfigurationException(request, var17);
this.sendError(request, response, 404, var17);
} catch (Exception var18) {
if(!this.handleException && !this.devMode) {
throw new ServletException(var18);
}
this.sendError(request, response, 500, var18);
} finally {
UtilTimerStack.pop(timerKey);
}
}
//StrutsActionProxy
public String execute() throws Exception {
ActionContext previous = ActionContext.getContext();
ActionContext.setContext(this.invocation.getInvocationContext());
String var2;
try {
//当前的invocation 实现类为DefaultActionInvocation
var2 = this.invocation.invoke();
} finally {
if(this.cleanupContext) {
ActionContext.setContext(previous);
}
}
return var2;
}
//DefaultActionInvocation
public String invoke() throws Exception {
String profileKey = "invoke: ";
String var21;
try {
UtilTimerStack.push(profileKey);
if(this.executed) {
throw new IllegalStateException("Action has already executed");
}
//拦截器组这里使用if 和 interceptor.getInterceptor().intercept(this);是递归的操作
if(this.interceptors.hasNext()) {
//依次获取拦截器 然后执行拦截器的intercept方法 然后我们看下struts-default.xml文件中的 <interceptor-stack name="defaultStack"> 下的拦截器
InterceptorMapping interceptor = (InterceptorMapping)this.interceptors.next();
String interceptorMsg = "interceptor: " + interceptor.getName();
UtilTimerStack.push(interceptorMsg);
try {
this.resultCode = interceptor.getInterceptor().intercept(this);
} finally {
UtilTimerStack.pop(interceptorMsg);
}
} else {
//之前的拦截器完成,进入我们的action
this.resultCode = this.invokeActionOnly();
}
//同上根据返回走返回的一对拦截器
if(!this.executed) {
if(this.preResultListeners != null) {
Iterator i$ = this.preResultListeners.iterator();
while(i$.hasNext()) {
Object preResultListener = (PreResultListener)i$.next();
PreResultListener listener = (PreResultListener)preResultListener;
String _profileKey = "preResultListener: ";
try {
UtilTimerStack.push(_profileKey);
listener.beforeResult(this, this.resultCode);
} finally {
UtilTimerStack.pop(_profileKey);
}
}
}
if(this.proxy.getExecuteResult()) {
this.executeResult();
}
this.executed = true;
}
var21 = this.resultCode;
} finally {
UtilTimerStack.pop(profileKey);
}
return var21;
}
protected String invokeAction(Object action, ActionConfig actionConfig) throws Exception {
String methodName = this.proxy.getMethod();
。。。
//ognl表达式直接掉方法
methodResult = this.ognlUtil.getValue(methodName + "()", this.getStack().getContext(), action);
。。。
String var22 = this.saveResult(actionConfig, methodResult);
return var22;
}
已exception 是 defaultStack拦截器组下第一个拦截器 对应的class是com.opensymphony.xwork2.interceptor.ExceptionMappingInterceptor
public String intercept(ActionInvocation invocation) throws Exception {
String result;
try {
//这里的invocation.invoke();就是上面过来的this,形成递归
result = invocation.invoke();
} catch (Exception var7) {
if(this.isLogEnabled()) {
this.handleLogging(var7);
}
....
拦截器使用
//使用拦截器的方式 实现Interceptor,继承AbstractInterceptor 以及继承MethodFilterInterceptor,三种这边直接使用第三种
public class MyIntercept3 extends MethodFilterInterceptor{
@Override
protected String doIntercept(ActionInvocation actionInvocation) throws Exception {
System.out.println("处理前");
//放行
actionInvocation.invoke();
System.out.println("处理后");
// return 的意义在于如果你不放行直接返回给result如果放行成功,这个return就没有意义
return "success";
}
}
xml中配置
<package name="hello" namespace="/hello" extends="struts-default">
<interceptors>
<!--注册拦截器-->
<interceptor name="MyInter3" class="com.fmt.intercept.MyIntercept3"></interceptor>
<!--注册拦截栈-->
<interceptor-stack name="mystack" >
<!-- 自定义拦截器-->
<interceptor-ref name="MyInter3">
<!--指定那些方法不拦截-->
<param name="excludeMethods">add,delete</param>
<!--指定那些方法 拦截-->
<param name="includeMethods">change</param>
</interceptor-ref>
<!--引用默认的20个拦截器-->
<interceptor-ref name="defaultStack"></interceptor-ref>
</interceptor-stack>
</interceptors>
<default-interceptor-ref name="mystack">
</default-interceptor-ref>
<action name="HelloAction" class="com.fmt.struct.HelloAction" method="hello">
<interceptor-ref name="mystack"></interceptor-ref>
<result name="success">/showjs.jsp</result>
</action>
</package>
参考文章
http://blog.csdn.net/tjcyjd/article/details/6850203
http://501565246-qq-com.iteye.com/blog/1748513