Spring MVC аннотированный контроллер в заводной - PullRequest
5 голосов
/ 24 сентября 2011

У меня есть это в src / main / groovy / ...

package com.mycompany.web;
// imports....

@Controller
class GroovyController {

    @RequestMapping("/status_groovy")
    public @ResponseBody String getStatus() {
        return "Hello World from groovy!";
    }
}

Использование maven 3 и пружины 3.1 (Milestone). Spring MVC отлично работает для контроллеров Java, и все настроено нормально. Класс groovy прекрасно компилируется и может быть найден в каталоге classes вместе с классами контроллера java.

У меня есть аналогичный контроллер, написанный на Java (JavaController) в том же пакете, но в src / main / java, и он правильно подбирается весной и отображается, и я вижу ответ на экране, когда нажимаю на URL.

package com.mycompany.web;
// imports....

@Controller
class JavaController {

    @RequestMapping("/status")
    public @ResponseBody String getStatus() {
        return "Hello World!";
    }
}

Jetty запускается нормально, без ошибок в журнале, но я не вижу, чтобы groovy url отображался, тогда как я вижу java.

2011-09-23 16:05:50,412 [main] INFO  org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping - Mapped "{[/status],methods=[],params=[],headers=[],consumes=[],produces=[]}" onto public java.lang.String com.mycompany.web.JavaController.getStatus()

Все настройки в порядке, так как другие части приложения работают нормально с аннотациями (компонентное сканирование и т. Д.), Просто я не могу отобразить URL в GroovyController

Может кто-нибудь объяснить, что нужно сделать, чтобы получить Controller s, написанные в отличной работе?

PS: я избегаю GroovyServlet для запуска сценариев, потому что он имеет серьезные недостатки, когда дело доходит до внедрения bean-компонентов и URL-путей.

Ответы [ 3 ]

6 голосов
/ 21 января 2012

При всем моем уважении к Бену (с которым я работаю) проблема не в том, что Spring создает прокси cglib. Скорее, он создает динамический JDK (или интерфейсный) прокси. Этот метод создания прокси может реализовывать только методы, объявленные в реализованных интерфейсах цели. Вы на самом деле хотите Spring для создания прокси-сервера cglib, который создает прокси, являющийся подклассом целевого объекта, и поэтому может воссоздать все его открытые методы. Если не указано иное, Spring создаст прокси cglib, если целевой объект не реализует никаких интерфейсов, и прокси на основе интерфейса в противном случае. Поскольку все объекты Groovy реализуют GroovyObject, вы получаете прокси на основе интерфейса, даже если вы явно не реализовали никаких интерфейсов в контроллере Groovy. Решение Бена правильное в том, что если вы создадите интерфейс со всеми вашими методами контроллера, вы получите ожидаемое поведение. Альтернативой является создание BeanFactoryPostProcessor, который инструктирует Spring для создания прокси-серверов cglib для классов, которые реализуют GroovyObject и только GroovyObject. Вот код:

/**
 * Finds all objects in the bean factory that implement GroovyObject and only GroovyObject, and sets the
 * AutoProxyUtils.PRESERVE_TARGET_CLASS_ATTRIBUTE value to true.  This will, in the case when a proxy
 * is necessary, force the creation of a CGLIB subclass proxy, rather than a dynamic JDK proxy, which
 * would create a useless proxy that only implements the methods of GroovyObject.
 *
 * @author caleb
 */
public class GroovyObjectTargetClassPreservingBeanFactoryPostProcessor implements BeanFactoryPostProcessor {
    private static final Logger logger = LoggerFactory.getLogger(GroovyObjectTargetClassPreservingBeanFactoryPostProcessor.class);

    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
        for (String beanDefName : beanFactory.getBeanDefinitionNames()) {
            BeanDefinition bd = beanFactory.getBeanDefinition(beanDefName);
            //ignore abstract definitions (parent beans)
            if (bd.isAbstract())
                continue;
            String className = bd.getBeanClassName();
            //ignore definitions with null class names
            if (className == null)
                continue;
            Class<?> beanClass;
            try {
                beanClass = ClassUtils.forName(className, beanFactory.getBeanClassLoader());
            }
            catch (ClassNotFoundException e) {
                throw new CannotLoadBeanClassException(bd.getResourceDescription(), beanDefName, bd.getBeanClassName(), e);
            }
            catch (LinkageError e) {
                throw new CannotLoadBeanClassException(bd.getResourceDescription(), beanDefName, bd.getBeanClassName(), e);
            }

            Class<?>[] interfaces = beanClass.getInterfaces();
            if (interfaces.length == 1 && interfaces[0] == GroovyObject.class) {
                logger.debug("Setting attribute {} to true for bean {}", AutoProxyUtils.PRESERVE_TARGET_CLASS_ATTRIBUTE, beanDefName);
                bd.setAttribute(AutoProxyUtils.PRESERVE_TARGET_CLASS_ATTRIBUTE, true);
            }
        }
    }
}

Просто включите боб этого типа в ваш контекст, и вуаля! Вы можете иметь контроллеры Groovy без необходимости определять интерфейсы.

2 голосов
/ 27 июня 2013

Прошу отличаться. Нет необходимости реализовывать интерфейс. Проблема в том, что по умолчанию AnnotationMethodHandlerAdapter не читает аннотации прокси. Следовательно, нам нужно было бы создать этот AnnotationMethodHandlerAdapter с поддержкой прокси, который расширяет значение по умолчанию AnnotationMethodHandlerAdapter пружины. Нам также нужно создать экземпляр bean-компонента для этого ProxyAwareAnnotationMethodHandlerAdapter в XML-файле Spring Configuration. Примечание. Эта функция недоступна в Spring 3.x, но поскольку весна 4.0 будет поддерживать классные bean-компоненты, эта функция должна быть рассмотрена.

//ProxyAwareAnnotationMethodHandlerAdapter.java

    package name.assafberg.spring;

    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;

    import org.springframework.aop.TargetSource;
    import org.springframework.aop.framework.Advised;
    import org.springframework.web.servlet.ModelAndView;
    import org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter;

    /**
     * Add proxy awareness to <code>AnnotationMethodHandlerAdapter</code>.
     * 
     * @author assaf
     */
    public class ProxyAwareAnnotationMethodHandlerAdapter extends AnnotationMethodHandlerAdapter {

        /**
         * @param request
         * @param response
         * @param handler
         * @return
         * @throws Exception
         * @see org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter#handle(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse, java.lang.Object)
         */
        @Override
        public ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
            handler = unwrapHandler(handler);

            return super.handle(request, response, handler);
        }

        /**
         * @param handler
         * @return
         * @see org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter#supports(java.lang.Object)
         */
        @Override
        public boolean supports(Object handler) {
            handler = unwrapHandler(handler);

            return super.supports(handler);
        }

        /**
         * Attempt to unwrap the given handler in case it is an AOP proxy
         * 
         * @param handler
         * @return Object
         */
        private Object unwrapHandler(Object handler) {
            if (handler instanceof Advised) {
                try {
                    TargetSource targetSource = ((Advised) handler).getTargetSource();
                    return targetSource.getTarget();

                } catch (Exception x) {
                    throw new RuntimeException(x);
                }

            } else {
                return handler;     
            }       
        }

    }

XML-файл конфигурации пружины должен иметь следующее. Вместо создания bean-компонента AnnotationMethodHandlerAdapter мы должны создать bean-компонент ProxyAwareAnnotationMethodHandlerAdapter.

<beans .........
...
...
      <bean class="full.qualified.name.of.ProxyAwareAnnotationMethodHandlerAdapter" />
...
...
      <lang:groovy script-source="classpath:com/example/mysample.groovy refresh-check-delay="1000" />
</beans>

Также Spring анализирует конфигурационный XML-файл с использованием SAX-анализатора (на основе события). Таким образом, чтобы Spring мог понять аннотации внутри скриптов groovy, компоненты groovy (использующие тег) должны быть созданы после ProxyAwareAnnotationMethodHandlerAdapter.

Надеюсь, чем поможет

Ссылка: http://forum.springsource.org/showthread.php?47271-Groovy-Controller

1 голос
/ 24 сентября 2011

К сожалению, если вы хотите запустить это в Groovy, вам нужно будет создать интерфейс для вашего класса Controller и аннотировать определения методов. Spring создает прокси для вашего класса, используя Cglib. Однако без создания пользовательского интерфейса для вашего контроллера Spring проксирует на groovy.lang.GroovyObject, потому что все объекты Groovy реализуют этот интерфейс по умолчанию.

interface GroovyControllerInterface {
    @RequestMapping("/status_groovy")
    @ResponseBody String getStatus()
}

@Controller
class GroovyController implements GroovyControllerInterface {
    @RequestMapping("/status_groovy")
    public @ResponseBody String getStatus() {
        return "Hello World from groovy!";
    }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...