struts2转换器详解

   web层框架有一个很重要的功能就是接收从页面端提交过来的数据封装到JavaBean中,或者说会提供一种接收页面端提交的数据的机制。struts2作为一个web层框架当然也不例外,只是说各个web框架所提供的这种机制实现是不一样的,当然你不使用框架提供的数据接收机制而用Servlet中request.getParameter(name);也是可以的。

   在struts2中我们接收页面端参数一般是通过在Action中写相应的属性,其本质是将页面端提交的数据设置到ValueStack中,Action也是位于ValueStack中的。在进行参数设置之前,我们有可能要进行一定的类型转换,因为从页面端提交过来的数据都是字符串型,而在Java世界中数据类型有很多,这样就出现了页面中的数据结构与Java世界的数据结构不一致的情况,所以我们需要一种能在这两者之间进行转换的工具以解决这种不匹配的矛盾,在struts2中就表现为转换器类(TypeConverter).这种转换的实现是通过OGNL实现的,在OGNL中定义了一个ognl.TypeConverter接口:

public interface TypeConverter {
    public Object convertValue(Map context, Object target, Member member, String propertyName, Object value, Class toType);
}

   要想让OGNL完成类型转换工作就必须向OGNL提供一个该接口的实现类,在OGNL中该接口的默认实现类是ognl.DefaultTypeConverter但在XWork中,并没有直接使用ognl.DefaultTypeConverter实现类,甚至都没有使用ognl.TypeConverter接口,而是XWork自己定义了一个与ognl.TypeConverter接口一样的com.opensymphony.xwork2.conversion.TypeConverter接口,其名称和接口函数定义完全相同。XWork自行定义
TypeConverter接口的目的在于对外屏蔽类型转换的实现细节,从而能够将XWork对TypeConverter的扩展实现纳入到XWork的容器中进行管理,从而方便对OGNL原始的TypeConverter接口进行扩展并支持更加广泛的类型转换逻辑。XWork对类型转换方式TypeConverter的默认实现类是XWorkConverter,并使用了装饰模式,将其接口的实现封闭在OgnlTypeConverterWrapper内部。这样一来就可以完成利用装饰模式优点,不仅屏蔽原始的ognl.TypeConverter默认实现,并且有利于XWork自身在装饰类中插入框架所提供的用户扩展功能。下面是OgnlTypeConverterWrapper的源代码:

public class OgnlTypeConverterWrapper implements ognl.TypeConverter {

    private TypeConverter typeConverter;
    
    public OgnlTypeConverterWrapper(TypeConverter conv) {
        if (conv == null) {
            throw new IllegalArgumentException("Wrapped type converter cannot be null");
        }
        this.typeConverter = conv;
    }
    
    public Object convertValue(Map context, Object target, Member member,
            String propertyName, Object value, Class toType) {
        return typeConverter.convertValue(context, target, member, propertyName, value, toType);
    }
    
    public TypeConverter getTarget() {
        return typeConverter;
    }
}

   OgnlTypeConverterWrapper实现了ognl.TypeConverter接口,这个类就可以交给OGNL进行类型转换,因为这里使用了装饰模式,所以真正做类型转换工作的是其内部的typeConverter变量,该变量在运行时就是一个XWorkConverter对象,所以才说XWorkConverter是XWork中TypeConverter的默认实现。
   下面的setRoot方法是创建ValueStack后设置root对象时调用的方法,在该方法进创建了OgnlContext对象,并把xworkConverter包装起来:

protected void setRoot(XWorkConverter xworkConverter,
                       CompoundRootAccessor accessor, CompoundRoot compoundRoot, boolean allowStaticMethodAccess) {
    this.root = compoundRoot;
    this.securityMemberAccess =  new SecurityMemberAccess(allowStaticMethodAccess);
    //创建OgnlContext对象,并且把xworkConverter包装在OgnlTypeConverterWrapper内部
    this.context = Ognl.createDefaultContext(this.root, accessor, new OgnlTypeConverterWrapper(xworkConverter),
           securityMemberAccess);
    context.put(VALUE_STACK, this);
    Ognl.setClassResolver(context, accessor);
    ((OgnlContext) context).setTraceEvaluations(false);
    ((OgnlContext) context).setKeepLastEvaluation(false);
}

   从源代码中我们可以看到,被OgnlTypeConverterWrapper所装饰的实际实现类已经是XWork框架中的TypeConverter,而这个TypeConverter接口也就成为我们进行自由扩展自定义类型转换方式的容器。在XWork中TypeConverter接口的直接实现类是com.opensymphony.xwork2.conversion.impl.DefaultTypeConverter,它实现了最为基本的类型转换方式,如基本数据类型转换,枚举类型的转换,这个类中的逻辑很简单就不贴源码了。

   到这里我们应该会有一个疑问,如果自定义TypeConverter从而能够将其插入到XWork框架之中并被XWork所识别呢,我们先看一下XWork中TypeConverter的实现结构图。

   从这个结构图中可以看到对于直接实现的DefaultTypeConverter使用了两个完全不同的扩展方式。首先,使用“继承”的方式对DefaultTypeConverter进行基本的扩展(XWorkBasicConterter),覆盖其默认行为方式,然后进一步使用装饰模式将这个被扩展的XWorkBasicConverter封装在内部,对外呈现出XWorkConverter这个装饰类。XWorkBasicConterter提供了比DefaultTypeConverter更为广泛的类型转换支持,除了基本数据类型外还包含了Date类型,Collection类型等。
在XWorkConverter中,使用了装饰模式,实现了我们期待的“自定义类型转换”的注册与使用。

   在XWorkConverter的converterValue()该方法中,会首先通过getConverter方法的调用来获取当前Java类所对应的类型转换处理器,即查找类级别的转换器与全局转换器,如果查找不到的话再使用缺省的转换器进行转换,这个缺省的转换器就是一个XWorkBasicConterter对象,在实现化XWorkConverter时通过struts2容器注入进来。通过这样一种机制就实现了自定义转换器的扩展。

   到这里虽然知道了struts2是如何使用转换器把页面提交的字符数据转换成我们想要的Java类型的数据,但是在struts2中,类型转换到底发生在什么时候。有一点知道的是struts2在接收页面提交数据是将数据设置到了ValueStack中,即调用的是ValueStack.setValue()方法,肯定是在此方法中先进提交的数据进行了相应的转换,而调用相应的setter方法进行赋值的,为此我故意让struts2类型转换失败让其打印出堆栈信息,如下图:


最终调用了ognl.OgnlRuntime.callAppropriateMethod方法:

public static Object callAppropriateMethod(OgnlContext context, Object source, Object target, String methodName,
                                           String propertyName, List methods, Object[] args)
        throws MethodFailedException
{
		//省略了很多代码...
	
		//先获取相应的方法,设置提交的参数时就是要获取相应的setter方法
        Method method = getAppropriateMethod(context, source, target, propertyName, methods, args, actualArgs);

        //省略了很多代码...
		//调用获取到的方法
        return invokeMethod(target, method, convertedArgs);
        
        //省略了很多代码...
}

现在关键的就是getAppropriateMethod方法,在该方法中又调用了getConvertedType方法,下面该方法源码:

public static Object getConvertedType(OgnlContext context, Object target, Member member, String propertyName,
                                          Object value, Class type)
{
    return context.getTypeConverter().convertValue(context, target, member, propertyName, value, type);
}

   到这里大家就应该明白了,其实就是在获取OgnlContext中的TypeConverter对象,即OgnlTypeConverterWrapper对象,使用该对象进行类型转换。

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值