struts2执行流程
1. 执行的流程
- 编写的页面,点击超链接,请求提交到服务器端。
- 请求会先经过Struts2的核心过滤器(StrutsPrepareAndExecuteFilter)
- 过滤器的功能是完成了一部分代码功能
- 就是一系列的拦截器执行了,进行一些处理工作。
- 咱们可以在struts-default.xml配置文件中看到有很多的拦截器。可以通过断点的方式来演示。
- 拦截器执行完后,会根据struts.xml的配置文件找到请求路径,找到具体的类,通过反射的方式让方法执行。
2.struts2配置文件加载顺序
1. Struts2框架的核心是StrutsPrepareAndExecuteFilter过滤器,该过滤器有两个功能
- Prepare -- 预处理,加载核心的配置文件
- Execute -- 执行,让部分拦截器执行
2. StrutsPrepareAndExecuteFilter过滤器会加载哪些配置文件呢?
- 通过源代码可以看到具体加载的配置文件和加载配置文件的顺序
- init_DefaultProperties(); -- 加载org/apache/struts2/default.properties
- init_TraditionalXmlConfigurations(); -- 加载struts-default.xml,struts-plugin.xml,struts.xml
- init_LegacyStrutsProperties(); -- 加载自定义的struts.properties.
- init_CustomConfigurationProviders(); -- 加载用户自定义配置提供者
- init_FilterInitParameters() ; -- 加载web.xml的初始化
3. 重点了解的配置文件
- default.properties -- 在org/apache/struts2/目录下,代表的是配置的是Struts2的常量的值
- struts-default.xml -- 在Struts2的核心包下,代表的是Struts2核心功能的配置(Bean、拦截器、结果类型等)
- struts.xml -- 重点中的重点配置,代表WEB应用的默认配置,在工作中,基本就配置它就可以了!!(可以配置常量)
- web.xml -- 配置前端控制器(可以配置常量
* 注意:
- 前3个配置文件是struts2框架的默认配置文件,基本不用修改。
- 后3个配置文件可以允许自己修改struts2的常量。但是有一个特点:后加载的配置文件修改的常量的值,会覆盖掉前面修改的常量的值。
4. 总结(重点掌握的配置文件)
- 先加载default.properties文件,在org/apache/struts2/default.properties文件,都是常量。
- 又加载struts-default.xml配置文件,在核心的jar包最下方,struts2框架的核心功能都是在该配置文件中配置的。
- 再加载struts.xml的配置文件,在src的目录下,代表用户自己配置的配置文件
- 最后加载web.xml的配置文件
- 后加载的配置文件会覆盖掉之前加载的配置文件(在这些配置文件中可以配置常量)
- 加载default.properties(默认常量值)
- 加载struts-default.xml(核心的功能),编写包的配置都需要继承struts-default,但是配置文件中不能编写常量的
- 加载struts.xml(开发必须要编写的配置文件),编写常量的(开发中编写常量的地方)
- 加载web.xml,也可以编写常量(开发中尽量不要在该配置文件中编写常量值)
5. 注意一个问题
* 哪些配置文件中可以配置常量?
- default.properties -- 默认值,咱们是不能修改的!!
- struts.xml -- 可以配置,开发中基本上都在该配置文件中配置常量
- struts.properties -- 可以配置,基本不会在该配置文件中配置
- web.xml -- 可以配置,基本不会在该配置文件中配置
后加载的配置文件会覆盖掉之前加载的配置!!
struts.xml配置详解
<package>标签
功能:用来定义不同的模块,管理和配置Action,并且实现包内配置复用(通过包继承)
如果要配置<Action>的标签,那么必须要先配置<package>标签,代表的包的概念
- name -- 包的名称,在struts容器中具有唯一性(在开发中可以用模块名作为包名),管理action配置
- extends -- 继承父package中的功能,通常继续struts-default,由于该默认包内定义了大量结果集类型和拦截器,所以struts强制继承struts-default,否则报错
- namespace -- 名称空间,一般与<action>标签中的name属性共同决定访问路径(通俗话:怎么来访问action),常见的配置如下
* namespace="/" -- 根名称空间
* namespace="/aaa" -- 带有名称的名称空间
* abstract -- 抽象的。这个属性基本很少使用,值如果是true,那么编写的包是被继承的
例:以昨天开发的工程基础上修改Java网课基础笔记(29)
修改hello.jsp里的路径
<body>
<!-- struts2框架默认处理后缀名为.action的请求 -->
<a href="${pageContext.request.contextPath}/xxx/hello.action">hello请求</a>
</body>
修改result.jsp内容
<body>
<h1>config result</h1>
</body>
修改HelloAction.java
import com.opensymphony.xwork2.Action;
public class HelloAction implements Action {
//HelloAction类实现Action接口后,执行HelloAction的时候默认执行execute()方法
//类似post请求执行doPost方法,返回的String类型是返回结果集
@Override
public String execute() throws Exception {
// TODO Auto-generated method stub
System.out.println("hello config");
//返回逻辑视图
return "success";
}
}
struts.xml增加以下内容
<package name="anotherPackage" extends="struts-default" namespace="/xxx">
<action name="hello" class="HelloAction">
<result name="success">/result.jsp</result>
</action>
</package>
测试(注意路径)
namespace:访问路径,
【面试知识点一】
如果rul是http://localhost:8080/project工程名/xxx/qqq.action,namespace就是/xxx
如果rul是http://localhost:8080/project工程名/xxx/yyy/qqq.action,namespace就是/xxx/yyy
【面试知识点二】
如果rul是http://localhost:8080/project工程名/xxx/yy/zz/hello.action
首先去搜索namespace为/xxx/yy/zz的package
如果找不到,去搜索/xxx/yy的package
如果找不到,去搜索/xxx的package
【面试知识点三】
namespace="" 和namespace="/"的区别
namespace="/"的优先级比namespace=""的优先级高】
即首先去搜索namespace="/",如果搜不到,则搜索namespace=""
<action>标签
功能:用来管理具体的Action,负责接收客户的请求,进行处理,并且完成响应
- name属性:action的名字,用于配置请求的URL路径
- class属性:action对应的完整包路径,该类编写了action具体业务逻辑代码。
- 找到action之后,系统会默认会执行Action类中的execute方法。
- 若没有class属性,系统会默认执行ActionSupport中execute方法,而ActionSupport的execute会默认返回 success逻辑视图,这种处理方式在struts-default.xml 文件已经进行了规定。
- 当访问的Action不存在的时候,我们可以自行设定默认执行的Action。在操作中,当系统访问不到对应请求的action的时候,会执行我们自行设定默认执行的action,这种方式需要我们在struts.xml文件中提前进行相关配置
例:以昨天开发的工程基础上修改Java网课基础笔记(29)
修改HelloAction.java
import com.opensymphony.xwork2.Action;
public class HelloAction implements Action {
//HelloAction类实现Action接口后,执行HelloAction的时候默认执行execute()方法
//类似post请求执行doPost方法,返回的String类型是返回结果集
@Override
public String execute() throws Exception {
// TODO Auto-generated method stub
System.out.println("hello action");
//返回逻辑视图
return "success";
}
}
- 问题一:如果action 没有配置class属性
在struts.xml中
<package name="anotherPackage" extends="struts-default"
namespace="/xxx">
<!--没有写class属性,程序会产生什么结果? 若没有class属性,系统会默认执行ActionSupport中execute方法,而ActionSupport的execute会默认返回
success逻辑视图,这种处理方式在struts-default.xml 文件已经进行了规定。 -->
<action name="hello">
<result name="success">/result.jsp</result>
</action>
</package>
还是会执行成功
- 问题二:action的name属性写错,即写错名字
<package name="anotherPackage" extends="struts-default"
namespace="/xxx">
<!-- action的name属性写错 -->
<action name="helle">
<result name="success">/result.jsp</result>
</action>
</package>
测试结果(会报错,找不到action)
像上面出现的404这种错误页面对用户来说很不友好,所有我们应该自己定义错误页面。
新建一个errorPage.jsp
<body>
错误
<img alt="" src="${pageContext.request.contextPath}/img/404.jpg">
</body>
找张404图片放在WebConent/img下
在struts.xml修改
<package name="anotherPackage" extends="struts-default"
namespace="/xxx">
<!-- 告诉struts框架,如果action找不到,就跳转到指定到Action 必须配置在action节点前面 -->
<default-action-ref name="errorPage"></default-action-ref>
<!-- action的name属性写错 -->
<action name="helle">
<result name="success">/result.jsp</result>
</action>
<!-- 配置一个出错要执行的Action -->
<action name="errorPage">
<result name="success">/errorPage.jsp</result>
</action>
</package>
测试结果
<constant>常量配置
- struts.xml(推荐)(在package包外面)
<!-- 配置后缀名常量 -->
<constant name="struts.action.extension" value="abc"></constant>
在hello.jsp中修改
<body>
<!-- struts2框架默认处理后缀名为.action的请求 -->
<a href="${pageContext.request.contextPath}/xxx/hello.action">hello请求</a>
<br>
<!-- 修改后缀名 -->
<a href="${pageContext.request.contextPath}/xxx/helle.abc">helle请求(别看差眼啦)</a>
</body>
测试结果(注意路径)
- struts.properties
key=value
- web.xml
<!-- 配置struts2的过滤器 -->
<filter>
<filter-name>struts2</filter-name>
<filter-class>org.apache.struts2.dispatcher.filter.StrutsPrepareAndExecuteFilter</filter-class>
<!-- 修改后缀名 -->
<init-param>
<param-name>struts.action.extension</param-name>
<param-value>xyz</param-value>
</init-param>
</filter>
在hello.jsp修改
<body>
<!-- struts2框架默认处理后缀名为.action的请求 -->
<a href="${pageContext.request.contextPath}/xxx/hello.action">hello请求</a>
<br>
<!-- 修改后缀名 -->
<a href="${pageContext.request.contextPath}/xxx/helle.abc">helle请求(别看差眼啦)</a>
<br>
<a href="${pageContext.request.contextPath}/xxx/helle.xyz">helle请求(xyz)</a>
</body>
测试结果
单击第三个
但是现在点击第二个(会报错)
原因:加载的配置文件会覆盖之前的配置文件
文件分离(分模块)
修改hello.jsp
<body>
<!-- struts2框架默认处理后缀名为.action的请求 -->
<a href="${pageContext.request.contextPath}/xxx/hello.action">hello请求</a>
<br>
<!-- 修改后缀名 -->
<a href="${pageContext.request.contextPath}/xxx/helle.abc">helle请求(别看差眼啦)</a>
<br>
<a href="${pageContext.request.contextPath}/xxx/helle.xyz">helle请求(xyz)</a>
<br>
<a href="${pageContext.request.contextPath}/part1/hello.xyz">文件分离</a>
</body>
在struts.xml引入其他配置文件
<!-- 引入分离文件 -->
<include file="struts-part1"></include>
增加struts-part1.xml
<package name="mypart1" extends="struts-default" namespace="/part1">
<action name="hello" class="HelloAction">
<result name="success">/result.jsp</result>
</action>
</package>
测试结果
注意:如要引用完整的包名:<include file="/ceshi/src/struts-part1.xml"></include>
Action编写方式(3种)
- 实现Action接口(可以使用结果集常量字符串)
- 继承ActionSupport类:(重点之重点,推荐方式)
对请求参数进行校验
设置错误信息
读取国际化信息
3.pojo类(非入侵性,简单,干净)没有extends父类,也没有implements接口
新建项目工程struts_01,配置上同
Action接口中有5个常量和一个execute方法
5个常量分别是:
public static final java.lang.String SUCCESS = "success"; 运行成功返回的逻辑视图
public static final java.lang.String NONE = "none";不返回任何结果页面
public static final java.lang.String ERROR = "error";运行失败时返回的逻辑视图
public static final java.lang.String INPUT = "input";校验失败时返回的逻辑视图
public static final java.lang.String LOGIN = "login";登录时返回的逻辑视图
新建my.jsp
<body>
<h1>
<a href="${pageContext.request.contextPath}/my1.action">Action的编写方式一 实现Action接口</a><br>
<a href="${pageContext.request.contextPath}/my2.action">Action的编写方式二 继承ActionSupport类</a><br>
<a href="${pageContext.request.contextPath}/my3.action">Action的编写方式三 普通Java类(pojo类)</a>
</h1>
</body>
新建result.jsp
<body>
<h1>result</h1>
</body>
方式一 实现Action接口
编写MyAction1.java
public class MyAction1 implements Action {
@Override
public String execute() throws Exception {
// TODO Auto-generated method stub
System.out.println("Action编写方式一");
return SUCCESS;
}
}
struts.xml配置文件
<package name="default" extends="struts-default" namespace="/">
<action name="my1" class="struts_01.MyAction1">
<result name="success">/result.jsp</result>
</action>
</package>
测试结果
方式二 继承ActionSupport类
继承ActionSupport类后,具有Action接口功能,还有严重功能(重点推荐的方式)
编写MyAction2.java
public class MyAction2 extends ActionSupport {
public String execute() throws Exception {
// TODO Auto-generated method stub
System.out.println("Action编写方式二");
return SUCCESS;
}
}
struts.xml配置文件增加action
<action name="my2" class="struts_01.MyAction2">
<result name="success">/result.jsp</result>
</action>
测试结果
方式三 普通Java类(pojo类)
编写MyAction3.java
//可去掉throws Exception 但一定要execute方法
public class MyAction3 {
public String execute() throws Exception {
System.err.println("Action编写方式三");
return "success";
}
}
struts.xml配置文件增加action
<action name="my3" class="struts_01.MyAction3">
<result name="success">/result.jsp</result>
</action>
测试结果
Action访问方式
方式一 通过<action>标签中的method属性,访问到Action中的具体的方法。
编写loginresult.jsp
<body>
<h1>resultLogin</h1>
</body>
编写registresult.jsp
<body>
<h1>regist result</h1>
</body>
编写UserAction.java
public class UserAction extends ActionSupport {
public String login() throws Exception {
System.err.println("执行了login方法");
return "success";
}
public String regist() throws Exception {
System.out.println("执行了regist方法");
return SUCCESS;
}
}
struts.xml配置文件
<!-- 第一种方式:method属性指明了要执行的方法名,达到不同的请求执行不同的方法 -->
<action name="userLogin" class="struts_01.UserAction" method="login">
<result name="success">/resultLogin.jsp</result>
</action>
<action name="userRegist" class="struts_01.UserAction" method="regist">
<result name="success">/resultRegist.jsp</result>
</action>
测试结果
方式二 通配符的访问方式:(访问的路径和方法的名称必须要有某种联系.)
- 一个通配符
- sruts.xml增加action
<!-- 第二种方式:(一个通配符)使用通配符配置执行方法 {1}自动匹配*方法 -->
<!-- http://localhost:8080/struts_01/user_login.action
http://localhost:8080/struts_01/user_regist.action -->
<action name="user_*" class="struts_01.UserAction" method="{1}">
<result name="success">/{1}result.jsp</result>
<allowed-methods>login,regist</allowed-methods>
</action>
- 测试结果
2.5版本以后,为了增加安全性,内部会验证是否允许访问该方法。
用通配符一定要加上<allowed-methods>action方法名</allowed-methods>
- 两个通配符
- sruts.xml增加action
<!-- 2个通配符方式 了解即可 -->
<!-- *_* 如UserAction_login 即UserAction匹配{1} login匹配{2} -->
<action name="*_*" class="struts_01.{1}" method="{2}">
<result name="success">/{2}result.jsp</result>
<allowed-methods>login,regist</allowed-methods>
</action>
- 测试结果
方式三 动态方法调用(了解)
需求信息:单纯使用method属性来配置action方法,调用不同的方法,需要配置不同的action,配置较多。
可以采用通配符的方式解决,也可以通过动态方法解决
优点:在配置action时无需配置method
缺点:不安全,不推荐使用,是方法名暴露在url
- sruts.xml增加action
<!-- 开启动态方法调用 -->
<constant name="struts.enable.DynamicMethodInvocation"
value="true"></constant>
<!-- 第三种方式:通过动态方法调用 在默认情况下,动态方法是关闭的,需要手动开启 -->
<action name="userDy" class="struts_01.UserAction">
<result name="success">/loginresult.jsp</result>
<allowed-methods>login</allowed-methods>
</action>
- 测试结果
同样的,也需要添加<allowed-methods>login</allowed-methods>