博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
struts2基础
阅读量:5859 次
发布时间:2019-06-19

本文共 18481 字,大约阅读时间需要 61 分钟。

一、Action二、result三、OGNL(用来从ValueStack和ValueContext中取值)四、Struts-Tags五、声明式异常处理(通过拦截器来实现的)六、拦截器原理七、类型转换
View Code

一、Action

helloWorld

1、下载struts-2.3.24.1解压,打开apps目录下的struts2-blank.war文件,拷贝lib目录下的jar包到项目.

2、拷贝web.xml到项目WEB-INF目录到:

struts2
org.apache.struts2.dispatcher.ng.filter. StrutsPrepareAndExecuteFilter
struts2
/*

3、拷贝struts.xml文件到src目录下:

/hello.jsp

4、hello.jsp

<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>      HelloStruts2        Hello Struts2

5、web.xml中添加如下常量配置代码,开始开发模式,struts.xml和action修改时无需重启服务器,web.xml修改时仍然需要重启服务器.

ps:所有常量配置默认值在struts2-core.jar包org.apache.struts2包下default.properties文件中.

6、源码的粘贴,javadoc的粘贴,xml dtd文件粘贴,略.

7、从浏览器:localhost:8080/TestStruts/hell到看到Hello Struts2分析整个流程:

1>浏览器根据localhost:8080基于TCP连接,连接到web服务器2>发送http请求到web服务器3>web服务器从客户端发送的HTTP请求中依次解析出客户端要访问的主机、web应用、web资源4>根据web.xml文件发现有Filter配置,执行StrutsPrepareAndExecuteFilter的doFilter方法.5>doFilter方法执行一些预处理后会根据struts.xml,结合namespace找到要访问的action,执行action中的目标方法后,在根据当前action的result配置转发或重定向到jsp页面.7>jsp(jsp会被编译为servlet)中的service执行会向response对象写入向客户端输出的数据8>服务器从response中取出数据,构建一个HTTP响应,返回给客户端9>浏览器解析HTTP响应,提取数据显示

ps:结合之前servlet的调用过程,把action当成servlet,其实过程基本一致,servlet是直接在web.xml中配置要访问的servlet,而struts2是采用拦截器机制拦截请求后,根据struts.xml配置文件访问action,结果页面的配置也放到了struts.xml中.

namespace

1、web.xml中package的作用仅仅是给action打包,防止重名,类似文件夹的作用.

2、namespace决定了action的访问路径,默认为"",接收所有路径的action,也就是说访问一个action时,先从对应namespace的package中找,找不到的话就会从namespace为空的package中找.

3、namespace可以写为/,或者/xxx,或者/xxx/yyy,对应的action访问路径为/index.action,/xxx/index.action,或者/xxx/yyy/index.action.

4、namespace最好用模块来进行命名.

action写法

1、之前我们没有在struts.xml中配置class,则默认执行的是ActionSupport中的execute方法,仅仅返回一个SUCCESS而已,如果要自定义action就需要在action中配置class.如下所示是class的配置和action的三种写法:

/ActionIntroduction.jsp
//jsp默认编码的设置window->preferences->jsp
public class IndexAction1 {    public String execute() {        return "success";    }}public class IndexAction2 implements Action {    @Override    public String execute() {        return "success";    }}public class IndexAction3 extends ActionSupport {    @Override    public String execute() {        return "success";    }}

2、具体视图的返回由用户自己定义的Action来决定、服务器会根据返回的字符串找到对应的配置项,来决定视图的内容,result不写name默认为SUCCESS.

3、具体Action的实现可以是一个普通的java类,里面有public String execute方法即可,或者实现Action接口,不过最常用的是从ActionSupport继承,好处在于可以直接使用Struts2封装好的方法.

ps:action在每次请求时会new一个,这是与servlet和struts1巨大的区别,这样不会有线程同步问题.

path

1、直接访问web项目根目录时,会访问web.xml中配置的welcome-file页面.

2、struts2中的路径问题是根据action的路径而不是jsp路径来确定,所以尽量不要使用相对路径.如下例:

/path.jsp
(/代表web项目根目录)

path.jsp页面中有如下一行(在web项目根目录下有如下两个文件path.jsp和index.jsp):

结果:

1>按理来说<a>是一个相对路径是可以访问的,但是点击这个<a>会跳到..../path/index.jsp,也就是说他是相对url的路径的而不是相对文件位置的路径.

2><a>标签href改为如下可以吗?

ps:不行的,不要忘记之前方立勋将的此处/代表的是整个站点根路径.

解决方案:全部使用绝对路径,利用el表达式

ActionMethod&DMI

我们之前一直没有指点Action的method这样的话默认执行的是Action的execute方法,指点method后会执行指定的方法,配置如下:

/user_add_success.jsp

这样有个问题就是每执行一个方法都要配置一个action较繁琐,可以使用动态方法调用DMI,如下:

/user_add_success.jsp

ActionWildcard

使用通配符,将配置量降到最低,不过,一定要遵守"约定优于配置"的原则

/Student{
1}_success.jsp
/{
1}_{
2}_success.jsp

ps:如果既匹配Student*又匹配Studentadd,那么则匹配Studentadd,首先匹配最精确的,如果匹配多个星号,则按action的前后顺序.

使用action属性接收参数

/user_add_success.jsp
public class UserAction extends ActionSupport {        private String name;    private int age;        public String add() {        System.out.println("name=" + name);        System.out.println("age=" + age);        return SUCCESS;    }}

ps:客户端访问new一个新的UserAction时,会将参数自动传递给属性(需要提供get,set方法,为简化代码,所有实例中没有写set,get方法),是根据set方法赋值的而不是属性名称,并且会进行参数类型的自动转换如name是String,age是int.

用DomainModel接收参数

/user_add_success.jsp
public class UserAction extends ActionSupport {        private User user;//不需要new    //private UserDTO ;    public String add() {        System.out.println("name=" + user.getName());        System.out.println("age=" + user.getAge());        return SUCCESS;    }}

ps:实际开发中很多时候传递的数据不能和domainModel匹配,比如注册的时候会传递确认密码到后台,而我们的domainModel中没有这个属性,这时候就要使用DTO(或叫VO或叫DO)来接收请求参数再传递给domainModel,如果匹配的话则不需要DTO.

用ModelDriven接收参数(实际开发用的不多)

/user_add_success.jsp
public class UserAction extends ActionSupport implements ModelDriven
{ private User user = new User(); public String add() { System.out.println("name=" + user.getName()); System.out.println("age=" + user.getAge()); return SUCCESS; } @Override public User getModel() { return user; }}

ps:如果name输入中文的话会有中文问题,配置如下常量即可,在web.xml中struts2过滤器前配置一个编码过滤器也可,但是很明显下面的常量配置简单.

简单处理数据校验

public class UserAction extends ActionSupport {    private String name;        public String add() {        if(name == null || !name.equals("admin")) {            this.addFieldError("name", "name is error");            this.addFieldError("name", "name is too long");            return ERROR;        }         return SUCCESS;    }}
User Add Error!

浏览器显示结果发现<s:fielderror>标签取出的错误信息会强加效果li,ul什么的,处理起来比较麻烦,所以我们想只取到错误信息的字符串,<s:property>即可,<s:debug>可打印值栈信息(一次request只有一次ValueStack):

ps:各种的错误以及各种消息的属性用相应struts标签取时都会强加格式,所以都采用<s:property>标签来取出存的字符串,value是ognl表达式的写法,errors是一个map,采用点,里面的name是一个数组(同一个name可以加多个错误),采用[],注:各种错误只是名称不同为了见名知意,其实原理相同.

AccessWebElements

访问web元素就是在action中访问servlet中的HttpServletRequest, HttpSession, ServletContext对应于jsp中就是request,session,application.如下是四种方式:

public class LoginAction1 extends ActionSupport {        private Map request;    private Map session;    private Map application; public LoginAction1() {        request = (Map)ActionContext.getContext().get("request");        session = ActionContext.getContext().getSession();        application = ActionContext.getContext().getApplication();    }    public String execute() {        request.put("r1", "r1");        session.put("s1", "s1");        application.put("a1", "a1");        return SUCCESS;     }}//DI dependency injection(IoC inverse of control)public class LoginAction extends ActionSupport implements RequestAware,SessionAware, ApplicationAware{        private Map
request; private Map
session; private Map
application; public String execute() { request.put("r1", "r1"); session.put("s1", "s1"); application.put("a1", "a1"); return SUCCESS; } @Override public void setRequest(Map
request) { this.request = request; } @Override public void setSession(Map
session) { this.session = session; } @Override public void setApplication(Map
application) { this.application = application; }}public class LoginAction3 extends ActionSupport { private HttpServletRequest request; private HttpSession session; private ServletContext application; public LoginAction3() { request = ServletActionContext.getRequest(); session = request.getSession(); application = session.getServletContext(); } public String execute() { request.setAttribute("r1", "r1"); session.setAttribute("s1", "s1"); application.setAttribute("a1", "a1"); return SUCCESS; }}public class LoginAction4 extends ActionSupport implements ServletRequestAware { private HttpServletRequest request; private HttpSession session; private ServletContext application; public String execute() { request.setAttribute("r1", "r1"); session.setAttribute("s1", "s1"); application.setAttribute("a1", "a1"); return SUCCESS; } @Override public void setServletRequest(HttpServletRequest request) { this.request = request; this.session = request.getSession(); this.application = session.getServletContext(); }}
| <%=request.getAttribute("r1") %>
| <%=session.getAttribute("s1") %>
| <%=application.getAttribute("a1") %>

ps:

1>第一种方式中ActionContext是ThreadLocal,每次请求(也就是每个线程)会创建一个.第二种方式依赖注入(底层使用反射很容易实现),这种方式用的最多,如果要使用HttpServletRequest中特有的方法采用第三种和第四种方式.

2><s:debug>看到的另一部分内容stack context如下:

 

#attr依次从各个域中查找key,用的不多,因为我们应当明确知道把值存在了哪个域中.

3>其实平常request很少拿,因为我们action中的属性会自动放到ValueStack中,不过其实valuestack就是把属性放到了request中,一般拿HttpSession来用,比如注册的时候.

模块包含

当配置很多时,还是很有意义

默认action

/default.jsp

ps:当访问的action不存在时会访问默认action,这点在设置错误页面时有意义,而不是直接返回404.

二、result

result_type

  1. dispatcher
  2. redirect
  3. chain
  4. redirectAction
  5. freemarker
  6. httpheader
  7. stream
  8. velocity
  9. xslt
  10. plaintext
  11. tiles
/r1.jsp
/r2.jsp
r1
r2

ps:当用到不常用的类型时查文档,切记查文档的重要性.

global-results&extends

/main.jsp
/user_success.jsp
/user_error.jsp
/admin.jsp
public class UserAction extends ActionSupport {    private int type;    public int getType() {        return type;    }    public void setType(int type) {        this.type = type;    }    @Override    public String execute() throws Exception {        if(type == 1) return "success";        else if (type == 2) return "error";        else return "mainpage";    }}

全局结果页面用在结果页面是同一个页面时简化配置.如果要用别的package中的global-results,可以通过extends继承别的package,extends默认配置是struts-default,这个包对应struts-default.xml文件中的配置(定义了各种bean,结果集,拦截器(和Filter原理一样)),这个文件用户不要修改,就是用来被继承的.

dynamic_result

${r}
public class UserAction extends ActionSupport {    private int type;    private String r;//提供set get    public String getR() {        return r;    }    public void setR(String r) {        this.r = r;    }    public int getType() {        return type;    }    public void setType(int type) {        this.type = type;    }    @Override    public String execute() throws Exception {        if(type == 1) r="/user_success.jsp";        else if (type == 2) r="/user_error.jsp";        return "success";    }}

${r}用于在配置文件中读取value statck中的值.

ResultWithParams

/user_success.jsp?t=${type}
public class UserAction extends ActionSupport {    private int type;       public int getType() {        return type;    }    public void setType(int type) {        this.type = type;    }    @Override    public String execute() throws Exception {        return "success";    }}
    User Success!    from valuestack: 
//取不到值 from actioncontext:
//可以取到值

ps:

1>只有客户端跳转时,才需要传值,服务器端不需要,因为是一次请求,共用一个值栈.

2><s:property value="t"/>是用来从值栈中取值的,上例中from valuestack取不到值是因为直接redirect到jsp页面是没有值栈的,只能从valueConetxt中取值.

3>书用来查,不要抠.

三、OGNL(用来从ValueStack和ValueContext中取值)

  1. 访问值栈中的action的普通属性: username =
  2. 访问值栈中对象的普通属性(get set方法):
    |
    |
  3. 访问值栈中对象的普通属性(get set方法):
  4. 访问值栈中对象的普通方法:
  5. 访问值栈中对象的普通方法:
  6. 访问值栈中action的普通方法:
  7. 访问静态方法:
  8. 访问静态属性:
  9. 访问Math类的静态方法:
  10. 访问普通类的构造方法:

  11. 访问List:
  12. 访问List中某个元素:
  13. 访问List中元素某个属性的集合:
  14. 访问List中元素某个属性的集合中的特定值:
    |
  15. 访问Set:
  16. 访问Set中某个元素:
  17. 访问Map:
  18. 访问Map中某个元素:
    |
    |
  19. 访问Map中所有的key:
  20. 访问Map中所有的value:
  21. 访问容器的大小:
    |

  22. 投影(过滤):
  23. 投影:
  24. 投影:
  25. 投影:

  26. []:
  27. //访问栈顶action中的username

ps:

1>ognl如果取不到值就不显示,而不是报错.

2>list,set,数组取数据的方式一模一样.

3>ValueStack中action永远在栈顶,如果服务器端跳转的时候会压入多个action.

四、Struts-Tags

  • property:
  • property 取值为字符串:
  • property 设定默认值:
  • property 设定HTML:
  • set 设定adminName值(默认为request和 ActionContext):
  • set 从request取值:
  • set 从ActionContext取值:
  • set 设定var,范围为ActionContext:
  • set 使用#取值:
  • set 从相应范围取值:
  • 定义bean,并使用param来设定新的属性值:
  • include _include1.html 包含静态英文文件
  • wrong age!
    too young!
    yeah!
    null
  • |
    | 遍历过的元素总数:
    | 遍历过的元素索引:
    | 当前是偶数?:
    | 当前是奇数?:
    | 是第一个元素吗?:
    | 是最后一个元素吗?:

    ps:$,#,%几个符号的作用

    1、ui标签,略

    2、$用于i18n和struts配置文件中读取值栈中的值

    3、#取得ActionContext的值

    4、%将原本的文本属性解析为ognl,对于本来就是ognl的属性不起作用

    4、OGNL和Struts-Tags结合el和JSTL一起学,基本一样,剩下的查文档,足够.

    五、声明式异常处理(通过拦截器来实现的)

    //action中,可以直接抛出异常,发现ActionSupport的execute方法也是有异常抛出的,就是告诉我们可以不处理,直接抛给struts2帮我们处理.public String list() throws Exception {  categories = categoryService.list();  return SUCCESS;}
    //categoryServicepublic List
    list() throws SQLException { Connection conn = DB.createConn(); String sql = "select * from _category_"; PreparedStatement ps = DB.prepare(conn, sql); List
    categories = new ArrayList
    (); try { ResultSet rs = ps.executeQuery(); Category c = null; while(rs.next()) { c = new Category(); c.setId(rs.getInt("id")); c.setName(rs.getString("name")); c.setDescription(rs.getString("description")); categories.add(c); } } catch (SQLException e) { e.printStackTrace(); throw(e); } DB.close(ps); DB.close(conn); return categories;}
    /admin/{1}-{2}.jsp
    /admin/{1}-{2}.jsp
    /error.jsp

    可以对异常进行统一处理,这样就不需要在每个action中进行配置,需要继承这个package:

    /error.jsp

    六、拦截器原理

    1、原理图

     

     

     

    2、ActionInvocation原理模拟

    //实际是ActionProxy中的代码public class Main {    public static void main(String[] args) {        new ActionInvocation().invoke();    }}public class ActionInvocation {    List
    interceptors = new ArrayList
    (); int index = -1; Action a = new Action(); public ActionInvocation() { this.interceptors.add(new FirstInterceptor()); this.interceptors.add(new SecondInterceptor()); } public void invoke() { index ++; if(index >= this.interceptors.size()) { a.execute(); }else { this.interceptors.get(index).intercept(this); } }}public interface Interceptor { public void intercept(ActionInvocation invocation) ;}public class FirstInterceptor implements Interceptor { public void intercept(ActionInvocation invocation) { System.out.println(1); invocation.invoke(); System.out.println(-1); }}public class SecondInterceptor implements Interceptor { public void intercept(ActionInvocation invocation) { System.out.println(2); invocation.invoke(); System.out.println(-2); }}public class Action { public void execute() { System.out.println("execute!"); }}

    ps:老马说自定义拦截器99.9不使用(有疑问),filter和interceport其实是一回事,具体intercepor的用途可以参考Filter,基于安全的拦截器 spring securty.

    3、自定义拦截器

    /test.jsp
    public class MyInterceptor implements Interceptor {    public String intercept(ActionInvocation invocation) throws Exception {        long start = System.currentTimeMillis();        String r = invocation.invoke();        long end = System.currentTimeMillis();        System.out.println("action time = " + (end - start));        return r;    }    public void destroy() {    }    public void init() {    }}
    public class TestAction extends ActionSupport {    @Override    public String execute() throws Exception {        return super.execute();    }}

    4、使用token拦截器控制表单重复提交

    浏览器输入:localhost:8080/struts2_3600_token_interceptor/input1

    /input.jsp
    /addOK.jsp
    /error.jsp
    public class InputAction extends ActionSupport {        @Override    public String execute() throws Exception {        return super.execute();    }}
    name:
    age:
    public class UserAction extends ActionSupport {        private String name;    private int age;    @Override    public String execute() throws Exception {        System.out.println("a user added!");        return super.execute();    }}

    七、类型转换

    所谓的类型转换就是把客户端传递过来的参数(全部默认为字符串)转换为特定类型,比如List,Map,Date,还有一些没有提供set方法的Jdk的对象(用的很少).

    1、对于date类型,参数按1988-08-08 12:22:22这种格式传递到后台,jsp中如下解析即可:

    2、对于checkbox这种向后台传递时一个名字对应多个值的情况,后台拿一个list,或set接受即可.

    3、当比如说要往Point对象中的X,Y属性传值时,因为没有set,get方法就需要自定义类型转换器,略,用到的时候查.

    ps: 上面没有讲到的内容:Lambda表达式、验证框架、UI标签、国际化、自定义类型转换、类型转换中的异常处理、上传与下载文件、Struts2注解、Struts2和framemark、velocity和结合、Struts2和ajax、json的结合,Struts2集合了大量的内容,但都很简单,知道那么一回事,用到的时候查即可,马哥说的话,Struts2的内容掌握百分之10即可,其它的用到的时候查.

    转载于:https://www.cnblogs.com/wangweiNB/p/5102758.html

    你可能感兴趣的文章
    王潮歌跨界指导HUAWEI P20系列发布会 颠覆传统 眼界大开!
    查看>>
    王高飞:微博已收购一直播 明年一季度重点是功能与流量打通
    查看>>
    趣头条发行区间7至9美元 预计9月14日美国上市
    查看>>
    新北市长侯友宜:两岸交流应从隔壁最亲近的人开始
    查看>>
    全面屏的Nokia X即将上线,不到2000元的信仰你要充值吗?
    查看>>
    HTML5音频audio属性
    查看>>
    ES6学习
    查看>>
    Centos7搭建Django环境
    查看>>
    序列化一个Intent
    查看>>
    JavaScript数据类型及语言基础--ife
    查看>>
    进阶 Nginx 高手必须跨越的 5 座大山
    查看>>
    部署P2P升级的脚本
    查看>>
    jenkins--ant持续集成测试build文件脚本 测试报告
    查看>>
    ubuntu下安装libxml2
    查看>>
    nginx_lua_waf安装测试
    查看>>
    easyui 只刷新当前页面的数据 datagrid reload 方法
    查看>>
    58到家完成3亿美金A轮融资 阿里平安等投资
    查看>>
    Mysql-mmm高可用方案安装及配置
    查看>>
    【狂人小白】MyBatis.001 学习巴提斯!
    查看>>
    全面解析C#中参数传递
    查看>>