Struts2框架使用及分析

简书链接:http://www.jianshu.com/p/eba232e1cb99

#####第一步导包,不多说了

#####第二步配置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中有基本的常量

外部修改常量的三种方式

  1. 在之前struts.xml的标签下填写
<!--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>
  1. 在src目录下创建strtus.properties,然后填写键值对如
struts.i18n.encoding=UTF-8
  1. 在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);
        }   
          .....   

获取参数

  1. 通过成员变量获取
    //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");
    }
}
  
  1. 封装一个对象来获取
//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.这种参数提交方式很奇怪 。

  1. 模型驱动
//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);
    }
}
  1. ValueStack贯穿整个action的生命周期,每一个action实例都拥有一个ValueStack对象,其中保存了当前action对象和其他相关对象.
  2. struts2把ValueStack对象保存在名为:struts.valueStack的request域中.即ValueStack作用域为request.当action创建的时候,ValueStack就创建了,action被销毁的时候,ValueStack就销毁了
  3. ValueStack中的数据分两部分存放:root(栈结构,CompoundRoot)和context(map形式,OgnlContext)

分析流程

enter image description here

首先是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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值