Spring aop,cglib增强类失去了通用信息

时间:2015-06-25 08:14:49

标签: java json spring struts2 cglib

我有一个struts 2动作类:

public class MyAction{
    private ArrayList<User> users;
    public void setUsers(ArrayList<User> users){
        this.users = users;
    }
    public String doMyAction(){
        //...
    }
}

doMyAction方法有一个AOP切入点,因此MyAction实际上是运行时的cglib代理类,users字段将由来自客户端的json数据填充,启用aop后,struts JSONInterceptor将无法将json数据填充到users字段中。我使用struts json插件的源代码进行了调试,并在org.apache.struts2.json.JSONPopulator中找到了这个:

public void populateObject(Object object, final Map elements) 
                                                            throws IllegalAccessException,
            InvocationTargetException, NoSuchMethodException, IntrospectionException,
            IllegalArgumentException, JSONException, InstantiationException {
        Class clazz = object.getClass();

        BeanInfo info = Introspector.getBeanInfo(clazz);
        PropertyDescriptor[] props = info.getPropertyDescriptors();

        // iterate over class fields
        for (int i = 0; i < props.length; ++i) {
            PropertyDescriptor prop = props[i];
            String name = prop.getName();

            if (elements.containsKey(name)) {
                Object value = elements.get(name);
                Method method = prop.getWriteMethod();

                if (method != null) {
                    JSON json = method.getAnnotation(JSON.class);
                    if ((json != null) && !json.deserialize()) {
                        continue;
                    }

                    // use only public setters
                    if (Modifier.isPublic(method.getModifiers())) {
                        Class[] paramTypes = method.getParameterTypes();
                        Type[] genericTypes = method.getGenericParameterTypes();

                        if (paramTypes.length == 1) {
                            Object convertedValue = this.convert(paramTypes[0], 
                                                           genericTypes[0], value, method);
                            method.invoke(object, new Object[] { convertedValue });
                        }
                    }
                }
            }
        }
    }

并在这一行:

Type[] genericTypes = method.getGenericParameterTypes();

启用AOP时,它会针对java.util.ArrayList字段的setter方法返回users。但java.util.ArrayList<User>预期。

在cglib代理时,我的动作类似乎失去了它的通用信息。我还发现了a old bug

我可以从aop配置中排除我的方法以解决此问题。但我还是想知道是否有更好的解决方案?

2 个答案:

答案 0 :(得分:2)

我的想法是尝试找到代理背后的实际类型。根据spring文档,从spring aop获得的任何代理都实现了org.springframework.aop.framework.Advised接口,并且此接口公开了查询目标类的方法。

Any AOP proxy obtained from Spring can be cast to this interface to allow manipulation of its AOP advice.

所以这里我们有一个相当大的选择,我们可以下载struts json插件源代码并构建我们自己的源代码,并对populateObject

JSONPopulator方法进行修改
public void populateObject(Object object, final Map elements) throws IllegalAccessException,
            InvocationTargetException, NoSuchMethodException, IntrospectionException,
            IllegalArgumentException, JSONException, InstantiationException {

        Class clazz = object.getClass();

        // if it is a proxy, find the actual type behind it
        if(Advised.class.isAssignableFrom(clazz)){
            clazz = ((Advised)object).getTargetSource().getTargetClass();
        }

        BeanInfo info = Introspector.getBeanInfo(clazz);
        PropertyDescriptor[] props = info.getPropertyDescriptors();

        // iterate over class fields
        for (int i = 0; i < props.length; ++i) {
            PropertyDescriptor prop = props[i];
            String name = prop.getName();

            if (elements.containsKey(name)) {
                Object value = elements.get(name);
                Method method = prop.getWriteMethod();

                if (method != null) {
                    JSON json = method.getAnnotation(JSON.class);
                    if ((json != null) && !json.deserialize()) {
                        continue;
                    }

                    // use only public setters
                    if (Modifier.isPublic(method.getModifiers())) {
                        Class[] paramTypes = method.getParameterTypes();


                        Type[] genericTypes = method.getGenericParameterTypes();

                        if (paramTypes.length == 1) {
                            Object convertedValue = this.convert(paramTypes[0], genericTypes[0], value,
                                    method);
                            method.invoke(object, new Object[] { convertedValue });
                        }
                    }
                }
            }
        }
    }

请注意我添加的这些内容:

 // if it is a proxy, find the actual type behind it
            if(Advised.class.isAssignableFrom(clazz)){
                clazz = ((Advised)object).getTargetSource().getTargetClass();
            }

答案 1 :(得分:1)

Cglib是在存在泛型类型之前创建的。代理生成为cglib中代理类的子类,它不保留泛型类型信息。因此,您无法从代理类中查询它。