我在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路径映射方面有很大的缺点。
答案 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!";
}
}