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>

猜你喜欢

转载自blog.csdn.net/qq_34801169/article/details/80673050