2009年1月17日星期六

开始学习struts2(六)

前两天实践了关于拦截器的具体实现,说实话关于底层实现还没有看明白,看jdk的源码中的
public static Class<?> getProxyClass(ClassLoader loader,Class<?>... interfaces)
方法,好长啊
迂回一下,今儿看struts2的具体拦截器Interceptor怎么配置
配置可比自己写实现拦截器容易多了
1、首先写一个拦截器类,拦截器类有两只写法(目前俺知道的)
一种是显示com.opensymphony.xwork2.interceptor.Interceptor接 口,com.opensymphony.xwork2.interceptor.Interceptor接口有三个方法destroy()、init() 和String intercept(ActionInvocation actionInvocation),跟过滤器差不多
这里指出的是init初始化方法将在容器启动是调用这个方法。
package com.test.interceptor;

/**
* Created by IntelliJ IDEA.
* User: Administrator
* Date: 2009-1-15
* Time: 16:34:17
* To change this template use File | Settings | File Templates.
*/
import com.opensymphony.xwork2.interceptor.Interceptor;
import com.opensymphony.xwork2.ActionInvocation;

public class MyInterceptor implements Interceptor{

    public void destroy() {

    }

    public void init() {

    }

    public String intercept(ActionInvocation actionInvocation) throws Exception {

        System.out.println("test intercept begin");
        String result = actionInvocation.invoke();
        System.out.println("test intercept finish");
        return result;
    }
}
另一种就是继承com.opensymphony.xwork2.interceptor.AbstractInterceptor,这是个抽象类,并实 现了com.opensymphony.xwork2.interceptor.Interceptor接口,分别实现了init和destroy方法, 但什么都没做,继承AbstractInterceptor后,实现intercept方法就行了,
这里指出的是在intercept方法中执行actionInvocation.invoke();执行所拦截的action中的方法;
2、拦截器写完了剩下就是配置了,这里要用到struts.xml的组织结构<struts>中有<package>包的的概 念,包与包之间可以继承extends,就像子类继承父类一样,子类将拥有父类的属性和配置,我们一般都继承extends="struts- default",而struts-default定义在struts2-core.jar 中的struts-default.xml中,struts-default包中定义了很多struts2提供的拦截器和拦截器栈(拦截器栈可以包含多个 拦截器或拦截器栈),struts2的好多功能都是实现在这些拦截器中,其中有个<default-interceptor-ref name="defaultStack"/>标签定义了默认的拦截器,如果<action>配置中没有拦截器配置,那就调用默认拦截 器,如果有拦截器配置,要么同时加上默认拦截器,要么在自己的package中加入设置默认拦截器的标签。
<package name="capinfo" extends="struts-default">
        <interceptors>
            <interceptor name="myInterceptor" class="com.test.interceptor.MyInterceptor">
            </interceptor>
        </interceptors>
        <action name="HelloWorld"
            class="com.capinfo.struts2.action.HelloWordAction">
            <result>/HelloWorld.jsp</result>
            <interceptor-ref name="myInterceptor"></interceptor-ref>
            <interceptor-ref name="defaultStack"></interceptor-ref>
        </action>

        <!-- Add your actions here -->
    </package>

2009年1月14日星期三

开始学习struts2(五)

说是struts2的核心就是拦截器
先看看拦截器的实现
实现拦截器有这么几个东西构成
1、目标对象-将被拦截的对象,这个对象中的方法被调用是将被拦截
要求目标对象面向接口编程,首先要定义接口package com.test.interceptor;

/**
* Created by IntelliJ IDEA.
* User: Administrator
* Date: 2009-1-9
* Time: 17:37:23
* 面向接口编程
* 目标对象的接口
*/
public interface TargetInterface {
    public void doSomething();
}目标对象实现
package com.test.interceptor;

/**
* Created by IntelliJ IDEA.
* User: Administrator
* Date: 2009-1-9
* Time: 17:36:26
* 目标对象
*/
public class Target implements TargetInterface{
    public void doSomething(){
        System.out.println("do something");
    }
}
2、拦截器对象-里面包含在调用目标对象前或后要调用的方法package com.test.interceptor;

/**
* Created by IntelliJ IDEA.
* User: Administrator
* Date: 2009-1-9
* Time: 17:39:32
* 拦截器
*/
public class Interceptor {
    public void before(){
        System.out.println("before");
    }

    public void after(){
        System.out.println("after");
    }
}
3、处理器-处理器决定拦截器如何拦截目标对象
package com.test.interceptor;

/**
* Created by IntelliJ IDEA.
* User: Administrator
* Date: 2009-1-9
* Time: 17:41:08
*/
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;

/**
* 处理器
* 在这里将目标对象Target和拦截器Interceptor联系在了一起
*/
public class MyHandler implements InvocationHandler{

    /**
     * 目标对象
     * 将来目标对象的方法将被注入interceptor
     */
    private Object object;

    /**
     * 拦截器
     * MyHandler负责将Interceptor这个拦截器注入到传入的目标对象object
     */
    private Interceptor interceptor = new Interceptor();

    public void setObject(Object object){
        this.object = object;
    }

    /**
     * 实现InvocationHandler接口的方法
     * @param o 在其上调用方法的代理实例
     * @param method 对应于在代理实例上调用的接口方法的 Method 实例
     * @param objects 包含传入代理实例上方法调用的参数值的对象数组
     * @return 从代理实例的方法调用返回的值
     * @throws Throwable
     */
    public Object invoke(Object o, Method method, Object[] objects) throws Throwable {
        Object result = null;
        interceptor.before();
        result = method.invoke(object,objects);
        interceptor.after();
        return result;
    }
}
4、代理对象-用于得到目标对象的代理对象
package com.test.interceptor;

/**
* Created by IntelliJ IDEA.
* User: Administrator
* Date: 2009-1-9
* Time: 17:50:52
* 代理类
*/
import java.lang.reflect.Proxy;
public class MyProxy {
    /**
     * 返回obj的,处理器为MyHandler的代理对象
     * @param obj 目标对象
     * @return 目标对象的代理对象
     */
    public Object getProxy(Object obj){
        MyHandler myHandler = new MyHandler();
        myHandler.setObject(obj);
        return Proxy.newProxyInstance(obj.getClass().getClassLoader(),
                obj.getClass().getInterfaces(),myHandler);
    }
}
5、最后客户调用代码package com.test.interceptor;

/**
* Created by IntelliJ IDEA.
* User: Administrator
* Date: 2009-1-9
* Time: 17:55:45
* 客户调用
*/
public class Client {
    public static void main(String[] args){
        //目标对象
        TargetInterface target = new Target();
        //代理对象
        MyProxy myProxy = new MyProxy();
        //通过代理对象得到目标对象的代理
        TargetInterface proxy = (TargetInterface)myProxy.getProxy(target);
        //通过目标对象的代理调用目标对象的方法
        proxy.doSomething();
    }
}

//输出:
//before
//do something
//after
将自己的理解写了注释,现在只理解到这个程度,像
Proxy.newProxyInstance
里面是怎么实现的,还没深入考究
继续努力

2009年1月8日星期四

开始学习struts2(四)

关于类型转换还有一种写法:public class ConverterPoint extends StrutsTypeConverter {

//    @Override
//    public Object convertValue(Map context, Object value, Class toType) {
//        if(Point.class == toType){
//            String[] params = (String[])value;
//            params = params[0].split(",");
//            Point point = new Point();
//            point.setX(Integer.parseInt(params[0]));
//            point.setY(Integer.parseInt(params[1]));
//            return point;
//           
//        }else if(String.class == toType){
//            Point point = (Point)value;
//            return "x=" + point.getX() + ",y=" + point.getY();
//        }
//        return super.convertValue(context, value, toType);
//    }

    @Override
    public Object convertFromString(Map arg0, String[] arg1, Class arg2) {
        String[] params = arg1;//(String[])value;
        params = params[0].split(",");
        Point point = new Point();
        point.setX(Integer.parseInt(params[0]));
        point.setY(Integer.parseInt(params[1]));
        return point;
    }

    @Override
    public String convertToString(Map arg0, Object arg1) {
        Point point = (Point)arg1;
        return "x=" + point.getX() + ",y=" + point.getY();
    }

   
}
原来是继承ognl.DefaultTypeConverter类,现在改成继承 org.apache.struts2.util.StrutsTypeConverter类了,看 org.apache.struts2.util.StrutsTypeConverter的源码可知道 org.apache.struts2.util.StrutsTypeConverter是个抽象类,并实现了 ognl.TypeConverter,org.apache.struts2.util.StrutsTypeConverter中有两个要实现的方法 一个convertFromString,一个convertToString,看方法名字就知道一个从字符串转换成Object,一个是将Object 转换成String,这样好像更加清晰

另外还有一种全局转换的配置,就是建立一个统一的属性文件,文件名为xwork-conversion.properties,与struts.xml位于统一目录下,里面的配置改成了
com.test.struts2.data.Point=com.test.struts2.action.ConverterPoint
意思是所有com.test.struts2.data.Point类型的对象都要通过com.test.struts2.action.ConverterPoint来转换

2009年1月5日星期一

开始学习struts2(三)

struts2局部类型转换

需求为:在页面的文本框输入一个x、y坐标,之间用","隔开,Action中有一个Point类型的属性,Point类里面有两个字段,分别是 x,y,int型,要求当页面提交时将文本框中的字符串转换成Point对象,当Action返回到页面时将Point转换成字符串显示

1、首先做一个jsp用于输入和提交:point.jsp

<s:form action="Converter">
<s:textfield name="point" label="Point"></s:textfield>
<s:textfield name="point.x" label="Point.X"></s:textfield>
<s:textfield name="point.y" label="Point.Y"></s:textfield>
<s:submit label="submit"></s:submit>
</s:form>

2、再建一个用于显示的jsp-converter.jsp

<body>
<s:property value="point"/><br>
<s:property value="point.x"/><br>
<s:property value="point.y"/><br>
</body>

3、Action类 ConverterAction

public class ConverterAction extends ActionSupport{

private Point point;

public Point getPoint() {
return point;
}

public void setPoint(Point point) {
this.point = point;
}

public String execute()throws Exception {
return "success";
}
}

4、struts.xml

<action name="Converter"
class="com.test.struts2.action.ConverterAction">
<result name="success">/converter.jsp</result>
<result name="input">/point.jsp</result>
</action>

5、转换类 ConverterPoint

public class ConverterPoint extends DefaultTypeConverter {

@Override
public Object convertValue(Map context, Object value, Class toType) {
if(Point.class == toType){
String[] params = (String[])value;
params = params[0].split(",");
Point point = new Point();
point.setX(Integer.parseInt(params[0]));
point.setY(Integer.parseInt(params[1]));
return point;

}else if(String.class == toType){
Point point = (Point)value;
return "x=" + point.getX() + ",y=" + point.getY();
}
return super.convertValue(context, value, toType);
}


}

6、配置转换
在ConverterAction 类的同一目录下定义属性文件ConverterAction-conversion.properties,注意文件 名,-conversion.properties为固定不变,ConverterAction为Action的名字,文件内容:

point=com.test.struts2.action.ConverterPoint

Ok了,这样就完工了

开始学习struts2(二)

前两天初步认识了一下struts2
今儿看来教程的第二三讲,搞清了一些前面的一些猜测或是疑问
1、struts2是不用<html:...>标签了,统一成了<s:...>
如下这样:
<s:form action="Converter">
<s:textfield name="point" label="Point"></s:textfield>
<s:submit label="submit"></s:submit>
</s:form>
显示效果:

注意到<s:textfield name="point" label="Point"></s:textfield>
中的label属性,它指定了文本框前面显示的内容,还自动加了冒号,哈哈,挺聪明的嘛,但是担心这样在复杂的页面设计中是不是好使。

哦对了,要想这么写,要在页面上方加上这个:<%@ taglib prefix="s" uri="/struts-tags" %>

2、Action
前面说的Action不再需要继承任何struts类,现在看来要失望了,为了方便起见还是建议集成ActionSupport类,目前觉得有用的是 ActionSupport中定义了几个static的result name,比如SUCCESS、ERROR,原来的return "success";现在可以写成return super.SUCCESS;,将标识用的字符串常量定义成static的是一直提倡的,还有就是validate()方法,验证有错误可以调用 addFieldError()方法,好像就是struts1 ActionForm里的东西,有所改进的是super.addFieldError("username", "username is null");将在页面中显示的效果为:错误信息"username is null"将在名字为"username"文本框的上面显示,这些如果能用ajax实现就好了。
对于Action解耦,可能在于它不再需要HttpServletRequest 、HttpServletResponse这样的容器运行时参数吧