Struts2

一、概念

Struts是流行和成熟的基于MVC设计模式的Web应用程序框架。
使用Struts的目的:
为了帮助我们减少在运用MVC设计模型来开发Web应用的时间。
MVC模式
MVC是模型视图控制器(Model View Contoller),一种软件设计典范,用一种业务逻辑,数据,界面显示分离的方法组织代码,将业务逻辑聚集到一个部件里面,在改进和个性化定制界面及用户交互的同,不需要重新编写业务逻辑。

二、Struts2环境搭建

  • 1.创建Web Project
  • 2.导入Jar
    要导入的包:(共9个)
    commons-fileupload(上传下载包)
    commons-io(输入输出包)
    commons-lang 3-3.2(基础包)
    commons-logging(日志包)
    freemarker(模板引擎,通过模板生成文本输出的通用工具)
    structs2-core(核心包)
    xwork-core(一些类基于xwork)
    ognl(表达式)
    javassist-3.11.0.GA.jar(解析java类文件的一个包)
  • 3.在web.xml配置struts2的过滤器
    web项目在启动tomcat时第一个启动的文件就是web.xml
    首先定义过滤器
<filter>
<filter-name>struct2</filter-name>
<filter-class>org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter</filter-class>
</filter>

这里写完后按住ctrl点击鼠标左键如果可以跳转则证明正确

<!--filter的映射-->
<filter-mapping>
<filter-name>struct2</filter-name>
<url-pattern>/*</url-pattern>
<!--/*是所有的都需要过滤-->
</filter-mapping>

映射与文件的filter-name应该保持一致

  • 4.创建Struts核心xml文件
<?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 name="default" namespace="/" extends="struts-default">
        <action name="helloword" class="imooc.acton.StrutsAction">
            <result>/result.jsp</result>
        </action>
    </package>
</struts>
  • 5.创建action类继承与ActionSupport
    Struts2中有一个默认的方法不指定方法名的话有一个execute()方法
  • 6.配置struts.xml

三、工作原理及过程

  • 工作原理
    在Struts2框架中的处理大概分为以下几个步骤
    1 客户端初始化一个指向Servlet容器(例如Tomcat)的请求
    2 这个请求经过一系列的过滤器(Filter)(这些过滤器中有一个叫做ActionContextCleanUp的可选过滤器,这个过滤器对于Struts2和其他框架的集成很有帮助,例如:SiteMesh Plugin)
    3 接着FilterDispatcher被调用,FilterDispatcher询问ActionMapper来决定这个请是否需要调用某个Action
    4 如果ActionMapper决定需要调用某个Action,FilterDispatcher把请求的处理交给ActionProxy
    5 ActionProxy通过Configuration Manager询问框架的配置文件,找到需要调用的Action类
    6 ActionProxy创建一个ActionInvocation的实例。
    7 ActionInvocation实例使用命名模式来调用,在调用Action的过程前后,涉及到相关拦截器(Intercepter)的调用。
    8 一旦Action执行完毕,ActionInvocation负责根据struts.xml中的配置找到对应的返回结果。返回结果通常是(但不总是,也可 能是另外的一个Action链)一个需要被表示的JSP或者FreeMarker的模版。在表示的过程中可以使用Struts2 框架中继承的标签。在这个过程中需要涉及到ActionMapper
  • 工作流程
    1、客户端浏览器发出HTTP请求.
    2、根据web.xml配置,该请求被FilterDispatcher接收
    3、根据struts.xml配置,找到需要调用的Action类和方法, 并通过IoC方式,将值注入给Aciton
    4、Action调用业务逻辑组件处理业务逻辑,这一步包含表单验证。
    5、Action执行完毕,根据struts.xml中的配置找到对应的返回结果result,并跳转到相应页面
    6、返回HTTP响应到客户端浏览器

四、核心配置文件

  • web.xml
Paste_Image.png
  • struts.xml
Paste_Image.png

Paste_Image.png

struts.xml模板文件

<!DOCTYPE struts PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 2.0//EN" "http://struts.apache.org/dtds/struts-2.0.dtd" >
<struts>

    <!-- include节点是struts2中组件化的方式 可以将每个功能模块独立到一个xml配置文件中 然后用include节点引用 -->
    <include file="struts-default.xml"></include>
    
    
    <!-- package提供了将多个Action组织为一个模块的方式
        package的名字必须是唯一的 package可以扩展 当一个package扩展自
        另一个package时该package会在本身配置的基础上加入扩展的package
        的配置 父package必须在子package前配置 
        name:package名称
        extends:继承的父package名称
        abstract:设置package的属性为抽象的 抽象的package不能定义action 值true:false
        namespace:定义package命名空间 该命名空间影响到url的地址,例如此命名空间为/test那么访问是的地址为http://localhost:8080/struts2/test/XX.action
     -->
    <package name="com.kay.struts2" extends="struts-default" namespace="/test">
        <interceptors>
            <!-- 定义拦截器 
                name:拦截器名称
                class:拦截器类路径
             -->
            <interceptor name="timer" class="com.kay.timer"></interceptor>
            <interceptor name="logger" class="com.kay.logger"></interceptor>
            <!-- 定义拦截器栈 -->
            <interceptor-stack name="mystack">
                <interceptor-ref name="timer"></interceptor-ref>
                <interceptor-ref name="logger"></interceptor-ref>
            </interceptor-stack>
        </interceptors>
        
        <!-- 定义默认的拦截器 每个Action都会自动引用
         如果Action中引用了其它的拦截器 默认的拦截器将无效 -->
        <default-interceptor-ref name="mystack"></default-interceptor-ref>
        
        
        <!-- 全局results配置 -->
        <global-results>
            <result name="input">/error.jsp</result>
        </global-results>
        
        <!-- Action配置 一个Action可以被多次映射(只要action配置中的name不同)
             name:action名称
             class: 对应的类的路径
             method: 调用Action中的方法名
        -->
        <action name="hello" class="com.kay.struts2.Action.LoginAction">
            <!-- 引用拦截器
                name:拦截器名称或拦截器栈名称
             -->
            <interceptor-ref name="timer"></interceptor-ref>
        
            <!-- 节点配置
                name : result名称 和Action中返回的值相同
                type : result类型 不写则选用superpackage的type struts-default.xml中的默认为dispatcher
             -->
         <result name="success" type="dispatcher">/talk.jsp</result>
         <!-- 参数设置 
             name:对应Action中的get/set方法 
         -->
         <param name="url">http://www.sina.com</param>
        </action>
    </package>
</struts>
  • struts.properties文件
Paste_Image.png

五、Struts用法

  • 1、访问Servlet API
    servlet api:
    httpRequest、httpResponse、servletContext
    3个api对应jsp面向对象:request、response、application
    servlet中可以直接调用servlet api
    struts2 Action中execute没有任何参数,也就是不存在servlet api
    struts2 访问servlet api方式:
    1.ActionContext类
    2.实现***Aware接口
    3.ServletActionCotext类
  • 2、Action搜索顺序
    如:http://localhost:8080/struts2/path1/path2/path3/student.action
    第一步:判断package是否存在,如:path1/path2/path3/
    如果package存在
    第二步:则判断该package中action是否存在,如果不存在则去默认namespace的package里面寻找action
    第三步:如果没有,则报错
    如果package不存在:
    第二步:检查上一级路径的package是否存在(直到默认namespace),重复第一步
    第三步:如果没有则报错
  • 3、动态方法调用
    作用:为了解决一个Action对应多个请求的处理,以免Action太多,Action类中又多个方法。
    三种方式:
    1、指定method属性:Action类中新建多个方法,在配置文件中添加action标签,并在标签中添加method选项来区分调用不同的方法。
<action name="addAction" method="add" class="com.imooc.action.HelloWorldAction">
<result>/add.jsp</result>

2、感叹号方式(不推荐)

<struts>
      <package name="default" namespace="/" extends="struts-default">
          <action name="helloWorld" class="com.Action.HelloWorldAction">
               <result >/result.jsp</result>
               <result name="add">/add.jsp</result>
               <result name="update">/update.jsp</result>
         </action>
      </package>
     <!--constant标签的value选项设置为true时,表示开启感叹号方式-->
      <constant name="struts.enable.DynamicMethodInvocation"value="true"></constant>
</struts>

在浏览器中调用不同方法: http://localhost:8080/HelloWorld/HelloWorld!add(或update).action
3、通配符方式
第一个代替{1},第二个代替{2},result里的name是Action的返回值,action的里method是Action里的方法名,调用某个方法时最后目标就输入 {1}_{2}.action;这样可以访问多个Action里的方法.

<action name="*_*" method="{2}" class="com.imooc.action.{1}Action">
      <result >/result.jsp</result>
      <result name="add">/{2}.jsp</result>
      <result name="update">/{2}.jsp</result>
 </action>

在浏览器中调用不同方法:http://localhost:8080/HelloWorld/HelloWorld_add.action

  • 4/指定多个配置文件
    当一个配置文件中存在多个action标签时,可以新建一个xml文件用来存储不同类型的action,将这些xml添加到一个主配置文件中。
    struts.xml主配置文件:
<struts>
      <!--当添加不通包的字配置文件时,添加包名<include file="/a/helloword.xml"> -->
      <include file="helloword.xml"> </include>
      <constant name="struts.i18n.encoding" value="UTF-8"> </constant>
</struts>

helloword配置文件:

<?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 name="default" namespace="/" extends="struts-default">
        <action name="*_*" method="{2}" class="com.imooc.action.{1}Action">
               <result >/result.jsp</result>
               <result name="add">/{2}.jsp</result>
               <result name="update">/{2}.jsp</result>
        </action>
    </package>
</struts>
  • 5、默认Action
    当输入网址找不到对应的action时,跳转到默认的action。
<default-action-ref name="index"></default-action-ref><br>
<action name="index">
     <result>/error.jsp</result>
</action>
  • 6、struts2后缀
    三种方式:
    1.struts.properties中:struts.action.extension=action,do,struts2
    2.struts.xml中增加常量constant:
<constant name="struts.action.extension" value="action,do,struts2"></constant>

3.在过滤器中配置intt-param参数:

<init-param>
  <param-name>struts.action.extension</param-name>
  <param-value>do,action,strtus2</param-value>
</init-param>
  • 7、接受参数
    1、使用Action的属性接受参数,在Action中定义需要接受的属性,并写它的set/get方法。
<!--传递用户登录信息-->
<form action="LoginAction.action" method="post">
    用户名:<input type="text" name="name">
    密码:<input type="password" name="password">
    <input type="submit" value="提交"/>
</form>

当传递的参数比较少是,用此种方法。创建一个登录action类,用来处理页面跳转。

public class LoginAction extends ActionSupport {
    //接受页面传来的值
    private String name;
    private String password;
    
    public String login(){
        System.out.println(name);
        return SUCCESS;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public String getPassword() {
        return password;
    }
    public void setPassword(String password) {
        this.password = password;
    }   
}

在配置文件中添加一个action。

<action name="LoginAction" method="login" class="imooc.action.LoginAction">
            <result>/success.jsp</result>
        </action>

2、使用DomainModel接受参数,创建实体类定义需要接受的属性,并set/get方法,在Action中创建实体类名属性。并在界面进行指定。
当传递的参数过多时,用此种方法。
定义一个User类,用来接受参数。

public class User {
    private String name;
    private String password;
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public String getPassword() {
        return password;
    }
    public void setPassword(String password) {
        this.password = password;
    }
}

在Action中创建User类对象来接受参数,此时User不需要实例化。

public class LoginAction extends ActionSupport {

    private User user;
    public String login(){
        System.out.println(user.getName());
        return SUCCESS;
    }
    public User getUser() {
        return user;
    }
    public void setUser(User user) {
        this.user = user;
    }
}

在登录界面设置参数传递目标。

<form action="LoginAction.action" method="post">
        <!--将name值传递给user对象-->
    用户名:<input type="text" name="usre.name">
    密码:<input type="password" name="user.password">
    <input type="submit" value="提交"/>
</form>

3、使用ModelDriver接受参数,在Action中实现ModelDriver<实体类名>接口,并实现方法返回当前需要转换的对象,删除set/get方法,并对 对象 进行实例化,并取消指定。
将LoginAction类继承一个ModelDriven接口,实现接口的getModel()方法,此时User必须实例化。

public class LoginAction extends ActionSupport implements ModelDriven<User>{

    private User user=new User();
    public String login(){
        System.out.println(user.getName());
        return SUCCESS;
    }
    @Override
    public User getModel() {
        // TODO 自动生成的方法存根
        return user;
    }   
}

登录界面不需要再用user进行传递。

<form action="LoginAction.action" method="post">
    用户名:<input type="text" name="name">
    密码:<input type="password" name="password">
    <input type="submit" value="提交"/>
</form>

实际应用中,最好使用第三种方法,即使user对象改变,也不需要改登录界面的那么值。

  • 8、处理结果类型
Paste_Image.png

Paste_Image.png

处理结果类型-result标签下还有param标签:
1)location:该属性定义了该视图对应的实际视图资源
2)parse:该参数指定是否可以再实际视图名字中使用OGNL表达式,默认值为TRUE,支持OGNL(Object-Graph Navigation Language)表达式
ognl表达式可以在jsp页面去写,也可以在struts2页面中去写,在实际开发中是不常用的,默认情况下ognl是允许的,是打开状态
<param name="parse">true</param>
<param name="location">...地址</param>
ognl表达式使用:
<param name="location">/${#request.path}.jsp</param>
在action类中对应的方法中写request.setAttribute("path","返回的jsp页面名称");

  • 9、input应用
    首先在struts.xml中添加一个input返回结果
<action name="LoginAction" method="login" class="imooc.action.LoginAction">
    <result>/success.jsp</result>
    <result name="input">login.jsp</result>
</action>

自动跳转到input界面方式有两种,其一是传入的参数类型转换错误,另一种是添加表单错误。
第一种:User类中添加一个int类型的年龄属性,如果在登录界面输入的年龄为字符串类型,点击提交后,页面直接跳转到input设置的登录界面。
第二种:在Action中添加addFieldError()方法,只要此方法获得值机会返回input界面。

public class LoginAction extends ActionSupport implements ModelDriven<User>{

    private User user=new User();
    public String login(){
        if(user.getName()==null||"".equals(user.getName()))
            this.addFieldError("name", "用户名不能为空");
            return INPUT;
    }
    @Override
    public User getModel() {
        // TODO 自动生成的方法存根
        return user;
    }
}

还可以通过重写validate方法来实现上述功能。

@Override
public void validate() {
        if(user.getName()==null||"".equals(user.getName()))
            this.addFieldError("name", "用户名不能为空");
    }

在登录界面设置

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!--添加次行代码后才能实现-->
<%@ taglib prefix="s" uri="/struts-tags" %>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>???????</title>
</head>
<body>
<form action="LoginAction.action" method="post">
    用户名:<input type="text" name="name"><s:fielderror name="name"></s:fielderror>
    密码:<input type="password" name="password">
    <input type="submit" value="提交"/>
</form>
</body>
</html>

结果类型input的效果
1.当参数类型转换错误时,如age输入框中的类型是字母等情况,方法自动返回input
2.当action中存在addFiledError时:
1)addFileError放在一般执行方法,addFieldError("", "");语句后面有返回input的语句
2)addFileError放在validate()中
3.FileError的表现形式:
在jsp页面中使用<s:fielderror/>标签,该标签name属性为addFieldError方法中的参数fieldName,在jsp页面中使用struts标签,
需要导入标签库 语句:<%@ taglib prefix="s" uri="/struts-tags" %>

六、拦截器

  • 1、拦截器方法在Action执行之前或者之后执行。
    拦截器站:由多个拦截器组成,作用与拦截器相同。
  • 2、工作原理
    类似于web中的过滤器。
Paste_Image.png
  • 3、拦截器的定义
Paste_Image.png
Paste_Image.png
/*
 * 计算执行Action花费的时间
 */
public class TimerInterceptor extends AbstractInterceptor {

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

推荐阅读更多精彩内容