2011-09-23 19 views
5

I tiene esto en src/main/maravilloso/...Spring MVC controlador anotada en maravilloso

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

@Controller 
class GroovyController { 

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

Usando Maven 3 y la primavera 3,1 (Milestone). Spring MVC funciona perfectamente bien para los controladores de Java y todo está bien configurado. La clase groovy compila bien y se puede encontrar en el directorio classes junto con las clases de controlador java.

Tengo un controlador similar escrito en java (JavaController) en el mismo paquete, pero bajo src/main/java y es recogido correctamente por la primavera y asignado y puedo ver la respuesta en la pantalla cuando toco la url.

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

@Controller 
class JavaController { 

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

embarcadero se inicia normalmente con ningún error en el registro pero en no veo url maravilloso conseguir mapeados mientras que yo puedo ver el 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() 

Todo el entorno están muy bien como otras partes de aplicación están trabajando muy bien con anotaciones (componente de escanear, etc.), sólo que no puedo obtener la URL asignada en GroovyController

Puede alguien explicar lo debe hacerse para obtener Controller s escrito en Groovy trabajando?

PD: Estoy evitando que GroovyServlet ejecute las secuencias de comandos porque tiene una desventaja importante cuando se trata de la inyección de bean y las asignaciones de ruta de la URL.

Respuesta

1

Desafortunadamente, si desea ejecutar esto en Groovy tendrá que crear una interfaz para su clase de controlador y anotar las definiciones de método también. Spring crea un proxy para tu clase usando Cglib. Sin embargo, sin crear una interfaz personalizada para su controlador, Spring está proxying en groovy.lang.GroovyObject porque todos los objetos Groovy implementan esa interfaz de forma predeterminada.

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!"; 
    } 
} 
6

Con todo el debido respeto a Ben (con quien trabajo), el problema aquí no es que Spring esté creando un proxy cglib. Por el contrario, está creando un proxy dinámico JDK (o basado en interfaz). Este método de creación de proxies solo puede implementar métodos declarados en las interfaces implementadas del objetivo. De hecho, quiere Spring para crear un proxy cglib, que crea un proxy que es una subclase del objeto de destino y, por lo tanto, puede recrear todos sus métodos públicos. A menos que especifique lo contrario, Spring creará un proxy cglib si el objeto objetivo no implementa ninguna interfaz, y un proxy basado en interfaz de lo contrario. Dado que todos los objetos de Groovy implementan GroovyObject, obtendrá un proxy basado en la interfaz, incluso si no implementó explícitamente ninguna interfaz en su controlador Groovy. La solución de Ben es correcta, ya que si creas una interfaz con todos tus métodos de control, obtendrás el comportamiento esperado. Una alternativa es crear un BeanFactoryPostProcessor que instruya a Spring para crear proxies cglib para las clases que implementan GroovyObject y solo GroovyObject. Aquí está el código:

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

Solo incluye un bean de este tipo en tu contexto, y listo! Puede tener controladores Groovy sin necesidad de definir interfaces.

+0

sugiero agregar la definición de frijol a su respuesta. – Zeki

2

I beg to different. No hay necesidad de implementar una interfaz. El problema aquí es que el valor predeterminado AnnotationMethodHandlerAdapter no lee las anotaciones de los proxies. Por lo tanto, tendríamos que crear este proxy consciente AnnotationMethodHandlerAdapter que extiende el predeterminado AnnotationMethodHandlerAdapter de la primavera. También necesitamos instanciar un bean para este ProxyAwareAnnotationMethodHandlerAdapter en el archivo Spring Configuration xml. Nota: Esta función no está disponible en Spring 3.x pero, dado que la primavera 4.0 es compatible con beans groovy, esta característica debe estar cubierta.

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

    } 

El archivo XML de configuración de primavera debe tener lo siguiente. En lugar de crear un bean de AnnotationMethodHandlerAdapter, debemos crear un bean ProxyAwareAnnotationMethodHandlerAdapter.

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

También Spring analiza el archivo XML de configuración utilizando un analizador SAX (basado en la ocurrencia del evento). Por lo tanto, para que la primavera comprenda las anotaciones dentro de las secuencias de comandos groovy, los beans fritos (usando la etiqueta) se deben crear después de ProxyAwareAnnotationMethodHandlerAdapter.

esperanza de ayuda

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

+0

¡Impresionante! Esa clase me alegraba el día. Ahora tengo controladores y servicios geniales. Gracias – Jason

Cuestiones relacionadas