Struts2核心技術 (二)

struts2中的參數封裝

靜態參數封裝

什麼是靜態參數? 
靜態參數就是硬編碼的,不可隨意改變。

例子

        我們首先創建一個Action類,裏面有兩個參數,用來封裝請求參數

        public class User extends ActionSupport {
            private String username;    //用戶名
            private int age;           //年齡

            public String getUsername() {
                return username;
            }

            public void setUsername(String username) {
                this.username = username;
            }

            public int getAge() {
                return age;
            }

            public void setAge(int age) {
                this.age = age;
            }

            public String adduser(){ 
                System.out.println(username+":"+age);
                return null;
            }
        } 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
        我們在struts.xml中配置靜態參數  

            <package name="p1" extends="struts-default">
                <action name="action1" class="com.cad.struts2.User"  method="adduser"> 
                    <!--通過注入的方式封裝靜態參數-->
                    <param name="username">張三</param>
                    <param name="age">18</param>
                </action>
            </package>  
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
    我們訪問action1動作時,會輸出 張三:18  
  • 1
  • 2

我們配置的靜態參數怎麼就被傳遞到動作類裏呢?

        我們在struts2的運行流程中提到過,我們動作類的動作方法執行之前,會有一系列的攔截器幫我們執行一些操作。 

        在struts-default.xml中,有很多攔截器,攔截器又分爲很多攔截器棧,我們默認是使用defaultStack的攔截器棧。  

        這個攔截器棧中有一個叫做staticParams的攔截器幫我們完成靜態參數的封裝,將配置的靜態方法通過action類中的set方法設置進去。 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

動態參數封裝

什麼是動態參數?

像我們用表單提交的數據,就是動態數據。數據是可變的。 
  • 1
  • 2

例子:

        第一步:我們先創建一個添加用戶的表單 

        <form action="${pageContext.request.contextPath }/action1" method="post">
            用戶名:<input type="text" name="username"><br>
            年 齡:<input type="text" name="age"><br>
            <input type="submit" value="提交">
        </form> 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
        第一種方式:數據參數和動作類寫在一起 

        要求是表單中的name必須和我們類中的set方法後面的名稱一致,例如表單中的username對應類中的setUsername,和參數並沒有太大關係,和set方法有關係。
                public class User extends ActionSupport {
                    private String username;
                    private int age;

                    public String getUsername() {
                        return username;
                    }

                    public void setUsername(String username) {
                        this.username = username;
                    }

                    public int getAge() {
                        return age;
                    }

                    public void setAge(int age) {
                        this.age = age;
                    }

                    public String adduser(){ 
                        System.out.println(username+":"+age);
                        return null;
                    }
                }  
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
        配置struts.xml文件 
        <package name="p1" extends="struts-default">

            <action name="action1" class="com.cad.struts2.User"  method="adduser">

            </action>
        </package>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

我們訪問添加用戶頁面,添加數據,會輸出我們添加的數據。

這一系列操作是由攔截器棧中的名爲params的攔截器幫我們完成的

上面這種方式將參數和動作類寫在一起,看起來太過混亂,我們能不能用一個JavaBean將參數給封裝起來。 答案是能。

    第二種方式,將參數數據和動作類分開寫  

                第一步,我們創建一個javaBean,User類,用來封裝請求參數 
                public class User  implements Serializable{
                    private String username;
                    private int age;

                    public String getUsername() {
                        return username;
                    }

                    public void setUsername(String username) {
                        this.username = username;
                    }

                    public int getAge() {
                        return age;
                    }

                    public void setAge(int age) {
                        this.age = age;
                    }
                } 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
                第二步:創建一個添加用戶的動作類,裏面有一個user對象 
                public class AdduserAction extends ActionSupport {
                    private User user;

                    public User getUser() {
                        return user;
                    }

                    public void setUser(User user) {
                        this.user = user;
                    }
                    public String adduser(){
                        System.out.println(user.getUsername()+":"+user.getAge());
                        return null;
                    }

                } 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
        第三步:我們得改變一些我們的jsp頁面的寫法,因爲表單的name要和類中的數據相對應,但類中只有一個user ,username去找動作類中的setUsername,肯定找不到。

            我們這樣寫,他就會先找類中的user對象,然後去找user對象的username和age
                <body>
                    <form action="${pageContext.request.contextPath }/action1" method="post">
                        用戶名:<input type="text" name="user.username"><br>
                        年 齡:<input type="text" name="user.age"><br>
                        <input type="submit" value="提交">
                    </form>
                </body>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

原理:

這一系列操作還是攔截器params幫我們完成。我們在getUser和setUser裏打印兩句話來看看他到底是怎麼執行的
                public User getUser() {
                    System.out.println("getuser");
                    return user;
                }

                public void setUser(User user) { 
                    System.out.println("setuser");
                    this.user = user;
                } 

                輸出
                getuser   :先判斷對象是否存在
                setuser   :如果判斷不存在,調用set方法,通過反射創建一個對象,並且設置給該類   
                getuser   :然後再獲取該對象,調用該對象的getset方法對參數賦值 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

模型驅動和屬性驅動

出於結構清晰的考慮,應該採用單獨的Model實例來封裝請求參數和處理結果,這就是所謂的模型驅動, 

 所謂模型驅動,就是使用單獨的JavaBean來貫穿整個MVC流程。 

 所謂屬性驅動,就是使用屬性來作爲貫穿MVC流程的信息攜帶者,當然屬性必須依附於對象,這個對象就是Action實例。 

 簡單說,模型驅動就是使用單獨的javaBean封裝請求參數。
        屬性驅動就是把屬性寫在Action類中。 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

我們發現上面的jsp中的name必須前面得加 user.username。。太過麻煩。我們使用模型驅動來解決這個問題。實際開發中使用這種方式

        模型驅動的要求:
              1.動作類實現ModelDriven接口
              2.實現接口中的getModel方法,返回我們的數據對象 
              3.數據模型對象必須由我們實例化。 

              我們改進動作類 

                public class AdduserAction extends ActionSupport implements ModelDriven<User>{
                    //數據模型對象由我們實例化
                    private User user=new User();

                    public User getUser() {
                        System.out.println("getuser");
                        return user;
                    }

                    public void setUser(User user) { 
                        System.out.println("setuser");
                        this.user = user;
                    }
                    public String adduser(){ 
                        System.out.println(user.getUsername()+":"+user.getAge());
                        return null;
                    }
                    //實現接口方法,返回我們的數據模型對象
                    public User getModel() {
                        // TODO Auto-generated method stub
                        return user;
                    }

                } 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
        我們在jsp上就能像以前那樣 ,name只用我們的參數相同即可 

        <body>
            <form action="${pageContext.request.contextPath }/action1" method="post">
                用戶名:<input type="text" name="username"><br>
                年 齡:<input type="text" name="age"><br>
                <input type="submit" value="提交">
            </form>
        </body> 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

原理:

    其實這時候表單的name已經不僅僅是一個簡單的字符串了。 

    這一系列的操作是由ModelDriven和params攔截器幫我們做的 。 

    params攔截器負責提取請求參數,如果是屬性驅動模式,則還負責將請求參數傳給Action類的屬性 

    模型驅動的話就只提取請求參數。 

    ModelDriven攔截器會先判斷我們的動作類是否屬於ModelDriven類型  

    屬於的話,就調用我們實現的getModel方法,獲取我們傳入的對象 

    然後將我們的對象給壓入棧中  
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

struts2註冊案例

    第一步:我們先創建數據庫表 

        create database demo;
        use demo;

        create table user(
            username varchar(100) primary key,
            password varchar(100),
            birthday date,
            hobby varchar(255),
            married boolean
        ); 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
    第二步:在domain包創建我們的user實體類 

        public class User {
            private String username;//用戶名
            private String password;//密碼
            private Date birthday; //生日
            private String hobby; //愛好
            private boolean married; //是否結婚
            public String getUsername() {
                return username;
            }
            public void setUsername(String username) {
                this.username = username;
            }
            public String getPassword() {
                return password;
            }
            public void setPassword(String password) {
                this.password = password;
            }
            public Date getBirthday() {
                return birthday;
            }
            public void setBirthday(Date birthday) {
                this.birthday = birthday;
            }
            public String getHobby() {
                return hobby;
            }
            public void setHobby(String hobby) {
                this.hobby = hobby;
            }
            public boolean isMarried() {
                return married;
            }
            public void setMarried(boolean married) {
                this.married = married;
            }

        }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
        第三步:完成數據層,我們在Dao層處理數據  

        public class UserDao {
            QueryRunner qr=new QueryRunner(JdbcUtils.getDataSource()); 

            //根據名字查找用戶
            public User findUserByUsername(String username){
                try {
                    String sql="select * from user where username=?";
                    return qr.query(sql, new BeanHandler<User>(User.class),username);
                } catch (Exception e) {
                    throw new RuntimeException(e);
                }
            }

            //添加用戶
            public int addUser(User user){
                String sql="insert into user values(?,?,?,?,?)";
                Object []params={user.getUsername(),user.getPassword(),user.getBirthday(),user.getHobby(),user.isMarried()};
                try {
                    return qr.update(sql,params);
                } catch (SQLException e) {
                    throw new RuntimeException(e);
                }
            }

        } 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
        第四步:完成業務邏輯層的編寫,service層 

        public class UserService {
            private UserDao userdao=new UserDao(); 
            //註冊
            public int regist(User user){
                return userdao.addUser(user);
            }
            //通過用戶名查找用戶
            public User findByUsername(String username){
                return userdao.findUserByUsername(username);
            }
        } 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
    第五步:創建web層,我們創建web層動作類  

    public class UserAction extends ActionSupport implements ModelDriven{
        private User user=new User();
        private UserService userservice=new UserService();  

        //註冊方法
        public String regist(){ 
            User _user=userservice.findByUsername(user.getUsername());  
            //判斷用戶是否存在,存在返回exists字符串
            if(_user!=null){
                return "exists";
            } 
            //獲取註冊成功更新的行數 
            int count=userservice.regist(user); 
            //如果>0,返回success 
            if(count>0){
            return SUCCESS;
            }
            return null;
        }

        public Object getModel() {
            // TODO Auto-generated method stub
            return user;
        }

        public User getUser() {
            return user;
        }

        public void setUser(User user) {
            this.user = user;
        }

    } 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
    第六步:我們編寫註冊的jsp頁面 

    <body>
        <form action="${pageContext.request.contextPath }/regist.action" method="post">
            用戶名:<input type="text" name="username"><br>
            密   碼:<input type="password" name="password"><br>
            生   日:<input type="text" name="birthday"><br>
            愛好:<input type="checkbox" name="hobby" value="籃球">籃球
                <input type="checkbox" name="hobby" value="足球">足球
                <input type="checkbox" name="hobby" value="寫代碼">寫代碼<br>
            已婚:<input type="checkbox" name="married" value="true"><br>
            <input type="submit" name="註冊"><br>
        </form>

    </body> 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
        第七步:配置 struts.xml 

        <package name="p1" extends="struts-default">

            <action name="regist" class="com.cad.web.action.UserAction"  method="regist">
                <result name="success">/success.jsp</result>
                <result name="exists">/msg.jsp</result>
            </action>
        </package> 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

struts2的異常處理機制

成熟的MVC框架應該提供成熟的異常處理機制。當然可以在方法中手動捕捉異常,當捕捉到特定異常時,返回特定邏輯視圖名。

這種方式非常繁瑣,需要在方法中寫大量try catch塊,最大的缺點還是一旦需要改變異常處理方法時,需要修改代碼。

最好的方式是通過聲明式的方式管理異常處理。struts2提供了一種聲明式的異常處理方式。

struts2的異常處理

    我們看Action接口中的execute方法聲明。
    public String execute() throws Exception 

    這就意味着我們重寫該方法時,無需進行任何異常處理,而是把異常拋給struts2框架處理. 

    struts2框架接收到Action拋出的異常後,根據struts.xml文件配置的異常映射,轉入指定的視圖資源。

    異常映射功能是由 exception的攔截器幫我們做的。

    struts2的異常處理機制是通過在struts.xml中配置<exception-mapping..../>元素完成。
        屬性:
            exception:異常類型
            result:出現該異常時,系統轉入result指定的結果 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

局部異常映射和全局異常映射

全局異常映射對所有的Action都有效,但局部異常映射只對該異常映射所在的Action有效。 
如果全局異常映射和局部異常映射配置了同一個異常類型,在該Action內,局部覆蓋全局。

            局部異常映射:將<exception-mapping..../>元素作爲<action.../>的子元素配置 

            全局異常映射:將<exception-mapping..../>元素作爲<global-exception-mappings>元素的子元素配置  
  • 1
  • 2
  • 3
  • 4

異常處理案例

        我們做一個簡單的登陸應用 

        第一步:我們編寫我們的Action類 

        public class LoginAction implements Action{
            private String username;
            private String password;
            public String getUsername() {
                return username;
            }
            public void setUsername(String username) {
                this.username = username;
            }
            public String getPassword() {
                return password;
            }
            public void setPassword(String password) {
                this.password = password;
            }
            public String execute()throws Exception{ 
                //當用戶名爲monster時,拋出我們的自定義異常
                if(getUsername().equals("monster")){
                    throw new MyException("自定義異常");
                } 
                //當用戶名爲sql時,拋出sql異常
                if(getUsername().equalsIgnoreCase("sql")){
                    throw new SQLException("用戶名不能爲sql");
                }
                if(getUsername().equals("cad")&&getPassword().equals("123456")){
                    return "success";
                }else
                {
                    return "error";
                }
            }

        } 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
        第二步:我們編寫我們的登陸jsp頁面  

        <body>
            <form action="${pageContext.request.contextPath }/login" method="post">
                用戶名:<input type="text" name="username"><br>
                密  碼:<input type="password" name="password"><br>
                <input type="submit" value="提交">
            </form>
        </body> 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
    第三步:我們編寫我們的配置文件struts.xml  

    <package name="p2" extends="struts-default">
        //全局結果視圖定義
        <global-results>
            <result name="sql">/exception.jsp</result>
        </global-results>

        //全局異常定義
        <global-exception-mappings>
            //出現sql異常,就轉入到視圖名爲sql的視圖結果
            <exception-mapping result="sql" exception="java.sql.Exception"></exception-mapping>
        </global-exception-mappings> 

        <action name="login" class="com.cad.struts2.LoginAction" > 
                //局部異常定義
                <exception-mapping result="my" exception="conm.cad.struts2.MyException"></exception-mapping>
                <result name="my">/exception.jsp</result>
                <result name="success">/welcome.jsp</result>
                <result name="error">/error.jsp</result>    
        </action>
    </package>  
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22

struts2的未知處理器

從struts2.1開始,struts2增加了未知處理器。  

當用戶請求未知Action,或指定Action裏的未知方法,或Action處理結束後返回一個未知的result。struts2允許使用未知處理器來處理這些情況。

未知處理器需要實現UnknownHandler接口,該接口包含三個方法 

-handleUnknownAction:用戶請求未知Action時,該方法會被回調
-handleUnknownActionMethod:用戶請求指定Action的未知方法時,該方法會被回調
-handleUnknownResult:Action處理結束後返回一個未知Result時,該方法會被回調 

一旦實現了自定義的未知處理器,就可以在struts.xml中通過<bean../>元素來配置未知處理器
例如 
        <bean name="myHandler" class="com.cad.struts2.MyUnknownHandler" type="com.opensymphony.xwork2.UnknownHandler"></bean> 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

未知處理器案例

        第一步:我們實現我們簡單的未知處理器

            public class MyUnknownHandler implements UnknownHandler {

                public ActionConfig handleUnknownAction(String namespace, String actionName) throws XWorkException {
                    System.out.println(actionName+"不存在");
                    return null;
                }


                public Result handleUnknownResult(ActionContext actionContext, String actionName, ActionConfig actionConfig,
                        String resultCode) throws XWorkException {
                    System.out.println(resultCode+"不存在");
                    return null;
                }


                public Object handleUnknownActionMethod(Object action, String methodName) throws NoSuchMethodException {
                    System.out.println(methodName+"不存在");
                    return null;
                }

            } 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
            第二步:在struts.xml中配置未知處理器 

            //定義我們的未知處理器
            <bean name="myHandler" class="com.cad.struts2.MyUnknownHandler" type="com.opensymphony.xwork2.UnknownHandler"></bean>

            <package name="p3" extends="struts-default">
                <action name="myAction"></action>
            </package> 
            //定義未知處理器棧
            <unknown-handler-stack>
                <unknown-handler-ref name="myHandler"></unknown-handler-ref>
            </unknown-handler-stack> 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
然後我們訪問myAction,會輸出success不存在。
是因爲不指定action的class屬性時,默認使用ActioSupport
一般未知處理器主要處理Action返回未知result時有用。
瞭解即可。 
  • 1
  • 2
  • 3
  • 4
  • 5

struts2的類型轉換

所有的MVC框架,都屬於表現層的解決方案,都需要負責收集用戶請求參數,並將請求參數傳給應用的控制器組件。

這時問題出現了,所有的請求參數都是字符串類型數據,因此MVC框架必須具備將這些字符串請求參數轉換成相應的數據類型。

struts2提供了非常強大的類型轉換機制,struts2的類型轉換是基於OGNL表達式

struts2提供了很好的擴展性,開發者可以開發出自己的類型轉換器。完成字符串到自定義類型之間的轉換。

如果類型轉換中出現未知異常,開發者無須關心異常處理,struts2的conversionError攔截器會自動處理該異常,並且在頁面上生成提示信息。

servlet中的類型轉換

表單中提交的所有數據都是字符串類型

例如我們有一個User類,name爲String類型,age爲int類型,birthday爲Date類型,我們必須在servlet中獲取表單傳入的參數,然後將其進行類型轉換,然後封裝到User對象中。

上述需要程序員自己進行類型轉換操作,比較繁瑣。

對於一個MVC框架而言,一樣需要將請求參數封裝成對象,也必須將請求參數轉換成對象屬性的數據類型,這就是類型轉換的意義。

Struts2內建的類型轉換器

struts內建的類型轉換器能自動將我們的表單數據(字符串)轉換成對應的數據類型。

完成字符串和日期類型之間的轉換時,日期格式必須使用請求用戶本地的格式。一般是yyyy-MM-dd,如果輸入的日期格式不是本地的日期格式,例如我們輸入1996/01/31,就會出現錯誤,類型轉換失敗。

自定義類型轉換器

需求:

當我們在表單中輸入的日期格式不是本地的格式時,就會出現類型轉換錯誤,我們也經常需要將字符串轉換成其他的格式,例如字符串轉換成對象之類的操作,這時我們就需要自定義類型轉換器。 

    struts2的類型轉換器實際上是基於OGNL實現的。xwork集成了OGNL。 

    實現類型轉換器必須實現TypeConverter接口。這個接口的方法太過複雜,所以還提供了一個該接口的實現類DefaultTypeConverter。 

    我們重寫DefaultTypeConverter類的convertValue方法即可。 

    我們基於DefaultTypeConverter類實現類型轉換器時,將字符串轉換成我們需要的類型通過convertValue方法實現,將我們的類型轉換成字符串也是通過convertValue方法實現,因此我們必須判斷轉換的類型來實現不同的邏輯。 

    爲了簡化類型轉換器的實現,struts2提供了一個StrutsTypeConverter抽象類,這個類是DefaultTypeConverter類的子類。

    我們看下這個類的源碼 :
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
    繼承DefaultTypeConverter
public abstract class StrutsTypeConverter extends DefaultTypeConverter { 

        //重寫DefaultTypeConverter類的convertValue方法
        public Object convertValue(Map context, Object o, Class toClass) { 
            //如果要轉換的類型是字符串類型,也就是把我們的類型轉換成字符串,調用convertToString方法
            if (toClass.equals(String.class)) {
                return convertToString(context, o);
            }
            //如果參數是字符串數組,也就是將字符串轉換成我們需要的類型,調用convertFromString方法        
            else if (o instanceof String[]) {
                return convertFromString(context, (String[]) o, toClass);
            } 
            //如果參數是字符串,也就是將字符串轉換成我們需要的類型,調用convertFromString方法
            else if (o instanceof String) {
                return convertFromString(context, new String[]{(String) o}, toClass);
            } else {
                return performFallbackConversion(context, o, toClass);
            }
        }


        protected Object performFallbackConversion(Map context, Object o, Class toClass) {
            return super.convertValue(context, o, toClass);
        }

        //將字符串轉換成我們需要的類型的方法
        public abstract Object convertFromString(Map context, String[] values, Class toClass);

        //將我們的類型轉換成字符串的方法
        public abstract String convertToString(Map context, Object o);
    } 

            三個參數 :
                    Map context:OGNL的上下文。暫時還沒學,後面會學到,暫時不用管。
                    value:需要轉換的參數。
                    toClass:轉換後的類型
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37

自定義類型轉換器案例

        需求:我們將我們前面的註冊案例中的生日改成  yyyy/MM/dd類型  

        第一步:創建自定義類型轉換器 

        public class MyConverter extends StrutsTypeConverter { 
                    //日期轉換器,轉換成指定的類型
                    private DateFormat format=new SimpleDateFormat("yyyy/MM/dd");

                    //將字符串轉換成日期類型
                    public Object convertFromString(Map context, String[] values, Class toClass) { 
                        //判斷參數是否爲空
                        if(values==null||values.length==0){
                            return null;
                        } 
                        //我們只有一個參數,就是表單的birthday
                        String date=values[0]; 
                        //判斷目標類型是否是Date
                        if(toClass==java.util.Date.class){
                            try { 
                                //進行轉換
                                return format.parse(date);
                            } catch (ParseException e) {
                                e.printStackTrace();
                            }
                        }
                        return null;    
                    }


                    //將日期類型轉換成字符串
                    public String convertToString(Map context, Object o) { 
                        //判斷當前參數是否是日期類型
                        if(o instanceof java.util.Date){
                            return format.format(o);
                        }
                        return null;
                    }

                }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
第二步:註冊類型轉換器

局部類型轉換器
        按照屬性來註冊
        如果屬性都在action中,那麼應該創建一個文件 Action名字-conversion.properties ,例如LoginAction-conversion.properties 
        如果屬性放到了javaBean中,那麼創建一個文件 javaBean名稱-conversion.properties 例如  User-conversion.properties

        文件由鍵值對組成。
        鍵爲需要轉換的屬性名字,值爲自己實現的類型轉換器全類名。

        我們創建 User-conversion.properties 

        內容 birthday=com.cad.web.convert.MyConverter 

        這時我們註冊時使用 1996/01/24這種格式進行註冊就不會出現類型轉換錯誤。
        用戶提交請求時,請求中的birthday參數會先被該類型轉換器處理。

全局類型轉換器
        所有的Action都能用。我們需要在src目錄下創建一個 xwork-conversion.properties 的文件

        因爲是全局的,就不存在只對birthday這個屬性進行轉換。
        這裏的鍵是要轉換的類型,值還是類型轉換器類。

        我們創建 xwork-conversion.properties 
        內容 java.util.Date=com.cad.web.convert.MyConverter 
        這樣當我們輸入日期的表單時,就可以使用我們自定義的日期格式。 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27

類型轉換中的錯誤處理

struts2提供了一個名爲conversionError的攔截器,這個攔截器被註冊在默認攔截器棧中。

當類型轉換器執行類型轉換出現錯誤時,該攔截器負責將對應錯誤封裝成表單域錯誤(fieldError),並將錯誤信息放入ActionContext中。

當攔截器對轉換異常進行處理後,系統會跳轉到名爲input的邏輯視圖。

            我們在struts.xml中配置 <result name="input">/regist.jsp</result> 
            當類型轉換失敗後,再跳轉到註冊頁面

            跳轉到input視圖以後,我們發現沒有任何錯誤提示信息。我們前面講過conversionError攔截器會將轉換錯誤封裝成fieldError,並放在ActionContext中。
            爲了在頁面中輸出錯誤信息,我們需要使用struts的標籤。我們先使用一些,後面會詳細介紹。

            我們在頁面添加<s:fielderror></s:fielderror>標籤
            當我們類型轉換失敗後,就會輸出錯誤信息。

            我們發現輸出的錯誤信息是英文的,我們希望能變爲中文的提示信息。

            我們只需要在創建一個properties文件  文件名爲  javabean名稱.properties  

            鍵爲invalid.fieldvalue.屬性名稱  例如 :invalid.fieldvalue.birthday 
            值爲要輸出的內容 例如 invalid.fieldvalue.birthday=生日格式不正確  
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

struts2的輸入校驗

輸入校驗是web應用必須處理的問題,要防止用戶的誤輸入和惡意非法輸入。struts2給我們提供了非常強大的輸入校驗體系。 

輸入校驗分爲客戶端校驗和服務器端校驗。一般開發中兩者都用,但是服務端校驗必須使用。 

客戶端校驗是通過javascript在表單頁面進行初步過濾。客戶端校驗並不安全,攻擊者有很多方法可以繞過客戶端校驗,所以服務端校驗是必不可少的。 

但是客戶端校驗必不可少,因爲大多數瀏覽者都是正常用戶,可以阻止一些誤操作,降低了服務器的負載。 

服務端校驗:
            我們以前在servlet中增加校驗。是通過獲取參數,然後判斷參數是否爲空和長度等等來進行校驗。
            在servlet中使用硬編碼進行輸入校驗乏味而又繁瑣,struts2提供了基於校驗框架的輸入校驗,只需要指定簡單的配置文件即可。 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

聲明式驗證:通過xml配置文件進行驗證

-校驗規則文件與Action類放在同一目錄下 
-校驗配置文件名稱爲 ActionClassName-validation.xml  例如 :UserAction-validation.xml 

增加校驗文件後,系統會自動加載該文件。當用戶提交請求時,struts2會根據該文件對用戶數據進行校驗 
  • 1
  • 2
  • 3
  • 4
  • 5

基於表單字段的配置風格

<?xml version="1.0" encoding="UTF-8"?>
    <!DOCTYPE validators PUBLIC
            "-//Apache Struts//XWork Validator 1.0.3//EN"
            "http://struts.apache.org/dtds/xwork-validator-1.0.3.dtd"> 
    //校驗文件的根元素      
    <validators>
        //被校驗表單項名稱
        <field name="username"> 
            //指定校驗器,struts2有很多內建校驗器,requiredstring是校驗數據是否爲空並去除兩端空格
            <field-validator type="requiredstring"> 
                //校驗失敗後的提示信息
                <message>用戶名不能爲空</message>
            </field-validator>
        </field>
    </validators>   
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
校驗失敗後,struts2會返回名爲input的邏輯視圖名,因此我們還需要添加一個<result>   

這個配置文件是全局驗證。也就是這個Action中的所有動作都會被驗證,我們Action有的方法並不需要驗證,加入驗證甚至會出錯。 

    我們有兩種方式可以讓不需要驗證的方法跳過驗證: 

    第一種:在不需要驗證的方法前面添加註解@SkipValidation  

    第二種:針對動作類中的某個方法進行驗證,創建的XML文件名爲 ActionClassName-ActionName-validation.xml ,這裏的ActionName不是方法名,而是配置的action名字 ,例如 :UserAction-regist-validation.xml 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

非字段配置風格(基於驗證器的配置風格)

<validators> 
                //驗證器類型
                <validator type="requiredstring">
                    //要校驗的參數名字
                    <param name="fieldName">password</param>
                    //校驗失敗後返回的信息
                    <message>密碼不能爲空</message>
                </validator>
            </validators>   
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

短路校驗器

校驗配置文件的<field-validator>和<validator>元素可以指定一個可選的short-circuit屬性,指定該校驗器是否是短路校驗器,默認是false。 

    短路校驗器的作用是如果一個字段內有多個校驗器,如果一個校驗器校驗失敗,其他校驗器根本不會繼續校驗。

    校驗器的執行順序
        -所有基於驗證器的配置風格的校驗器優先於字段風格的校驗器
        -所有的基於驗證器風格的校驗器,排在前面的先執行 
        -所有的基於字段風格的校驗器,排在前面的先執行。 

    校驗器的短路原則
        -所有的非字段校驗器最先執行,如果某個非字段校驗器校驗失敗,則該字段上的所有字段校驗器都不會執行
        -非字段校驗器校驗失敗,不會阻止其他非字段校驗器的執行
        -如果某個字段校驗器校驗失敗,則該字段下的後面的字段校驗器都不會執行
        -字段校驗器永遠都不會阻止非字段校驗器的執行 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

struts2的內建校驗器

            required:必填驗證器,要求指定的字段必須有值。使用非字段風格的配置時,可以配置fieldName屬性來設置要校驗的表單項名稱。 

            requiredstring:必填字符串驗證器。 

            int、long、short:整數校驗器。要求字段的整數值必須在指定範圍內。參數:min指定該屬性最小值,不指定不檢查最小值。max指定該屬性最大值,不指定不檢查最大值。 

            date:日期校驗器。要求字段的日期值必須在指定範圍內。參數:min最小日期 ,max最大日期 

            expression:表達式校驗器,它只能被非字段風格配置。參數:expression指定一個邏輯表達式。 

            fieldexpression:字段表達式校驗器。要求字段滿足一個邏輯表達式。 

            email:郵件校驗器。要求被檢查字段非空,並且必須是合法的郵箱地址,底層是正則表達式。 

            url:網址校驗器。要求被檢查字段非空並且是個發的url地址,底層是正則表達式。 

            stringlength:字符串長度校驗器。要求字段長度必須在指定的範圍內。參數:manLength 最大長度  minLength最小長度 

            regex:正則表達式校驗器。
            等等。 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21

自定義校驗。

struts2內建的校驗器可以完成大部分輸入校驗。但是有時候無法滿足一些特殊的要求,struts2允許通過手動方式完成自定義校驗。 


        繼承ActionSupport,重寫validate方法
            public void validate() {
                if(user.getUsername()==null||user.getUsername().isEmpty()){
                    addFieldError("username", "用戶名不能爲空!!!");
                }
            } 

        重寫validate方法會檢驗action類裏的所有方法,我們不需要校驗某些方法,有兩種方法。 

        第一種:在不需要校驗的方法前加上註解@SkipValidation 
        第二種:重寫validateXxx方法,Xxx即爲要驗證的方法名 
            public void validateRegist() {
                if(user.getUsername()==null||user.getUsername().isEmpty()){
                    addFieldError("username", "用戶名不能爲空!!!");
                }

            } 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20

struts2的輸入校驗流程

1.對字符串類型的請求參數進行類型轉換,並且設置給JavaBean的屬性 

2.類型轉換中可能出現異常,如果出現異常,將異常信息保存並封裝 

3.調用struts2的輸入校驗規則進行輸入校驗(根據各種vatidate.xml文件裏定義的校驗規則) 

4.通過反射調用validateXxx方法進行校驗  

5.調用validate方法校驗 

6.上面的校驗出現錯誤,轉到input對應的視圖資源。沒錯誤,調用action中的處理方法。
文章轉載:http://blog.csdn.net/c99463904/article/details/72231558

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章