Spring MVC注释控制器在groovy中

时间:2011-09-23 20:36:32

标签: java spring-mvc groovy

我在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和spring 3.1(里程碑)。 Spring MVC非常适合java控制器,一切都设置得很好。 groovy类编译得很好,可以在classes目录中找到java控制器类。

我在java(JavaController)中用相同的包编写了类似的控制器,但是在src / main / java下,它被spring和map正确选中,当我点击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()

所有设置都很好,因为应用程序的其他部分正常工作注释(组件扫描等),只是我无法获取GroovyController中映射的网址

任何人都可以解释为了让Controller以常规方式编写而需要做些什么吗?

PS:我避免使用GroovyServlet来运行脚本,因为它在bean注入和url路径映射方面有很大的缺点。

3 个答案:

答案 0 :(得分:6)

对Ben(我与之合作)的所有应有的尊重,这里的问题不是Spring正在创建一个cglib代理。相反,它正在创建一个动态JDK(或基于接口的)代理。这种创建代理的方法只能实现在目标实现的接口中声明的方法。实际上你想要 Spring来创建一个cglib代理,它创建一个代理,它是目标对象的子类,因此可以重新创建它的所有公共方法。除非另外指定,否则Spring将在目标对象未实现任何接口时创建cglib代理,否则创建基于接口的代理。由于所有Groovy对象都实现了GroovyObject,因此即使您没有在Groovy控制器中显式实现任何接口,也会获得基于接口的代理。 Ben的解决方案是正确的,因为如果您使用所有控制器方法创建一个接口,您将获得预期的行为。另一种方法是创建一个BeanFactoryPostProcessor,它指示Spring为实现GroovyObject和只有GroovyObject的类创建cglib代理。这是代码:

/**
 * 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);
            }
        }
    }
}

只需在您的上下文中包含此类型的bean,瞧!您可以使用Groovy控制器而无需定义接口。

答案 1 :(得分:2)

我不同意见。无需实现接口。这里的问题是默认的AnnotationMethodHandlerAdapter不会从代理中读取注释。因此,我们必须创建这个代理感知的AnnotationMethodHandlerAdapter,它扩展了spring的默认AnnotationMethodHandlerAdapter。我们还需要在Spring Configuration xml文件中为此ProxyAwareAnnotationMethodHandlerAdapter实例化一个bean。 注意:此功能在Spring 3.x中不可用,但由于Spring 4.0将支持groovy 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;     
            }       
        }

    }

spring配置XML文件必须具有以下内容。我们必须创建一个ProxyAwareAnnotationMethodHandlerAdapter bean,而不是创建AnnotationMethodHandlerAdapter的bean。

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

Spring还使用SAX解析器解析配置XML文件(基于事件发生)。因此,为了使spring能够理解groovy脚本中的注释,必须在ProxyAwareAnnotationMethodHandlerAdapter之后创建groovy bean(使用标记)。

希望比帮助

参考:http://forum.springsource.org/showthread.php?47271-Groovy-Controller

答案 2 :(得分:1)

不幸的是,如果你想在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!";
    }
}