Struts2学习-1
Strtuts2和struts1简介
Strtuts2和struts1都是遵循MVC的WEB框架
Struts2与Servlet的API是低耦合的。
struts1和WebWork同一时期的,而Struts2是在WebWork这个框架基础上发展起来的,学习Struts2其实就是学习WebWork。
Struts2知识点很零散,难学,但是Struts2强大之处就是在这里,灵活
webwork的包名: com.opensymphony.xwork2 真正的MVC,如下图,(模型(model)-视图(view)-控制器(controller)的缩写)
Struts2原理分析
先写一个过滤器,在过滤器的初始化文件中读取配置文件的信息。
servlet能做的,filter也可以做,servlet做不了的,filter也可以做。
然后写jsp文件,提交表单的时候,把数据封装到对应的javaBean中去。
先写一个配置文件
<?xml version="1.0" encoding="UTF-8"?>
<!-- 核心配置文件 -->
<framework>
<!--
action:
name:必须有,且唯一。如同之前请求的什么operation
class:必须有,要执行哪个JavaBean的类名
method:可以没有。没有的话就是默认值execute。JavaBean中对应的操作方法。该方法的特点是:public String addCustomer(){}
-->
<action name="addCustomer" class="cn.itcast.domain.Customer" method="addCustomer">
<!--
result:代表着一种结果。
type:可以没有,默认是dispatcher。转向目标的类型。dispatcher代表着请求转发
name:必须有。对应的是JavaBean中addCustomer的返回值
主体内容:必须有。目标资源的URI
-->
<result type="redirect" name="success">/success.jsp</result>
<result type="dispatcher" name="error">/error.jsp</result>
</action>
<action name="updateCustomer" class="cn.itcast.domain.Customer" method="updateCustomer">
<result type="dispatcher" name="success">/success.jsp</result>
</action>
<action name="updateStudent" class="cn.itcast.domain.Student">
<result name="success">/studentOk.jsp</result>
<result name="failer">/studentF.jsp</result>
</action>
</framework>
|
再写一个过滤器(取代servlet)
public class CenterFilter implements Filter {
//存取配置文件信息。key:对应action中的name value:Action对象
private Map<String, Action> actions = new HashMap<String, Action>();
private FilterConfig filterConfig;
public void init(FilterConfig filterConfig) throws ServletException {
initCfg();//初始化配置文件
this.filterConfig = filterConfig;
}
//初始化配置文件
private void initCfg() {
//读取XML配置文件:把配置文件中的信息封装到对象中.再放到actions中
Document document = Dom4JUtil.getDocument();
Element root = document.getRootElement();
//得到所有的action元素,创建Action对象,封装信息
List<Element> actionElements = root.elements("action");
if(actionElements!=null&&actionElements.size()>0){
for(Element actionElement:actionElements){
//封装action信息开始
Action action = new Action();
action.setName(actionElement.attributeValue("name"));
action.setClassName(actionElement.attributeValue("class"));
String methodXmlAttrValue = actionElement.attributeValue("method");
if(methodXmlAttrValue!=null)
action.setMethod(methodXmlAttrValue);
//封装action信息结束
//得到每个action元素中的result元素,创建Result对象,封装信息
//封装属于当前action的结果
List<Element> resultElements = actionElement.elements("result");
if(resultElements!=null&&resultElements.size()>0){
for(Element resultElement:resultElements){
Result result = new Result();
result.setName(resultElement.attributeValue("name"));
result.setTargetUri(resultElement.getText().trim());
String typeXmlValue = resultElement.attributeValue("type");
if(typeXmlValue!=null){
result.setType(ResultType.valueOf(typeXmlValue));
}
action.getResults().add(result);
}
}
//封装属于当前action的结果
//把Action对象都放到Map中
actions.put(action.getName(), action);
}
}
}
public void doFilter(ServletRequest req, ServletResponse resp,
FilterChain chain) throws IOException, ServletException {
try {
HttpServletRequest request = (HttpServletRequest)req;
HttpServletResponse response = (HttpServletResponse)resp;
//真正的控制器部分
//取一个配置参数
String aciontPostFixs [] = {"action","","do"};//你请求的地址以action\do\空结尾的话,才真正过滤。默认值
String aciontPostFix = filterConfig.getInitParameter("aciontPostFix");
if(aciontPostFix!=null){
aciontPostFixs = aciontPostFix.split("\\,");
}
//解析用户请求的URI
String uri = request.getRequestURI();// /strutsDay01MyFramework/addCustomer.action
//截取后缀名,看看是否需要我们的框架进行处理
//切后缀名
String extendFileName = uri.substring(uri.lastIndexOf(".")+1);// /strutsDay01MyFramework/addCustomer.action action
// /strutsDay01MyFramework/addCustomer.do do
// /strutsDay01MyFramework/addCustomer ""
boolean needProcess = false;
for(String s:aciontPostFixs){
if(extendFileName.equals(s)){
needProcess = true;
break;
}
}
if(needProcess){
//需要框架处理
//解析uri中的动作名称
String requestActionName = uri.substring(uri.lastIndexOf("/")+1, uri.lastIndexOf("."));
System.out.println("您的请求动作名是:"+requestActionName);
//查找actions中对应的Action对象
if(actions.containsKey(requestActionName)){
Action action = actions.get(requestActionName);
//得到类名称的字节码
Class clazz = Class.forName(action.getClassName());
//封装数据到JavaBean中,利用BeanUtils框架
Object bean = clazz.newInstance();
BeanUtils.populate(bean, request.getParameterMap());
//实例化,调用其中指定的方法名称
Method m = clazz.getMethod(action.getMethod(), null);
//根据方法的返回值,遍历结果
String resultValue = (String)m.invoke(bean, null);
List<Result> results = action.getResults();
if(results!=null&&results.size()>0){
for(Result result:results){
if(resultValue.equals(result.getName())){
//根据结果中的type决定是转发还是重定向
//重定向的目标就是结果中的targetUri
if("dispatcher".equals(result.getType().toString())){
//转发
request.getRequestDispatcher(result.getTargetUri()).forward(request, response);
}
if("redirect".equals(result.getType().toString())){
//重定向
response.sendRedirect(request.getContextPath()+result.getTargetUri());
}
}
}
}
}else{
throw new RuntimeException("The action "+requestActionName+" is not founded in your config files!");
}
}else{
chain.doFilter(request, response);
}
} catch (Exception e) {
throw new RuntimeException(e);
}
}
public void destroy() {
}
}
|
再写相关的JavaBean方法,并把相关的方法也写到JavaBean中去。
struts2的学习和开发环境
参看Struts2自带的例子,搭建环境是一定要参照该例子,以便省去细节的记忆之苦和提高工作效率!!!
在struts-2.3.15.1\apps中寻找例子struts2-blank,这个是空白例子
把例子中的jar包和web.xml文件拷贝进工程
把例子中的struts.xml拷贝到工程中的src中,并进行配置
在web.xml里其实就是加入了如下配置(具体还是要看例子)
<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>
|
在struts2-core-*.jar根目录中有一个Struts2的核心配置文件struts-default.xml
在struts.xml里面进行配置:
package标签:必须直接或间接地继承自struts-default的包。
作用:方便管理我们的动作(struts-default是核心配置文件)
属性:
abstract:可选值为true|false。说明他是一个抽象包。抽象包中没有action元素的。(默认为false)
name:包名。不能重复。方便管理动作的。
namespace:名称空间
extends:继承什么
action标签:
name:必须的,动作名称
<package name="p2" extends="struts-default">
<!-- 只要找不到的action的name,找act4。默认动作名称 -->
<default-action-ref name="act4"></default-action-ref>
</package>
|
class:可选的。默认值是com.opensymphony.xwork2.ActionSupport
<package name="p2" extends="struts-default">
<!-- 只要找不到的action的class,找com.opensymphony.xwork2.ActionSupport。默认class -->
<default-class-ref name="com.opensymphony.xwork2.ActionSupport"></default-class-ref>
</package>
|
method:可选的。默认值是public String execute(){return "success"}
result标签:
type:默认值dispatcher。转发,目标JSP
name:默认值是success。
<package name="default" namespace="/test" extends="struts-default">
<action name="hello" class="com.itheima.action.HelloAction" method="execute">
<result name="female">/female.jsp</result>
<result name="male">/male.jsp</result>
</action>
</package>
访问包中带有名称空间的动作时:
http://localhost:8080/day22_01_strutsHello/test/hello.action
http://localhost:8080/day22_01_strutsHello/test/aaa/bbb/hello.action
动作有搜索顺寻:从/test/aaa/bbb找,不存在
从/test/aaa找,不存在
从/test,找到了
一旦找到就不向上找了
Tips:
namespace="/":确实一个名称空间
namespace="":默认的名称空间。如果访问的uri地址,有对应的名称空间,但是没有对应的动作,直接到默认名称空间中找。
如果访问的uri地址,没有对应的名称空间,安层次一步一步放上找,只要找到一个名称空间对应的,但是没有对应的动作,直接到默认名称空间中找。
全局结果视图和局部结果视图
<package name="base" extends="struts-default">
<!-- 配置局部结果视图 -->
<result-types>
<result-type name="captchaResults" class="com.itheima.action.CaptchaResults"></result-type>
</result-types>
<!-- 配置全局结果视图 -->
<global-results>
<result name="error">/error.jsp</result>
</global-results>
</package>
|
增加配置文件标签提示(注意引入Schema约束会报错)
1,在struts-2.3.15.1\apps\struts2-blank\WEB-INF\lib中找到struts的jar包
2,打开jar包,找到对应的struts-2.3.dtd约束文件,拷贝进工程
3,打开约束文件,找到引用的语句,放进工程的struts.xml中。如:
<!DOCTYPE struts PUBLIC
"-//Apache Software Foundation//DTD Struts Configuration 2.3//EN"
"http://struts.apache.org/dtds/struts-2.3.dtd">
|
4,引入dtd约束文件,在Myeclipse里面进行如下操作
window-->Preferences-->搜索xml-->Catalog-->点add-->
选择dtd文件的地点,选择URI,URI的key值http://struts.apache.org/dtds/struts-2.3.dtd
以上意思是,制定dtd约束文件地址,key是制定dtd文件下载地址,如果找不到,使用本地的。
三、struts2中的结果类型
1、结果类型其实就是一个实现com.opensymphony.xwork2.Result的类,用来输出你想要的结果
2、在struts-default.xml文件中已经提供了内置的几个结果类型
<result-type name="chain" class="com.opensymphony.xwork2.ActionChainResult"/>
转发到另一个动作。
如果转发的动作在一个名称空间中
<action name="testChain1" class="com.itheima.action.CaptchaAction" method="download">
<result name="success" type="chain">testChain2</result>
</action>
<action name="testChain2" class="com.itheima.action.CaptchaAction" method="download">
<result name="success" type="dispatcher">/2.jsp<result>
</action>
|
如果转发的动作不在一个名称空间中
<action name="testChain1" class="com.itheima.action.CaptchaAction" method="download">
<result name="success" type="chain">
<!-- 如果需要转发的动作不在一个名称空间内,则需要进行参数的设置(原理看chain源码) -->
<!-- 源码中有setNamespace和setActionName方法,去掉set,第一个字母改小写 -->
<param name="namespace">/result1</param>
<param name="actionName">testChain2</param>
</result>
</action>
</package>
<package name="p2" namespace="/result1" extends="base">
<action name="testChain2" class="com.itheima.action.CaptchaAction" method="download">
<result name="success" type="dispatcher">/2.jsp<result>
</action>
</package>
|
<result-type name="dispatcher" class="org.apache.struts2.dispatcher.ServletDispatcherResult" default="true"/>(默认的)
请求转发(地址栏不会变)。struts配置
<action name="testChain2" class="com.itheima.action.CaptchaAction" method="download">
<result name="success" type="dispatcher">/2.jsp<result>
</action>
|
以下两个设置效果相同
<action name="testRedirect2" class="com.itheima.action.CaptchaAction" method="download">
<result name="success" type="dispatcher">
<param name="location">/2.jsp<result>
</result>
<!--
<result name="success" type="dispatcher">/2.jsp</result>
-->
|
<result-type name="httpheader" class="org.apache.struts2.dispatcher.HttpHeaderResult"/>
<result-type name="redirectAction" class="org.apache.struts2.dispatcher.ServletActionRedirectResult"/>
请求重定向到另一个动作
<action name="testRedirect1" class="com.itheima.action.CaptchaAction" method="download">
<result name="success" type="redirectAction">testRedirect2</result>
</action>
|
<result-type name="redirect" class="org.apache.struts2.dispatcher.ServletRedirectResult"/>
请求重定向(地址栏会变)。struts.xml配置
<action name="testRedirect" class="com.itheima.action.CaptchaAction" method="download">
<result name="success" type="redirect">/2.jsp<result>
</action>
|
<result-type name="stream" class="org.apache.struts2.dispatcher.StreamResult"/>
结果类型为流,例如用于文件下载(具体原理需要看源码,而源码的核心就是execute方法)
struts.xml的配置(属性参数对应的都是类中的set方法)
<action name="testStream" class="com.itheima.action.CaptchaAction" method="download">
<!-- 不需要转向或重定向的页面,因为直接下载就可以了 -->
<result name="success" type="stream"><!-- 在文档中拷贝以下参数 -->
<!-- 为了能让所有类型的文件都能下载,在Tomcat/conf/web.xml里面搜索bin,找到以下mapping参数 -->
<param name="contentType">application/octet-stream</param>
<!-- 查看Stream对应的类源代码,对应里面的字符串变量,根据这个字符串找输入流-->
<param name="inputName">imageStream</param>
<!-- 消息头的设置,指定下载,且指定下载文件名称 -->
<param name="contentDisposition">attachment;filename="1.jpg"</param>
<!-- 设置缓存的大小 -->
<param name="bufferSize">1024</param>
</result>
</action>
|
按照配置文件,写一个类(先写配置文件思路清晰)
public class CaptchaAction {
//设置一个流,并生成set,get方法。
private InputStream imageStream;
public InputStream getImageStream() {
return imageStream;
}
public void setImageStream(InputStream imageStream) {
this.imageStream = imageStream;
}
public String download() throws FileNotFoundException{
//获得文件的真实路径
String realPath = ServletActionContext.getServletContext().getRealPath("/WEB-INF/111.jpg");
//获得文件输入流
imageStream = new FileInputStream(realPath);
return "success";
}
public String method1(){
try{
//int i=1/0;//人为制造异常,可以让catch转向全局结果集,error.jsp
return "success";
}catch(Exception e){
return "error";
}
}
}
|
<result-type name="freemarker" class="org.apache.struts2.views.freemarker.FreemarkerResult"/>显示模板。。。需要实验。
<result-type name="velocity" class="org.apache.struts2.dispatcher.VelocityResult"/>显示模板。。。需要实验。
<result-type name="xslt" class="org.apache.struts2.views.xslt.XSLTResult"/>(显示样式?)
<result-type name="plainText" class="org.apache.struts2.dispatcher.PlainTextResult" />
显示指定页面的源代码(不好用,实验的时候,不必没有jsp语句。只有java语句才能显示源代码)
</action>
<action name="testPlanText" class="com.itheima.action.CaptchaAction" method="showPlanText">
<result name="success" type="plainText">/1.jsp<result>
</action>
|
3、如果提供的结果类型不够用,就需要自定义了(注意需要实现Result接口)
自定义完的结果类型,需要先声明,才能使用:(随机验证码图片结果类型,实际应用的案例)
<package name="base" extends="struts-default">
<!-- 配置局部结果视图 -->
<result-types>
<result-type name="captchaResults" class="com.itheima.action.CaptchaResults"></result-type>
</result-types>
<!-- 配置全局结果视图 -->
<global-results>
<result name="error">/error.jsp</result>
</global-results>
</package>
<!-- 需继承base,因为自定义的局部结果视图配置在base里面,且base继承了核心配置文件 -->
<package name="p1" namespace="/results" extends="base">
<action name="captcha" class="com.itheima.action.CaptchaAction" method="genImage">
<result type="captchaResults" name="success">
<!-- 调用结果处理类的setter方法,注入参数的值-->
<param name="width">600</param>
<param name="height">400</param>
</result>
</action>
</package>
|
4、<result>标签中的name与动作类中的动作方法的返回值对应
四、struts中存在一些内置常量
在struts2-core-*.jar的org.apache.struts2的default.properties文件中存在一些内置常量
常用的内置常量设置
<!-- 针对post请求参数编码有效 -->
<constant name="struts.i18n.encoding" value="UTF-8"></constant>
<!-- 配置需要struts框架处理的uri的扩展名 -->
<constant name="struts.action.extension" value="do,,action"></constant>
<!-- 开发模式:打印更多的异常信息。配置文件会自动加载 -->
<!-- devMode模式是开发模式,开启它则默认开启了struts.i18n.reload、struts.configuration.xml.reload -->
<constant name="struts.devMode" value="true"></constant>
<!-- 配置不支持动态方法调用 -->
<constant name="struts.enable.DynamicMethodInvocation" value="false"></constant>
<!-- 让struts重新加载配置文件,但不会导致web应用重新启动。 -->
<constant name="struts.configuration.xml.reload" value="false"></constant>
<!-- 指定每次请求到达,重新加载资源文件 -->
<constant name="struts.i18n.reload" value="true"/>
<!-- 表达式直接访问static静态方法的开关 -->
<constant name="struts.ognl.allowStaticMethodAccess" value="true"></constant>
<!-- 配置全局国际化消息资源包,value写资源包的基名,多个资源包之间用逗号,分隔-->
<constant name="struts.custom.i18n.resources" value="com.itheima.resources.msg"></constant>
<!-- 更改strutsUI标签的显示样式模板,参考struts2-core-*.jar中的template -->
<constant name="struts.ui.theme" value="xhtml"></constant>
<!-- 动作名字里面默认是不允许出现/的,以下常量设置可以出现/ -->
<constant name="struts.enable.SlashesInActionNames" value="true"></constant>
<!-- 动作名字里面默认是不允许出现/的,如果有名称空间,除了以上常量,还需要打开这个开关 -->
<constant name="struts.mapper.alwaysSelectFullNamespace" value="true"></constant>
|
常量可以在下面多个文件中进行定义,struts2加载常量的搜索顺序如下,后面的设置可以覆盖前面的设置:
default.properties文件
struts-default.xml
struts-plugin.xml
struts.xml
struts.properties(为了与webwork向后兼容而提供)
web.xml
包含配置(<include>):在struts.xml文件这,使用<include>属性来包含其他配置文件,需要放在<struts>下,<package>外。
<include file="struts-mobile.xml"></include> |
五、动态方法调用和*通配符
如果Action中存在多个方法时,在struts.xml文件中可以只配置一个<action>元素,访问路径可以在紧跟action名称的后面增加一个感叹号(!)后接着指定要访问的方法名,这就是动态方法调用,如下:
<package name="p1" extends="struts-default">
<action name="act1" class="com.itheima.action.ActionDemo1"></action>
</package>
|
以下地址都可以访问成功:
http://localhost:8080/day22_04_strutsDI/act1!m1.action
http://localhost:8080/day22_04_strutsDI/act1!m2.action
尽量不要使用动态方法调用,因为不安全,会暴露方法。可以采用*通配符的方式进行替代。
在action元素的name属性中可以使用*通配符,它可以匹配除了/以外的多个连续字符,例如:
<action name="*_*" class="com.itheima.action.{1}Action" method="{2}">
<result>/{2}{1}.jsp<result>
</action>
|