【问题标题】:Spring aop, cglib enhanced class lose generic infoSpring aop,cglib 增强类丢失通用信息
【发布时间】:2015-06-25 08:14:49
【问题描述】:

我有一个 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代理类,当启用aop时,users字段将由客户端的json数据填充, 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 时,它会针对 users 字段的 setter 方法返回 java.util.ArrayList。但应该是java.util.ArrayList&lt;User&gt;

似乎我的动作类在被 cglib 代理时丢失了它的通用信息。我还找到了a old bug 关于这个。

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

【问题讨论】:

    标签: java json spring struts2 cglib


    【解决方案1】:

    我的想法是尝试找到代理背后的实际类型。根据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插件源代码并构建我们自己的,修改JSONPopulatorpopulateObject方法

    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();
                }
    

    【讨论】:

      【解决方案2】:

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

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2015-01-19
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2016-04-06
        相关资源
        最近更新 更多