Отражение Java не согласуется с объявлением метода - PullRequest
4 голосов
/ 21 декабря 2010

Извините за длину этого вопроса. Я новичок в Java, и я столкнулся с чем-то, что действительно вводит меня в заблуждение. Я настолько новичок в Java, что даже не знаю всей терминологии, поэтому, пожалуйста, потерпите меня; У меня около 3 лет опыта работы с PHP (в основном процедурный, а не OO), но очень мало Java. Я также знаю, что отладка с помощью System.out.println - неправильный способ сделать это, но он работает, и это то, к чему я привык (вставьте шутку о программистах PHP здесь, если необходимо). Я все еще пытаюсь понять, как использовать отладчик NetBeans.

Я работаю над добавлением функции в веб-приложение, которое использует Struts (1.x). Кажется, проблема в том, что метод объявлен так, что ему требуется передать строку, но выполнение Reflection для этого метода говорит о том, что он хочет String [] (массив строк). Я ограничен в том, что не могу действительно вносить серьезные структурные изменения в приложение, и, конечно, я должен убедиться, что я ничего не сломал в приложении, которое работает в данный момент, поэтому я пытаюсь изменения в контексте того, что уже есть. Итак, к проблеме ...

Вот где объявляется метод (из него вырезано много строк, показывающих только то, что, я надеюсь, являются соответствующими битами):

AEReportBean.java:

public class AEReportBean {
    private String selectedDownloadFields = null;

    public String getSelectedDownloadFields() {
        return selectedDownloadFields;
    }

    // Note that there is no overloading of this function anywhere, this is the only declaration.
    public void setSelectedDownloadFields(String selectedDownloadFields) {
        this.selectedDownloadFields = selectedDownloadFields;
    }
}

Когда пользователь нажимает кнопку Отправить в форме, он обрабатывается AEReportSubmitAction.java:

.
public class AEReportSubmitAction extends BaseAction {
    public ActionForward doExecute(
            ActionMapping mapping,
            ActionForm form,
            HttpServletRequest request,
            HttpServletResponse response
        ) throws Exception {
        // This works fine, the paramater is getting passed in the request:
        System.out.println("URL parameter: " + request.getParameter("selectedDownloadFields");

        AEReportBean bean = new AEReportBean(request.getLocale(), 0);

        PropertyUtil.setAllFromRequest(request, bean);
        // This prints "Null", meaning the setAllFromRequest line above is failing to set this property.
        System.out.println("AEReportSubmitAction.java - bean.getSelectedDownloadFields() after setAllFromRequest: " + bean.getSelectedDownloadFields());
    }
}

PropertyUtil.setAllFromRequest () - это место, где происходит волшебство и настоящая проблема:

public class PropertyUtil {
    /**
     * Takes all the parameters from the request object and if there's a matching
     * mutator method in the bean, sets it
     */
    static public void setAllFromRequest(ServletRequest request, Object out) {
        // Iterate through all the request parameter names and try to set each one.
        for (Enumeration parameterNames = request.getParameterNames(); parameterNames.hasMoreElements();) {
            String name = (String) parameterNames.nextElement();
            try {
                PropertyUtil.setSimpleProperty(out, name, request.getParameter(name));
            }
            catch (Exception e) {
                log.info("Exception while setting properties from the Request. parameterName=" + name, e);
            }
        }
    }

    /**
     * Sets the property from an object using the object's mutator method.
     * Assumes naming conventions for accessor methods
     * @param bean the object to get the property from
     * @param property the name of the property to obtain
     * @param newProperty the object to set
     */
    // NOTE: This just seems to be a wrapper for the method below it...
    static public void setSimpleProperty(Object bean, String property, Object newProperty) throws Exception {
        PropertyUtil.setSimpleProperty(bean, property, newProperty, null);
    }

    /**
     * Sets the property from an object using the object's mutator method.
     * Assumes naming conventions for accessor methods
     * @param bean the object to get the property from
     * @param property the name of the property to obtain
     * @param newProperty the object to set
     */
    static public void setSimpleProperty(Object bean, String property, Object newProperty, Class type) throws Exception {
        // Capitalize the first letter in the property and append "set" to the front
        String methodName = "set" + property.substring(0, 1).toUpperCase() + property.substring(1);
        Method method;

        Class[] parameters;

        // If the Type was passed in when this method was called, simply add it to the Class array.
        if (type != null) {
            parameters = new Class[]{type};
        }
        // If the Type was not specified, determine the Type's class by calling getClass() on it; that class will be used below to call the appropriate setter method.
        else {
            parameters = new Class[]{newProperty.getClass()};
        }

        // Here's the reflection problem...
        // Iterate through all the methods in the bean.  If the method is named "setSelectedDownloadFields", print out some info about it.
        for (Method m : bean.getClass().getMethods()) {
            if (m.getName().equals("setSelectedDownloadFields")) {
                // newProperty is the incoming data that ultimately comes from the HTML form field.
                System.out.println("newProperty.getClass(): " + newProperty.getClass()); // Prints "class java.lang.String"

                    // Added for Cameron Skinner in comments.
                    System.out.println("m.toGenericString: " + m.toGenericString()); // Prints "public void com.[company deleted].bean.AEReportBean.setSelectedDownloadFields(java.lang.String[])"

                System.out.println("m.getName(): " + m.getName()); // Prints "setSelectedDownloadFields"
                System.out.println("parameters:");
                for (Class c : m.getParameterTypes()) {
                    System.out.println("--c.getCanonicalName(): " + c.getCanonicalName()); // Prints "java.lang.String[]"
                    System.out.println("--c.getName(): " + c.getName()); // Prints "[Ljava.lang.String;"
                }
            }
        }


        // And here's where it fails...
        try {
            System.out.println("bean.getClass(): " + bean.getClass());  // Prints "class com.[company deleted].bean.AEReportBean"
            System.out.println("methodName: " + methodName); // Prints "setSelectedDownloadFields"
            System.out.println("for (Class p : parameters):");
            for (Class p : parameters) {
                System.out.println("--p.getCanonicalName(): " + p.getCanonicalName()); // Prints "java.lang.String"
            }

            // Here it looks for a method called, effectively, AEReportBean.setSelectedDownloadFields(String s), but above we see that reflection is showing it as AEReportBean.setSelectedDownloadFields(String[] s), so the try block fails.
            method = bean.getClass().getMethod(methodName, parameters);
        }
        catch (NoSuchMethodException e) {
            // All lines below here also fail until it bombs out with the exception at the bottom...

            // If no method can be found, then see if it's a primitive type that
            // has been wrapped
            Class valueClass = newProperty.getClass();
            //System.out.println("valueClass.toString() = " + valueClass.toString());
            try {
                if (valueClass.equals(Integer.class)) {
                    method = bean.getClass().getMethod(methodName, new Class[]{int.class});
                }
                else if (valueClass.equals(Double.class)) {
                    method = bean.getClass().getMethod(methodName, new Class[]{double.class});
                }
                else if (valueClass.equals(Long.class)) {
                    method = bean.getClass().getMethod(methodName, new Class[]{long.class});
                }
                else if (valueClass.equals(Float.class)) {
                    method = bean.getClass().getMethod(methodName, new Class[]{float.class});
                }
                else {
                    throw new Exception(e.getMessage());
                }
            }
            catch (NoSuchMethodException ex) {
                throw new Exception(ex.getMessage());
            }
        }

        // If it had gotten to this point, it would call the method with the appropriate parameters, and the property would be set.
        try {
            // Now execute the method
            method.invoke(bean, new Object[]{newProperty});
        }
        catch (Exception ex) {
            throw new Exception(ex.getMessage());
        }
    }
}

Я действительно не знаю, что мне здесь не хватает, но должно быть что-то. Другие элементы формы HTML на той же странице работают отлично. Пожалуйста, дайте мне знать, если вам нужна дополнительная информация. Спасибо!

1 Ответ

4 голосов
/ 21 декабря 2010

Результаты кода не лгут. По сути, это говорит о том, что класс не тот, который вы ожидаете. У вас есть несколько AEReportBean классов разных версий в пути к классам вашего проекта, возможно, в разных пакетах, и неправильный был импортирован или получил приоритет при загрузке классов. Выполните поиск по типу / классу в Netbeans, чтобы найти все классы с заданным именем в classpath (я не делаю Netbeans, но в Eclispe это Ctrl + Shift + T, эквивалент Netbeans, вероятно, Alt + Shift + O ) .

Обновление : еще одна возможная причина - Netbeans не собирал проект автоматически при сохранении исходного файла (среда IDE должна создавать / обновлять .class файлы во время сборки). Посмотри где-нибудь в настройках.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...