在上篇我们提到了使用SetAndGet注解进行自动生成set/get方法,那么在开发模式下, 我们所使用的IDE,在进行了代码修改之后,会自动地进行重新编译代码并进行hotswap操作。这个操作会使得在启动时生成的set/get失效或者被删除。解决这个问题的一个方法,就是使用字段级参数注入,即我们在开发模式下不需要生成set/get,而是直接使用相应的字段进行操作。
在当前的struts2版本中,默认是不支持字段设置值的,但是ognl本身是支持的。在ognl中,提供了以下2个方法来完成字段的获取和设置。
OgnlRuntime.setFieldValue(ognlContext, o, name.toString(), value);//设置字段的值 OgnlRuntime.getFieldValue(ognlContext, o, name.toString());//获取字段的值
那么,我们可以通过相应的处理。让在开发模式下,在使用常规参数处理之后,再启用字段级参数处理就达到相应的目的了。在整个处理中,我们需要保证以下2点就可以达到目的了。
- 在action进行参数注入时,可以支持字段级的注入,涉及的访问器为CompoundRootAccessor
- 在相应的字段为null时,我们可以按照常规的处理自动创建相应的值,以便进行注入,涉及的处理器为InstantiatingNullHandler
1 字段参数注入
原生的CompoundRootAccessor类中没有处理字段的参数设置,那么我们需要扩展相应的功能。需要处理的方法包括获取值和设置值,那么即要override方法setProperty和getProperty
在方法setProperty中,我们只需要保证在没有找到属性值,加入我们的字段处理即可。代码如下:
//处理Property else if(OgnlRuntime.hasField(ognlContext, o, o.getClass(), name.toString())) { OgnlRuntime.setFieldValue(ognlContext, o, name.toString(), value); return; } //处理map类型
在方法getProperty中,与set类似,在相应的逻辑中加入字段处理。
else if(OgnlRuntime.hasField(ognlContext, o, o.getClass(), name.toString())) { try { return OgnlRuntime.getFieldValue(ognlContext, o, name.toString()); } catch(NoSuchFieldException e) { logger.warn("没有字段'" + name.toString() + "'的值", e); } }
2 创建NULL值
原生的InstantiatingNullHandler并没有针对字段进行处理,那么我们也只需要继承并override相应的逻辑即可。如下所示:
PropertyDescriptor pd = reflectionProvider.getPropertyDescriptor(realTarget.getClass(), propName); clazz = pd == null ? null : pd.getPropertyType(); if(clazz == null) { Field field = reflectionProvider.getField(realTarget.getClass(), propName); if(field != null) clazz = field.getType(); }
其中的reflectionProvider.getField即是新增加的代码。
3 启用新增加的处理器
我们新增加的这2个类均是直接扩展(修改)原有的struts内部类,而这些类的定义是放在sturts-default.xml中的。在不修改相应jar包的情况下,如何放入我们的类呢。其实不难,我们只需要将我们的类追加进原有struts2的containerBuilder即可。containerBuilder是用于启动struts2时创建容器的构造器,我们可以实现接口ConfigurationProvider以提供特定的扩展。那么,这样就很好处理了,只需要实现之,并加入相应代码即可。如下所示:
builder.setAllowDuplicates(true);//这一行必须调用,即允许重复定义。在此模式下,后面的定义将覆盖之前的定义 builder.factory(ognl.MethodAccessor.class, "com.opensymphony.xwork2.util.CompoundRoot", CompoundRootAccessor.class);//我们自实现的字段注入类 builder.factory(ognl.PropertyAccessor.class, "com.opensymphony.xwork2.util.CompoundRoot", CompoundRootAccessor.class);//我们自实现的字段注入类 builder.factory(com.opensymphony.xwork2.conversion.NullHandler.class, "java.lang.Object", InstantiatingNullHandler.class);//我们自实现的null处理类
总结
经过这样的处理,struts2就能使用字段级注入了。但直接访问字段在安全或效率上或多或少存在一些问题。即我不推荐在生产模式中启用这种处理方式。具体如何切换,可以参考前一篇文章。我们可以在开发模式中,打开这种处理;而在生产模式下关注这种处理即可,在保证代码整洁的情况下,代码一点也无需修改。
转载请标明出处:i flym
本文地址:https://www.iflym.com/index.php/code/201312250001.html