Struts2框架使用及分析

原文链接: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中有基本的常量

外部修改常量的三种方式

  1. 在之前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>
  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

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,684评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,143评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,214评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,788评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,796评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,665评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,027评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,679评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 41,346评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,664评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,766评论 1 331
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,412评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,015评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,974评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,203评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,073评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,501评论 2 343

推荐阅读更多精彩内容

  • 概述 什么是Struts2的框架Struts2是Struts1的下一代产品,是在 struts1和WebWork的...
    inke阅读 2,242评论 0 50
  • 1.什么是框架 什么是框架,框架从何而来,为什么使用框架? 1).框架(framework)——半成品: 1.是一...
    贾里阅读 2,827评论 0 0
  • action中如何接受页面传过来的参数 第一种情况:(同名参数) 例如:通过页面要把id=1 name=tom a...
    清枫_小天阅读 2,932评论 1 22
  • 1、struts2工作流程 Struts 2框架本身大致可以分为3个部分: 核心控制器FilterDispatch...
    重山杨阅读 1,512评论 0 38
  • 有没有那么一首歌,深深的藏在你的心里。可能很久不会去听它,但是只要前奏一想起,你就会深深的陷入回忆。 看惯了身边各...
    周诗汶阅读 505评论 11 2